shad0w_wa1k3r
shad0w_wa1k3r

Reputation: 13372

PyCryptoDome : AES-256 giving different output with same key & data

The following code produces a different ciphertext every time I execute it, which shouldn't happen since the key & data being passed is same for every execution.

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from base64 import b64encode, b64decode

key = '/I02fMuSSvnouuu+/vyyD7NuSEVDB/0gte/z50dM0b4='
data = 'hello world!'

cipher = AES.new(b64decode(key), AES.MODE_CBC)
padded_data = pad(data.encode(), cipher.block_size)
print(b64encode(padded_data))
# b'aGVsbG8gd29ybGQhBAQEBA=='
ciphertext = cipher.encrypt(padded_data)
print(b64encode(ciphertext))
# b'rEHH0MWIWCWUldjYBco9TA=='
ciphertext = cipher.encrypt(padded_data)
print(b64encode(ciphertext))
# b'FTpLrkZttDxMlpre3Kq8qQ=='

I am actually trying to replicate a sample PHP code to Python, the PHP code gives the same output and my Python code gives different outputs, none of which match the PHP one.

Python version 3.6.x
PyCryptoDome version 3.4.7

Upvotes: 3

Views: 10109

Answers (2)

SquareRootOfTwentyThree
SquareRootOfTwentyThree

Reputation: 7776

Every time you generate with Pycryptodome an AES cipher object in CBC mode, a random IV is created and used. It can be accessed as the attribute called iv (for instance cipher.iv).

The unique (and unpredictable) IV achieves the goal of randomizing the output even if the same message is getting encrypted multiple times (with the same key), which is a piece of information an attacker can often take advantage of.

You don't show the PHP code, but if its output does NOT change every time, it means that the IV is fixed and the code has a security vulnerability.

Upvotes: 4

shad0w_wa1k3r
shad0w_wa1k3r

Reputation: 13372

I forgot to pass the iv parameter while creating the cipher object.

It should be something like -

cipher = AES.new(b64decode(key), AES.MODE_CBC, iv=b'0123456789abcdef')

And yeah, as correctly pointed out by Rawing, repeatedly using the same cipher object to encrypt will give different results, but it will always give same output if you reconstruct the cipher object.

cipher = AES.new(b64decode(key), AES.MODE_CBC, iv=b'0123456789abcdef')
padded_data = pad(data.encode(), cipher.block_size)
print(b64encode(padded_data))
# b'aGVsbG8gd29ybGQhBAQEBA=='
ciphertext = cipher.encrypt(padded_data)
print(b64encode(ciphertext))
# b'8G0KL2UiCv7Uo+pKMm9G+A=='
ciphertext = cipher.encrypt(padded_data)
print(b64encode(ciphertext))
# b'tBXcf/Nf6MtxM1ulzNnIlw=='


cipher = AES.new(b64decode(key), AES.MODE_CBC, iv=b'0123456789abcdef')
padded_data = pad(data.encode(), cipher.block_size)
ciphertext = cipher.encrypt(padded_data)
print(b64encode(ciphertext))
# b'8G0KL2UiCv7Uo+pKMm9G+A=='

Upvotes: 3

Related Questions