TL;DR: How PyCryptodome AES context chains messages?
Consider this piece of code:
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
# Create AES-128-CBC context; pad message; encrypt x2.
cipher = AES.new(b'Sixteen byte key', AES.MODE_CBC, b'16-byte init.vec')
pt = pad(b'The quick brown fox jumped over the lazy dog', 16)
print(cipher.encrypt(pt).hex()) # First output: 95c44b...
print(cipher.encrypt(pt).hex()) # Second output: dfa658...
As you can see, same operation with same init. vector gives different results. The cause of this is reusing the AES context (cipher
in this case).
The question is, How PyCryptodome chains encrypted messages to get different result. In other words, how is it described as an abstract algorithm? How to get second output without using built-in chaining?
I already looked at PyCryptodome documentation and even source but didn't find an answer. I think it recreates an IV or something..
As @Topaco stated in comments, PyCryptodome uses last block of previous encryption as IV for next message. It can be demonstrated with this code:
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
cipher = AES.new(b'Sixteen byte key', AES.MODE_CBC, b'16-byte init.vec')
pt = pad(b'The quick brown fox jumped over the lazy dog', 16)
lb = cipher.encrypt(pt)[-16:] # last block of ciphertext
# Reinitialize context - for demonstration purposes
cipher = AES.new(b'Sixteen byte key', AES.MODE_CBC, lb)
print(cipher.encrypt(pt).hex()) # Second output: dfa658...