0

my goal is to have a very simple AES 128 CBC scheme which encrypts a plaintext and then decrypts it based on a given key in Python. I'm using pycryptodome framework and I couldnt find any documentation with an example of the AES CBC scheme.

Following is my code. The decrypted data is not same as the data before encryption. Will be fantastic if someone can help me identify what is going wrong here.

key = b'Sixteen byte key'
data = 'Jeevan B Manoj'.encode("UTF-8")
data = pad(data,16)
cipher = AES.new(key, AES.MODE_CBC)
print("data before encryption")
print(data)
ciphertext = cipher.encrypt(data)
cipher = AES.new(key, AES.MODE_CBC)
plaintext = cipher.decrypt(ciphertext)
print(plaintext)

2 Answers2

3

As t.m.adam noted, the CBC mode of operation requires an initialization vector (IV) to work. Because the IV is commonly forgotten (also that it has to be unique and unpredictable, e.g. random), Pycryptodome creates a random one when a cipher object is initialized.

The IV must be unique for each encryption and is required for decryption. Common practice (source?) is to put the IV at the start of the ciphertext (the IV does not to need to be secret).

To make your example work:

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

# Do not use raw passwords as keys,
# use a derivation functions to generate keys from them
key = b'Sixteen byte key'  
data = 'Jeevan B Manoj'.encode("UTF-8")
data = pad(data, AES.block_size)
encryptor = AES.new(key, AES.MODE_CBC)
iv = encryptor.IV
decryptor = AES.new(key, AES.MODE_CBC, IV=iv)

ciphertext = encryptor.encrypt(data)
plaintext = decryptor.decrypt(ciphertext)

assert plaintext == data

Important note: The ciphertext and IV must be authenticated for security (so data cannot be tampered with). For that, Pycryptodome offers AEAD modes like EAX and GCM as pointed out by Hans-Peter Jansen on GitHub. For many of them padding is not required.

Rob
  • 26,989
  • 16
  • 82
  • 98
S. Biewald
  • 31
  • 1
  • 1
0

If you use MODE_ECB instead of MODE_CBC it works. I also didn't know what padding routine you are using so I used this one. I have several other examples here: https://github.com/SolarDon/pycryptodome/tree/master/Examples

from Crypto.Cipher import AES
 # Padding for the input string --not related to encryption itself.
BLOCK_SIZE = 16  # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]

key = b'Sixteen byte key'
data = 'Jeevan B Manoj'.encode("UTF-8")
data = pad(data)
cipher = AES.new(key, AES.MODE_ECB) # AES.MODE_CBC
print("data before encryption")
print(data)
ciphertext = cipher.encrypt(data)
cipher = AES.new(key, AES.MODE_ECB) # MODE_CBC
plaintext = cipher.decrypt(ciphertext)
print(unpad(plaintext))
Donald Wagner
  • 129
  • 1
  • 5