As3Crypto (flex) with PyCrypto - how to make AES work? - python

I have a client in Flex and a server in Python and i'm trying to make AES work between them but for some reason it doesn't work.
My server code:
import sys
from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
def encrypt(str):
cipher = AES.new(unhexlify('some 64 byte key here'), AES.MODE_CBC, '16 byte iv')
hex_str = hexlify(cipher.encrypt(pad(str)))
return hex_str
My client code:
static public function decrypt(txt:String) : String
{
var k:String = "some 64 byte key here";
var pad:IPad = new PKCS5();
var mode:ICipher = Crypto.getCipher("aes-cbc", Hex.toArray(k), pad);
pad.setBlockSize(mode.getBlockSize());
var ivmode:IVMode = mode as IVMode;
ivmode.IV = Hex.toArray(Hex.fromString("16 byte iv"));
var data:ByteArray = Hex.toArray(Hex.toString(txt));
mode.decrypt(data);
return Hex.fromArray(data);
}
It seems like a simple case but I'm doing something wrong. What is it?
BTW: I get RangeError: Error #2006: The supplied index is out of bounds from mode.decrypt(data);

I finally managed to get it to work.
The client code should look like this:
static public function decrypt(txt:String) : String
{
var k:String = "some 64 byte key here";
var pad:IPad = new PKCS5();
var mode:ICipher = Crypto.getCipher("aes-cbc", Hex.toArray(k), pad);
pad.setBlockSize(mode.getBlockSize());
var ivmode:IVMode = mode as IVMode;
ivmode.IV = Hex.toArray(Hex.fromString("16 byte iv"));
var data:ByteArray = Hex.toArray(txt);
mode.decrypt(data);
return Hex.toString(Hex.fromArray(data));
}

Related

Decrypt Fernet Encrypted Text(PYTHON) in SWIFT

