summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/freebl/ppc-gcm-wrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/freebl/ppc-gcm-wrap.c')
-rw-r--r--security/nss/lib/freebl/ppc-gcm-wrap.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/security/nss/lib/freebl/ppc-gcm-wrap.c b/security/nss/lib/freebl/ppc-gcm-wrap.c
new file mode 100644
index 0000000000..ac58744cbd
--- /dev/null
+++ b/security/nss/lib/freebl/ppc-gcm-wrap.c
@@ -0,0 +1,458 @@
+/* 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 PowerPC optimized implementation of AES-GCM */
+
+#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 <stdio.h>
+
+#include "ppc-gcm.h"
+#include "rijndael.h"
+
+struct ppc_AES_GCMContextStr {
+ unsigned char Htbl[8 * 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 ppc_aes_gcmInitCounter(ppc_AES_GCMContext *gcm,
+ const unsigned char *iv,
+ unsigned long ivLen, unsigned long tagBits,
+ const unsigned char *aad, unsigned long aadLen);
+
+ppc_AES_GCMContext *
+ppc_AES_GCM_CreateContext(void *context,
+ freeblCipherFunc cipher,
+ const unsigned char *params)
+{
+ ppc_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(ppc_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 */
+ ppc_aes_gcmINIT(gcm->Htbl, 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 = ppc_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
+ppc_aes_gcmInitCounter(ppc_AES_GCMContext *gcm,
+ const unsigned char *iv, unsigned long ivLen,
+ unsigned long tagBits,
+ const unsigned char *aad, unsigned long aadLen)
+{
+ unsigned int j;
+ 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;
+
+ /* Initial TAG value is zero */
+ PORT_Memset(gcm->T, 0, AES_BLOCK_SIZE);
+ PORT_Memset(gcm->X0, 0, AES_BLOCK_SIZE);
+
+ /* Init the counter */
+ if (ivLen == 12) {
+ PORT_Memcpy(gcm->CTR, iv, AES_BLOCK_SIZE - 4);
+ gcm->CTR[12] = 0;
+ gcm->CTR[13] = 0;
+ gcm->CTR[14] = 0;
+ gcm->CTR[15] = 1;
+ } else {
+ /* If IV size is not 96 bits, then the initial counter value is GHASH
+ * of the IV */
+ ppc_aes_gcmHASH(gcm->Htbl, iv, ivLen, gcm->T);
+
+ ppc_aes_gcmTAG(
+ gcm->Htbl,
+ gcm->T,
+ ivLen,
+ 0,
+ gcm->X0,
+ gcm->CTR);
+
+ /* TAG should be zero again */
+ PORT_Memset(gcm->T, 0, AES_BLOCK_SIZE);
+ }
+
+ /* 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 */
+ gcm->CTR[14] += !(++gcm->CTR[15]);
+ gcm->CTR[13] += !(gcm->CTR[15]) && !(gcm->CTR[14]);
+ gcm->CTR[12] += !(gcm->CTR[15]) && !(gcm->CTR[14]) && !(gcm->CTR[13]);
+
+ /* 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 */
+ ppc_aes_gcmHASH(gcm->Htbl, aad, aadLen, gcm->T);
+ gcm->Alen += aadLen;
+ return SECSuccess;
+}
+
+void
+ppc_AES_GCM_DestroyContext(ppc_AES_GCMContext *gcm, PRBool freeit)
+{
+ PORT_Memset(gcm, 0, sizeof(ppc_AES_GCMContext));
+ if (freeit) {
+ PORT_Free(gcm);
+ }
+}
+
+SECStatus
+ppc_AES_GCM_EncryptUpdate(ppc_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 &&
+ inlen >= ((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;
+ }
+
+ ppc_aes_gcmCRYPT(
+ inbuf,
+ outbuf,
+ inlen,
+ gcm->CTR,
+ gcm->aes_context->k.expandedKey,
+ gcm->aes_context->Nr);
+ ppc_aes_gcmHASH(
+ gcm->Htbl,
+ outbuf,
+ inlen,
+ gcm->T);
+
+ gcm->Mlen += inlen;
+
+ ppc_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
+ppc_AES_GCM_DecryptUpdate(ppc_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 &&
+ inlen >= ((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;
+ }
+
+ ppc_aes_gcmHASH(
+ gcm->Htbl,
+ inbuf,
+ inlen,
+ gcm->T);
+ ppc_aes_gcmCRYPT(
+ inbuf,
+ outbuf,
+ inlen,
+ gcm->CTR,
+ gcm->aes_context->k.expandedKey,
+ gcm->aes_context->Nr);
+
+ gcm->Mlen += inlen;
+ ppc_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
+ppc_AES_GCM_EncryptAEAD(ppc_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 &&
+ inlen >= ((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 = ppc_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;
+
+ ppc_aes_gcmCRYPT(inbuf, outbuf, inlen, gcm->CTR, gcm->aes_context->k.expandedKey,
+ gcm->aes_context->Nr);
+ ppc_aes_gcmHASH(gcm->Htbl, outbuf, inlen, gcm->T);
+
+ gcm->Mlen += inlen;
+
+ ppc_aes_gcmTAG(gcm->Htbl, gcm->T, gcm->Mlen, gcm->Alen, gcm->X0, T);
+
+ *outlen = inlen;
+ PORT_Memcpy(gcmParams->pTag, T, tagBytes);
+ return SECSuccess;
+}
+
+SECStatus
+ppc_AES_GCM_DecryptAEAD(ppc_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 &&
+ inlen >= ((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 = ppc_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);
+
+ ppc_aes_gcmHASH(gcm->Htbl, inbuf, inlen, gcm->T);
+ ppc_aes_gcmCRYPT(inbuf, outbuf, inlen, gcm->CTR, gcm->aes_context->k.expandedKey,
+ gcm->aes_context->Nr);
+
+ gcm->Mlen += inlen;
+ ppc_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;
+}