Python Question

ValueError: AES key must be either 16, 24, or 32 bytes long PyCrypto 2.7a1

I'm making programm for my school project and have one problem above.
Here's my code:

def aes():
#aes
os.system('cls')
print('1. Encrypt')
print('2. Decrypt')

c = input('Your choice:')

if int(c) == 1:
#cipher
os.system('cls')
print("Let's encrypt, alright")
print('Input a text to be encrypted')
text = input()

f = open('plaintext.txt', 'w')
f.write(text)
f.close()

BLOCK_SIZE = 32
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
secret = os.urandom(BLOCK_SIZE)

f = open('aeskey.txt', 'w')
f.write(str(secret))
f.close()

f = open('plaintext.txt', 'r')
privateInfo = f.read()
f.close()

cipher = AES.new(secret)

encoded = EncodeAES(cipher, privateInfo)

f = open('plaintext.txt', 'w')
f.write(str(encoded))
f.close()
print(str(encoded))

if int(c) == 2:
os.system('cls')
print("Let's decrypt, alright")

f = open('plaintext.txt','r')
encryptedString = f.read()
f.close()

PADDING = '{'
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
encryption = encryptedString

f = open('aeskey.txt', 'r')
key = f.read()
f.close()

cipher = AES.new(key)
decoded = DecodeAES(cipher, encryption)

f = open('plaintext.txt', 'w')
f.write(decoded)
f.close()

print(decoded)


Full error text:

Traceback (most recent call last): File "C:/Users/vital/Desktop/Prog/Python/Enc_dec/Enc_dec.py", line 341, in aes()
File "C:/Users/vital/Desktop/Prog/Python/Enc_dec/Enc_dec.py", line 180, in aes cipher = AES.new(key)
File "C:\Users\vital\AppData\Local\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\AES.py", line 179, in new return AESCipher(key, *args, **kwargs)
File "C:\Users\vital\AppData\Local\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\AES.py", line 114, in init blockalgo.BlockAlgo.init(self, _AES, key, *args, **kwargs)
File "C:\Users\vital\AppData\Local\Programs\Python\Python35-32\lib\site-packages\Crypto\Cipher\blockalgo.py", line 401, in init self._cipher = factory.new(key, *args, **kwargs)
ValueError: AES key must be either 16, 24, or 32 bytes long

Process finished with exit code 1


What am I doing wrong?

Answer

The error is very clear. The key must be exactly of that size. os.urandom will return you the correct key. However this key is a bytes (binary string value). Furthermore, by using str(secret), the value of repr(secret) is written into the file instead of secret.

What is more confusing is that AES.new allows you to pass the key as Unicode! However, suppose the key was the ASCII bytes 1234123412341234. Now,

f.write(str(secret))

will write b'1234123412341234' to the text file! Instead of 16 bytes, it now contains those 16 bytes + the b, and two ' quote characters; 19 bytes in total.

Or if you take a random binary string from os.urandom,

>>> os.urandom(16)
b'\xd7\x82K^\x7fe[\x9e\x96\xcb9\xbf\xa0\xd9s\xcb'

now, instead of writing 16 bytes D7, 82,.. and so forth, it now writes that string into the file. And the error occurs because the decryption tries to use

"b'\\xd7\\x82K^\\x7fe[\\x9e\\x96\\xcb9\\xbf\\xa0\\xd9s\\xcb'"

as the decryption key, which, when encoded as UTF-8 results in

b"b'\\xd7\\x82K^\\x7fe[\\x9e\\x96\\xcb9\\xbf\\xa0\\xd9s\\xcb'"

which is a 49-bytes long bytes value.


You have 2 good choices. Either you continue to write your key to a text file, but convert it to hex, or write the key into a binary file; then the file should be exactly the key length in bytes. I am going for the latter here:

Thus for storing the key, use

    with open('aeskey.bin', 'wb') as keyfile:
        keyfile.write(secret)

and

    with open('aeskey.bin', 'rb') as keyfile:
        key = keyfile.read()

Same naturally applies to the cipher text (that is the encrypted binary), you must write and read it to and from a binary file:

    with open('ciphertext.bin', 'wb') as f:
        f.write(encoded)

and

    with open('ciphertext.bin', 'rb') as f:
        encryptedString = f.read()

If you want to base64-encode it, do note that base64.b64encode/decode are bytes-in/bytes-out.

By the way, plaintext is the original, unencrypted text; the encrypted text is called ciphertext. AES is a cipher that can encrypt plaintext to ciphertext and decrypt ciphertext to plaintext using a key.

Despite these being called "-text" neither of them is textual data per se, as understood by Python, but they're binary data, and should be represented as bytes.

Comments