3
$\begingroup$

Counter (CTR) mode, which is a block cipher mode of operation, has some desirable qualities (no padding, parallel encryption and decryption), but at the cost of failing badly when non-unique counter blocks (the nonce combined with the counter that acts as input to the block cipher) are used. I'd like to share my ideas about how the counter block can be made reasonably unique even when there is application state loss (reinstalls, etc.) that may otherwise cause non-unique nonces. If my ideas turn out to be reasonable maybe they'll be helpful to others.

According to appendix B of the NIST guidelines the counter block should either be a simple counter that is incremented without concern for message boundaries (the "first approach") or it should be a combination of a nonce that is changed with each message and a counter (the "second approach"). Either approach is sound, but what if the history of either the counter and or the nonce is lost due to an application reinstall or other reasons?

For a cipher with a 16 byte block size, such as AES, I'm proposing a counter block that has the following layout:

NNNNNN0CCCCCCCCC

The six "N"s are the nonce, followed by "0", a zero byte, and finally the nine "C"s for the counter bytes. Consider the following pseudo code for getting the nonce:

static last_nonce = 0
synchronized get_nonce()
    if (last_nonce == 0)
        last_nonce = get_last_nonce_from_db() // returns 0 on failure
    local nonce = last_nonce + 1
    local since_epoch = get_time_since_epoch_in_msecs()
    if (nonce < since_epoch)
        nonce = since_epoch
    last_nonce = nonce
    return nonce

For each new message the counter ("C") bytes are random. The increment function simply treats the entire 16 byte counter block as a 16 byte big endian integer where the right most bytes are incremented first. This is the way Java's CounterMode.increment() seems to do it. This approach should have the following properties:

  1. In the normal case where the database is intact the nonce is always unique. In this case the "0" assures that it's possible for the message to be at least 256*10 - 256*9 blocks long before colliding with the next possible nonce.
  2. If the database is reset (this could be restoring the database from a backup, reverting a snapshot on the database system, etc., combined with restarting the application) the system time should assure unique nonces.
  3. If both the database is reset and the system time is reverted the nine random counter bytes should assure that the odds of two one block messages colliding (having a counter block in common) with the same nonce is only one in 256**9. As the messages get longer the odds get worse.

So it should work correctly in the normal case, but still behave reasonably in the degraded case.

I know this was long, but I wanted to explain it clearly. I also realize that some of this leverages existing concepts (I think getting a unique incrementing value the way I did with the system time is similar to what's often done for unique identifiers), but I'm interested in how these concepts can be applied to CTR.

$\endgroup$

2 Answers 2

4
$\begingroup$

Relying solely on randomization for the block counter is actually more likely to cause a nonce collision in case of a system time reset. This only gets worse as the message length increases. This is further exacerbated if the PRNG takes the system time as input, or does not have enough seed entropy. There is also no reason for the static 0 byte in the nonce.

How about a 48-bit time(in ms) counter, a 48-bit random value, and a 32-bit counter than increments from 0 for each block of the data?

This gives 9000 years of unique nonces each millisecond, a 280 trillion to 1 chance of a random collision in case of a system time reset, and a 64GB max data limit per nonce. This also makes the code for nonce incrementation more simple since it starts at 0 and is only a 32-bit unsigned integer.

Values above are approximations, but close and provide a reasonable example of the periods with those given bit sizes.

$\endgroup$
8
  • $\begingroup$ Thanks for your feedback. When you talk about the system time being reset do you mean that the nonce stored in the database is also lost (my case #3)? $\endgroup$ Commented Oct 6, 2013 at 1:16
  • $\begingroup$ A time reset may be malicious, such as someone intercepting NTP traffic to force a nonce overlap. If the prior timecode is stored in the database, the scheme should detect this and warn you. If the database is corrupted or lost, a time reset attack is still valid, and will be more effective; at that point you are relying on the random value in the nonce as your primary means of preventing nonce reuse. $\endgroup$ Commented Oct 6, 2013 at 1:21
  • $\begingroup$ Good point about the malicious NTP resets. And I agree that you don't want your PRNG to be correlated with the time since it would redundant, but a good PRNG should get bytes from /dev/urandom, or something like that. Your proposed system is good too - good odds in exchange for fewer blocks (2**32) that can be encrypted per message. $\endgroup$ Commented Oct 6, 2013 at 1:32
  • $\begingroup$ As to the point of the zero ("0") byte - my goal was to make the system rock solid in the functioning case (#1 and #2), but to work somewhat well in the degraded case (#3). In the functioning case the zero byte guarantees a minimum number (25610 - 2569) of blocks that can be encrypted before running into the next possible nonce. $\endgroup$ Commented Oct 6, 2013 at 1:34
  • $\begingroup$ Having a fixed byte just means that something else is 256 times smaller than it could be, and does not provide any guarantee of anything if your nonce incrementation does not overflow into that byte. With a 9 byte incrementation you are already at 67 million terabytes per unique encrypted message BEFORE it overflows into the 0. $\endgroup$ Commented Oct 6, 2013 at 8:09
2
$\begingroup$

An even more robust approach could be to use something like SIV mode (RFC 5297) with a nonce composed of a timestamp and a random value (and possibly, if practical, a message number).

Basically, when encrypting a message, SIV mode first computes a MAC of the plaintext and the nonce (and any other associated data) using a modified form of CMAC, and then uses the output as the initial counter value for CTR mode encryption. After decryption, the MAC is recalculated and compared against the value included with the message to verify message integrity.

Thus, SIV mode, used in the manner suggested above, provides the following advantages:

  • SIV is an authenticated encryption mode, and thus protects not just confidentiality, but also message integrity. This is generally a highly desirable feature.

  • Because the nonce is fed through CMAC (along with the plaintext) to derive the IV, it can have any length you want. This frees you from having to worry about security tradeoffs between its component parts. Want to use a 64-bit timestamp, a 128-bit random number and a 32-bit message number? Go right ahead! You can also include things like sender and received IDs in the associated data to further reduce the chance of collision, and also to protect against "mirror" replay attacks.

  • SIV is designed to be "maximally misuse-resistant", meaning that, even if the entire nonce value is accidentally reused, the IVs for two different messages will not be the same (except by chance, with negligible probability). Thus, the only additional information an adversary can learn due to nonce reuse is whether or not two messages using the same nonce are exactly identical. This is much safer than plain CTR mode, where nonce reuse is generally catastrophic.

All that said, SIV mode does have one major shortcoming: it is an intrinsically serial two-pass encryption mode, meaning that the plaintext must be processed twice, and the second (encryption) pass cannot begin before the first (MAC) pass is done. Thus, SIV is generally not as fast or parallelizable as plain CTR mode, or even some other AE modes like OCB, GCM or Poly1305. It is also not an "on-line" mode, i.e. it cannot be used to encrypt a long data stream at the same time as it is being transmitted, although this may, to some extent, be worked around by splitting the stream into reasonable-sized segments (which is a good idea for AE modes anyway, since it allows authentication of partial streams).

$\endgroup$
1
  • 1
    $\begingroup$ Should be noted though that even if you're using something crazy like a 1024-bit random number as your nonce in SIV mode you'll still only have as much entropy as the block size of the underlying cipher allows since it will be compressed into a MAC of that size. $\endgroup$
    – Raniz
    Commented Jun 5, 2015 at 6:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.