This adds the much faster AESNI version of the CCM and CBC encryption/authentication.
Ah, sorry, I did not realize we hadn't defined a CMAC_DIGEST_LEN. Maybe we should. I don't feel strongly about it.
Sure, sizeof(tag) sounds appropriate here.
In general, when something is a local array x[YYY], I prefer using sizeof(x) (or nitems(x)) to the YYY constant (named or not).
Is copying out harmful if there was an error? I suppose it could be misused or accidentally leaked in some malicious way.
So: no objection to the approach you've taken here.
A little more input on aesni_ccm itself for discussion.
Use of this union is somewhat problematic.
It's use is very much in violation of the spirit of the C standard, around what unions are; and what behavior is defined, implementation-defined, or undefined.
I think I'd suggest just typedef __m128i aes_block_t (or removing use of the aes_block_t type entirely) instead of this union. Most of the code is already defined in terms of the __m128i type anyway.
(Style(9) nitpick on the name, anyway: unambiguously says not to define typedefs ending with "_t".)
For example, here it is not clear which member of the union is supposed to be initialized by s0.
And here we reference the union pointer directly for initialization.
Some feedback on the meat of the patch.
In this implementation, I'm not sure the 2-pass decrypt buys us anything. We already pre-allocate space (if input is non-contiguous) — checking the tag first doesn't save us from a DoS in terms of memory use. If the (unexpected case) tag verification fails, we'd need to do extra work to clobber anything we output, maybe? I might be forgetting something about CCM.
This file is more or less new code; it's free to follow style(9) from the beginning.
So nitpicks here: Blank line between variable declarations and first statement; and return (retval);.
ditto the blank line thing
Seems like retval could be named something more descriptive, like B0 or MAC.
And temp_block is maybe auth_block or something (staging_block would match software CBC-MAC).
Does __m128i foo = 0; generate different code than __m128i foo = _mm_setzero_si128();? It isn't important, I'm just vaguely curious.
Edit: Ahhh, __m128i is literally a vector, rather than integral, type, according to the compiler. So it cannot simply be initialized with an integer.
style nit: auth_len > 0, as auth_len is not a boolean
I'm not asking you to change these, but in new code I'd encourage use of the C89 memcpy/memmove/memset macros over the pre-ANSI bcopy etc routines.
(For me as a reviewer, I have to think about destination/source argument order every time I look at one of these b* routines, whereas I don't have to think about memcpy.)
Could just use
What do you mean by calling convention? The immediate function takes a size_t, which can represent larger values.
I understand that internally OCF deals only in int sizes, but I'd suggest either implementing the 3rd mode or asserting we're within range of the 2nd mode anyway.
be16enc(&temp_block, 0xfffe); be32enc((char *)&temp_block + 2, auth_len);
style nit: sizeof(temp_block), for consistency with the object we're acting on
usually spelled "4 GB" or "4 gigabytes" but not "4gbytes"
I don't know what these pre-processor error directives (there are a few) are intended to be, but probably remove them before commit. (Figure out how to incorporate them into comments or code, if necessary.)
Ugh. I know you just inherited this mess from GCM, but what an obnoxious API :P.
Is it impossible to generate a tag for AAD without any ciphered input?
You don't need explicit_bzero for ordinary initializations. Here _mm_setzero_si128 would be fine. explicit_bzero is for deleting private keys when you release memory.
Also, current_block is never used?
So really last_block is the rolling MAC value. I'd suggest a name incorporating MAC, for clarity.
Right, ok, we're generating a keystream from an encrypted series of counter blocks.
style nit: Usually we at declare variables at the beginning of some scope, if not function scope.
This step doesn't have any dependency on the data, right? We could do it earlier, as soon as we have saved a copy in s_x for use in generating the keystream.
style: Empty return
Here is where you would explicit_bzero() sensitive data, like maybe last_block, s0, temp_block, and X.
maybe this is just phabricator, but it looks like there are extra spaces between __m128i and s0
style(9) nit: pointers aren't booleans; compare with null in expressions. Ditto below.
It's not a hash. It's a MAC, or tag.
You already zero padded temp_block on lines 322-325.
Not that they're different types, but since this acts on pad_block, sizeof(pad_block) is slightly more appropriate
probably also the last block of decrypted plaintext is sensitive (temp_block)? And perhaps the tag, hash_block.
No verifying of the tag over the AAD?
s_x is never used?
style(9) nit: return (0);
The reason I still did it that was was because AESNI_CCM_decrypt doesn't allocate the memory -- the caller does, and if something else were to call it (or the aesni wrapping code ever changes how it gets the buffers), it'd expose data it shouldn't.
Just a couple of thoughts from what I've seen flying by:
- I think it's correct to decrypt twice (but I think everyone agrees on that now)
- I have patches to make ccr(4) do CCM.
- It certainly seems like you should be able to do a CCM request that only has AAD and no payload. GCM supports this and the NIST-KAT tests for GCM include examples of this.
- As part of my ccr(4) work I'm working on adding the CCM NIST-KAT vectors to the security/nist-kat port and hooking those up to the python tester (I thought we had asked for that earlier as part of the CCM changes, but I don't see them in the tree and haven't seen them in a review, so I'm just going to work on it as part of testing ccr(4)).
New diff coming right after this.
I find memcpy backwards, bcopy sane. But it's also kernel code, and bcopy/bzero is more prevalent than memcpy/memset.
ha. I did not know about those. Much nicer.
I deal with bits (networking stuff) so often that my habit is to specify Xbits or Xbytes as appropriate.
Old debugging code, I've gotten rid of all of them.
I hate any function with more than 6 arguments. That's from compiler work decades ago ;).
I am, *always*, horrible at naming things. I changed it to "rolling_mac".
I put it there because of how the spec was written.
There was in at least one place, fixed.
Done, I believe.
Is the tag sensitive? I've got it in the new changes, but since it's part of the message, I'm not sure it's sensitive?
I changed both of them to check for message and aad len being zero for early exit.
Good catch, holdover from the older code.
Looking better, thanks
How did you arrive at that conclusion? Recursive grep suggests the opposite is true.
I think we lost 0xfffe?
Being explicit about bytes is harmless, but we're missing a space and a full SI-prefix.
This is the correct tag, as opposed to the one provided by the attacker in the message. It's sensitive, no?
I lost the length descriptor prefix in the last change. I've put it back, and run cryptocheck with -A lengths of 0, 13, 16, 32, 192102, and 127091. (User-space can't test more than 256k unfortunately.)
Only some small suggestions / nits from me. I think it looks good overall. Does this pass the limited subset of CCM vectors the latest python script test runs?
FWIW, I've sorted these alphabetically in the manpage for ccr and think it wouldn't hurt to do the same here.
Unrelated, it seems like SHA224 is missing (that might be a cem@ bug and a separate commit to fix).
So we do have AES_CBC_MAC_HASH_LEN which is what I used in ccr(4) as the equivalent of GMAC_DIGEST_LEN. I prefer the explicit constants as right now this is hardcoding the assumption that CCM and GCM have the same tags. We could instead add a _Static_assert() that 'sizeof(tag) >= GMAC_DIGEST_LEN' for the GCM case and something similar for CCM.
FWIW, this will be the 3rd copy of this code in the tree (plain software generates the B0 block, ccr has a 'generate_ccm_b0' function). Someday we should export a shared helper function, but we don't have to do that as part of this change.
For actual CCM I think the limits are 7 and 13.
I'm updating and build testing before uploading new diffs.
I didn't touch that -- but it's possible I'm out of date. I'll do an update before I upload these modified diffs.
That may be right, the spec just said "15-L octets"; it clearly can't be 0, since it says it must also be unique. But the other constraints on L aren't as clear to me.