Changeset View
Standalone View
sys/crypto/aesni/aesni_ccm.c
- This file was added.
/*- | |||||
* Copyright (c) 2014 The FreeBSD Foundation | |||||
* Copyright (c) 2018 iXsystems, Inc | |||||
* All rights reserved. | |||||
* | |||||
* This software was developed by John-Mark Gurney under | |||||
* the sponsorship of the FreeBSD Foundation and | |||||
* Rubicon Communications, LLC (Netgate). | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
* | |||||
* $FreeBSD$ | |||||
* | |||||
* This file implements AES-CCM+CBC-MAC, as described | |||||
* at https://tools.ietf.org/html/rfc3610, using Intel's | |||||
* AES-NI instructions. | |||||
* | |||||
*/ | |||||
#include <sys/types.h> | |||||
#include <sys/endian.h> | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <crypto/aesni/aesni.h> | |||||
#include <crypto/aesni/aesni_os.h> | |||||
#include <crypto/aesni/aesencdec.h> | |||||
#define AESNI_ENC(d, k, nr) aesni_enc(nr-1, (const __m128i*)k, d) | |||||
#include <wmmintrin.h> | |||||
#include <emmintrin.h> | |||||
#include <smmintrin.h> | |||||
typedef union { | |||||
__m128i block; | |||||
uint8_t bytes[sizeof(__m128i)]; | |||||
} aes_block_t; | |||||
cem: Use of this union is somewhat problematic.
It's use is very much in violation of the spirit of… | |||||
/* | |||||
* Encrypt a single 128-bit block after | |||||
* doing an xor. This is also used to | |||||
* decrypt (yay symmetric encryption). | |||||
*/ | |||||
static inline __m128i | |||||
xor_and_encrypt(__m128i a, __m128i b, const unsigned char *k, int nr) | |||||
{ | |||||
__m128 retval = _mm_xor_si128(a, b); | |||||
retval = AESNI_ENC(retval, k, nr); | |||||
return retval; | |||||
Done Inline ActionsNit: s/put/Put/ jhb: Nit: s/put/Put/ | |||||
Done Inline ActionsKeyboard issues. sef: Keyboard issues. | |||||
} | |||||
Not Done Inline ActionsThis 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);. cem: This file is more or less new code; it's free to follow style(9) from the beginning.
So… | |||||
/* | |||||
* put value at the end of block, starting at offset. | |||||
* (This goes backwards, putting bytes in *until* it | |||||
* reaches offset.) | |||||
*/ | |||||
static void | |||||
append_int(size_t value, __m128i *block, size_t offset) | |||||
{ | |||||
int indx = sizeof(*block) - 1; | |||||
uint8_t *bp = (uint8_t*)block; | |||||
while (indx > (sizeof(*block) - offset)) { | |||||
bp[indx] = value & 0xff; | |||||
Not Done Inline Actionsditto the blank line thing cem: ditto the blank line thing | |||||
indx--; | |||||
value >>= 8; | |||||
} | |||||
} | |||||
/* | |||||
* Start the CBC-MAC process. This handles the auth data. | |||||
*/ | |||||
static __m128i | |||||
cbc_mac_start(const unsigned char *auth_data, size_t auth_len, | |||||
const unsigned char *nonce, size_t nonce_len, | |||||
const unsigned char *key, int nr, | |||||
size_t data_len, size_t tag_len) | |||||
{ | |||||
aes_block_t retval, temp_block; | |||||
/* This defines where the message length goes */ | |||||
int L = sizeof(__m128i) - 1 - nonce_len; | |||||
Not Done Inline ActionsSeems 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). cem: Seems like `retval` could be named something more descriptive, like `B0` or `MAC`.
And… | |||||
Not Done Inline ActionsFWIW, 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. jhb: FWIW, this will be the 3rd copy of this code in the tree (plain software generates the B0 block… | |||||
/* | |||||
* Set up B0 here. This has the flags byte, | |||||
* followed by the nonce, followed by the | |||||
* length of the message. | |||||
*/ | |||||
retval.block = _mm_setzero_si128(); | |||||
retval.bytes[0] = (auth_len ? 1 : 0) * 64 | | |||||
(((tag_len - 2) / 2) * 8) | | |||||
(L - 1); | |||||
Not Done Inline ActionsDoes __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. cem: Does `__m128i foo = 0;` generate different code than `__m128i foo = _mm_setzero_si128();`? It… | |||||
bcopy(nonce, &retval.bytes[1], nonce_len); | |||||
append_int(data_len, &retval.block, L+1); | |||||
Not Done Inline Actionsstyle nit: auth_len > 0, as auth_len is not a boolean cem: style nit: `auth_len > 0`, as auth_len is not a boolean | |||||
retval.block = AESNI_ENC(retval.block, key, nr); | |||||
if (auth_len != 0) { | |||||
Not Done Inline ActionsI'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.) cem: I'm not asking you to change these, but in new code I'd encourage use of the C89… | |||||
Done Inline ActionsI find memcpy backwards, bcopy sane. But it's also kernel code, and bcopy/bzero is more prevalent than memcpy/memset. sef: I find memcpy backwards, bcopy sane. But it's also kernel code, and bcopy/bzero is more… | |||||
Not Done Inline Actions
How did you arrive at that conclusion? Recursive grep suggests the opposite is true. cem: > bcopy/bzero is more prevalent than memcpy/memset.
How did you arrive at that conclusion? | |||||
Done Inline ActionsOlder check. "_was_ more prevalent" it turns out. sef: Older check. "_was_ more prevalent" it turns out. | |||||
/* | |||||
* We need to start by appending the length descriptor. | |||||
*/ | |||||
uint32_t auth_amt; | |||||
size_t copy_amt; | |||||
const uint8_t *auth_ptr = auth_data; | |||||
temp_block.block = _mm_setzero_si128(); | |||||
if (auth_len < ((1<<16) - (1<<8))) { | |||||
uint16_t *ip = (uint16_t*)&temp_block; | |||||
*ip = htobe16(auth_len); | |||||
auth_amt = 2; | |||||
} else { | |||||
/* | |||||
* The current calling convention means that | |||||
Not Done Inline ActionsCould just use be16enc(&temp_block, auth_len); cem: Could just use
```
be16enc(&temp_block, auth_len);
``` | |||||
Done Inline Actionsha. I did not know about those. Much nicer. sef: ha. I did not know about those. Much nicer. | |||||
* there can never be more than 4g of authentication | |||||
* data, so we don't handle the 0xffff case. | |||||
*/ | |||||
uint32_t *ip = (uint32_t*)&temp_block.bytes[2]; | |||||
temp_block.bytes[0] = 0xff; | |||||
Done Inline ActionsI think we lost 0xfffe? cem: I think we lost `0xfffe`? | |||||
Done Inline ActionsYeah, put back and explicitly tested the larger AAD lengths. sef: Yeah, put back and explicitly tested the larger AAD lengths. | |||||
temp_block.bytes[1] = 0xfe; | |||||
Not Done Inline ActionsWhat 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. cem: What do you mean by calling convention? The immediate function takes a size_t, which can… | |||||
*ip = htobe32(auth_len); | |||||
auth_amt = 2 + sizeof(*ip); | |||||
} | |||||
/* | |||||
* Need to copy abytes into blocks. The first block is | |||||
* already partially filled, by auth_amt, so we need | |||||
* to handle that. The last block needs to be zero padded. | |||||
Not Done Inline ActionsSimplified: be16enc(&temp_block, 0xfffe); be32enc((char *)&temp_block + 2, auth_len); cem: Simplified:
```
be16enc(&temp_block, 0xfffe);
be32enc((char *)&temp_block + 2, auth_len);
``` | |||||
*/ | |||||
copy_amt = MIN(auth_len - auth_amt, | |||||
sizeof(temp_block) - auth_amt); | |||||
bcopy(auth_ptr, &temp_block.bytes[auth_amt], copy_amt); | |||||
auth_ptr += copy_amt; | |||||
retval.block = xor_and_encrypt(retval.block, temp_block.block, | |||||
key, nr); | |||||
while (auth_ptr < auth_data + auth_len) { | |||||
copy_amt = MIN((auth_data + auth_len) - auth_ptr, | |||||
sizeof(temp_block)); | |||||
if (copy_amt < sizeof(retval)) | |||||
bzero(&temp_block, sizeof(temp_block)); | |||||
bcopy(auth_ptr, &temp_block, copy_amt); | |||||
retval.block = xor_and_encrypt(retval.block, | |||||
temp_block.block, key, nr); | |||||
auth_ptr += copy_amt; | |||||
} | |||||
Not Done Inline Actionsstyle nit: sizeof(temp_block), for consistency with the object we're acting on cem: style nit: `sizeof(temp_block)`, for consistency with the object we're acting on | |||||
} | |||||
return retval.block; | |||||
} | |||||
/* | |||||
* Implement AES CCM+CBC-MAC encryption and authentication. | |||||
* | |||||
* A couple of notes: | |||||
* The specification allows for a different number of tag lengths; | |||||
* however, they're always truncated from 16 bytes, and the tag | |||||
* length isn't passed in. (This could be fixed by changing the | |||||
* code in aesni.c:aesni_cipher_crypt().) | |||||
* Similarly, although the nonce length is passed in, the | |||||
* OpenCrypto API that calls us doesn't have a way to set the nonce | |||||
* other than by having different crypto algorithm types. As a result, | |||||
* this is currently always called with nlen=12; this means that we | |||||
* also have a maximum message length of 16MBytes. And similarly, | |||||
* since abyes is limited to a 32 bit value here, the AAD is | |||||
* limited to 4gbytes or less. | |||||
*/ | |||||
#if 0 | |||||
#error Using (abytes % key len) != 0 fails (padding issue?) | |||||
#endif | |||||
Not Done Inline Actionstypo: abytes cem: typo: abytes | |||||
Not Done Inline Actionsusually spelled "4 GB" or "4 gigabytes" but not "4gbytes" cem: usually spelled "4 GB" or "4 gigabytes" but not "4gbytes" | |||||
Done Inline ActionsI deal with bits (networking stuff) so often that my habit is to specify Xbits or Xbytes as appropriate. sef: I deal with bits (networking stuff) so often that my habit is to specify Xbits or Xbytes as… | |||||
Not Done Inline ActionsBeing explicit about bytes is harmless, but we're missing a space and a full SI-prefix. cem: Being explicit about bytes is harmless, but we're missing a space and a full SI-prefix. | |||||
Done Inline Actionsstill present cem: still present | |||||
void | |||||
AES_CCM_encrypt(const unsigned char *in, unsigned char *out, | |||||
const unsigned char *addt, const unsigned char *nonce, | |||||
unsigned char *tag, uint32_t nbytes, uint32_t abytes, int nlen, | |||||
Not Done Inline ActionsI 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.) cem: I don't know what these pre-processor error directives (there are a few) are intended to be… | |||||
Done Inline ActionsOld debugging code, I've gotten rid of all of them. sef: Old debugging code, I've gotten rid of all of them. | |||||
const unsigned char *key, int nr) | |||||
{ | |||||
static const int tag_length = 16; /* 128 bits */ | |||||
int L; | |||||
int counter = 1; /* S0 has 0, S1 has 1 */ | |||||
size_t copy_amt, total = 0; | |||||
Not Done Inline ActionsUgh. I know you just inherited this mess from GCM, but what an obnoxious API :P. cem: Ugh. I know you just inherited this mess from GCM, but what an obnoxious API :P. | |||||
Done Inline ActionsI hate any function with more than 6 arguments. That's from compiler work decades ago ;). sef: I hate any function with more than 6 arguments. That's from compiler work decades ago ;). | |||||
aes_block_t s0, last_block, current_block, s_x, temp_block; | |||||
if (nbytes == 0) | |||||
return; | |||||
if (nlen < 0 || nlen > 15) | |||||
panic("%s: bad nonce length %d", __FUNCTION__, nlen); | |||||
/* | |||||
* We need to know how many bytes to use to describe | |||||
Not Done Inline ActionsIs it impossible to generate a tag for AAD without any ciphered input? cem: Is it impossible to generate a tag for AAD without any ciphered input? | |||||
Done Inline ActionsHuh. I hadn't thought about that, but looks like it can. Thanks! (I'm working on the other feedback as well.) sef: Huh. I hadn't thought about that, but looks like it can. Thanks! (I'm working on the other… | |||||
* the length of the data. Normally, nlen should be | |||||
* 12, which leaves us 3 bytes to do that -- 16mbytes of | |||||
* data to encrypt. But it can be longer or shorter; | |||||
* this impacts the length of the message. | |||||
*/ | |||||
Not Done Inline ActionsFor actual CCM I think the limits are 7 and 13. jhb: For actual CCM I think the limits are 7 and 13. | |||||
Done Inline ActionsThat 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. sef: That may be right, the spec just said "15-L octets"; it clearly can't be 0, since it says it… | |||||
Done Inline ActionsNIST 800-38c §A.1 states n ∈ [7, 13]. cem: NIST 800-38c §A.1 states `n ∈ [7, 13]`. | |||||
L = sizeof(__m128i) - 1 - nlen; | |||||
/* | |||||
* Now, this shouldn't happen, but let's make sure that | |||||
* the data length isn't too big. | |||||
*/ | |||||
KASSERT(nbytes <= ((1 << (8 * L)) - 1), | |||||
("%s: nbytes is %u, but length field is %d bytes", | |||||
__FUNCTION__, nbytes, L)); | |||||
/* | |||||
* Clear out the blocks | |||||
*/ | |||||
explicit_bzero(&s0, sizeof(s0)); | |||||
explicit_bzero(¤t_block, sizeof(current_block)); | |||||
last_block.block = cbc_mac_start(addt, abytes, nonce, nlen, | |||||
key, nr, nbytes, tag_length); | |||||
/* s0 has flags, nonce, and then 0 */ | |||||
Done Inline ActionsYou 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? cem: You don't need explicit_bzero for ordinary initializations. Here `_mm_setzero_si128` would be… | |||||
s0.bytes[0] = L - 1; /* but the flags byte only has L' */ | |||||
bcopy(nonce, &s0.bytes[1], nlen); | |||||
/* | |||||
* Now to cycle through the rest of the data. | |||||
*/ | |||||
bcopy(&s0, &s_x, sizeof(s0)); | |||||
while (total < nbytes) { | |||||
/* | |||||
* Copy the plain-text data into temp_block. | |||||
* This may need to be zero-padded. | |||||
*/ | |||||
copy_amt = MIN(nbytes - total, sizeof(temp_block)); | |||||
bcopy(in+total, &temp_block, copy_amt); | |||||
if (copy_amt < sizeof(temp_block)) { | |||||
bzero(&temp_block.bytes[copy_amt], | |||||
sizeof(temp_block) - copy_amt); | |||||
} | |||||
last_block.block = xor_and_encrypt(last_block.block, | |||||
temp_block.block, key, nr); | |||||
/* Put the counter into the s_x block */ | |||||
append_int(counter++, &s_x.block, L+1); | |||||
/* Encrypt that */ | |||||
__m128i X = AESNI_ENC(s_x.block, key, nr); | |||||
/* XOR the plain-text with the encrypted counter block */ | |||||
temp_block.block = _mm_xor_si128(temp_block.block, X); | |||||
Not Done Inline ActionsSo really last_block is the rolling MAC value. I'd suggest a name incorporating MAC, for clarity. cem: So really `last_block` is the rolling MAC value. I'd suggest a name incorporating MAC, for… | |||||
Done Inline ActionsI am, *always*, horrible at naming things. I changed it to "rolling_mac". sef: I am, *always*, horrible at naming things. I changed it to "rolling_mac". | |||||
Not Done Inline ActionsThanks cem: Thanks | |||||
/* And copy it out */ | |||||
bcopy(&temp_block, out+total, copy_amt); | |||||
total += copy_amt; | |||||
} | |||||
Not Done Inline Actionsstyle nit: Usually we at declare variables at the beginning of some scope, if not function scope. cem: style nit: Usually we at declare variables at the beginning of some scope, if not function… | |||||
Not Done Inline ActionsRight, ok, we're generating a keystream from an encrypted series of counter blocks. cem: Right, ok, we're generating a keystream from an encrypted series of counter blocks. | |||||
/* | |||||
* Allgedly done with it! Except for the tag. | |||||
*/ | |||||
s0.block = AESNI_ENC(s0.block, key, nr); | |||||
temp_block.block = _mm_xor_si128(s0.block, last_block.block); | |||||
bcopy(&temp_block, tag, tag_length); | |||||
return; | |||||
} | |||||
/* | |||||
Not Done Inline ActionsThis 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. cem: This step doesn't have any dependency on the data, right? We could do it earlier, as soon as… | |||||
Done Inline ActionsI put it there because of how the spec was written. sef: I put it there because of how the spec was written. | |||||
* Implement AES CCM+CBC-MAC decryption and authentication. | |||||
* Returns 0 on failure, 1 on success. | |||||
* | |||||
Not Done Inline Actionsstyle: Empty return Here is where you would explicit_bzero() sensitive data, like maybe last_block, s0, temp_block, and X. cem: style: Empty return
Here is where you would explicit_bzero() sensitive data, like maybe… | |||||
* The primary difference here is that each encrypted block | |||||
* needs to be hashed&encrypted after it is decrypted (since | |||||
* the CBC-MAC is based on the plain text). This means that | |||||
* we do the decryption twice -- first to verify the tag, | |||||
* and second to decrypt and copy it out. | |||||
* | |||||
* To avoid annoying code copying, we implement the main | |||||
* loop as a separate function. | |||||
* | |||||
* Call with out as NULL to not store the decrypted results; | |||||
* call with hashp as NULL to not run the authentication. | |||||
* Calling with neither as NULL does the decryption and | |||||
* authentication as a single pass (which is not allowed | |||||
* per the specification, really). | |||||
* | |||||
* If hashp is non-NULL, it points to the post-AAD computed | |||||
* checksum. | |||||
*/ | |||||
static void | |||||
decrypt_loop(const unsigned char *in, unsigned char *out, size_t nbytes, | |||||
aes_block_t s0, size_t nonce_length, aes_block_t *hashp, | |||||
const unsigned char *key, int nr) | |||||
{ | |||||
size_t total = 0; | |||||
aes_block_t s_x = s0, hash_block; | |||||
int counter = 1; | |||||
Not Done Inline ActionsFor example, here it is not clear which member of the union is supposed to be initialized by s0. cem: For example, here it is not clear which member of the `union` is supposed to be initialized by… | |||||
const size_t L = sizeof(__m128i) - 1 - nonce_length; | |||||
Not Done Inline Actionsmaybe this is just phabricator, but it looks like there are extra spaces between __m128i and s0 cem: maybe this is just phabricator, but it looks like there are extra spaces between `__m128i` and… | |||||
Done Inline ActionsThere was in at least one place, fixed. sef: There was in at least one place, fixed. | |||||
__m128i pad_block; | |||||
/* | |||||
* The starting hash (post AAD, if any). | |||||
*/ | |||||
if (hashp) | |||||
hash_block = *hashp; | |||||
Done Inline ActionsI still find hashp and hash_block problematic here; these have specific cryptographic meaning that AES-CBC-MAC does not satisfy. macp/mac_block (or tagp/tag_block) would be totally fine. cem: I still find `hashp` and `hash_block` problematic here; these have specific cryptographic… | |||||
while (total < nbytes) { | |||||
aes_block_t temp_block; | |||||
size_t copy_amt = MIN(nbytes - total, sizeof(temp_block)); | |||||
if (copy_amt < sizeof(temp_block)) { | |||||
Done Inline Actionsstyle(9) nit: pointers aren't booleans; compare with null in expressions. Ditto below. cem: style(9) nit: pointers aren't booleans; compare with null in expressions. Ditto below. | |||||
temp_block.block = _mm_setzero_si128(); | |||||
} | |||||
bcopy(in+total, &temp_block, copy_amt); | |||||
Not Done Inline ActionsAnd here we reference the union pointer directly for initialization. cem: And here we reference the union pointer directly for initialization. | |||||
/* | |||||
* temp_block has the current block of input data, | |||||
* zero-padded if necessary. This is used in computing | |||||
* both the decrypted data, and the authentication hash. | |||||
*/ | |||||
append_int(counter++, &s_x.block, L+1); | |||||
/* | |||||
* The hash is computed based on the decrypted data. | |||||
*/ | |||||
pad_block = AESNI_ENC(s_x.block, key, nr); | |||||
if (copy_amt < sizeof(temp_block)) { | |||||
/* | |||||
* Need to pad out both blocks with 0. | |||||
*/ | |||||
uint8_t *end_of_buffer = (uint8_t*)&pad_block; | |||||
Done Inline ActionsIt's not a hash. It's a MAC, or tag. cem: It's not a hash. It's a MAC, or tag. | |||||
bzero(&temp_block.bytes[copy_amt], | |||||
sizeof(temp_block) - copy_amt); | |||||
bzero(end_of_buffer + copy_amt, | |||||
sizeof(temp_block) - copy_amt); | |||||
} | |||||
temp_block.block = _mm_xor_si128(temp_block.block, | |||||
pad_block); | |||||
if (out) | |||||
bcopy(&temp_block, out+total, copy_amt); | |||||
Not Done Inline ActionsYou already zero padded temp_block on lines 322-325. cem: You already zero padded temp_block on lines 322-325. | |||||
Done Inline ActionsDone, I believe. sef: Done, I believe. | |||||
if (hashp) | |||||
Done Inline ActionsNot that they're different types, but since this acts on pad_block, sizeof(pad_block) is slightly more appropriate cem: Not that they're different types, but since this acts on `pad_block`, `sizeof(pad_block)` is… | |||||
hash_block.block = xor_and_encrypt(hash_block.block, | |||||
temp_block.block, key, nr); | |||||
total += copy_amt; | |||||
} | |||||
explicit_bzero(&pad_block, sizeof(pad_block)); | |||||
if (hashp) | |||||
*hashp = hash_block; | |||||
return; | |||||
} | |||||
/* | |||||
* The exposed decryption routine. This is practically a | |||||
* copy of the encryption routine, except that the order | |||||
* in which the hash is created is changed. | |||||
* XXX combine the two functions at some point! | |||||
Not Done Inline Actionsempty return probably also the last block of decrypted plaintext is sensitive (temp_block)? And perhaps the tag, hash_block. cem: empty return
probably also the last block of decrypted plaintext is sensitive (`temp_block`)? | |||||
Done Inline ActionsIs 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? sef: Is the tag sensitive? I've got it in the new changes, but since it's part of the message, I'm… | |||||
Not Done Inline ActionsThis is the correct tag, as opposed to the one provided by the attacker in the message. It's sensitive, no? cem: This is the correct tag, as opposed to the one provided by the attacker in the message. It's… | |||||
*/ | |||||
int | |||||
AES_CCM_decrypt(const unsigned char *in, unsigned char *out, | |||||
const unsigned char *addt, const unsigned char *nonce, | |||||
const unsigned char *tag, uint32_t nbytes, uint32_t abytes, int nlen, | |||||
const unsigned char *key, int nr) | |||||
{ | |||||
static const int tag_length = 16; /* 128 bits */ | |||||
int L; | |||||
aes_block_t s0, last_block, current_block, s_x, temp_block; | |||||
if (nbytes == 0) | |||||
return 1; // No message means no decryption! | |||||
if (nlen < 0 || nlen > 15) | |||||
panic("%s: bad nonce length %d", __FUNCTION__, nlen); | |||||
/* | |||||
* We need to know how many bytes to use to describe | |||||
* the length of the data. Normally, nlen should be | |||||
* 12, which leaves us 3 bytes to do that -- 16mbytes of | |||||
* data to encrypt. But it can be longer or shorter. | |||||
Not Done Inline ActionsNo verifying of the tag over the AAD? cem: No verifying of the tag over the AAD? | |||||
Done Inline ActionsI changed both of them to check for message and aad len being zero for early exit. sef: I changed both of them to check for message and aad len being zero for early exit. | |||||
*/ | |||||
L = sizeof(__m128i) - 1 - nlen; | |||||
/* | |||||
* Now, this shouldn't happen, but let's make sure that | |||||
* the data length isn't too big. | |||||
*/ | |||||
if (nbytes > ((1 << (8 * L)) - 1)) | |||||
panic("%s: nbytes is %u, but length field is %d bytes", | |||||
__FUNCTION__, nbytes, L); | |||||
/* | |||||
* Clear out the blocks | |||||
*/ | |||||
s0.block = _mm_setzero_si128(); | |||||
current_block = s0; | |||||
last_block.block = cbc_mac_start(addt, abytes, nonce, nlen, | |||||
key, nr, nbytes, tag_length); | |||||
/* s0 has flags, nonce, and then 0 */ | |||||
s0.bytes[0] = L-1; /* but the flags byte only has L' */ | |||||
bcopy(nonce, &s0.bytes[1], nlen); | |||||
/* | |||||
* Now to cycle through the rest of the data. | |||||
*/ | |||||
s_x = s0; | |||||
decrypt_loop(in, NULL, nbytes, s0, nlen, &last_block, key, nr); | |||||
/* | |||||
* Compare the tag. | |||||
*/ | |||||
temp_block.block = _mm_xor_si128(AESNI_ENC(s0.block, key, nr), | |||||
last_block.block); | |||||
if (bcmp(&temp_block, tag, tag_length) != 0) { | |||||
Not Done Inline Actionss_x is never used? cem: s_x is never used? | |||||
Done Inline ActionsGood catch, holdover from the older code. sef: Good catch, holdover from the older code. | |||||
return 0; | |||||
} | |||||
/* | |||||
* Push out the decryption results this time. | |||||
*/ | |||||
decrypt_loop(in, out, nbytes, s0, nlen, NULL, key, nr); | |||||
return 1; | |||||
Not Done Inline Actionstimingsafe_bcmp cem: `timingsafe_bcmp` | |||||
} | |||||
Not Done Inline Actionsstyle(9) nit: return (0); cem: style(9) nit: `return (0);` |
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".)