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