/security/nss/lib/freebl/aeskeywrap.c
http://github.com/zpao/v8monkey · C · 417 lines · 274 code · 24 blank · 119 comment · 70 complexity · 6fcaf84bc1b743ed1193d9c43418942f MD5 · raw file
- /*
- * aeskeywrap.c - implement AES Key Wrap algorithm from RFC 3394
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape security libraries.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- /* $Id: aeskeywrap.c,v 1.5 2008/11/18 19:48:21 rrelyea%redhat.com Exp $ */
- /* $Id: aeskeywrap.c,v 1.5 2008/11/18 19:48:21 rrelyea%redhat.com Exp $ */
- #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(unsigned char *A, unsigned char *T)
- {
- 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];
- if (!T[7]--)
- if (!T[6]--)
- if (!T[5]--)
- if (!T[4]--)
- if (!T[3]--)
- if (!T[2]--)
- if (!T[1]--)
- T[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];
- #define A B[0]
- /* 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);
- A = 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
- A ^= t--;
- #else
- xor_and_decrement((unsigned char *)&A, (unsigned char *)&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(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
- if (!bad) {
- memcpy(output, &R[1], outLen);
- if (pOutputLen)
- *pOutputLen = outLen;
- } else {
- PORT_SetError(SEC_ERROR_BAD_DATA);
- if (pOutputLen)
- *pOutputLen = 0;
- }
- } else if (pOutputLen) {
- *pOutputLen = 0;
- }
- PORT_ZFree(R, inputLen);
- return s;
- }
- #undef A