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: /* Copyright(c) 2013, Intel Corp. */ michael@0: michael@0: /* Wrapper functions for Intel optimized implementation of AES-GCM */ michael@0: michael@0: #ifdef USE_HW_AES michael@0: michael@0: #ifdef FREEBL_NO_DEPEND michael@0: #include "stubs.h" michael@0: #endif michael@0: michael@0: #include "blapii.h" michael@0: #include "blapit.h" michael@0: #include "gcm.h" michael@0: #include "ctr.h" michael@0: #include "secerr.h" michael@0: #include "prtypes.h" michael@0: #include "pkcs11t.h" michael@0: michael@0: #include michael@0: michael@0: #include "intel-gcm.h" michael@0: #include "rijndael.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: michael@0: struct intel_AES_GCMContextStr{ michael@0: unsigned char Htbl[16*AES_BLOCK_SIZE]; michael@0: unsigned char X0[AES_BLOCK_SIZE]; michael@0: unsigned char T[AES_BLOCK_SIZE]; michael@0: unsigned char CTR[AES_BLOCK_SIZE]; michael@0: AESContext *aes_context; michael@0: unsigned long tagBits; michael@0: unsigned long Alen; michael@0: unsigned long Mlen; michael@0: }; michael@0: michael@0: intel_AES_GCMContext *intel_AES_GCM_CreateContext(void *context, michael@0: freeblCipherFunc cipher, michael@0: const unsigned char *params, michael@0: unsigned int blocksize) michael@0: { michael@0: intel_AES_GCMContext *gcm = NULL; michael@0: AESContext *aes = (AESContext*)context; michael@0: const CK_GCM_PARAMS *gcmParams = (const CK_GCM_PARAMS *)params; michael@0: unsigned char buff[AES_BLOCK_SIZE]; /* aux buffer */ michael@0: michael@0: unsigned long IV_whole_len = gcmParams->ulIvLen & (~0xful); michael@0: unsigned int IV_remainder_len = gcmParams->ulIvLen & 0xful; michael@0: unsigned long AAD_whole_len = gcmParams->ulAADLen & (~0xful); michael@0: unsigned int AAD_remainder_len = gcmParams->ulAADLen & 0xful; michael@0: michael@0: __m128i BSWAP_MASK = _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); michael@0: __m128i ONE = _mm_set_epi32(0,0,0,1); michael@0: unsigned int j; michael@0: SECStatus rv; michael@0: michael@0: if (blocksize != AES_BLOCK_SIZE) { michael@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); michael@0: return NULL; michael@0: } michael@0: gcm = PORT_ZNew(intel_AES_GCMContext); michael@0: michael@0: if (gcm == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* initialize context fields */ michael@0: gcm->aes_context = aes; michael@0: gcm->tagBits = gcmParams->ulTagBits; michael@0: gcm->Alen = 0; michael@0: gcm->Mlen = 0; michael@0: michael@0: /* first prepare H and its derivatives for ghash */ michael@0: intel_aes_gcmINIT(gcm->Htbl, (unsigned char*)aes->expandedKey, aes->Nr); michael@0: michael@0: /* Initial TAG value is zero */ michael@0: _mm_storeu_si128((__m128i*)gcm->T, _mm_setzero_si128()); michael@0: _mm_storeu_si128((__m128i*)gcm->X0, _mm_setzero_si128()); michael@0: michael@0: /* Init the counter */ michael@0: if (gcmParams->ulIvLen == 12) { michael@0: _mm_storeu_si128((__m128i*)gcm->CTR, michael@0: _mm_setr_epi32(((unsigned int*)gcmParams->pIv)[0], michael@0: ((unsigned int*)gcmParams->pIv)[1], michael@0: ((unsigned int*)gcmParams->pIv)[2], michael@0: 0x01000000)); michael@0: } else { michael@0: /* If IV size is not 96 bits, then the initial counter value is GHASH michael@0: * of the IV */ michael@0: intel_aes_gcmAAD(gcm->Htbl, gcmParams->pIv, IV_whole_len, gcm->T); michael@0: michael@0: /* Partial block */ michael@0: if (IV_remainder_len) { michael@0: PORT_Memset(buff, 0, AES_BLOCK_SIZE); michael@0: PORT_Memcpy(buff, gcmParams->pIv + IV_whole_len, IV_remainder_len); michael@0: intel_aes_gcmAAD(gcm->Htbl, buff, AES_BLOCK_SIZE, gcm->T); michael@0: } michael@0: michael@0: intel_aes_gcmTAG( michael@0: gcm->Htbl, michael@0: gcm->T, michael@0: gcmParams->ulIvLen, michael@0: 0, michael@0: gcm->X0, michael@0: gcm->CTR); michael@0: michael@0: /* TAG should be zero again */ michael@0: _mm_storeu_si128((__m128i*)gcm->T, _mm_setzero_si128()); michael@0: } michael@0: michael@0: /* Encrypt the initial counter, will be used to encrypt the GHASH value, michael@0: * in the end */ michael@0: rv = (*cipher)(context, gcm->X0, &j, AES_BLOCK_SIZE, gcm->CTR, michael@0: AES_BLOCK_SIZE, AES_BLOCK_SIZE); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* Promote the counter by 1 */ michael@0: _mm_storeu_si128((__m128i*)gcm->CTR, _mm_shuffle_epi8(_mm_add_epi32(ONE, _mm_shuffle_epi8(_mm_loadu_si128((__m128i*)gcm->CTR), BSWAP_MASK)), BSWAP_MASK)); michael@0: michael@0: /* Now hash AAD - it would actually make sense to seperate the context michael@0: * creation from the AAD, because that would allow to reuse the H, which michael@0: * only changes when the AES key changes, and not every package, like the michael@0: * IV and AAD */ michael@0: intel_aes_gcmAAD(gcm->Htbl, gcmParams->pAAD, AAD_whole_len, gcm->T); michael@0: if (AAD_remainder_len) { michael@0: PORT_Memset(buff, 0, AES_BLOCK_SIZE); michael@0: PORT_Memcpy(buff, gcmParams->pAAD + AAD_whole_len, AAD_remainder_len); michael@0: intel_aes_gcmAAD(gcm->Htbl, buff, AES_BLOCK_SIZE, gcm->T); michael@0: } michael@0: gcm->Alen += gcmParams->ulAADLen; michael@0: return gcm; michael@0: michael@0: loser: michael@0: if (gcm) { michael@0: PORT_Free(gcm); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: void intel_AES_GCM_DestroyContext(intel_AES_GCMContext *gcm, PRBool freeit) michael@0: { michael@0: if (freeit) { michael@0: PORT_Free(gcm); michael@0: } michael@0: } michael@0: michael@0: SECStatus intel_AES_GCM_EncryptUpdate(intel_AES_GCMContext *gcm, michael@0: 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 tagBytes; michael@0: unsigned char T[AES_BLOCK_SIZE]; michael@0: unsigned int j; michael@0: michael@0: tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; michael@0: if (UINT_MAX - inlen < tagBytes) { michael@0: PORT_SetError(SEC_ERROR_INPUT_LEN); michael@0: return SECFailure; michael@0: } michael@0: if (maxout < inlen + tagBytes) { michael@0: *outlen = inlen + tagBytes; michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: return SECFailure; michael@0: } michael@0: michael@0: intel_aes_gcmENC( michael@0: inbuf, michael@0: outbuf, michael@0: gcm, michael@0: inlen); michael@0: michael@0: gcm->Mlen += inlen; michael@0: michael@0: intel_aes_gcmTAG( michael@0: gcm->Htbl, michael@0: gcm->T, michael@0: gcm->Mlen, michael@0: gcm->Alen, michael@0: gcm->X0, michael@0: T); michael@0: michael@0: *outlen = inlen + tagBytes; michael@0: michael@0: for (j = 0; j < tagBytes; j++) { michael@0: outbuf[inlen + j] = T[j]; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus intel_AES_GCM_DecryptUpdate(intel_AES_GCMContext *gcm, michael@0: 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 tagBytes; michael@0: unsigned char T[AES_BLOCK_SIZE]; michael@0: const unsigned char *intag; michael@0: michael@0: tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; michael@0: michael@0: /* get the authentication block */ michael@0: if (inlen < tagBytes) { michael@0: PORT_SetError(SEC_ERROR_INPUT_LEN); michael@0: return SECFailure; michael@0: } michael@0: michael@0: inlen -= tagBytes; michael@0: intag = inbuf + inlen; 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: michael@0: intel_aes_gcmDEC( michael@0: inbuf, michael@0: outbuf, michael@0: gcm, michael@0: inlen); michael@0: michael@0: gcm->Mlen += inlen; michael@0: intel_aes_gcmTAG( michael@0: gcm->Htbl, michael@0: gcm->T, michael@0: gcm->Mlen, michael@0: gcm->Alen, michael@0: gcm->X0, michael@0: T); michael@0: michael@0: if (NSS_SecureMemcmp(T, intag, tagBytes) != 0) { michael@0: memset(outbuf, 0, inlen); michael@0: *outlen = 0; michael@0: /* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */ michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: *outlen = inlen; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: #endif