IV api broken from 2.5 -> 2.6
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
Python-Crypto |
New
|
Undecided
|
Unassigned |
Bug Description
This code worked in pycrypto 2.5:
from Crypto.Cipher import AES
key = 'abcdefghijklmnop'
IV1 = '1111111111111111'
aes = AES.new(key, AES.MODE_CBC, IV=IV1)
plaintext1 = 'aaaaaaaaaaaaaa
ciphertext1 = aes.encrypt(
IV2 = '2222222222222222'
aes.IV = IV2
plaintext2 = 'bbbbbbbbbbbbbb
ciphertext2 = aes.encrypt(
aes.IV = IV1
assert plaintext1 == aes.decrypt(
aes.IV = IV2
assert plaintext2 == aes.decrypt(
As of 2.6, the ability to manipulate IV on an existing AES object (at least in CBC mode) seems to have evaporated, requiring code that looks like this instead:
from Crypto.Cipher import AES
key = 'abcdefghijklmnop'
IV1 = '1111111111111111'
aes = AES.new(key, AES.MODE_CBC, IV=IV1)
plaintext1 = 'aaaaaaaaaaaaaa
ciphertext1 = aes.encrypt(
IV2 = '2222222222222222'
aes = AES.new(key, AES.MODE_CBC, IV=IV2)
plaintext2 = 'bbbbbbbbbbbbbb
ciphertext2 = aes.encrypt(
aes = AES.new(key, AES.MODE_CBC, IV=IV1)
assert plaintext1 == aes.decrypt(
aes = AES.new(key, AES.MODE_CBC, IV=IV2)
assert plaintext2 == aes.decrypt(
Notice that the 2.6 code has to new() up 4 AES objects whereas the old code only needed 1.
The 2.5 example code was written this way as a performance enhancement (imagine instead of 4 ops we have thousands or millions). Since CBC mode is often used for streaming, it makes sense to be able to manipulate the IV in this way without having to create new objects to get around the lack of this functionality in 2.6, doesn't it?
A legitimate use of this pattern: imagine a file stream, encrypted with CBC and a known key/IV pair.
For seeking: because of the way CBC implements IVs, we don't have to save any IV values except for the first -- we just have to decrypt an extra block when we want, so, for example, to seek to position 100, we actually seek to position 80 and decrypt the block 80-96, which will result in garbage but will give us the IV for block 96-112, which we can decrypt correctly now. However, now if we need to seek back to position 0 of the file, there is *no way* to do that without newing up a new AES object, because we don't have a block from negative 16 to 0, and we can't just set IV=the_
PEP 272 says the following about the IV attribute:
"After encrypting or decrypting a string,
this value is updated to reflect the modified feedback text.
It is read-only, and cannot be assigned a new value".
PyCrypto 2.5 was not truly PEP272 compliant in that the IV could be modified.
PyCrypto 2.6 is not truly PEP272 compliant either even if the IV is read-only because it is not updated on encryption or decryption.
Of the two, I find the second non-compliancy better than the first and even better than PEP272,
because in case the crypto implementation was actually via an external engine (OpenSSL, etc),
it is not guaranteed that such engine would expose the updated IV.
Apart from that, I don't personally think that "modifying the IV" is a clear operation,
especially because its behavior is strongly dependent on the type of chaining and therefore error prone.
For the reasons above, I don't think this is really a bug.
In the example application you present, it is customary to use other chaining modes, like CTR and XTS.
They are actually designed on purpose to work with streaming or random access.
CBC is not "often used for streaming" and the way you use it is highly suboptimal (although probably not dangerous).