Source code

Revision control

Copy as Markdown

Other Tools

/* 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 <limits.h>
#include "intel-gcm.h"
#include "rijndael.h"
#include <emmintrin.h>
#include <tmmintrin.h>
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;
freeblCipherFunc cipher;
PRBool ctr_context_init;
gcmIVContext gcm_iv;
};
SECStatus intel_aes_gcmInitCounter(intel_AES_GCMContext *gcm,
const unsigned char *iv,
unsigned long ivLen, unsigned long tagBits,
const unsigned char *aad, unsigned long aadLen);
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_NSS_GCM_PARAMS *gcmParams = (const CK_NSS_GCM_PARAMS *)params;
SECStatus rv;
gcm = PORT_ZNew(intel_AES_GCMContext);
if (gcm == NULL) {
return NULL;
}
/* initialize context fields */
gcm->aes_context = aes;
gcm->cipher = cipher;
gcm->Alen = 0;
gcm->Mlen = 0;
gcm->ctr_context_init = PR_FALSE;
/* first prepare H and its derivatives for ghash */
intel_aes_gcmINIT(gcm->Htbl, (unsigned char *)aes->k.expandedKey, aes->Nr);
gcm_InitIVContext(&gcm->gcm_iv);
/* if gcmParams is NULL, then we are creating an PKCS #11 MESSAGE
* style context, in which we initialize the key once, then do separate
* iv/aad's for each message. If we are doing that kind of operation,
* we've finished with init here. We'll init the Counter in each AEAD
* call */
if (gcmParams == NULL) {
return gcm;
}
rv = intel_aes_gcmInitCounter(gcm, gcmParams->pIv,
gcmParams->ulIvLen, gcmParams->ulTagBits,
gcmParams->pAAD, gcmParams->ulAADLen);
if (rv != SECSuccess) {
PORT_Free(gcm);
return NULL;
}
gcm->ctr_context_init = PR_TRUE;
return gcm;
}
SECStatus
intel_aes_gcmInitCounter(intel_AES_GCMContext *gcm,
const unsigned char *iv, unsigned long ivLen,
unsigned long tagBits,
const unsigned char *aad, unsigned long aadLen)
{
unsigned char buff[AES_BLOCK_SIZE]; /* aux buffer */
unsigned long IV_whole_len = ivLen & (~0xful);
unsigned int IV_remainder_len = ivLen & 0xful;
unsigned long AAD_whole_len = aadLen & (~0xful);
unsigned int AAD_remainder_len = aadLen & 0xful;
unsigned int j;
__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);
SECStatus rv;
if (ivLen == 0) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (tagBits != 128 && tagBits != 120 && tagBits != 112 &&
tagBits != 104 && tagBits != 96 && tagBits != 64 &&
tagBits != 32) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
gcm->tagBits = tagBits;
/* reset the aad and message length counters */
gcm->Alen = 0;
gcm->Mlen = 0;
// Limit AADLen in accordance with SP800-38D
if (sizeof(AAD_whole_len) >= 8 && AAD_whole_len > (1ULL << 61) - 1) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
return SECFailure;
}
/* 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 (ivLen == 12) {
_mm_storeu_si128((__m128i *)gcm->CTR,
_mm_setr_epi32(((unsigned int *)iv)[0],
((unsigned int *)iv)[1],
((unsigned int *)iv)[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, (unsigned char *)iv, IV_whole_len, gcm->T);
/* Partial block */
if (IV_remainder_len) {
PORT_Memset(buff, 0, AES_BLOCK_SIZE);
PORT_Memcpy(buff, iv + IV_whole_len, IV_remainder_len);
intel_aes_gcmAAD(gcm->Htbl, buff, AES_BLOCK_SIZE, gcm->T);
}
intel_aes_gcmTAG(
gcm->Htbl,
gcm->T,
ivLen,
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 = (*gcm->cipher)(gcm->aes_context, gcm->X0, &j, AES_BLOCK_SIZE, gcm->CTR,
AES_BLOCK_SIZE, AES_BLOCK_SIZE);
if (rv != SECSuccess) {
return SECFailure;
}
/* 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, (unsigned char *)aad, AAD_whole_len, gcm->T);
if (AAD_remainder_len) {
PORT_Memset(buff, 0, AES_BLOCK_SIZE);
PORT_Memcpy(buff, aad + AAD_whole_len, AAD_remainder_len);
intel_aes_gcmAAD(gcm->Htbl, buff, AES_BLOCK_SIZE, gcm->T);
}
gcm->Alen += aadLen;
return SECSuccess;
}
void
intel_AES_GCM_DestroyContext(intel_AES_GCMContext *gcm, PRBool freeit)
{
PORT_Memset(gcm, 0, sizeof(intel_AES_GCMContext));
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;
// GCM has a 16 octet block, with a 32-bit block counter
// Limit in accordance with SP800-38D
if (sizeof(inlen) > 4) {
unsigned long long inlen_ull = inlen;
if (inlen_ull >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
return SECFailure;
}
}
if (!gcm->ctr_context_init) {
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
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;
if (!gcm->ctr_context_init) {
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
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;
// GCM has a 16 octet block, with a 32-bit block counter
// Limit in accordance with SP800-38D
if (sizeof(inlen) > 4) {
unsigned long long inlen_ull = inlen;
if (inlen_ull >= ((1ULL << 32) - 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;
}
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;
}
SECStatus
intel_AES_GCM_EncryptAEAD(intel_AES_GCMContext *gcm,
unsigned char *outbuf,
unsigned int *outlen, unsigned int maxout,
const unsigned char *inbuf, unsigned int inlen,
void *params, unsigned int paramLen,
const unsigned char *aad, unsigned int aadLen,
unsigned int blocksize)
{
unsigned int tagBytes;
unsigned char T[AES_BLOCK_SIZE];
const CK_GCM_MESSAGE_PARAMS *gcmParams =
(const CK_GCM_MESSAGE_PARAMS *)params;
SECStatus rv;
// GCM has a 16 octet block, with a 32-bit block counter
// Limit in accordance with SP800-38D
if (sizeof(inlen) > 4) {
unsigned long long inlen_ull = inlen;
if (inlen_ull >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
return SECFailure;
}
}
/* paramLen comes all the way from the application layer, make sure
* it's correct */
if (paramLen != sizeof(CK_GCM_MESSAGE_PARAMS)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
/* if we were initialized with the C_EncryptInit, we shouldn't be in this
* function */
if (gcm->ctr_context_init) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
if (maxout < inlen) {
*outlen = inlen;
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
return SECFailure;
}
rv = gcm_GenerateIV(&gcm->gcm_iv, gcmParams->pIv, gcmParams->ulIvLen,
gcmParams->ulIvFixedBits, gcmParams->ivGenerator);
if (rv != SECSuccess) {
return SECFailure;
}
rv = intel_aes_gcmInitCounter(gcm, gcmParams->pIv, gcmParams->ulIvLen,
gcmParams->ulTagBits, aad, aadLen);
if (rv != SECSuccess) {
return SECFailure;
}
tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE;
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;
PORT_Memcpy(gcmParams->pTag, T, tagBytes);
return SECSuccess;
}
SECStatus
intel_AES_GCM_DecryptAEAD(intel_AES_GCMContext *gcm,
unsigned char *outbuf,
unsigned int *outlen, unsigned int maxout,
const unsigned char *inbuf, unsigned int inlen,
void *params, unsigned int paramLen,
const unsigned char *aad, unsigned int aadLen,
unsigned int blocksize)
{
unsigned int tagBytes;
unsigned char T[AES_BLOCK_SIZE];
const unsigned char *intag;
const CK_GCM_MESSAGE_PARAMS *gcmParams =
(const CK_GCM_MESSAGE_PARAMS *)params;
SECStatus rv;
/* paramLen comes all the way from the application layer, make sure
* it's correct */
if (paramLen != sizeof(CK_GCM_MESSAGE_PARAMS)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
/* if we were initialized with the C_DecryptInit, we shouldn't be in this
* function */
if (gcm->ctr_context_init) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
// GCM has a 16 octet block, with a 32-bit block counter
// Limit in accordance with SP800-38D
if (sizeof(inlen) > 4) {
unsigned long long inlen_ull = inlen;
if (inlen_ull >= ((1ULL << 32) - 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;
}
rv = intel_aes_gcmInitCounter(gcm, gcmParams->pIv, gcmParams->ulIvLen,
gcmParams->ulTagBits, aad, aadLen);
if (rv != SECSuccess) {
return SECFailure;
}
tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE;
intag = gcmParams->pTag;
PORT_Assert(tagBytes != 0);
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