michael@0: /* michael@0: * aeskeywrap.c - implement AES Key Wrap algorithm from RFC 3394 michael@0: * 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: michael@0: #ifdef FREEBL_NO_DEPEND michael@0: #include "stubs.h" michael@0: #endif michael@0: michael@0: #include "prcpucfg.h" michael@0: #if defined(IS_LITTLE_ENDIAN) || defined(SHA_NO_LONG_LONG) michael@0: #define BIG_ENDIAN_WITH_64_BIT_REGISTERS 0 michael@0: #else michael@0: #define BIG_ENDIAN_WITH_64_BIT_REGISTERS 1 michael@0: #endif michael@0: #include "prtypes.h" /* for PRUintXX */ michael@0: #include "secport.h" /* for PORT_XXX */ michael@0: #include "secerr.h" michael@0: #include "blapi.h" /* for AES_ functions */ michael@0: #include "rijndael.h" michael@0: michael@0: struct AESKeyWrapContextStr { michael@0: unsigned char iv[AES_KEY_WRAP_IV_BYTES]; michael@0: AESContext aescx; michael@0: }; michael@0: michael@0: /******************************************/ michael@0: /* michael@0: ** AES key wrap algorithm, RFC 3394 michael@0: */ michael@0: michael@0: AESKeyWrapContext * michael@0: AESKeyWrap_AllocateContext(void) michael@0: { michael@0: AESKeyWrapContext * cx = PORT_New(AESKeyWrapContext); michael@0: return cx; michael@0: } michael@0: michael@0: SECStatus michael@0: AESKeyWrap_InitContext(AESKeyWrapContext *cx, michael@0: const unsigned char *key, michael@0: unsigned int keylen, michael@0: const unsigned char *iv, michael@0: int x1, michael@0: unsigned int encrypt, michael@0: unsigned int x2) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: if (!cx) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: if (iv) { michael@0: memcpy(cx->iv, iv, sizeof cx->iv); michael@0: } else { michael@0: memset(cx->iv, 0xA6, sizeof cx->iv); michael@0: } michael@0: rv = AES_InitContext(&cx->aescx, key, keylen, NULL, NSS_AES, encrypt, michael@0: AES_BLOCK_SIZE); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Create a new AES context suitable for AES encryption/decryption. michael@0: ** "key" raw key data michael@0: ** "keylen" the number of bytes of key data (16, 24, or 32) michael@0: */ michael@0: extern AESKeyWrapContext * michael@0: AESKeyWrap_CreateContext(const unsigned char *key, const unsigned char *iv, michael@0: int encrypt, unsigned int keylen) michael@0: { michael@0: SECStatus rv; michael@0: AESKeyWrapContext * cx = AESKeyWrap_AllocateContext(); michael@0: if (!cx) michael@0: return NULL; /* error is already set */ michael@0: rv = AESKeyWrap_InitContext(cx, key, keylen, iv, 0, encrypt, 0); michael@0: if (rv != SECSuccess) { michael@0: PORT_Free(cx); michael@0: cx = NULL; /* error should already be set */ michael@0: } michael@0: return cx; michael@0: } michael@0: michael@0: /* michael@0: ** Destroy a AES KeyWrap context. michael@0: ** "cx" the context michael@0: ** "freeit" if PR_TRUE then free the object as well as its sub-objects michael@0: */ michael@0: extern void michael@0: AESKeyWrap_DestroyContext(AESKeyWrapContext *cx, PRBool freeit) michael@0: { michael@0: if (cx) { michael@0: AES_DestroyContext(&cx->aescx, PR_FALSE); michael@0: /* memset(cx, 0, sizeof *cx); */ michael@0: if (freeit) michael@0: PORT_Free(cx); michael@0: } michael@0: } michael@0: michael@0: #if !BIG_ENDIAN_WITH_64_BIT_REGISTERS michael@0: michael@0: /* The AES Key Wrap algorithm has 64-bit values that are ALWAYS big-endian michael@0: ** (Most significant byte first) in memory. The only ALU operations done michael@0: ** on them are increment, decrement, and XOR. So, on little-endian CPUs, michael@0: ** and on CPUs that lack 64-bit registers, these big-endian 64-bit operations michael@0: ** are simulated in the following code. This is thought to be faster and michael@0: ** simpler than trying to convert the data to little-endian and back. michael@0: */ michael@0: michael@0: /* A and T point to two 64-bit values stored most signficant byte first michael@0: ** (big endian). This function increments the 64-bit value T, and then michael@0: ** XORs it with A, changing A. michael@0: */ michael@0: static void michael@0: increment_and_xor(unsigned char *A, unsigned char *T) michael@0: { michael@0: if (!++T[7]) michael@0: if (!++T[6]) michael@0: if (!++T[5]) michael@0: if (!++T[4]) michael@0: if (!++T[3]) michael@0: if (!++T[2]) michael@0: if (!++T[1]) michael@0: ++T[0]; michael@0: michael@0: A[0] ^= T[0]; michael@0: A[1] ^= T[1]; michael@0: A[2] ^= T[2]; michael@0: A[3] ^= T[3]; michael@0: A[4] ^= T[4]; michael@0: A[5] ^= T[5]; michael@0: A[6] ^= T[6]; michael@0: A[7] ^= T[7]; michael@0: } michael@0: michael@0: /* A and T point to two 64-bit values stored most signficant byte first michael@0: ** (big endian). This function XORs T with A, giving a new A, then michael@0: ** decrements the 64-bit value T. michael@0: */ michael@0: static void michael@0: xor_and_decrement(unsigned char *A, unsigned char *T) michael@0: { michael@0: A[0] ^= T[0]; michael@0: A[1] ^= T[1]; michael@0: A[2] ^= T[2]; michael@0: A[3] ^= T[3]; michael@0: A[4] ^= T[4]; michael@0: A[5] ^= T[5]; michael@0: A[6] ^= T[6]; michael@0: A[7] ^= T[7]; michael@0: michael@0: if (!T[7]--) michael@0: if (!T[6]--) michael@0: if (!T[5]--) michael@0: if (!T[4]--) michael@0: if (!T[3]--) michael@0: if (!T[2]--) michael@0: if (!T[1]--) michael@0: T[0]--; michael@0: michael@0: } michael@0: michael@0: /* Given an unsigned long t (in host byte order), store this value as a michael@0: ** 64-bit big-endian value (MSB first) in *pt. michael@0: */ michael@0: static void michael@0: set_t(unsigned char *pt, unsigned long t) michael@0: { michael@0: pt[7] = (unsigned char)t; t >>= 8; michael@0: pt[6] = (unsigned char)t; t >>= 8; michael@0: pt[5] = (unsigned char)t; t >>= 8; michael@0: pt[4] = (unsigned char)t; t >>= 8; michael@0: pt[3] = (unsigned char)t; t >>= 8; michael@0: pt[2] = (unsigned char)t; t >>= 8; michael@0: pt[1] = (unsigned char)t; t >>= 8; michael@0: pt[0] = (unsigned char)t; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /* michael@0: ** Perform AES key wrap. michael@0: ** "cx" the context michael@0: ** "output" the output buffer to store the encrypted data. michael@0: ** "outputLen" how much data is stored in "output". Set by the routine michael@0: ** after some data is stored in output. michael@0: ** "maxOutputLen" the maximum amount of data that can ever be michael@0: ** stored in "output" michael@0: ** "input" the input data michael@0: ** "inputLen" the amount of input data michael@0: */ michael@0: extern SECStatus michael@0: AESKeyWrap_Encrypt(AESKeyWrapContext *cx, unsigned char *output, michael@0: unsigned int *pOutputLen, unsigned int maxOutputLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: PRUint64 * R = NULL; michael@0: unsigned int nBlocks; michael@0: unsigned int i, j; michael@0: unsigned int aesLen = AES_BLOCK_SIZE; michael@0: unsigned int outLen = inputLen + AES_KEY_WRAP_BLOCK_SIZE; michael@0: SECStatus s = SECFailure; michael@0: /* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */ michael@0: PRUint64 t; michael@0: PRUint64 B[2]; michael@0: michael@0: #define A B[0] michael@0: michael@0: /* Check args */ michael@0: if (!inputLen || 0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) { michael@0: PORT_SetError(SEC_ERROR_INPUT_LEN); michael@0: return s; michael@0: } michael@0: #ifdef maybe michael@0: if (!output && pOutputLen) { /* caller is asking for output size */ michael@0: *pOutputLen = outLen; michael@0: return SECSuccess; michael@0: } michael@0: #endif michael@0: if (maxOutputLen < outLen) { michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: return s; michael@0: } michael@0: if (cx == NULL || output == NULL || input == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return s; michael@0: } michael@0: nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE; michael@0: R = PORT_NewArray(PRUint64, nBlocks + 1); michael@0: if (!R) michael@0: return s; /* error is already set. */ michael@0: /* michael@0: ** 1) Initialize variables. michael@0: */ michael@0: memcpy(&A, cx->iv, AES_KEY_WRAP_IV_BYTES); michael@0: memcpy(&R[1], input, inputLen); michael@0: #if BIG_ENDIAN_WITH_64_BIT_REGISTERS michael@0: t = 0; michael@0: #else michael@0: memset(&t, 0, sizeof t); michael@0: #endif michael@0: /* michael@0: ** 2) Calculate intermediate values. michael@0: */ michael@0: for (j = 0; j < 6; ++j) { michael@0: for (i = 1; i <= nBlocks; ++i) { michael@0: B[1] = R[i]; michael@0: s = AES_Encrypt(&cx->aescx, (unsigned char *)B, &aesLen, michael@0: sizeof B, (unsigned char *)B, sizeof B); michael@0: if (s != SECSuccess) michael@0: break; michael@0: R[i] = B[1]; michael@0: /* here, increment t and XOR A with t (in big endian order); */ michael@0: #if BIG_ENDIAN_WITH_64_BIT_REGISTERS michael@0: A ^= ++t; michael@0: #else michael@0: increment_and_xor((unsigned char *)&A, (unsigned char *)&t); michael@0: #endif michael@0: } michael@0: } michael@0: /* michael@0: ** 3) Output the results. michael@0: */ michael@0: if (s == SECSuccess) { michael@0: R[0] = A; michael@0: memcpy(output, &R[0], outLen); michael@0: if (pOutputLen) michael@0: *pOutputLen = outLen; michael@0: } else if (pOutputLen) { michael@0: *pOutputLen = 0; michael@0: } michael@0: PORT_ZFree(R, outLen); michael@0: return s; michael@0: } michael@0: #undef A michael@0: michael@0: /* michael@0: ** Perform AES key unwrap. michael@0: ** "cx" the context michael@0: ** "output" the output buffer to store the decrypted data. michael@0: ** "outputLen" how much data is stored in "output". Set by the routine michael@0: ** after some data is stored in output. michael@0: ** "maxOutputLen" the maximum amount of data that can ever be michael@0: ** stored in "output" michael@0: ** "input" the input data michael@0: ** "inputLen" the amount of input data michael@0: */ michael@0: extern SECStatus michael@0: AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output, michael@0: unsigned int *pOutputLen, unsigned int maxOutputLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: PRUint64 * R = NULL; michael@0: unsigned int nBlocks; michael@0: unsigned int i, j; michael@0: unsigned int aesLen = AES_BLOCK_SIZE; michael@0: unsigned int outLen; michael@0: SECStatus s = SECFailure; michael@0: /* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */ michael@0: PRUint64 t; michael@0: PRUint64 B[2]; michael@0: michael@0: #define A B[0] michael@0: michael@0: /* Check args */ michael@0: if (inputLen < 3 * AES_KEY_WRAP_BLOCK_SIZE || michael@0: 0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) { michael@0: PORT_SetError(SEC_ERROR_INPUT_LEN); michael@0: return s; michael@0: } michael@0: outLen = inputLen - AES_KEY_WRAP_BLOCK_SIZE; michael@0: #ifdef maybe michael@0: if (!output && pOutputLen) { /* caller is asking for output size */ michael@0: *pOutputLen = outLen; michael@0: return SECSuccess; michael@0: } michael@0: #endif michael@0: if (maxOutputLen < outLen) { michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: return s; michael@0: } michael@0: if (cx == NULL || output == NULL || input == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return s; michael@0: } michael@0: nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE; michael@0: R = PORT_NewArray(PRUint64, nBlocks); michael@0: if (!R) michael@0: return s; /* error is already set. */ michael@0: nBlocks--; michael@0: /* michael@0: ** 1) Initialize variables. michael@0: */ michael@0: memcpy(&R[0], input, inputLen); michael@0: A = R[0]; michael@0: #if BIG_ENDIAN_WITH_64_BIT_REGISTERS michael@0: t = 6UL * nBlocks; michael@0: #else michael@0: set_t((unsigned char *)&t, 6UL * nBlocks); michael@0: #endif michael@0: /* michael@0: ** 2) Calculate intermediate values. michael@0: */ michael@0: for (j = 0; j < 6; ++j) { michael@0: for (i = nBlocks; i; --i) { michael@0: /* here, XOR A with t (in big endian order) and decrement t; */ michael@0: #if BIG_ENDIAN_WITH_64_BIT_REGISTERS michael@0: A ^= t--; michael@0: #else michael@0: xor_and_decrement((unsigned char *)&A, (unsigned char *)&t); michael@0: #endif michael@0: B[1] = R[i]; michael@0: s = AES_Decrypt(&cx->aescx, (unsigned char *)B, &aesLen, michael@0: sizeof B, (unsigned char *)B, sizeof B); michael@0: if (s != SECSuccess) michael@0: break; michael@0: R[i] = B[1]; michael@0: } michael@0: } michael@0: /* michael@0: ** 3) Output the results. michael@0: */ michael@0: if (s == SECSuccess) { michael@0: int bad = memcmp(&A, cx->iv, AES_KEY_WRAP_IV_BYTES); michael@0: if (!bad) { michael@0: memcpy(output, &R[1], outLen); michael@0: if (pOutputLen) michael@0: *pOutputLen = outLen; michael@0: } else { michael@0: s = SECFailure; michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: if (pOutputLen) michael@0: *pOutputLen = 0; michael@0: } michael@0: } else if (pOutputLen) { michael@0: *pOutputLen = 0; michael@0: } michael@0: PORT_ZFree(R, inputLen); michael@0: return s; michael@0: } michael@0: #undef A