I have generated an Encrypted Text is Python using cryptography
from cryptography.fernet import Fernet
message = "my deep dark secret".encode()
f = Fernet(key)
encrypted = f.encrypt(message)
# decrypting
from cryptography.fernet import Fernet
encrypted = b"...encrypted bytes..."
f = Fernet(key)
decrypted = f.decrypt(encrypted)
ENCRYPTION INFO:
KEY: b'3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE='
ENC_MESSAGE: b'gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs='
Now I'm trying to Decrypt it in Swift but to no luck.
So Far I've Tried CryptoSwift with the following:
func testdec(){
let str = "3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE="
let ba = "gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs="
let encodedString = Base64FS.decodeString(str: String(str.utf8))
print(encodedString.count)
let first4 = String(ba.prefix(25))
let start = first4.index(first4.startIndex, offsetBy: 9)
let end = first4.index(first4.endIndex, offsetBy: 0)
let iv = String(first4[start..<end])
let starta = ba.index(ba.startIndex, offsetBy: 25)
let enda = ba.index(ba.endIndex, offsetBy: -32)
let cipher_text = String(ba[starta..<enda])
let cipher_text_bt: [UInt8] = [UInt8](base64: cipher_text)
print(cipher_text)
print(iv)
let cipher_text_bta: [UInt8] = [UInt8](base64: ba)
// print(encodedString.bytes.count)
// let key_bta: [UInt8] = [UInt8](base64: "RgSADaf8w4v9vokuncyzWRbP5hkdhXSETdxIHLDHtKg=")
// let iv_bt: [UInt8] = [UInt8](base64: "7KUDrsPmb28KQqOWv00KXw==")
// let cipher_text_bt: [UInt8] = [UInt8](base64: "gAAAAABhBQ837KUDrsPmb28KQqOWv00KX2KjsP2ar6lHLqIPUKSvF1WHiruquG-tiAEkrCZZbm-lFR9ZwxsqVcXovmQ3Hv6pWw==")
do{
print("A")
let aes = try AES(key: encodedString, blockMode: CBC(iv: iv.bytes), padding: .pkcs7)
print("B")
let cipherTexta = try aes.decrypt(cipher_text_bt)
print(cipherTexta)
}catch{
print(error)
}
}
OUTPUT:
16
WaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxw
RBGKSwa7AluNJYhw
A
B
invalidData
Any Help would be appreciated
I've managed to get your cipher text decrypted using only Apple provided sources. If you support iOS 13 and up, I suggest you use CryptoKit to verify the HMAC, but for now, I've adopted a full CommonCrypto solution.
First a minor extension to create Data from base64 URL strings.
import Foundation
import CommonCrypto
extension Data {
init?(base64URL base64: String) {
var base64 = base64
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
if base64.count % 4 != 0 {
base64.append(String(repeating: "=", count: 4 - base64.count % 4))
}
self.init(base64Encoded: base64)
}
}
The decrypt function is a bit obscure, but it supports the very old CommonCrypto syntax. withUnsafeBytes syntax would be cleaner, but this is a quick workaround.
func decrypt(ciphertext: Data, key: Data, iv: Data) -> Data {
var decryptor: CCCryptorRef?
defer {
CCCryptorRelease(decryptor)
}
var key = Array(key)
var iv = Array(iv)
var ciphertext = Array(ciphertext)
CCCryptorCreate(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), &key, key.count, &iv, &decryptor)
var outputBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(decryptor, ciphertext.count, false))
CCCryptorUpdate(decryptor, &ciphertext, ciphertext.count, &outputBytes, outputBytes.count, nil)
var movedBytes = 0
var finalBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(decryptor, 0, true))
CCCryptorFinal(decryptor, &finalBytes, finalBytes.count, &movedBytes)
return Data(outputBytes + finalBytes[0 ..< movedBytes])
}
Then the HMAC. I suggest you use CryptoKit if you can. This function is of course fixed, there might be ways to make this dynamic. For Fernet however, only SHA256 is supported.
func verifyHMAC(_ mac: Data, authenticating data: Data, using key: Data) -> Bool {
var data = Array(data)
var key = Array(key)
var macOut = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), &key, key.count, &data, data.count, &macOut)
return Array(mac) == macOut
}
All of that together comes down to the following code. Note that I do not check the version and/or timestamp, which should be done according to the spec.
let fernetKey = Data(base64URL: "3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE=")!
let signingKey = fernetKey[0 ..< 16]
let cryptoKey = fernetKey[16 ..< fernetKey.count]
let fernetToken = Data(base64URL: "gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs=")!
let version = Data([fernetToken[0]])
let timestamp = fernetToken[1 ..< 9]
let iv = fernetToken[9 ..< 25]
let ciphertext = fernetToken[25 ..< fernetToken.count - 32]
let hmac = fernetToken[fernetToken.count - 32 ..< fernetToken.count]
let plainText = decrypt(ciphertext: ciphertext, key: cryptoKey, iv: iv)
print(plainText, String(data: plainText, encoding: .utf8) ?? "Non utf8")
print(verifyHMAC(hmac, authenticating: version + timestamp + iv + ciphertext, using: signingKey))

Generating a random key in AES using aes python package

