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