Stephen Sprunk wrote:

In the specification of CTR mode, as proposed for AES, you will find the
statement "The number /nonce/ is incremented following each encryption."  I
interpreted this to mean that the top 2^64 bits are to be incremented for
each successive block, and this is how I implemented the code.

I assume that 'number /nonce/' should mean the result of the concatenated parts of the IV. In case of the AES-CTR IPSec draft, you get the IV by concatenating a msg number, a nonce, and the counter (at start initialized to 0). You can then safely incerement the 128 bit IV, *unless* you overflow the counter part (the lower 32 bits of the IV, according to the AES-CTR IPSec draft).

Further review has indicated everyone else seems to think this means the
nonce is incremented only between streams and it's the counter that is
incremented between blocks;


If you have a message number in the IV, you'd naturally incement the message number for each message (each message consisting of an arbitrary number of blocks), but then you'd also clear the counter to 0 and maybe even choose a new random nonce part. In the Schneier / Fergusson example, a 48 bit message number, a 16 bit random part and a 64 bit counter are used. When you start a new message, from my understanding you'd clear bits 0-63, and increment the msg number spanning bits 80-127. Probably, if your protocol or format allows for it, you'd also choose a new nonce (i.e. fill bits 64-79 with new random data). Since the recepient of the encryptded data can't guess the 16 bit random part if it changes, it is necessary to prepend the new IV, and accomodate for that in the message format or communication protocol. For online transmissions, it would be reasonable to start a re-keying handshake for this.

On the matter of overflowing the lower 64 bits, this is not specifically
addressed in the submission (I think they assumed nobody would exceed 2^64
blocks per stream, which is reasonable) and thus I think wrapping is the
correct failure.


I'm not sure what you mean with 'the correct failure', but the clean way to handle a counter overflow would be to fail / report an error, instead of continuing to encrypt. But this is also a matter of interoperability. Since you should not wrap the counter around (as it would offer a weakness for attackers), nobody who is trying to decrypt the data would expect to allow for wrapping. On the other hand, the same function is used for decrypting and encrypting, and when decrypting, failing might be worse than continuing.

This discussion is academic for 64 bit counters, of course, as you probably won't see a 64 bit counter wrap very often. With a 32 bit counter however, the risk is a lot higher. Unfortunately, the current basic crypto functions offered by OpenSSL don't support returning an error condition, so 'failing' is not an option anyway.

To increment the nonce introduces a more insidious failure
mode where the user unintentionally reuses nonces.

It is debatable whether wrapping the counter would be less bad than incrementing the nonce part. A wrapping counter is more predictable for an attacker (as it would occur *always* when the communication exceeds 2**32 or 2 **64 blocks) than an unlucky nonce choice. In other words: If we let the counter wrap, the user is always affected when the counter overflows, and the weak spot is predictable. If we allow to increment the nonce instead of wrapping the counter, the user is only affected when the counter overflows, *and* he continues to reuse the same key but chooses a new nonce, *and* the choosen nonce value collides with the incremented nonce.

Other specifications have suggested that nonce be of unspecified length, not
exactly 64 bits; this implies that the lower bits wrap in an overflow
condition, otherwise there would be no need to specify a boundary between
the different parts of counter at all.

I think the intention of the boundary for the counter part is NOT to allow for wrapping, but to have a mechanism in place to ensure that a single IV (nonce + counter) is not used twice. If you just seeded the whole 128 bits with random data, and incremented them as if the counter was 128 bit, *and* you were using the same symmetric key for more than one message / transmission, you could be unlucky (most likely if your PRNG is suboptimal) and generate an IV that you've used already because of the counting. Separating the counter and the nonce leads to having a minimum distance of 32 or 64 bits between IVs, when you are careful to always use a fresh random nonce part. Implementing a mechanism to avoid collisions of IVs would be cumbersome otherwise.

Unfortunately, implementing this (as
has been requested) would require more significant changes to the API than I
know how to make,as passing parameters to a mode doesn't appear to be
supported today.

The easiest way to go about it would be to increment the user supplied IV by 1 for each encrypted block, and leave it to the user of this function to make sure that no overflow in the counter can occur. This obligation to the programmer should be written somewhere in big letters ;-) (i.e. DON'T USE COUNTER MODE TO ENCRYPT MORE THAN 2**32 [or 2**64, depending on the counter size] BLOCKS WITH THE SAME KEY!). Higher level routines, i.e. the SSL BIO, should avoid counter overflows automatically for the user by initiating a rehandshake.

- David

______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to