Search code examples
pythonpython-3.xaes

python 3 pycrypto iv must be 16 bytes long


so I have been trying to build an AES encryption program based off of the github pycrypto guide link to github however when I go to decode an error shows up:

Traceback (most recent call last):
File "/home/pi/Desktop/aes/newAES.py", line 24, in <module>
    print(decrypt(key,msg,iv))
File "/home/pi/Desktop/aes/newAES.py", line 13, in decrypt
    cipher = AES.new(key,AES.MODE_CFB)
File "/usr/lib/python3/dist-packages/Crypto/Cipher/AES.py", line 94, in new
    return AESCipher(key, *args, **kwargs)
File "/usr/lib/python3/dist-packages/Crypto/Cipher/AES.py", line 59, in __init__
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
File "/usr/lib/python3/dist-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
    self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long

my code is:

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
def encrypt(key,msg):
    if key == 0:
        key=get_random_bytes(16)
        print("key: "+key)
    iv = get_random_bytes(16)
    print('iv: '+str(iv))
    cipher = AES.new(key,AES.MODE_CFB,iv)
    ciphertext= cipher.decrypt(msg)
    return("your encrypted message: "+str(ciphertext))
def decrypt(key,ciphertext,iv):
    cipher = AES.new(key,AES.MODE_CFB)
    msg = cipher.decrypt(ciphertext)
ed = input('(e)ncrypt or (d)ecrypt: ')
if ed=='e':
    key = input('16 digit key: ')
    msg = input('message: ')
    print(encrypt(key,msg))
elif ed =='d':
    key = input('16 digit key: ')
    iv = bytes(input('iv: '),'utf-8')
    msg = bytes(input('encrypted message:'),'utf-8')
    print(decrypt(key,msg,iv))

I would appreciate any help offered on solving this issue, hopefully it isn't some stupid error


Solution

  • The problem with iv is that it consists of random bytes, but it is being read into your program as a string. Calling bytes on that string does not do what you expect.

    >>> iv = b'\xba\x0eyO8\x17\xcf\x97=\xf2&l34#('
    >>> siv = str(iv)
    >>> siv
    "b'\\xba\\x0eyO8\\x17\\xcf\\x97=\\xf2&l34#('"   # Note 'b' is part of the string
    >>> biv = bytes(siv, 'utf-8')
    >>> biv
    b"b'\\xba\\x0eyO8\\x17\\xcf\\x97=\\xf2&l34#('"  # Now there are two 'b's!
    

    You can resolve this by using ast.literal_eval:

    >>> ast.literal_eval(siv)
    b'\xba\x0eyO8\x17\xcf\x97=\xf2&l34#('
    

    Here's a working version of your code - I removed the need to copy/paste iv, but the same observations about inputting bytes applies to the ciphertext.

    import ast
    
    from Crypto.Cipher import AES
    from Crypto.Random import get_random_bytes
    
    
    def encrypt(key, msg):
        iv = get_random_bytes(16)
        cipher = AES.new(key, AES.MODE_CFB, iv)
        ciphertext = cipher.encrypt(msg)    # Use the right method here
        return iv + ciphertext
    
    
    def decrypt(key, ciphertext):
        iv = ciphertext[:16]
        ciphertext = ciphertext[16:]
        cipher = AES.new(key, AES.MODE_CFB, iv)
        msg = cipher.decrypt(ciphertext)
        return msg.decode("utf-8")
    
    
    if __name__ == "__main__":
        ed = input("(e)ncrypt or (d)ecrypt: ")
        if ed == "e":
            key = input("16 digit key: ")
            msg = input("message: ")
            print("Encrypted message: ", encrypt(key, msg))
        elif ed == "d":
            key = input("16 digit key: ")
            smsg = input("encrypted message: ")
            msg = ast.literal_eval(smsg)
            print("Decrypted message: ", decrypt(key, msg))
    

    The code in action:

    (e)ncrypt or (d)ecrypt: e
    16 digit key: abcdabcdabcdabcd
    message: Spam, spam, spam
    Encrypted message:  b'\xa4?\xa9RI>\x1f\xb5*\xb2,NWN\x0c\xfd"yB|\x1f\x82\x96\xd5\xb4\xd4\x1d&\x8bM\xdb\x07'
    
    (e)ncrypt or (d)ecrypt: d
    16 digit key: abcdabcdabcdabcd
    encrypted message: b'\xa4?\xa9RI>\x1f\xb5*\xb2,NWN\x0c\xfd"yB|\x1f\x82\x96\xd5\xb4\xd4\x1d&\x8bM\xdb\x07'
    Decrypted message:  Spam, spam, spam