diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/freebl/ctr.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/security/nss/lib/freebl/ctr.c b/security/nss/lib/freebl/ctr.c new file mode 100644 index 0000000000..239a60da24 --- /dev/null +++ b/security/nss/lib/freebl/ctr.c @@ -0,0 +1,276 @@ +/* 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" + +#ifdef USE_HW_AES +#ifdef NSS_X86_OR_X64 +#include "intel-aes.h" +#endif +#include "rijndael.h" +#endif + +#if defined(__ARM_NEON) || defined(__ARM_NEON__) +#include <arm_neon.h> +#endif + +SECStatus +CTR_InitContext(CTRContext *ctr, void *context, freeblCipherFunc cipher, + const unsigned char *param) +{ + const CK_AES_CTR_PARAMS *ctrParams = (const CK_AES_CTR_PARAMS *)param; + + if (ctrParams->ulCounterBits == 0 || + ctrParams->ulCounterBits > AES_BLOCK_SIZE * PR_BITS_PER_BYTE) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Invariant: 0 < ctr->bufPtr <= AES_BLOCK_SIZE */ + ctr->checkWrap = PR_FALSE; + ctr->bufPtr = AES_BLOCK_SIZE; /* no unused data in the buffer */ + ctr->cipher = cipher; + ctr->context = context; + ctr->counterBits = ctrParams->ulCounterBits; + if (AES_BLOCK_SIZE > sizeof(ctr->counter) || + AES_BLOCK_SIZE > sizeof(ctrParams->cb)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + PORT_Memcpy(ctr->counter, ctrParams->cb, AES_BLOCK_SIZE); + if (ctr->counterBits < 64) { + PORT_Memcpy(ctr->counterFirst, ctr->counter, AES_BLOCK_SIZE); + ctr->checkWrap = PR_TRUE; + } + return SECSuccess; +} + +CTRContext * +CTR_CreateContext(void *context, freeblCipherFunc cipher, + const unsigned char *param) +{ + 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); + 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; +#if defined(__ARM_NEON) || defined(__ARM_NEON__) + while (count >= 16) { + vst1q_u8(target, veorq_u8(vld1q_u8(x), vld1q_u8(y))); + target += 16; + x += 16; + y += 16; + count -= 16; + } +#endif + 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; + + // Limit block count to 2^counterBits - 2 + if (ctr->counterBits < (sizeof(unsigned int) * 8) && + inlen > ((1 << ctr->counterBits) - 2) * AES_BLOCK_SIZE) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + 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 (ctr->checkWrap) { + if (PORT_Memcmp(ctr->counter, ctr->counterFirst, blocksize) == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + } + 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 (ctr->checkWrap) { + if (PORT_Memcmp(ctr->counter, ctr->counterFirst, blocksize) == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + } + if (rv != SECSuccess) { + return SECFailure; + } + ctr_xor(outbuf, inbuf, ctr->buffer, inlen); + ctr->bufPtr = inlen; + *outlen += inlen; + return SECSuccess; +} + +#if defined(USE_HW_AES) && defined(_MSC_VER) && defined(NSS_X86_OR_X64) +SECStatus +CTR_Update_HW_AES(CTRContext *ctr, unsigned char *outbuf, + unsigned int *outlen, unsigned int maxout, + const unsigned char *inbuf, unsigned int inlen, + unsigned int blocksize) +{ + unsigned int fullblocks; + unsigned int tmp; + SECStatus rv; + + // Limit block count to 2^counterBits - 2 + if (ctr->counterBits < (sizeof(unsigned int) * 8) && + inlen > ((1 << ctr->counterBits) - 2) * AES_BLOCK_SIZE) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + 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); + } + + if (inlen >= blocksize) { + rv = intel_aes_ctr_worker(((AESContext *)(ctr->context))->Nr)( + ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + fullblocks = (inlen / blocksize) * blocksize; + *outlen += fullblocks; + outbuf += fullblocks; + inbuf += fullblocks; + inlen -= fullblocks; + } + + 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; +} +#endif |