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