I am not an expert in AES and moreover I have to deliver the deciphering function to a very limited Python 2 environment.
I have a 32-byte key and a 16-byte initialisation vector along with a 64-byte test cipher message - all of type str
. AES operates in a cipher-block chaining mode.
With pycrypto
it all works and I get a 64-symbol decrypted message of type unicode
.
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_CBC, vector)
decryption = cipher.decrypt(message).decode()
print(decryption)
Unfortunately the pycrypto
itself is not supported in the final environment as I believe it has some fancy compiled dependencies for AES.
There is a purely Python alternative called pyaes
:
import pyaes
cipher = pyaes.AESModeOfOperationCBC(key, vector)
decryption = cipher.decrypt(message[0:16]).decode()
print(decryption)
Notice that I sliced the first 16 bytes and got the first 16 symbols of the decrypted message (of type unicode
) correctly.
However, once I try the full message I get
ValueError: ciphertext block must be 16 bytes
.
Even if I slice message[16:32]
, I get
UnicodeDecodeError: 'ascii' codec can't decode byte 0xb8 in position 0: ordinal not in range(128)
.
So how to use the pyaes
and decrypt longer ciphertexts in my case?
The secret plaintext looks like this
{"valueInt":123, "valueFloat":1.23, "valueString":"123"}
with trailing (padded) spaces.
Possibility 1. The case of the message[16:32] is that the first part of your unencrypted text could have multibyte characters, so after taking 16 bytes, 1+ byte is leaking to the second part of your original/unencrypted string. Though this is only if you are using multibyte characters.
If you are also encrypting them, you could try converting the text to binary first if your encoding is utf8:
cipher.encrypt(mystring.encode('utf8'))
and in reverse
cipher.decrypt(mystring.decode('utf8'))
Possiblity 2. If you are decrypting with the same AESModeOfOperationCFB you should try with a new instance for decryption. The following code fails to decrypt the correct text which contains non valid ascii bytes.
cipher = pyaes.AESModeOfOperationCBC(key, vector)
encrypted = cipher.decrypt('Hello world')
print(encrypted)
# ?Eg?m??K?(|
decrypted = cipher.decrypt(encrypted)
print(decrypted)
# ?XL=?-QE??Y?=k
But if you do this way:
encrypter = pyaes.AESModeOfOperationCBC(key, vector)
encrypted = cipher.encrypt('Hello world !!!!')
print(encrypted)
# ?Eg?m??K?(|
decrypter = pyaes.AESModeOfOperationCBC(key, vector)
decrypted = decrypter.decrypt(encrypted)
# Hello world !!!!
If you are encrypting 2 blocks with same instance and then concatenate them, you need a new instance which decrypt 2 new blocks. Or encrypt/decrypt each block with a new instance each.