IV api broken from 2.5 -> 2.6

Bug #1014715 reported by A Peacock
26
This bug affects 5 people
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 = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
ciphertext1 = aes.encrypt(plaintext1)

IV2 = '2222222222222222'
aes.IV = IV2
plaintext2 = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
ciphertext2 = aes.encrypt(plaintext2)

aes.IV = IV1
assert plaintext1 == aes.decrypt(ciphertext1)

aes.IV = IV2
assert plaintext2 == aes.decrypt(ciphertext2)

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 = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
ciphertext1 = aes.encrypt(plaintext1)

IV2 = '2222222222222222'
aes = AES.new(key, AES.MODE_CBC, IV=IV2)
plaintext2 = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
ciphertext2 = aes.encrypt(plaintext2)

aes = AES.new(key, AES.MODE_CBC, IV=IV1)
assert plaintext1 == aes.decrypt(ciphertext1)

aes = AES.new(key, AES.MODE_CBC, IV=IV2)
assert plaintext2 == aes.decrypt(ciphertext2)

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_known_initial_IV (because of the above bug).

Revision history for this message
Legrandin (gooksankoo) wrote :

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).

Revision history for this message
Darsey Litzenberger (dlitz) wrote :

I agree that using CBC as described here is inappropriate. (Seriously, CBC mode is very fragile. You should not use it for anything except encrypting entire plaintexts at once with a random IV. If you want to muck about with fancy use cases, use something like CTR mode or XTS mode.)

That said, I think PyCrypto should have an API for reinitializing a block cipher with a new IV without necessarily running the key-scheduling algorithm again (especially for ciphers like Blowfish, which have a slow KSA), although I'm not sure what this API should look like. It's great to *say* that people should use XTS mode, but PyCrypto doesn't currently support XTS mode, ESSIV mode, or any of the fancy modes of operation used for disk encryption. Having some way to manipulate the IV from Python code would make it easier for researchers to implement some of these extra modes of operation in Python code.

PyCrypto has historically been useful as a tool that allowed crypto researchers to experiment with, and I'd like us to retain that ability where it makes sense.

Revision history for this message
Darsey Litzenberger (dlitz) wrote :

I'm going to mark this as a duplicate of https://bugs.launchpad.net/pycrypto/+bug/241117, which more-or-less asks for the same thing.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.