michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef FREEBL_NO_DEPEND michael@0: #include "stubs.h" michael@0: #endif michael@0: #include "prtypes.h" michael@0: #include "blapit.h" michael@0: #include "blapii.h" michael@0: #include "ctr.h" michael@0: #include "pkcs11t.h" michael@0: #include "secerr.h" michael@0: michael@0: #ifdef USE_HW_AES michael@0: #include "intel-aes.h" michael@0: #include "rijndael.h" michael@0: #endif michael@0: michael@0: SECStatus michael@0: CTR_InitContext(CTRContext *ctr, void *context, freeblCipherFunc cipher, michael@0: const unsigned char *param, unsigned int blocksize) michael@0: { michael@0: const CK_AES_CTR_PARAMS *ctrParams = (const CK_AES_CTR_PARAMS *)param; michael@0: michael@0: if (ctrParams->ulCounterBits == 0 || michael@0: ctrParams->ulCounterBits > blocksize * PR_BITS_PER_BYTE) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Invariant: 0 < ctr->bufPtr <= blocksize */ michael@0: ctr->bufPtr = blocksize; /* no unused data in the buffer */ michael@0: ctr->cipher = cipher; michael@0: ctr->context = context; michael@0: ctr->counterBits = ctrParams->ulCounterBits; michael@0: if (blocksize > sizeof(ctr->counter) || michael@0: blocksize > sizeof(ctrParams->cb)) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: PORT_Memcpy(ctr->counter, ctrParams->cb, blocksize); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: CTRContext * michael@0: CTR_CreateContext(void *context, freeblCipherFunc cipher, michael@0: const unsigned char *param, unsigned int blocksize) michael@0: { michael@0: CTRContext *ctr; michael@0: SECStatus rv; michael@0: michael@0: /* first fill in the Counter context */ michael@0: ctr = PORT_ZNew(CTRContext); michael@0: if (ctr == NULL) { michael@0: return NULL; michael@0: } michael@0: rv = CTR_InitContext(ctr, context, cipher, param, blocksize); michael@0: if (rv != SECSuccess) { michael@0: CTR_DestroyContext(ctr, PR_TRUE); michael@0: ctr = NULL; michael@0: } michael@0: return ctr; michael@0: } michael@0: michael@0: void michael@0: CTR_DestroyContext(CTRContext *ctr, PRBool freeit) michael@0: { michael@0: PORT_Memset(ctr, 0, sizeof(CTRContext)); michael@0: if (freeit) { michael@0: PORT_Free(ctr); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Used by counter mode. Increment the counter block. Not all bits in the michael@0: * counter block are part of the counter, counterBits tells how many bits michael@0: * are part of the counter. The counter block is blocksize long. It's a michael@0: * big endian value. michael@0: * michael@0: * XXX Does not handle counter rollover. michael@0: */ michael@0: static void michael@0: ctr_GetNextCtr(unsigned char *counter, unsigned int counterBits, michael@0: unsigned int blocksize) michael@0: { michael@0: unsigned char *counterPtr = counter + blocksize - 1; michael@0: unsigned char mask, count; michael@0: michael@0: PORT_Assert(counterBits <= blocksize*PR_BITS_PER_BYTE); michael@0: while (counterBits >= PR_BITS_PER_BYTE) { michael@0: if (++(*(counterPtr--))) { michael@0: return; michael@0: } michael@0: counterBits -= PR_BITS_PER_BYTE; michael@0: } michael@0: if (counterBits == 0) { michael@0: return; michael@0: } michael@0: /* increment the final partial byte */ michael@0: mask = (1 << counterBits)-1; michael@0: count = ++(*counterPtr) & mask; michael@0: *counterPtr = ((*counterPtr) & ~mask) | count; michael@0: return; michael@0: } michael@0: michael@0: static void michael@0: ctr_xor(unsigned char *target, const unsigned char *x, michael@0: const unsigned char *y, unsigned int count) michael@0: { michael@0: unsigned int i; michael@0: for (i=0; i < count; i++) { michael@0: *target++ = *x++ ^ *y++; michael@0: } michael@0: } michael@0: michael@0: SECStatus michael@0: CTR_Update(CTRContext *ctr, unsigned char *outbuf, michael@0: unsigned int *outlen, unsigned int maxout, michael@0: const unsigned char *inbuf, unsigned int inlen, michael@0: unsigned int blocksize) michael@0: { michael@0: unsigned int tmp; michael@0: SECStatus rv; michael@0: michael@0: if (maxout < inlen) { michael@0: *outlen = inlen; michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: return SECFailure; michael@0: } michael@0: *outlen = 0; michael@0: if (ctr->bufPtr != blocksize) { michael@0: unsigned int needed = PR_MIN(blocksize-ctr->bufPtr, inlen); michael@0: ctr_xor(outbuf, inbuf, ctr->buffer + ctr->bufPtr, needed); michael@0: ctr->bufPtr += needed; michael@0: outbuf += needed; michael@0: inbuf += needed; michael@0: *outlen += needed; michael@0: inlen -= needed; michael@0: if (inlen == 0) { michael@0: return SECSuccess; michael@0: } michael@0: PORT_Assert(ctr->bufPtr == blocksize); michael@0: } michael@0: michael@0: while (inlen >= blocksize) { michael@0: rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize, michael@0: ctr->counter, blocksize, blocksize); michael@0: ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: ctr_xor(outbuf, inbuf, ctr->buffer, blocksize); michael@0: outbuf += blocksize; michael@0: inbuf += blocksize; michael@0: *outlen += blocksize; michael@0: inlen -= blocksize; michael@0: } michael@0: if (inlen == 0) { michael@0: return SECSuccess; michael@0: } michael@0: rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize, michael@0: ctr->counter, blocksize, blocksize); michael@0: ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: ctr_xor(outbuf, inbuf, ctr->buffer, inlen); michael@0: ctr->bufPtr = inlen; michael@0: *outlen += inlen; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: #if defined(USE_HW_AES) && defined(_MSC_VER) michael@0: SECStatus michael@0: CTR_Update_HW_AES(CTRContext *ctr, unsigned char *outbuf, michael@0: unsigned int *outlen, unsigned int maxout, michael@0: const unsigned char *inbuf, unsigned int inlen, michael@0: unsigned int blocksize) michael@0: { michael@0: unsigned int fullblocks; michael@0: unsigned int tmp; michael@0: SECStatus rv; michael@0: michael@0: if (maxout < inlen) { michael@0: *outlen = inlen; michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: return SECFailure; michael@0: } michael@0: *outlen = 0; michael@0: if (ctr->bufPtr != blocksize) { michael@0: unsigned int needed = PR_MIN(blocksize-ctr->bufPtr, inlen); michael@0: ctr_xor(outbuf, inbuf, ctr->buffer + ctr->bufPtr, needed); michael@0: ctr->bufPtr += needed; michael@0: outbuf += needed; michael@0: inbuf += needed; michael@0: *outlen += needed; michael@0: inlen -= needed; michael@0: if (inlen == 0) { michael@0: return SECSuccess; michael@0: } michael@0: PORT_Assert(ctr->bufPtr == blocksize); michael@0: } michael@0: michael@0: intel_aes_ctr_worker(((AESContext*)(ctr->context))->Nr)( michael@0: ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize); michael@0: /* XXX intel_aes_ctr_worker should set *outlen. */ michael@0: PORT_Assert(*outlen == 0); michael@0: fullblocks = (inlen/blocksize)*blocksize; michael@0: *outlen += fullblocks; michael@0: outbuf += fullblocks; michael@0: inbuf += fullblocks; michael@0: inlen -= fullblocks; michael@0: michael@0: if (inlen == 0) { michael@0: return SECSuccess; michael@0: } michael@0: rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize, michael@0: ctr->counter, blocksize, blocksize); michael@0: ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: ctr_xor(outbuf, inbuf, ctr->buffer, inlen); michael@0: ctr->bufPtr = inlen; michael@0: *outlen += inlen; michael@0: return SECSuccess; michael@0: } michael@0: #endif