Search code examples
pythonencryptionpycryptodome

pycryptodome & AES CBC : failing to decrypt a string


I'm trying to implement a simple encryption-decryption script with pycryptodome and AES-CBC, that is:

  • no iv,
  • no padding, therefore the string to encrypt is stripped do 16 characters
  • key is not random and is a fixed string. However I fail by decrypting the message. Here is the script:
from Crypto.Cipher import AES
from Crypto import Random
#import itertools

plain_text = "This is the text to encrypt"
key = "0361231230000000"

def encrypt(plain_text, key):
    key = bytes(key, "UTF-8")
    cipher = AES.new(key, AES.MODE_CBC)
    print("Encryption Cipher: ", cipher)
    # When there is no padding, the block size must equal the cipher length
    # Padding is necessary for texts with length different from 16 bytes
    cbytes = cipher.encrypt(bytes(plain_text[:16], "UTF-8"))
    return cbytes


def decrypt(enc_text):
    k = bytes(key, "UTF-8")
    cipher = AES.new(k, AES.MODE_CBC)
    print("Decryption Cipher: ")
    return cipher.decrypt(enc_text).decode("UTF-8")


if __name__ == "__main__":
    enc_text = encrypt(plain_text, key)
    print(enc_text)

    dec_text = decrypt(enc_text)
    print(dec_text)

The error message is the following one: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

If in the decryption process I replace "UTF-8" by "Latin-1", the output does not match:

Decryption Cipher: 
Ï(¨e¨^î

What did I miss ?


Solution

  • Unlike ECB, CBC does require an initialization vector. As the documentation says:

    If [iv argument] not provided, a random byte string is generated (you must then read its value with the iv attribute).

    To apply that to your code:

    from Crypto.Cipher import AES
    from Crypto import Random
    
    plain_text = "This is the text to encrypt"
    key = b"0361231230000000"
    
    def encrypt(plain_text, key):
        cipher = AES.new(key, AES.MODE_CBC)
        b = plain_text.encode("UTF-8")
        return cipher.iv, cipher.encrypt(b)
    
    def decrypt(iv, enc_text):
        cipher = AES.new(key, AES.MODE_CBC, iv=iv)
        return cipher.decrypt(enc_text).decode("UTF-8")
        
    if __name__ == "__main__":
        iv, enc_text = encrypt(plain_text[:16], key)
        dec_text = decrypt(iv, enc_text)
        print(dec_text)