| Index: nss/lib/freebl/intel-gcm-wrap.c
 | 
| diff --git a/nss/lib/freebl/intel-gcm-wrap.c b/nss/lib/freebl/intel-gcm-wrap.c
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..afd3029383739922578f305f487b5bb4a14d0c79
 | 
| --- /dev/null
 | 
| +++ b/nss/lib/freebl/intel-gcm-wrap.c
 | 
| @@ -0,0 +1,240 @@
 | 
| +/* 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;
 | 
| +};
 | 
| +
 | 
| +intel_AES_GCMContext *intel_AES_GCM_CreateContext(void *context, 
 | 
| +               freeblCipherFunc cipher,
 | 
| +               const unsigned char *params, 
 | 
| +               unsigned int blocksize)
 | 
| +{
 | 
| +    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 */
 | 
| +    
 | 
| +    int IV_whole_len = gcmParams->ulIvLen&(~0xf);
 | 
| +    int IV_remainder_len = gcmParams->ulIvLen&0xf;
 | 
| +    int AAD_whole_len = gcmParams->ulAADLen&(~0xf);
 | 
| +    int AAD_remainder_len = gcmParams->ulAADLen&0xf;
 | 
| +    
 | 
| +    __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 (blocksize != AES_BLOCK_SIZE) {
 | 
| +      PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
 | 
| +      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:
 | 
| +    if (gcm) {
 | 
| +        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];
 | 
| +    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
 | 
| 
 |