Discussion:
question about AES-GCM counter implementation
Sid Shetye
2013-03-10 18:56:53 UTC
Permalink
Inside

Org.BouncyCastle.Crypto.Modes.GcmBlockCipher

I see:

private byte[] GetNextCounterBlock()
{
for (int i = 15; i >= 12; --i)
{
if (++_counter[i] != 0) break;
}

var tmp = new byte[BlockSize];
// TODO Sure would be nice if ciphers could operate on int[]
_cipher.ProcessBlock(_counter, 0, tmp, 0);
return tmp;
}

Questions:
1. Why do we only increment the top 4 bytes and not the entire block (...
;i>=0;...)? The early break maintains the performance of doing minimum work
as needed. Granted this hard coded limit is very very high at 2^(32+128)
bytes (32 for 4 bytes and 128 for the block size) but the hard coding just
stood out.
2. Why do we increment the top bytes and not the bottom bytes? I presume
increment should operate on the LSBs. Unless the byte order is flipped ...

Thanks
Sid
Peter Dettman
2013-03-11 04:06:13 UTC
Permalink
Hi Sid,
Incrementing of the counter is described in the spec
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf)
as follows:
"Successive counter values are generated using the function incr(),
which treats the rightmost 32 bits of its argument as a nonnegative
integer with the least significant bit on the right, and increments this
value modulo 2**32. More formally, the value of incr(F || I) is F || (I
+ 1 mod 2**32)."

Whilst counter values should not be reused (GCM limits the message
length to just under 2**32 blocks - see Table 2), they may still "wrap
around", so the loop must be explicitly limited to the last 4 bytes.
Where the nonce is exactly 96 bits, the counter starts at 1, so it won't
overflow, but for
nonces of other lengths the counter is initialised to the output of the
hash function and so the guard is necessary.

Regards,
Pete Dettman
Post by Sid Shetye
Inside
Org.BouncyCastle.Crypto.Modes.GcmBlockCipher
private byte[] GetNextCounterBlock()
{
for (int i = 15; i >= 12; --i)
{
if (++_counter[i] != 0) break;
}
var tmp = new byte[BlockSize];
// TODO Sure would be nice if ciphers could operate on int[]
_cipher.ProcessBlock(_counter, 0, tmp, 0);
return tmp;
}
1. Why do we only increment the top 4 bytes and not the entire block (...
;i>=0;...)? The early break maintains the performance of doing minimum work
as needed. Granted this hard coded limit is very very high at 2^(32+128)
bytes (32 for 4 bytes and 128 for the block size) but the hard coding just
stood out.
2. Why do we increment the top bytes and not the bottom bytes? I presume
increment should operate on the LSBs. Unless the byte order is flipped ...
Thanks
Sid
Sid Shetye
2013-03-12 18:13:07 UTC
Permalink
Thanks Peter, it's been sometime since I read that paper - appreciate the
insight.

I'm also trying to understand some code that probably relates to a use case
that's not obvious to me

In
GcmBlockCipher.DoFinal()
we have, at the end,
Reset(clearMac:false);
Which in turn triggers:
...
if (clearMac)
{
_macBlock = null;
}

if (_initialAssociatedText != null)
{
ProcessAadBytes(_initialAssociatedText, 0,
_initialAssociatedText.Length);
}
...

Question:
1. Why do we care about preserving the _macBlock reference if we're done
with even the final block?
2. Why is there the ProcessAadBytes() again in the Reset() section? I was
under the impression we were done with the AAD at the beginning, inside
Init() itself.

I suspect the above two are related to a specific use case. Would appreciate
your comments.

Thanks
Sid

-----Original Message-----
From: Peter Dettman [mailto:pkd-PQo3LEzKn7yGw+***@public.gmane.org]
Sent: Sunday, March 10, 2013 9:06 PM
To: 'dev-crypto-csharp-***@public.gmane.org'
Subject: Re: [dev-crypto-csharp] question about AES-GCM counter
implementation

Hi Sid,
Incrementing of the counter is described in the spec
(http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-
spec.pdf)
as follows:
"Successive counter values are generated using the function incr(), which
treats the rightmost 32 bits of its argument as a nonnegative integer with
the least significant bit on the right, and increments this value modulo
2**32. More formally, the value of incr(F || I) is F || (I
+ 1 mod 2**32)."

Whilst counter values should not be reused (GCM limits the message length to
just under 2**32 blocks - see Table 2), they may still "wrap around", so the
loop must be explicitly limited to the last 4 bytes.
Where the nonce is exactly 96 bits, the counter starts at 1, so it won't
overflow, but for nonces of other lengths the counter is initialised to the
output of the hash function and so the guard is necessary.

Regards,
Pete Dettman
Peter Dettman
2013-03-13 05:08:19 UTC
Permalink
1. It's there to allow the user to call GetMac() after DoFinal().

2. The general contract of the cipher classes is that calling Reset()
returns a cipher to the same state it was in after the most recent
Init(). DoFinal() also must implicitly Reset() the cipher. It's a sort
of oddity of the AEAD ciphers that AAD is included in the initialisation
parameters, and so it must be processed again, although processing of
AAD could conceivably be made lazy. Note that changes since 1.7 allow
AAD to be streamed to an Snippet IAeadBlockCipher using the
ProcessAadByte/s methods. AAD can even be interleaved with the
plaintext/ciphertext processing, at the cost of a binary field
exponentiation during DoFinal.

Regards,
Pete Dettman
Post by Sid Shetye
Thanks Peter, it's been sometime since I read that paper - appreciate the
insight.
I'm also trying to understand some code that probably relates to a use case
that's not obvious to me
In
GcmBlockCipher.DoFinal()
we have, at the end,
Reset(clearMac:false);
...
if (clearMac)
{
_macBlock = null;
}
if (_initialAssociatedText != null)
{
ProcessAadBytes(_initialAssociatedText, 0,
_initialAssociatedText.Length);
}
...
1. Why do we care about preserving the _macBlock reference if we're done
with even the final block?
2. Why is there the ProcessAadBytes() again in the Reset() section? I was
under the impression we were done with the AAD at the beginning, inside
Init() itself.
I suspect the above two are related to a specific use case. Would appreciate
your comments.
Loading...