diff --git a/mozilla/security/nss/lib/freebl/blapii.h b/mozilla/security/nss/lib/freebl/blapii.h index 04f4a405698..3ba7b7c22c0 100644 --- a/mozilla/security/nss/lib/freebl/blapii.h +++ b/mozilla/security/nss/lib/freebl/blapii.h @@ -10,6 +10,15 @@ #include "blapit.h" +/* max block size of supported block ciphers */ +#define MAX_BLOCK_SIZE 16 + +typedef SECStatus (*freeblCipherFunc)(void *cx, unsigned char *output, + unsigned int *outputLen, unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen, + unsigned int blocksize); +typedef void (*freeblDestroyFunc)(void *cx, PRBool freeit); + SEC_BEGIN_PROTOS #if defined(XP_UNIX) && !defined(NO_FORK_CHECK) diff --git a/mozilla/security/nss/lib/freebl/blapit.h b/mozilla/security/nss/lib/freebl/blapit.h index 7979fa3c5a6..74ff05e82ff 100644 --- a/mozilla/security/nss/lib/freebl/blapit.h +++ b/mozilla/security/nss/lib/freebl/blapit.h @@ -4,7 +4,7 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* $Id: blapit.h,v 1.29 2012-06-14 18:55:10 wtc%google.com Exp $ */ +/* $Id: blapit.h,v 1.30 2012-09-28 22:46:32 rrelyea%redhat.com Exp $ */ #ifndef _BLAPIT_H_ #define _BLAPIT_H_ @@ -34,6 +34,9 @@ /* AES operation modes */ #define NSS_AES 0 #define NSS_AES_CBC 1 +#define NSS_AES_CTS 2 +#define NSS_AES_CTR 3 +#define NSS_AES_GCM 4 /* Camellia operation modes */ #define NSS_CAMELLIA 0 diff --git a/mozilla/security/nss/lib/freebl/ctr.c b/mozilla/security/nss/lib/freebl/ctr.c new file mode 100644 index 00000000000..3a2f1a6b82b --- /dev/null +++ b/mozilla/security/nss/lib/freebl/ctr.c @@ -0,0 +1,167 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef FREEBL_NO_DEPEND +#include "stubs.h" +#endif +#include "prtypes.h" +#include "blapit.h" +#include "blapii.h" +#include "ctr.h" +#include "pkcs11t.h" +#include "secerr.h" + +SECStatus +CTR_InitContext(CTRContext *ctr, void *context, freeblCipherFunc cipher, + const unsigned char *param, unsigned int blocksize) +{ + const CK_AES_CTR_PARAMS *ctrParams = (const CK_AES_CTR_PARAMS *)param; + + if (ctrParams->ulCounterBits == 0 || + ctrParams->ulCounterBits > blocksize * PR_BITS_PER_BYTE) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Invariant: 0 < ctr->bufPtr <= blocksize */ + ctr->bufPtr = blocksize; /* no unused data in the buffer */ + ctr->cipher = cipher; + ctr->context = context; + ctr->counterBits = ctrParams->ulCounterBits; + if (blocksize > sizeof(ctr->counter) || + blocksize > sizeof(ctrParams->cb)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + PORT_Memcpy(ctr->counter, ctrParams->cb, blocksize); + return SECSuccess; +} + +CTRContext * +CTR_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *param, unsigned int blocksize) +{ + CTRContext *ctr; + SECStatus rv; + + /* first fill in the Counter context */ + ctr = PORT_ZNew(CTRContext); + if (ctr == NULL) { + return NULL; + } + rv = CTR_InitContext(ctr, context, cipher, param, blocksize); + if (rv != SECSuccess) { + CTR_DestroyContext(ctr, PR_TRUE); + ctr = NULL; + } + return ctr; +} + +void +CTR_DestroyContext(CTRContext *ctr, PRBool freeit) +{ + PORT_Memset(ctr, 0, sizeof(CTRContext)); + if (freeit) { + PORT_Free(ctr); + } +} + +/* + * Used by counter mode. Increment the counter block. Not all bits in the + * counter block are part of the counter, counterBits tells how many bits + * are part of the counter. The counter block is blocksize long. It's a + * big endian value. + * + * XXX Does not handle counter rollover. + */ +static void +ctr_GetNextCtr(unsigned char *counter, unsigned int counterBits, + unsigned int blocksize) +{ + unsigned char *counterPtr = counter + blocksize - 1; + unsigned char mask, count; + + PORT_Assert(counterBits <= blocksize*PR_BITS_PER_BYTE); + while (counterBits >= PR_BITS_PER_BYTE) { + if (++(*(counterPtr--))) { + return; + } + counterBits -= PR_BITS_PER_BYTE; + } + if (counterBits == 0) { + return; + } + /* increment the final partial byte */ + mask = (1 << counterBits)-1; + count = ++(*counterPtr) & mask; + *counterPtr = ((*counterPtr) & ~mask) | count; + return; +} + +static void +ctr_xor(unsigned char *target, const unsigned char *x, + const unsigned char *y, unsigned int count) +{ + unsigned int i; + for (i=0; i < count; i++) { + *target++ = *x++ ^ *y++; + } +} + +SECStatus +CTR_Update(CTRContext *ctr, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize) +{ + unsigned int tmp; + SECStatus rv; + + if (maxout < inlen) { + *outlen = inlen; + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + *outlen = 0; + if (ctr->bufPtr != blocksize) { + unsigned int needed = PR_MIN(blocksize-ctr->bufPtr, inlen); + ctr_xor(outbuf, inbuf, ctr->buffer+ctr->bufPtr, needed); + ctr->bufPtr += needed; + outbuf += needed; + inbuf += needed; + *outlen += needed; + inlen -= needed; + if (inlen == 0) { + return SECSuccess; + } + PORT_Assert(ctr->bufPtr == blocksize); + } + + while (inlen >= blocksize) { + rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize, + ctr->counter, blocksize, blocksize); + ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + ctr_xor(outbuf, inbuf, ctr->buffer, blocksize); + outbuf += blocksize; + inbuf += blocksize; + *outlen += blocksize; + inlen -= blocksize; + } + if (inlen == 0) { + return SECSuccess; + } + rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize, + ctr->counter, blocksize, blocksize); + ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + ctr_xor(outbuf, inbuf, ctr->buffer, inlen); + ctr->bufPtr = inlen; + *outlen += inlen; + return SECSuccess; +} diff --git a/mozilla/security/nss/lib/freebl/ctr.h b/mozilla/security/nss/lib/freebl/ctr.h new file mode 100644 index 00000000000..69ef150b061 --- /dev/null +++ b/mozilla/security/nss/lib/freebl/ctr.h @@ -0,0 +1,44 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CTR_H +#define CTR_H 1 + +#include "blapii.h" + +/* This structure is defined in this header because both ctr.c and gcm.c + * need it. */ +struct CTRContextStr { + freeblCipherFunc cipher; + void *context; + unsigned char counter[MAX_BLOCK_SIZE]; + unsigned char buffer[MAX_BLOCK_SIZE]; + unsigned long counterBits; + unsigned int bufPtr; +}; + +typedef struct CTRContextStr CTRContext; + +SECStatus CTR_InitContext(CTRContext *ctr, void *context, + freeblCipherFunc cipher, const unsigned char *param, + unsigned int blocksize); + +/* + * The context argument is the inner cipher context to use with cipher. The + * CTRContext does not own context. context needs to remain valid for as long + * as the CTRContext is valid. + * + * The cipher argument is a block cipher in the ECB encrypt mode. + */ +CTRContext * CTR_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *param, unsigned int blocksize); + +void CTR_DestroyContext(CTRContext *ctr, PRBool freeit); + +SECStatus CTR_Update(CTRContext *ctr, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize); + +#endif diff --git a/mozilla/security/nss/lib/freebl/cts.c b/mozilla/security/nss/lib/freebl/cts.c new file mode 100644 index 00000000000..4061de9e69d --- /dev/null +++ b/mozilla/security/nss/lib/freebl/cts.c @@ -0,0 +1,304 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef FREEBL_NO_DEPEND +#include "stubs.h" +#endif +#include "blapit.h" +#include "blapii.h" +#include "cts.h" +#include "secerr.h" + +struct CTSContextStr { + freeblCipherFunc cipher; + void *context; + /* iv stores the last ciphertext block of the previous message. + * Only used by decrypt. */ + unsigned char iv[MAX_BLOCK_SIZE]; +}; + +CTSContext * +CTS_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *iv, unsigned int blocksize) +{ + CTSContext *cts; + + if (blocksize > MAX_BLOCK_SIZE) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + cts = PORT_ZNew(CTSContext); + if (cts == NULL) { + return NULL; + } + PORT_Memcpy(cts->iv, iv, blocksize); + cts->cipher = cipher; + cts->context = context; + return cts; +} + +void +CTS_DestroyContext(CTSContext *cts, PRBool freeit) +{ + if (freeit) { + PORT_Free(cts); + } +} + +/* + * See addemdum to NIST SP 800-38A + * Generically handle cipher text stealing. Basically this is doing CBC + * operations except someone can pass us a partial block. + * + * Output Order: + * CS-1: C1||C2||C3..Cn-1(could be partial)||Cn (NIST) + * CS-2: pad == 0 C1||C2||C3...Cn-1(is full)||Cn (Schneier) + * CS-2: pad != 0 C1||C2||C3...Cn||Cn-1(is partial)(Schneier) + * CS-3: C1||C2||C3...Cn||Cn-1(could be partial) (Kerberos) + * + * The characteristics of these three options: + * - NIST & Schneier (CS-1 & CS-2) are identical to CBC if there are no + * partial blocks on input. + * - Scheier and Kerberos (CS-2 and CS-3) have no embedded partial blocks, + * which make decoding easier. + * - NIST & Kerberos (CS-1 and CS-3) have consistent block order independent + * of padding. + * + * PKCS #11 did not specify which version to implement, but points to the NIST + * spec, so this code implements CTS-CS-1 from NIST. + * + * To convert the returned buffer to: + * CS-2 (Schneier): do + * unsigned char tmp[MAX_BLOCK_SIZE]; + * pad = *outlen % blocksize; + * if (pad) { + * memcpy(tmp, outbuf+*outlen-blocksize, blocksize); + * memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad); + * memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize); + * } + * CS-3 (Kerberos): do + * unsigned char tmp[MAX_BLOCK_SIZE]; + * pad = *outlen % blocksize; + * if (pad == 0) { + * pad = blocksize; + * } + * memcpy(tmp, outbuf+*outlen-blocksize, blocksize); + * memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad); + * memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize); + */ +SECStatus +CTS_EncryptUpdate(CTSContext *cts, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize) +{ + unsigned char lastBlock[MAX_BLOCK_SIZE]; + unsigned int tmp; + int fullblocks; + int written; + SECStatus rv; + + if (inlen < blocksize) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + if (maxout < inlen) { + *outlen = inlen; + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + fullblocks = (inlen/blocksize)*blocksize; + rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, + fullblocks, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Assert(*outlen == fullblocks); + inbuf += fullblocks; + inlen -= fullblocks; + if (inlen == 0) { + return SECSuccess; + } + written = *outlen - (blocksize - inlen); + outbuf += written; + maxout -= written; + + /* + * here's the CTS magic, we pad our final block with zeros, + * then do a CBC encrypt. CBC will xor our plain text with + * the previous block (Cn-1), capturing part of that block (Cn-1**) as it + * xors with the zero pad. We then write this full block, overwritting + * (Cn-1**) in our buffer. This allows us to have input data == output + * data since Cn contains enough information to reconver Cn-1** when + * we decrypt (at the cost of some complexity as you can see in decrypt + * below */ + PORT_Memcpy(lastBlock, inbuf, inlen); + PORT_Memset(lastBlock + inlen, 0, blocksize - inlen); + rv = (*cts->cipher)(cts->context, outbuf, &tmp, maxout, lastBlock, + blocksize, blocksize); + PORT_Memset(lastBlock, 0, blocksize); + if (rv == SECSuccess) { + PORT_Assert(tmp == blocksize); + *outlen = written + blocksize; + } + return rv; +} + + +#define XOR_BLOCK(x,y,count) for(i=0; i < count; i++) x[i] = x[i] ^ y[i] + +/* + * See addemdum to NIST SP 800-38A + * Decrypt, Expect CS-1: input. See the comment on the encrypt side + * to understand what CS-2 and CS-3 mean. + * + * To convert the input buffer to CS-1 from ... + * CS-2 (Schneier): do + * unsigned char tmp[MAX_BLOCK_SIZE]; + * pad = inlen % blocksize; + * if (pad) { + * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); + * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); + * memcpy(inbuf+inlen-blocksize, tmp, blocksize); + * } + * CS-3 (Kerberos): do + * unsigned char tmp[MAX_BLOCK_SIZE]; + * pad = inlen % blocksize; + * if (pad == 0) { + * pad = blocksize; + * } + * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); + * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); + * memcpy(inbuf+inlen-blocksize, tmp, blocksize); + */ +SECStatus +CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize) +{ + unsigned char *Pn; + unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */ + unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */ + unsigned char Cn[MAX_BLOCK_SIZE]; /* block Cn */ + unsigned char lastBlock[MAX_BLOCK_SIZE]; + const unsigned char *tmp; + unsigned int tmpLen; + int fullblocks, pad; + unsigned int i; + SECStatus rv; + + if (inlen < blocksize) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + if (maxout < inlen) { + *outlen = inlen; + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + + fullblocks = (inlen/blocksize)*blocksize; + + /* even though we expect the input to be CS-1, CS-2 is easier to parse, + * so convert to CS-2 immediately. NOTE: this is the same code as in + * the comment for encrypt. NOTE2: since we can't modify inbuf unless + * inbuf and outbuf overlap, just copy inbuf to outbuf and modify it there + */ + pad = blocksize + (inlen - fullblocks); + if (pad != blocksize) { + if (inbuf != outbuf) { + memcpy(outbuf, inbuf, inlen); + /* keep the names so we logically know how we are using the + * buffers */ + inbuf = outbuf; + } + memcpy(lastBlock, inbuf+inlen-blocksize-pad, blocksize); + /* we know inbuf == outbuf now, inbuf is declared const and can't + * be the target, so use outbuf for the target here */ + memcpy(outbuf+inlen-blocksize-pad, inbuf+inlen-pad, pad); + memcpy(outbuf+inlen-blocksize, lastBlock, blocksize); + } + /* save the previous to last block so we can undo the misordered + * chaining */ + tmp = (fullblocks < blocksize*2) ? cts->iv : + inbuf+fullblocks-blocksize*2; + PORT_Memcpy(Cn_2, tmp, blocksize); + PORT_Memcpy(Cn, inbuf+fullblocks-blocksize, blocksize); + rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, + fullblocks, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Assert(*outlen == fullblocks); + inbuf += fullblocks; + inlen -= fullblocks; + if (inlen == 0) { + return SECSuccess; + } + outbuf += fullblocks; + maxout -= fullblocks; + + /* recover the stolen text */ + PORT_Memset(lastBlock, 0, blocksize); + PORT_Memcpy(lastBlock, inbuf, inlen); + PORT_Memcpy(Cn_1, inbuf, inlen); + Pn = outbuf-blocksize; + /* inbuf points to Cn-1* in the input buffer */ + /* NOTE: below there are 2 sections marked "make up for the out of order + * cbc decryption". You may ask, what is going on here. + * Short answer: CBC automatically xors the plain text with the previous + * encrypted block. We are decrypting the last 2 blocks out of order, so + * we have to 'back out' the decrypt xor and 'add back' the encrypt xor. + * Long answer: When we encrypted, we encrypted as follows: + * Pn-2, Pn-1, (Pn || 0), but on decryption we can't + * decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in + * Cn (see below). So above we decrypted all the full blocks: + * Cn-2, Cn, + * to get: + * Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we + * xor'd Pn || 0 with Cn-1, but on decrypt we xor'd it with Cn-2 + * To recover Pn, we xor the block with Cn-1* || 0 (in last block) and + * Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer + * and we can now reunite Cn-1. With the full Cn-1 we can decrypt it, + * but now decrypt is going to xor the decrypted data with Cn instead of + * Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now + * write that oout to the buffer */ + + /* make up for the out of order CBC decryption */ + XOR_BLOCK(lastBlock, Cn_2, blocksize); + XOR_BLOCK(lastBlock, Pn, blocksize); + /* last buf now has Pn || Cn-1**, copy out Pn */ + PORT_Memcpy(outbuf, lastBlock, inlen); + *outlen += inlen; + /* copy Cn-1* into last buf to recover Cn-1 */ + PORT_Memcpy(lastBlock, Cn-1, inlen); + /* note: because Cn and Cn-1 were out of order, our pointer to Pn also + * points to where Pn-1 needs to reside. From here on out read Pn in + * the code as really Pn-1. */ + rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock, + blocksize, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Assert(tmpLen == blocksize); + /* make up for the out of order CBC decryption */ + XOR_BLOCK(Pn, Cn_2, blocksize); + XOR_BLOCK(Pn, Cn, blocksize); + /* reset iv to Cn */ + PORT_Memcpy(cts->iv, Cn, blocksize); + /* This makes Cn the last block for the next decrypt operation, which + * matches the encrypt. We don't care about the contexts of last block, + * only the side effect of setting the internal IV */ + (void) (*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn, + blocksize, blocksize); + /* clear last block. At this point last block contains Pn xor Cn_1 xor + * Cn_2, both of with an attacker would know, so we need to clear this + * buffer out */ + PORT_Memset(lastBlock, 0, blocksize); + /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */ + return SECSuccess; +} diff --git a/mozilla/security/nss/lib/freebl/cts.h b/mozilla/security/nss/lib/freebl/cts.h new file mode 100644 index 00000000000..97b385f4af3 --- /dev/null +++ b/mozilla/security/nss/lib/freebl/cts.h @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CTS_H +#define CTS_H 1 + +#include "blapii.h" + +typedef struct CTSContextStr CTSContext; + +/* + * The context argument is the inner cipher context to use with cipher. The + * CTSContext does not own context. context needs to remain valid for as long + * as the CTSContext is valid. + * + * The cipher argument is a block cipher in the CBC mode. + */ +CTSContext *CTS_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *iv, unsigned int blocksize); + +void CTS_DestroyContext(CTSContext *cts, PRBool freeit); + +SECStatus CTS_EncryptUpdate(CTSContext *cts, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize); +SECStatus CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize); + +#endif diff --git a/mozilla/security/nss/lib/freebl/gcm.c b/mozilla/security/nss/lib/freebl/gcm.c new file mode 100644 index 00000000000..d4391608dae --- /dev/null +++ b/mozilla/security/nss/lib/freebl/gcm.c @@ -0,0 +1,855 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef FREEBL_NO_DEPEND +#include "stubs.h" +#endif +#include "blapii.h" +#include "blapit.h" +#include "gcm.h" +#include "ctr.h" +#include "secerr.h" +#include "prtypes.h" +#include "pkcs11t.h" + +#include + +/************************************************************************** + * First implement the Galois hash function of GCM (gcmHash) * + **************************************************************************/ +#define GCM_HASH_LEN_LEN 8 /* gcm hash defines lengths to be 64 bits */ + +typedef struct gcmHashContextStr gcmHashContext; + +static SECStatus gcmHash_InitContext(gcmHashContext *hash, + const unsigned char *H, + unsigned int blocksize); +static void gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit); +static SECStatus gcmHash_Update(gcmHashContext *ghash, + const unsigned char *buf, unsigned int len, + unsigned int blocksize); +static SECStatus gcmHash_Sync(gcmHashContext *ghash, unsigned int blocksize); +static SECStatus gcmHash_Final(gcmHashContext *gcm, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + unsigned int blocksize); +static SECStatus gcmHash_Reset(gcmHashContext *ghash, + const unsigned char *inbuf, + unsigned int inbufLen, unsigned int blocksize); + +/* compile time defines to select how the GF2 multiply is calculated. + * There are currently 2 algorithms implemented here: MPI and ALGORITHM_1. + * + * MPI uses the GF2m implemented in mpi to support GF2 ECC. + * ALGORITHM_1 is the Algorithm 1 in both NIST SP 800-38D and + * "The Galois/Counter Mode of Operation (GCM)", McGrew & Viega. + */ +#if !defined(GCM_USE_ALGORITHM_1) && !defined(GCM_USE_MPI) +#define GCM_USE_MPI 1 /* MPI is about 5x faster with the + * same or less complexity. It's possible to use + * tables to speed things up even more */ +#endif + +/* GCM defines the bit string to be LSB first, which is exactly + * opposite everyone else, including hardware. build array + * to reverse everything. */ +static const unsigned char gcm_byte_rev[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + + +#ifdef GCM_TRACE +#include + +#define GCM_TRACE_X(ghash,label) { \ + unsigned char _X[MAX_BLOCK_SIZE]; int i; \ + gcm_getX(ghash, _X, blocksize); \ + printf(label,(ghash)->m); \ + for (i=0; i < blocksize; i++) printf("%02x",_X[i]); \ + printf("\n"); } +#define GCM_TRACE_BLOCK(label,buf,blocksize) {\ + printf(label); \ + for (i=0; i < blocksize; i++) printf("%02x",buf[i]); \ + printf("\n"); } +#else +#define GCM_TRACE_X(ghash,label) +#define GCM_TRACE_BLOCK(label,buf,blocksize) +#endif + +#ifdef GCM_USE_MPI + +#ifdef GCM_USE_ALGORITHM_1 +#error "Only define one of GCM_USE_MPI, GCM_USE_ALGORITHM_1" +#endif +/* use the MPI functions to calculate Xn = (Xn-1^C_i)*H mod poly */ +#include "mpi.h" +#include "secmpi.h" +#include "mplogic.h" +#include "mp_gf2m.h" + +/* state needed to handle GCM Hash function */ +struct gcmHashContextStr { + mp_int H; + mp_int X; + mp_int C_i; + const unsigned int *poly; + unsigned char buffer[MAX_BLOCK_SIZE]; + unsigned int bufLen; + int m; /* XXX what is m? */ + unsigned char counterBuf[2*GCM_HASH_LEN_LEN]; + PRUint64 cLen; +}; + +/* f = x^128 + x^7 + x^2 + x + 1 */ +static const unsigned int poly_128[] = { 128, 7, 2, 1, 0 }; +/* f = x^64 + x^4 + x^3 + x + 1 */ +static const unsigned int poly_64[] = { 64, 4, 3, 1, 0 }; + +/* sigh, GCM defines the bit strings exactly backwards from everything else */ +static void +gcm_reverse(unsigned char *target, const unsigned char *src, + unsigned int blocksize) +{ + unsigned int i; + for (i=0; i < blocksize; i++) { + target[blocksize-i-1] = gcm_byte_rev[src[i]]; + } +} + +/* Initialize a gcmHashContext */ +static SECStatus +gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, + unsigned int blocksize) +{ + mp_err err = MP_OKAY; + unsigned char H_rev[MAX_BLOCK_SIZE]; + + MP_DIGITS(&ghash->H) = 0; + MP_DIGITS(&ghash->X) = 0; + MP_DIGITS(&ghash->C_i) = 0; + CHECK_MPI_OK( mp_init(&ghash->H) ); + CHECK_MPI_OK( mp_init(&ghash->X) ); + CHECK_MPI_OK( mp_init(&ghash->C_i) ); + + mp_zero(&ghash->X); + gcm_reverse(H_rev, H, blocksize); + CHECK_MPI_OK( mp_read_unsigned_octets(&ghash->H, H_rev, blocksize) ); + + /* set the irreducible polynomial. Each blocksize has its own polynomial. + * for now only blocksizes 16 (=128 bits) and 8 (=64 bits) are defined */ + switch (blocksize) { + case 16: /* 128 bits */ + ghash->poly = poly_128; + break; + case 8: /* 64 bits */ + ghash->poly = poly_64; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + ghash->cLen = 0; + ghash->bufLen = 0; + ghash->m = 0; + PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf)); + return SECSuccess; +cleanup: + gcmHash_DestroyContext(ghash, PR_FALSE); + return SECFailure; +} + +/* Destroy a HashContext (Note we zero the digits so this function + * is idempotent if called with freeit == PR_FALSE */ +static void +gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit) +{ + mp_clear(&ghash->H); + mp_clear(&ghash->X); + mp_clear(&ghash->C_i); + MP_DIGITS(&ghash->H) = 0; + MP_DIGITS(&ghash->X) = 0; + MP_DIGITS(&ghash->C_i) = 0; + if (freeit) { + PORT_Free(ghash); + } +} + +static SECStatus +gcm_getX(gcmHashContext *ghash, unsigned char *T, unsigned int blocksize) +{ + int len; + mp_err err; + unsigned char tmp_buf[MAX_BLOCK_SIZE]; + unsigned char *X; + + len = mp_unsigned_octet_size(&ghash->X); + if (len <= 0) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + X = tmp_buf; + PORT_Assert((unsigned int)len <= blocksize); + if ((unsigned int)len > blocksize) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* zero pad the result */ + if (len != blocksize) { + PORT_Memset(X,0,blocksize-len); + X += blocksize-len; + } + + err = mp_to_unsigned_octets(&ghash->X, X, len); + if (err < 0) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + gcm_reverse(T, X, blocksize); + return SECSuccess; +} + +static SECStatus +gcm_HashMult(gcmHashContext *ghash, const unsigned char *buf, + unsigned int count, unsigned int blocksize) +{ + SECStatus rv = SECFailure; + mp_err err = MP_OKAY; + unsigned char tmp_buf[MAX_BLOCK_SIZE]; + unsigned int i; + + for (i=0; i < count; i++, buf += blocksize) { + ghash->m++; + gcm_reverse(tmp_buf, buf, blocksize); + CHECK_MPI_OK(mp_read_unsigned_octets(&ghash->C_i, tmp_buf, blocksize)); + CHECK_MPI_OK(mp_badd(&ghash->X, &ghash->C_i, &ghash->C_i)); + /* + * Looking to speed up GCM, this the the place to do it. + * There are two areas that can be exploited to speed up this code. + * + * 1) H is a constant in this multiply. We can precompute H * (0 - 255) + * at init time and this becomes an blockize xors of our table lookup. + * + * 2) poly is a constant for each blocksize. We can calculate the + * modulo reduction by a series of adds and shifts. + * + * For now we are after functionality, so we will go ahead and use + * the builtin bmulmod from mpi + */ + CHECK_MPI_OK(mp_bmulmod(&ghash->C_i, &ghash->H, + ghash->poly, &ghash->X)); + GCM_TRACE_X(ghash, "X%d = ") + } + rv = SECSuccess; +cleanup: + if (rv != SECSuccess) { + MP_TO_SEC_ERROR(err); + } + return rv; +} + +static void +gcm_zeroX(gcmHashContext *ghash) +{ + mp_zero(&ghash->X); + ghash->m = 0; +} + +#endif + +#ifdef GCM_USE_ALGORITHM_1 +/* use algorithm 1 of McGrew & Viega "The Galois/Counter Mode of Operation" */ + +#define GCM_ARRAY_SIZE (MAX_BLOCK_SIZE/sizeof(unsigned long)) + +struct gcmHashContextStr { + unsigned long H[GCM_ARRAY_SIZE]; + unsigned long X[GCM_ARRAY_SIZE]; + unsigned long R; + unsigned char buffer[MAX_BLOCK_SIZE]; + unsigned int bufLen; + int m; + unsigned char counterBuf[2*GCM_HASH_LEN_LEN]; + PRUint64 cLen; +}; + +static void +gcm_bytes_to_longs(unsigned long *l, const unsigned char *c, unsigned int len) +{ + int i,j; + int array_size = len/sizeof(unsigned long); + + PORT_Assert(len % sizeof(unsigned long) == 0); + for (i=0; i < array_size; i++) { + unsigned long tmp = 0; + int byte_offset = i * sizeof(unsigned long); + for (j=sizeof(unsigned long)-1; j >= 0; j--) { + tmp = (tmp << PR_BITS_PER_BYTE) | gcm_byte_rev[c[byte_offset+j]]; + } + l[i] = tmp; + } +} + +static void +gcm_longs_to_bytes(const unsigned long *l, unsigned char *c, unsigned int len) +{ + int i,j; + int array_size = len/sizeof(unsigned long); + + PORT_Assert(len % sizeof(unsigned long) == 0); + for (i=0; i < array_size; i++) { + unsigned long tmp = l[i]; + int byte_offset = i * sizeof(unsigned long); + for (j=0; j < sizeof(unsigned long); j++) { + c[byte_offset+j] = gcm_byte_rev[tmp & 0xff]; + tmp = (tmp >> PR_BITS_PER_BYTE); + } + } +} + + +/* Initialize a gcmHashContext */ +static SECStatus +gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, + unsigned int blocksize) +{ + PORT_Memset(ghash->X, 0, sizeof(ghash->X)); + PORT_Memset(ghash->H, 0, sizeof(ghash->H)); + gcm_bytes_to_longs(ghash->H, H, blocksize); + + /* set the irreducible polynomial. Each blocksize has it's own polynommial + * for now only blocksizes 16 (=128 bits) and 8 (=64 bits) are defined */ + switch (blocksize) { + case 16: /* 128 bits */ + ghash->R = (unsigned long) 0x87; /* x^7 + x^2 + x +1 */ + break; + case 8: /* 64 bits */ + ghash->R = (unsigned long) 0x1b; /* x^4 + x^3 + x + 1 */ + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + ghash->cLen = 0; + ghash->bufLen = 0; + ghash->m = 0; + PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf)); + return SECSuccess; +cleanup: + return SECFailure; +} + +/* Destroy a HashContext (Note we zero the digits so this function + * is idempotent if called with freeit == PR_FALSE */ +static void +gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit) +{ + if (freeit) { + PORT_Free(ghash); + } +} + +static unsigned long +gcm_shift_one(unsigned long *t, unsigned int count) +{ + unsigned long carry = 0; + unsigned long nextcarry = 0; + unsigned int i; + for (i=0; i < count; i++) { + nextcarry = t[i] >> ((sizeof(unsigned long)*PR_BITS_PER_BYTE)-1); + t[i] = (t[i] << 1) | carry; + carry = nextcarry; + } + return carry; +} + +static SECStatus +gcm_getX(gcmHashContext *ghash, unsigned char *T, unsigned int blocksize) +{ + gcm_longs_to_bytes(ghash->X, T, blocksize); + return SECSuccess; +} + +#define GCM_XOR(t, s, len) \ + for (l=0; l < len; l++) t[l] ^= s[l] + +static SECStatus +gcm_HashMult(gcmHashContext *ghash, const unsigned char *buf, + unsigned int count, unsigned int blocksize) +{ + unsigned long C_i[GCM_ARRAY_SIZE]; + unsigned int arraysize = blocksize/sizeof(unsigned long); + unsigned int i, j, k, l; + + for (i=0; i < count; i++, buf += blocksize) { + ghash->m++; + gcm_bytes_to_longs(C_i, buf, blocksize); + GCM_XOR(C_i, ghash->X, arraysize); + /* multiply X = C_i * H */ + PORT_Memset(ghash->X, 0, sizeof(ghash->X)); + for (j=0; j < arraysize; j++) { + unsigned long H = ghash->H[j]; + for (k=0; k < sizeof(unsigned long)*PR_BITS_PER_BYTE; k++) { + if (H & 1) { + GCM_XOR(ghash->X, C_i, arraysize); + } + if (gcm_shift_one(C_i, arraysize)) { + C_i[0] = C_i[0] ^ ghash->R; + } + H = H >> 1; + } + } + GCM_TRACE_X(ghash, "X%d = ") + } + return SECSuccess; +} + + +static void +gcm_zeroX(gcmHashContext *ghash) +{ + PORT_Memset(ghash->X, 0, sizeof(ghash->X)); + ghash->m = 0; +} +#endif + +/* + * implement GCM GHASH using the freebl GHASH function. The gcm_HashMult + * function always takes blocksize lengths of data. gcmHash_Update will + * format the data properly. + */ +static SECStatus +gcmHash_Update(gcmHashContext *ghash, const unsigned char *buf, + unsigned int len, unsigned int blocksize) +{ + unsigned int blocks; + SECStatus rv; + + ghash->cLen += (len*PR_BITS_PER_BYTE); + + /* first deal with the current buffer of data. Try to fill it out so + * we can hash it */ + if (ghash->bufLen) { + unsigned int needed = PR_MIN(len, blocksize - ghash->bufLen); + PORT_Memcpy(ghash->buffer+ghash->bufLen, buf, needed); + buf += needed; + len -= needed; + ghash->bufLen += needed; + if (len == 0) { + /* didn't add enough to hash the data, nothing more do do */ + return SECSuccess; + } + PORT_Assert(ghash->bufLen == blocksize); + /* hash the buffer and clear it */ + rv = gcm_HashMult(ghash, ghash->buffer, 1, blocksize); + PORT_Memset(ghash->buffer, 0, blocksize); + ghash->bufLen = 0; + if (rv != SECSuccess) { + return SECFailure; + } + } + /* now hash any full blocks remaining in the data stream */ + blocks = len/blocksize; + if (blocks) { + rv = gcm_HashMult(ghash, buf, blocks, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + buf += blocks*blocksize; + len -= blocks*blocksize; + } + + /* save any remainder in the buffer to be hashed with the next call */ + if (len != 0) { + PORT_Memcpy(ghash->buffer, buf, len); + ghash->bufLen = len; + } + return SECSuccess; +} + +/* + * write out any partial blocks zero padded through the GHASH engine, + * save the lengths for the final completion of the hash + */ +static SECStatus +gcmHash_Sync(gcmHashContext *ghash, unsigned int blocksize) +{ + int i; + SECStatus rv; + + /* copy the previous counter to the upper block */ + PORT_Memcpy(ghash->counterBuf, &ghash->counterBuf[GCM_HASH_LEN_LEN], + GCM_HASH_LEN_LEN); + /* copy the current counter in the lower block */ + for (i=0; i < GCM_HASH_LEN_LEN; i++) { + ghash->counterBuf[GCM_HASH_LEN_LEN+i] = + (ghash->cLen >> ((GCM_HASH_LEN_LEN-1-i)*PR_BITS_PER_BYTE)) & 0xff; + } + ghash->cLen = 0; + + /* now zero fill the buffer and hash the last block */ + if (ghash->bufLen) { + PORT_Memset(ghash->buffer+ghash->bufLen, 0, blocksize - ghash->bufLen); + rv = gcm_HashMult(ghash, ghash->buffer, 1, blocksize); + PORT_Memset(ghash->buffer, 0, blocksize); + ghash->bufLen = 0; + if (rv != SECSuccess) { + return SECFailure; + } + } + return SECSuccess; +} + +/* + * This does the final sync, hashes the lengths, then returns + * "T", the hashed output. + */ +static SECStatus +gcmHash_Final(gcmHashContext *ghash, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + unsigned int blocksize) +{ + unsigned char T[MAX_BLOCK_SIZE]; + SECStatus rv; + + rv = gcmHash_Sync(ghash, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = gcm_HashMult(ghash, ghash->counterBuf, (GCM_HASH_LEN_LEN*2)/blocksize, + blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + + GCM_TRACE_X(ghash, "GHASH(H,A,C) = ") + + rv = gcm_getX(ghash, T, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + + if (maxout > blocksize) maxout = blocksize; + PORT_Memcpy(outbuf, T, maxout); + *outlen = maxout; + return SECSuccess; +} + +SECStatus +gcmHash_Reset(gcmHashContext *ghash, const unsigned char *AAD, + unsigned int AADLen, unsigned int blocksize) +{ + SECStatus rv; + + ghash->cLen = 0; + PORT_Memset(ghash->counterBuf, 0, GCM_HASH_LEN_LEN*2); + ghash->bufLen = 0; + gcm_zeroX(ghash); + + /* now kick things off by hashing the Additional Authenticated Data */ + if (AADLen != 0) { + rv = gcmHash_Update(ghash, AAD, AADLen, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + rv = gcmHash_Sync(ghash, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + } + return SECSuccess; +} + +/************************************************************************** + * Now implement the GCM using gcmHash and CTR * + **************************************************************************/ + +/* state to handle the full GCM operation (hash and counter) */ +struct GCMContextStr { + gcmHashContext ghash_context; + CTRContext ctr_context; + unsigned long tagBits; + unsigned char tagKey[MAX_BLOCK_SIZE]; +}; + +GCMContext * +GCM_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *params, unsigned int blocksize) +{ + GCMContext *gcm = NULL; + gcmHashContext *ghash; + unsigned char H[MAX_BLOCK_SIZE]; + unsigned int tmp; + PRBool freeCtr = PR_FALSE; + PRBool freeHash = PR_FALSE; + const CK_AES_GCM_PARAMS *gcmParams = (const CK_AES_GCM_PARAMS *)params; + CK_AES_CTR_PARAMS ctrParams; + SECStatus rv; + + if (blocksize > MAX_BLOCK_SIZE || blocksize > sizeof(ctrParams.cb)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + gcm = PORT_ZNew(GCMContext); + if (gcm == NULL) { + return NULL; + } + /* first fill in the ghash context */ + ghash = &gcm->ghash_context; + PORT_Memset(H, 0, blocksize); + rv = (*cipher)(context, H, &tmp, blocksize, H, blocksize, blocksize); + if (rv != SECSuccess) { + goto loser; + } + rv = gcmHash_InitContext(ghash, H, blocksize); + if (rv != SECSuccess) { + goto loser; + } + freeHash = PR_TRUE; + + /* fill in the Counter context */ + ctrParams.ulCounterBits = 32; + PORT_Memset(ctrParams.cb, 0, sizeof(ctrParams.cb)); + if ((blocksize == 8) && (gcmParams->ulIvLen == 4)) { + ctrParams.cb[3] = 1; + PORT_Memcpy(&ctrParams.cb[4], gcmParams->pIv, gcmParams->ulIvLen); + } else if ((blocksize == 16) && (gcmParams->ulIvLen == 12)) { + PORT_Memcpy(ctrParams.cb, gcmParams->pIv, gcmParams->ulIvLen); + ctrParams.cb[blocksize-1] = 1; + } else { + rv = gcmHash_Update(ghash, gcmParams->pIv, gcmParams->ulIvLen, + blocksize); + if (rv != SECSuccess) { + goto loser; + } + rv = gcmHash_Final(ghash, ctrParams.cb, &tmp, blocksize, blocksize); + if (rv != SECSuccess) { + goto loser; + } + } + rv = CTR_InitContext(&gcm->ctr_context, context, cipher, + (unsigned char *)&ctrParams, blocksize); + if (rv != SECSuccess) { + goto loser; + } + freeCtr = PR_TRUE; + + /* fill in the gcm structure */ + gcm->tagBits = gcmParams->ulTagBits; /* save for final step */ + /* calculate the final tag key. NOTE: gcm->tagKey is zero to start with. + * if this assumption changes, we would need to explicitly clear it here */ + rv = CTR_Update(&gcm->ctr_context, gcm->tagKey, &tmp, blocksize, + gcm->tagKey, blocksize, blocksize); + if (rv != SECSuccess) { + goto loser; + } + + /* finally mix in the AAD data */ + rv = gcmHash_Reset(ghash, gcmParams->pAAD, gcmParams->ulAADLen, blocksize); + if (rv != SECSuccess) { + goto loser; + } + + return gcm; + +loser: + if (freeCtr) { + CTR_DestroyContext(&gcm->ctr_context, PR_FALSE); + } + if (freeHash) { + gcmHash_DestroyContext(&gcm->ghash_context, PR_FALSE); + } + if (gcm) { + PORT_Free(gcm); + } + return NULL; +} + +void +GCM_DestroyContext(GCMContext *gcm, PRBool freeit) +{ + /* these two are statically allocated and will be freed when we free + * gcm. call their destroy functions to free up any locally + * allocated data (like mp_int's) */ + CTR_DestroyContext(&gcm->ctr_context, PR_FALSE); + gcmHash_DestroyContext(&gcm->ghash_context, PR_FALSE); + if (freeit) { + PORT_Free(gcm); + } +} + +static SECStatus +gcm_GetTag(GCMContext *gcm, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + unsigned int blocksize) +{ + unsigned int tagBytes; + unsigned int extra; + unsigned int i; + SECStatus rv; + + tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE; + extra = tagBytes*PR_BITS_PER_BYTE - gcm->tagBits; + + if (outbuf == NULL) { + *outlen = tagBytes; + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + + if (maxout < tagBytes) { + *outlen = tagBytes; + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + maxout = tagBytes; + rv = gcmHash_Final(&gcm->ghash_context, outbuf, outlen, maxout, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + + GCM_TRACE_BLOCK("GHASH=", outbuf, blocksize); + GCM_TRACE_BLOCK("Y0=", gcm->tagKey, blocksize); + for (i=0; i < *outlen; i++) { + outbuf[i] ^= gcm->tagKey[i]; + } + GCM_TRACE_BLOCK("Y0=", gcm->tagKey, blocksize); + GCM_TRACE_BLOCK("T=", outbuf, blocksize); + /* mask off any extra bits we got */ + if (extra) { + outbuf[tagBytes-1] &= ~((1 << extra)-1); + } + return SECSuccess; +} + + +/* + * See The Galois/Counter Mode of Operation, McGrew and Viega. + * GCM is basically counter mode with a specific initialization and + * built in macing operation. + */ +SECStatus +GCM_EncryptUpdate(GCMContext *gcm, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize) +{ + SECStatus rv; + unsigned int tagBytes; + unsigned int len; + + tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE; + if (UINT_MAX - inlen < tagBytes) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + if (maxout < inlen + tagBytes) { + *outlen = inlen + tagBytes; + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + + rv = CTR_Update(&gcm->ctr_context, outbuf, outlen, maxout, + inbuf, inlen, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + rv = gcmHash_Update(&gcm->ghash_context, outbuf, *outlen, blocksize); + if (rv != SECSuccess) { + PORT_Memset(outbuf, 0, *outlen); /* clear the output buffer */ + *outlen = 0; + return SECFailure; + } + rv = gcm_GetTag(gcm, outbuf + *outlen, &len, maxout - *outlen, blocksize); + if (rv != SECSuccess) { + PORT_Memset(outbuf, 0, *outlen); /* clear the output buffer */ + *outlen = 0; + return SECFailure; + }; + *outlen += len; + return SECSuccess; +} + +/* + * See The Galois/Counter Mode of Operation, McGrew and Viega. + * GCM is basically counter mode with a specific initialization and + * built in macing operation. NOTE: the only difference between Encrypt + * and Decrypt is when we calculate the mac. That is because the mac must + * always be calculated on the cipher text, not the plain text, so for + * encrypt, we do the CTR update first and for decrypt we do the mac first. + */ +SECStatus +GCM_DecryptUpdate(GCMContext *gcm, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize) +{ + SECStatus rv; + unsigned int tagBytes; + unsigned char tag[MAX_BLOCK_SIZE]; + const unsigned char *intag; + unsigned int len; + + tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE; + + /* get the authentication block */ + if (inlen < tagBytes) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + inlen -= tagBytes; + intag = inbuf + inlen; + + /* verify the block */ + rv = gcmHash_Update(&gcm->ghash_context, inbuf, inlen, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + rv = gcm_GetTag(gcm, tag, &len, blocksize, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + /* Don't decrypt if we can't authenticate the encrypted data! + * This assumes that if tagBits is not a multiple of 8, intag will + * preserve the masked off missing bits. */ + if (NSS_SecureMemcmp(tag, intag, tagBytes) != 0) { + /* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */ + PORT_SetError(SEC_ERROR_BAD_DATA); + return SECFailure; + } + /* finish the decryption */ + return CTR_Update(&gcm->ctr_context, outbuf, outlen, maxout, + inbuf, inlen, blocksize); +} diff --git a/mozilla/security/nss/lib/freebl/gcm.h b/mozilla/security/nss/lib/freebl/gcm.h new file mode 100644 index 00000000000..4dd71b4c8fc --- /dev/null +++ b/mozilla/security/nss/lib/freebl/gcm.h @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GCM_H +#define GCM_H 1 + +#include "blapii.h" + +typedef struct GCMContextStr GCMContext; + +/* + * The context argument is the inner cipher context to use with cipher. The + * GCMContext does not own context. context needs to remain valid for as long + * as the GCMContext is valid. + * + * The cipher argument is a block cipher in the ECB encrypt mode. + */ +GCMContext * GCM_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *params, unsigned int blocksize); +void GCM_DestroyContext(GCMContext *gcm, PRBool freeit); +SECStatus GCM_EncryptUpdate(GCMContext *gcm, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize); +SECStatus GCM_DecryptUpdate(GCMContext *gcm, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize); + +#endif diff --git a/mozilla/security/nss/lib/freebl/manifest.mn b/mozilla/security/nss/lib/freebl/manifest.mn index c6e35385b7e..f2b462aa323 100644 --- a/mozilla/security/nss/lib/freebl/manifest.mn +++ b/mozilla/security/nss/lib/freebl/manifest.mn @@ -99,6 +99,9 @@ CSRCS = \ desblapi.c \ des.c \ drbg.c \ + cts.c \ + ctr.c \ + gcm.c \ rijndael.c \ aeskeywrap.c \ camellia.c \ diff --git a/mozilla/security/nss/lib/freebl/mpi/mp_gf2m-priv.h b/mozilla/security/nss/lib/freebl/mpi/mp_gf2m-priv.h index 6e6ea488b0c..b9c2f3bb164 100644 --- a/mozilla/security/nss/lib/freebl/mpi/mp_gf2m-priv.h +++ b/mozilla/security/nss/lib/freebl/mpi/mp_gf2m-priv.h @@ -11,8 +11,14 @@ extern const mp_digit mp_gf2m_sqr_tb[16]; #if defined(MP_USE_UINT_DIGIT) #define MP_DIGIT_BITS 32 +/* enable fast divide and mod operations on MP_DIGIT_BITS */ +#define MP_DIGIT_BITS_LOG_2 5 +#define MP_DIGIT_BITS_MASK 0x1f #else #define MP_DIGIT_BITS 64 +/* enable fast divide and mod operations on MP_DIGIT_BITS */ +#define MP_DIGIT_BITS_LOG_2 6 +#define MP_DIGIT_BITS_MASK 0x3f #endif /* Platform-specific macros for fast binary polynomial squaring. */ diff --git a/mozilla/security/nss/lib/freebl/mpi/mp_gf2m.c b/mozilla/security/nss/lib/freebl/mpi/mp_gf2m.c index 3ed452afd36..e84f3a0440d 100644 --- a/mozilla/security/nss/lib/freebl/mpi/mp_gf2m.c +++ b/mozilla/security/nss/lib/freebl/mpi/mp_gf2m.c @@ -324,7 +324,8 @@ mp_bmod(const mp_int *a, const unsigned int p[], mp_int *r) z = MP_DIGITS(r); /* start reduction */ - dN = p[0] / MP_DIGIT_BITS; + /*dN = p[0] / MP_DIGIT_BITS; */ + dN = p[0] >> MP_DIGIT_BITS_LOG_2; used = MP_USED(r); for (j = used - 1; j > dN;) { @@ -338,9 +339,11 @@ mp_bmod(const mp_int *a, const unsigned int p[], mp_int *r) for (k = 1; p[k] > 0; k++) { /* reducing component t^p[k] */ n = p[0] - p[k]; - d0 = n % MP_DIGIT_BITS; + /*d0 = n % MP_DIGIT_BITS; */ + d0 = n & MP_DIGIT_BITS_MASK; d1 = MP_DIGIT_BITS - d0; - n /= MP_DIGIT_BITS; + /*n /= MP_DIGIT_BITS; */ + n >>= MP_DIGIT_BITS_LOG_2; z[j-n] ^= (zz>>d0); if (d0) z[j-n-1] ^= (zz<> d0); if (d0) @@ -359,19 +363,26 @@ mp_bmod(const mp_int *a, const unsigned int p[], mp_int *r) /* final round of reduction */ while (j == dN) { - d0 = p[0] % MP_DIGIT_BITS; + /* d0 = p[0] % MP_DIGIT_BITS; */ + d0 = p[0] & MP_DIGIT_BITS_MASK; zz = z[dN] >> d0; if (zz == 0) break; d1 = MP_DIGIT_BITS - d0; /* clear up the top d1 bits */ - if (d0) z[dN] = (z[dN] << d1) >> d1; + if (d0) { + z[dN] = (z[dN] << d1) >> d1; + } else { + z[dN] = 0; + } *z ^= zz; /* reduction t^0 component */ for (k = 1; p[k] > 0; k++) { /* reducing component t^p[k]*/ - n = p[k] / MP_DIGIT_BITS; - d0 = p[k] % MP_DIGIT_BITS; + /* n = p[k] / MP_DIGIT_BITS; */ + n = p[k] >> MP_DIGIT_BITS_LOG_2; + /* d0 = p[k] % MP_DIGIT_BITS; */ + d0 = p[k] & MP_DIGIT_BITS_MASK; d1 = MP_DIGIT_BITS - d0; z[n] ^= (zz << d0); tmp = zz >> d1; diff --git a/mozilla/security/nss/lib/freebl/rijndael.c b/mozilla/security/nss/lib/freebl/rijndael.c index 85113fb8a40..571c91539a8 100644 --- a/mozilla/security/nss/lib/freebl/rijndael.c +++ b/mozilla/security/nss/lib/freebl/rijndael.c @@ -1,7 +1,7 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* $Id: rijndael.c,v 1.27 2012-04-25 14:49:43 gerv%gerv.net Exp $ */ +/* $Id: rijndael.c,v 1.28 2012-09-28 22:46:32 rrelyea%redhat.com Exp $ */ #ifdef FREEBL_NO_DEPEND #include "stubs.h" @@ -15,6 +15,10 @@ #include "blapi.h" #include "rijndael.h" +#include "cts.h" +#include "ctr.h" +#include "gcm.h" + #if USE_HW_AES #include "intel-aes.h" #include "mpi.h" @@ -956,8 +960,13 @@ AESContext * AES_AllocateContext(void) } -SECStatus -AES_InitContext(AESContext *cx, const unsigned char *key, unsigned int keysize, +/* +** Initialize a new AES context suitable for AES encryption/decryption in +** the ECB or CBC mode. +** "mode" the mode of operation, which must be NSS_AES or NSS_AES_CBC +*/ +static SECStatus +aes_InitContext(AESContext *cx, const unsigned char *key, unsigned int keysize, const unsigned char *iv, int mode, unsigned int encrypt, unsigned int blocksize) { @@ -1018,18 +1027,20 @@ AES_InitContext(AESContext *cx, const unsigned char *key, unsigned int keysize, memcpy(cx->iv, iv, blocksize); #if USE_HW_AES if (use_hw_aes) { - cx->worker = intel_aes_cbc_worker(encrypt, keysize); + cx->worker = (freeblCipherFunc) + intel_aes_cbc_worker(encrypt, keysize); } else #endif - cx->worker = (encrypt + cx->worker = (freeblCipherFunc) (encrypt ? &rijndael_encryptCBC : &rijndael_decryptCBC); } else { #if USE_HW_AES if (use_hw_aes) { - cx->worker = intel_aes_ecb_worker(encrypt, keysize); + cx->worker = (freeblCipherFunc) + intel_aes_ecb_worker(encrypt, keysize); } else #endif - cx->worker = (encrypt + cx->worker = (freeblCipherFunc) (encrypt ? &rijndael_encryptECB : &rijndael_decryptECB); } PORT_Assert((cx->Nb * (cx->Nr + 1)) <= RIJNDAEL_MAX_EXP_KEY_SIZE); @@ -1062,11 +1073,77 @@ AES_InitContext(AESContext *cx, const unsigned char *key, unsigned int keysize, goto cleanup; } } + cx->worker_cx = cx; + cx->destroy = NULL; + cx->isBlock = PR_TRUE; return SECSuccess; cleanup: return SECFailure; } +SECStatus +AES_InitContext(AESContext *cx, const unsigned char *key, unsigned int keysize, + const unsigned char *iv, int mode, unsigned int encrypt, + unsigned int blocksize) +{ + int basemode = mode; + PRBool baseencrypt = encrypt; + SECStatus rv; + + switch (mode) { + case NSS_AES_CTS: + basemode = NSS_AES_CBC; + break; + case NSS_AES_GCM: + case NSS_AES_CTR: + basemode = NSS_AES; + baseencrypt = PR_TRUE; + break; + } + rv = aes_InitContext(cx, key, keysize, iv, basemode, + baseencrypt, blocksize); + if (rv != SECSuccess) { + AES_DestroyContext(cx, PR_TRUE); + return rv; + } + + /* finally, set up any mode specific contexts */ + switch (mode) { + case NSS_AES_CTS: + cx->worker_cx = CTS_CreateContext(cx, cx->worker, iv, blocksize); + cx->worker = (freeblCipherFunc) + (encrypt ? CTS_EncryptUpdate : CTS_DecryptUpdate); + cx->destroy = (freeblDestroyFunc) CTS_DestroyContext; + cx->isBlock = PR_FALSE; + break; + case NSS_AES_GCM: + cx->worker_cx = GCM_CreateContext(cx, cx->worker, iv, blocksize); + cx->worker = (freeblCipherFunc) + (encrypt ? GCM_EncryptUpdate : GCM_DecryptUpdate); + cx->destroy = (freeblDestroyFunc) GCM_DestroyContext; + cx->isBlock = PR_FALSE; + break; + case NSS_AES_CTR: + cx->worker_cx = CTR_CreateContext(cx, cx->worker, iv, blocksize); + cx->worker = (freeblCipherFunc) CTR_Update ; + cx->destroy = (freeblDestroyFunc) CTR_DestroyContext; + cx->isBlock = PR_FALSE; + break; + default: + /* everything has already been set up by aes_InitContext, just + * return */ + return SECSuccess; + } + /* check to see if we succeeded in getting the worker context */ + if (cx->worker_cx == NULL) { + /* no, just destroy the existing context */ + cx->destroy = NULL; /* paranoia, though you can see a dozen lines */ + /* below that this isn't necessary */ + AES_DestroyContext(cx, PR_TRUE); + return SECFailure; + } + return SECSuccess; +} /* AES_CreateContext * @@ -1099,6 +1176,9 @@ void AES_DestroyContext(AESContext *cx, PRBool freeit) { /* memset(cx, 0, sizeof *cx); */ + if (cx->worker_cx && cx->destroy) { + (*cx->destroy)(cx->worker_cx, PR_TRUE); + } if (freeit) PORT_Free(cx); } @@ -1121,7 +1201,7 @@ AES_Encrypt(AESContext *cx, unsigned char *output, return SECFailure; } blocksize = 4 * cx->Nb; - if (inputLen % blocksize != 0) { + if (cx->isBlock && (inputLen % blocksize != 0)) { PORT_SetError(SEC_ERROR_INPUT_LEN); return SECFailure; } @@ -1130,7 +1210,7 @@ AES_Encrypt(AESContext *cx, unsigned char *output, return SECFailure; } *outputLen = inputLen; - return (*cx->worker)(cx, output, outputLen, maxOutputLen, + return (*cx->worker)(cx->worker_cx, output, outputLen, maxOutputLen, input, inputLen, blocksize); } @@ -1152,7 +1232,7 @@ AES_Decrypt(AESContext *cx, unsigned char *output, return SECFailure; } blocksize = 4 * cx->Nb; - if (inputLen % blocksize != 0) { + if (cx->isBlock && (inputLen % blocksize != 0)) { PORT_SetError(SEC_ERROR_INPUT_LEN); return SECFailure; } @@ -1161,6 +1241,6 @@ AES_Decrypt(AESContext *cx, unsigned char *output, return SECFailure; } *outputLen = inputLen; - return (*cx->worker)(cx, output, outputLen, maxOutputLen, + return (*cx->worker)(cx->worker_cx, output, outputLen, maxOutputLen, input, inputLen, blocksize); } diff --git a/mozilla/security/nss/lib/freebl/rijndael.h b/mozilla/security/nss/lib/freebl/rijndael.h index ab46b2b8d80..a6608819deb 100644 --- a/mozilla/security/nss/lib/freebl/rijndael.h +++ b/mozilla/security/nss/lib/freebl/rijndael.h @@ -1,19 +1,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* $Id: rijndael.h,v 1.12 2012-04-25 14:49:43 gerv%gerv.net Exp $ */ +/* $Id: rijndael.h,v 1.13 2012-09-28 22:46:32 rrelyea%redhat.com Exp $ */ #ifndef _RIJNDAEL_H_ #define _RIJNDAEL_H_ 1 +#include "blapii.h" + #define RIJNDAEL_MIN_BLOCKSIZE 16 /* bytes */ #define RIJNDAEL_MAX_BLOCKSIZE 32 /* bytes */ -typedef SECStatus AESFunc(AESContext *cx, unsigned char *output, - unsigned int *outputLen, unsigned int maxOutputLen, - const unsigned char *input, unsigned int inputLen, - unsigned int blocksize); - typedef SECStatus AESBlockFunc(AESContext *cx, unsigned char *output, const unsigned char *input); @@ -49,15 +46,23 @@ typedef SECStatus AESBlockFunc(AESContext *cx, * Nb - the number of bytes in a block, specified by user * Nr - the number of rounds, specified by a table * expandedKey - the round keys in 4-byte words, the length is Nr * Nb - * worker - the encryption/decryption function to use with this context + * worker - the encryption/decryption function to use with worker_cx + * destroy - if not NULL, the destroy function to use with worker_cx + * worker_cx - the context for worker and destroy + * isBlock - is the mode of operation a block cipher or a stream cipher? */ struct AESContextStr { unsigned int Nb; unsigned int Nr; - AESFunc *worker; + freeblCipherFunc worker; + /* NOTE: The offsets of iv and expandedKey are hardcoded in intel-aes.s. + * Don't add new members before them without updating intel-aes.s. */ unsigned char iv[RIJNDAEL_MAX_BLOCKSIZE]; PRUint32 expandedKey[RIJNDAEL_MAX_EXP_KEY_SIZE]; + freeblDestroyFunc destroy; + void *worker_cx; + PRBool isBlock; }; #endif /* _RIJNDAEL_H_ */ diff --git a/mozilla/security/nss/lib/softoken/pkcs11.c b/mozilla/security/nss/lib/softoken/pkcs11.c index f49d9118c5d..226b9c60ba5 100644 --- a/mozilla/security/nss/lib/softoken/pkcs11.c +++ b/mozilla/security/nss/lib/softoken/pkcs11.c @@ -347,6 +347,9 @@ static const struct mechanismList mechanisms[] = { {CKM_AES_MAC, {16, 32, CKF_SN_VR}, PR_TRUE}, {CKM_AES_MAC_GENERAL, {16, 32, CKF_SN_VR}, PR_TRUE}, {CKM_AES_CBC_PAD, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE}, + {CKM_AES_CTS, {16, 32, CKF_EN_DE}, PR_TRUE}, + {CKM_AES_CTR, {16, 32, CKF_EN_DE}, PR_TRUE}, + {CKM_AES_GCM, {16, 32, CKF_EN_DE}, PR_TRUE}, /* ------------------------- Camellia Operations --------------------- */ {CKM_CAMELLIA_KEY_GEN, {16, 32, CKF_GENERATE}, PR_TRUE}, {CKM_CAMELLIA_ECB, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE}, @@ -611,8 +614,8 @@ sftk_hasNullPassword(SFTKSlot *slot, SFTKDBHandle *keydb) * value and len */ CK_RV -sftk_defaultAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type,void *value, - unsigned int len) +sftk_defaultAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type, + const void *value, unsigned int len) { if ( !sftk_hasAttribute(object, type)) { return sftk_AddAttributeType(object,type,value,len); diff --git a/mozilla/security/nss/lib/softoken/pkcs11c.c b/mozilla/security/nss/lib/softoken/pkcs11c.c index 660091550e6..c312283dd60 100644 --- a/mozilla/security/nss/lib/softoken/pkcs11c.c +++ b/mozilla/security/nss/lib/softoken/pkcs11c.c @@ -436,6 +436,25 @@ sftk_InitGeneric(SFTKSession *session,SFTKSessionContext **contextPtr, return CKR_OK; } +static int +sftk_aes_mode(CK_MECHANISM_TYPE mechanism) +{ + switch (mechanism) { + case CKM_AES_CBC_PAD: + case CKM_AES_CBC: + return NSS_AES_CBC; + case CKM_AES_ECB: + return NSS_AES; + case CKM_AES_CTS: + return NSS_AES_CTS; + case CKM_AES_CTR: + return NSS_AES_CTR; + case CKM_AES_GCM: + return NSS_AES_GCM; + } + return -1; +} + /** NSC_CryptInit initializes an encryption/Decryption operation. * * Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey. @@ -750,6 +769,9 @@ finish_des: case CKM_AES_ECB: case CKM_AES_CBC: context->blockSize = 16; + case CKM_AES_CTS: + case CKM_AES_CTR: + case CKM_AES_GCM: if (key_type != CKK_AES) { crv = CKR_KEY_TYPE_INCONSISTENT; break; @@ -762,7 +784,7 @@ finish_des: context->cipherInfo = AES_CreateContext( (unsigned char*)att->attrib.pValue, (unsigned char*)pMechanism->pParameter, - pMechanism->mechanism == CKM_AES_ECB ? NSS_AES : NSS_AES_CBC, + sftk_aes_mode(pMechanism->mechanism), isEncrypt, att->attrib.ulValueLen, 16); sftk_FreeAttribute(att); if (context->cipherInfo == NULL) { diff --git a/mozilla/security/nss/lib/softoken/pkcs11i.h b/mozilla/security/nss/lib/softoken/pkcs11i.h index e02e701add9..95fe3b75e29 100644 --- a/mozilla/security/nss/lib/softoken/pkcs11i.h +++ b/mozilla/security/nss/lib/softoken/pkcs11i.h @@ -573,8 +573,7 @@ extern SFTKAttribute *sftk_FindAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type); extern void sftk_FreeAttribute(SFTKAttribute *attribute); extern CK_RV sftk_AddAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type, - void *valPtr, - CK_ULONG length); + const void *valPtr, CK_ULONG length); extern CK_RV sftk_Attribute2SecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object, CK_ATTRIBUTE_TYPE type); extern CK_RV sftk_MultipleAttribute2SecItem(PLArenaPool *arena, @@ -600,9 +599,9 @@ extern void sftk_nullAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type); extern CK_RV sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, CK_ULONG *longData); extern CK_RV sftk_forceAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, - void *value, unsigned int len); + const void *value, unsigned int len); extern CK_RV sftk_defaultAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type, - void *value, unsigned int len); + const void *value, unsigned int len); extern unsigned int sftk_MapTrust(CK_TRUST trust, PRBool clientAuth); extern SFTKObject *sftk_NewObject(SFTKSlot *slot); diff --git a/mozilla/security/nss/lib/softoken/pkcs11u.c b/mozilla/security/nss/lib/softoken/pkcs11u.c index 4681767c4fa..a864182076f 100644 --- a/mozilla/security/nss/lib/softoken/pkcs11u.c +++ b/mozilla/security/nss/lib/softoken/pkcs11u.c @@ -24,7 +24,7 @@ */ static SFTKAttribute * sftk_NewAttribute(SFTKObject *object, - CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG len) + CK_ATTRIBUTE_TYPE type, const void *value, CK_ULONG len) { SFTKAttribute *attribute; @@ -496,7 +496,7 @@ sftk_nullAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type) static CK_RV sftk_forceTokenAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type, - void *value, unsigned int len) + const void *value, unsigned int len) { CK_ATTRIBUTE attribute; SFTKDBHandle *dbHandle = NULL; @@ -523,8 +523,8 @@ sftk_forceTokenAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type, * force an attribute to a specifc value. */ CK_RV -sftk_forceAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type, void *value, - unsigned int len) +sftk_forceAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type, + const void *value, unsigned int len) { SFTKAttribute *attribute; void *att_val = NULL; @@ -783,8 +783,8 @@ sftk_DeleteAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type) } CK_RV -sftk_AddAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type,void *valPtr, - CK_ULONG length) +sftk_AddAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type, + const void *valPtr, CK_ULONG length) { SFTKAttribute *attribute; attribute = sftk_NewAttribute(object,type,valPtr,length); diff --git a/mozilla/security/nss/lib/util/pkcs11t.h b/mozilla/security/nss/lib/util/pkcs11t.h index 155b1c1d544..3fc0df5d780 100644 --- a/mozilla/security/nss/lib/util/pkcs11t.h +++ b/mozilla/security/nss/lib/util/pkcs11t.h @@ -885,6 +885,12 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_AES_MAC 0x00001083 #define CKM_AES_MAC_GENERAL 0x00001084 #define CKM_AES_CBC_PAD 0x00001085 +/* new for v2.20 amendment 3 */ +#define CKM_AES_CTR 0x00001086 +/* new for v2.30 */ +#define CKM_AES_GCM 0x00001087 +#define CKM_AES_CCM 0x00001088 +#define CKM_AES_CTS 0x00001089 /* BlowFish and TwoFish are new for v2.20 */ #define CKM_BLOWFISH_KEY_GEN 0x00001090 @@ -1489,6 +1495,34 @@ typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS { typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR; +typedef struct CK_AES_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +} CK_AES_CTR_PARAMS; + +typedef CK_AES_CTR_PARAMS CK_PTR CK_AES_CTR_PARAMS_PTR; + +typedef struct CK_AES_GCM_PARAMS { + CK_BYTE_PTR pIv; + CK_ULONG ulIvLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +} CK_AES_GCM_PARAMS; + +typedef CK_AES_GCM_PARAMS CK_PTR CK_AES_GCM_PARAMS_PTR; + +typedef struct CK_AES_CCM_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE_PTR pNonce; + CK_ULONG ulNonceLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulMACLen; +} CK_AES_CCM_PARAMS; + +typedef CK_AES_CCM_PARAMS CK_PTR CK_AES_CCM_PARAMS_PTR; + /* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the * CKM_SKIPJACK_PRIVATE_WRAP mechanism */ /* CK_SKIPJACK_PRIVATE_WRAP_PARAMS is new for v2.0 */