I'm making a program that encrypts a text with aes package. I generated a random key using secrets.token_bytes(16) and i tried secrets.token_hex(16) too.
But every time i run a code i get an error that says TypeError: unsupported operand type(s) for >>: 'bytes' and 'int' and this part of aes package is shown
def __toarray(self, ints):
if type(ints) == list and len(ints) == 16:
return ints
arr = [((ints >> (8 * (15 - i))) & 0xFF) for i in range(16)]
I don't know how can i fix this. Is there any way to generate an random key?
This is how my code looks like:
import secrets
import aes
text = "ala ma kota a marysia ma rysia"
key = secrets.token_bytes(16)
key_list = list(key)
key_hex = []
for i in range(len(key_list)):
key_hex.append(hex(key_list[i])
def padding_and_hex(txt):
txt_hex = []
txt_dec = list(txt)
if len(txt_dec) % 16 != 0:
for i in range(16 - len(txt_dec) % 16):
txt_dec.append(0)
for i in range(len(txt_dec)):
txt_hex.append(hex(txt_dec[i]))
return txt_hex
text_to_encrypt = padding_and_hex(text)
how_many_times = int(len(text_to_encrypt)//16)
for i in range(how_many_times):
cipher = aes.aes(key)
cipher.encrypt(text_to_encrypt[i:i+16])
i += 16

getting the error "Invalid DES key size. Key must be exactly 8 bytes long" while encrypting message in python

I have the below java code to encode a string with a pass key
public static String encrypt(String message, String passkey) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest(passkey.getBytes("utf-8"));
final byte[] keyBytes = ( byte[])resizeArray(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
String encryptedString = Base64.encodeBase64String(cipherText);
return encryptedString;
}
Now I converted the same code into python(Python 2.7), and I tried as below.
def encrypt(message, passkey):
hash_object = hashlib.sha1(passkey.encode("utf-8"))
digested_passkey = hash_object.digest() //hashing
key24 = "{: <24}".format(digested_passkey) // for resizing the byte array to size 24
import pyDes
des = pyDes.des(key24);(at this line I m getting the error "Invalid DES key size. Key must be exactly 8 bytes long".
message = message.encode('utf-8')
message = message + (16 - len(message) % 16) * chr(16 - len(message) % 16) // this is for padding
iv = Random.new().read(AES.block_size)
cipher = AES.new(des, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(message))
At the line des = pyDes.des(key24), I am getting the error "Invalid DES key size. Key must be exactly 8 bytes long."
The passkey that I sent as parameter is "f!16*hw$sda66"
Can anyone please let me know if there is anything wrong with the line
des = pyDes.des(key24)
I think the reason you are getting this error is because the Class initialisation method is expecting the key to be exactly 8, if it's anything else it raises the error you are seeing, this is the init of the class you are calling from pyDes:
# Initialisation
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
# Sanity checking of arguments.
if len(key) != 8:
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
If you do this for debugging:
def encrypt(message, passkey):
hash_object = hashlib.sha1(passkey.encode("utf-8"))
digested_passkey = hash_object.digest() //hashing
key24 = "{: <24}".format(digested_passkey)
print len(key24)
You will see the length of the key is 24, which is why I think it is not being accepted.
I might be wrong but at a quick glance that looks like the issue.

Encrypt data using Objective-C and Decrypt in Python

I have the same issue as this question but unfortunately there was no answer on it.
I have the following objective-c code to encrypt using CCCrypt:
(NSData *)doCrypt:(NSData *)data usingKey:(NSData *)key withInitialVector:(NSData *)iv mode:(int)mode error: (NSError *)error
{
int buffersize = 0;
if(data.length % 16 == 0) { buffersize = data.length + 16; }
else { buffersize = (data.length / 16 + 1) * 16 + 16; }
// int buffersize = (data.length <= 16) ? 16 : data.length;
size_t numBytesEncrypted = 0;
void *buffer = malloc(buffersize * sizeof(uint8_t));
CCCryptorStatus result = CCCrypt(mode, 0x0, 0x1, [key bytes], [key length], [iv bytes], [data bytes], [data length], buffer, buffersize, &numBytesEncrypted);
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted freeWhenDone:YES];
}
I use kCCAlgorithmAES128 with kCCOptionPKCS7Padding as options and call the function with [Cryptor doCrypt:data usingKey:key withInitialVector:nil mode:0x0 error:nil];
Now I would like to decrypt it using python and to do so I have the following code:
def decrypt(self, data, key):
iv = '\x00' * 16
encoder = PKCS7Encoder()
padded_text = encoder.encode(data)
mode = AES.MODE_CBC
cipher = AES.new(key, mode, iv)
decoded = cipher.decrypt(padded_text)
return decoded
The PKCS7Encoder looks like this:
class PKCS7Encoder():
"""
Technique for padding a string as defined in RFC 2315, section 10.3,
note #2
"""
class InvalidBlockSizeError(Exception):
"""Raised for invalid block sizes"""
pass
def __init__(self, block_size=16):
if block_size < 2 or block_size > 255:
raise PKCS7Encoder.InvalidBlockSizeError('The block size must be ' \
'between 2 and 255, inclusive')
self.block_size = block_size
def encode(self, text):
text_length = len(text)
amount_to_pad = self.block_size - (text_length % self.block_size)
if amount_to_pad == 0:
amount_to_pad = self.block_size
pad = chr(amount_to_pad)
return text + pad * amount_to_pad
def decode(self, text):
pad = ord(text[-1])
return text[:-pad]
Yet whenever I call the decrypt() function, it returns garbage. Am I missing something or having a wrong option enabled somewhere?
Example in and output:
NSData *keyData = [[NSData alloc] initWithRandomData:16];
NSLog(#"key: %#", [keyData hex]);
NSString *str = #"abcdefghijklmno";
NSLog(#"str: %#", str);
NSData *encrypted = [Cryptor encrypt:[str dataUsingEncoding:NSUTF8StringEncoding] usingKey:keyData];
NSLog(#"encrypted str: %#", [encrypted hex]);
Gives:
key: 08b6cb24aaec7d0229312195e43ed829
str: a
encrypted str: 52d61265d22a05efee2c8c0c6cd49e9a
And python:
cryptor = Cryptor()
encrypted_hex_string = "52d61265d22a05efee2c8c0c6cd49e9a"
hex_key = "08b6cb24aaec7d0229312195e43ed829"
print cryptor.decrypt(encrypted_hex_string.decode("hex"), hex_key.decode("hex"))
Result:
láz
Which is weird, but if dump the hex I get 610f0f0f0f0f0f0f0f0f0f0f0f0f0f0fb02b09fd58cccf04f042e2c90d6ce17a and 61 = a so I think it just shows wrong.
A bigger input:
key: 08b6cb24aaec7d0229312195e43ed829
str: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
encrypted str: 783fce3eca7ebe60d58b01da3d90105a93bf2d659cfcffc1c2b7f7be7cc0af4016b310551965526ac211f4d6168e3cc5
Result:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaôNÍ“ƒ˜�Üšw6C%
Here you see that the a's are printed with garbage... so I assume this is a padding error or something like that
The IV is nill at the iOs side and 16x 0's at the Python side (see the code)
Your decryption: aes_decrypt(pkcs7_pad(ciphertext))
Correct decryption: pkcs7_unpad(aes_decrypt(ciphertext))
It has to be done this way, because AES in CBC mode expects plaintexts of a multiple of the block size, but you generally want to encrypt arbitrary plaintexts. Therefore, you need to apply the padding before encryption and remove the padding after decryption.
Keep in mind that a - (b % a) cannot be 0 for any (positive) value of a or b. This means that
if amount_to_pad == 0:
amount_to_pad = self.block_size
is unreachable code and can be removed. Good thing is that a - (b % a) already does what you wanted to do with the if block.
You also should extend the unpad (decode) function to actually check whether every padding byte is the same byte. You should also check that the every padding byte is not zero or larger than the block size.

Cipher program losing accuracy

I have a program in python that takes two strings. One is the plain text string, another is the cipher key. what it does is go over each of the characters and xors the bits with the cipher characters. But when going back and forth a few of the letter do not seem to change properly. Here is the code:
//turns int into bin string length 8
def bitString(n):
bin_string = bin(n)[2:]
bin_string = ("0" * (8 - len(bin_string))) + bin_string
return bin_string
//xors the bits
def bitXOR(b0, b1):
nb = ""
for x in range(min(len(b0), len(b1))):
nb += "0" if b0[x] == b1[x] else "1"
return nb
//takes 2 chars, turns them into bin strings, xors them, then returns the new char
def cypherChar(c0, c1):
return chr(int(bitXOR(bitString(ord(c0)), bitString(ord(c1))), 2))
//takes s0 (the plaintext) and encrypts it using the cipher key (s1)
def cypherString(s0, s1):
ns = ""
for x in range(len(s0)):
ns += cypherChar(s0[x], s1[x%len(s1)])
return ns
For example sometimes in a long string the word 'test' will cipher back into 'eest', and stuff like that
I have checked over the code a dozen times and I can't figure out whats causing some of the characters to change. Is it possible some characters just behave strangely?
EDIT:
example:
This is a test
Due to the fact that in the last test
Some symbols: !##$%^&*()
were not changed properly
I am retesting
END
using the cipher key : 'cypher key'
translates back to :
This is a test
Due to toe aact that in the last sest
Some symbols: !##$%^&*()
were not changed properly
I am retestiig
END
Sorry it its a little messy, I put it together real quick
from binascii import hexlify, unhexlify
from sys import version_info
def bit_string(string):
if version_info >= (3, 0):
return bin(int.from_bytes(string.encode(), 'big'))
else:
return bin(int(hexlify(string), 16))
def bitXOR_encrypt(plain_text, key):
encrypted_list = []
for j in range(2, len(plain_text)):
encrypted_list.append(int(plain_text[j]) ^ int(key[j])) #Assume the key and string are the same length
return encrypted_list
def decrypt(cipher_text, key):
decrypted_list = []
for j in range(2, len(cipher_text)): #or xrange
decrypted_list.append(int(cipher_text[j]) ^ int(key[j])) #Again assumes key is the same length as the string
decrypted_list = [str(i) for i in decrypted_list]
add_binary = "0b" + "".join(decrypted_list)
decrypted_string = int(add_binary, 2)
if version_info >= (3, 0):
message = decrypted_string.to_bytes((decrypted_string.bit_length() + 7) // 8, 'big').decode()
else:
message = unhexlify('%x' % decrypted_string)
return message
def main():
plain_text = "Hello"
plain_text_to_bits = bit_string(plain_text)
key_to_bits = bit_string("candy")
#Encrypt
cipher_text = bitXOR_encrypt(plain_text_to_bits, key_to_bits)
#make Strings
cipher_text_string = "".join([str(i) for i in cipher_text])
key_string = "".join([str(i) for i in key_to_bits])
#Decrypt
decrypted_string = decrypt("0B"+cipher_text_string, key_string)
print("plain text: %s" % plain_text)
print("plain text to bits: % s" % plain_text_to_bits)
print("key string in bits: %s" % key_string)
print("Ciphered Message: %s" %cipher_text_string)
print("Decrypted String: %s" % decrypted_string)
main()
for more details or example code you can visit my repository either on github
https://github.com/marcsantiago/one_time_pad_encryption
Also, I know that in this example the key is the same length as the string. If you want to use a string that is smaller than the string try wrapping it like in a vigenere cipher (http://en.wikipedia.org/wiki/Vigenère_cipher)
I think you are overcomplicating things:
def charxor(s1, s2):
return chr(ord(s1) ^ ord(s2))
def wordxor(w1, w2):
return ''.join(charxor(w1[i], w2[i]) for i in range(min(len(w1), len(w2))))
word = 'test'
key = 'what'
cyphered = wordxor(word, key)
uncyphered = wordxor(cyphered, key)
print(repr(cyphered))
print(uncyphered)
You get
'\x03\r\x12\x00'
test
There is a fairly good explanation of Python's bit arithmetic in How do you get the logical xor of two variables in Python?
I could find nothing wrong with the results of your functions when testing with your input data and key. To demonstrate, you could try this test code which should not fail:
import random
def random_string(n):
return ''.join(chr(random.getrandbits(8)) for _ in range(n))
for i in range(1000):
plaintext = random_string(500)
key = random_string(random.randrange(1,100))
ciphertext = cypherString(plaintext, key)
assert cypherString(ciphertext, key) == plaintext
If you can provide a definitive sample of plain text, key, and cipher text that fails, I can look further.

Categories

Resources