Back to index

lightning-sunbird  0.9+nobinonly
aeskeywrap.c
Go to the documentation of this file.
00001 /*
00002  * aeskeywrap.c - implement AES Key Wrap algorithm from RFC 3394
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is the Netscape security libraries.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2002
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 /* $Id: aeskeywrap.c,v 1.4 2005/08/06 07:24:21 nelsonb%netscape.com Exp $ */
00040 
00041 /* $Id: aeskeywrap.c,v 1.4 2005/08/06 07:24:21 nelsonb%netscape.com Exp $ */
00042 
00043 #include "prcpucfg.h"
00044 #if defined(IS_LITTLE_ENDIAN) || defined(SHA_NO_LONG_LONG)
00045 #define BIG_ENDIAN_WITH_64_BIT_REGISTERS 0
00046 #else
00047 #define BIG_ENDIAN_WITH_64_BIT_REGISTERS 1
00048 #endif
00049 #include "prtypes.h" /* for PRUintXX */
00050 #include "secport.h" /* for PORT_XXX */
00051 #include "secerr.h"
00052 #include "blapi.h"   /* for AES_ functions */
00053 #include "rijndael.h"
00054 
00055 struct AESKeyWrapContextStr {
00056      unsigned char iv[AES_KEY_WRAP_IV_BYTES];
00057      AESContext    aescx;
00058 };
00059 
00060 /******************************************/
00061 /*
00062 ** AES key wrap algorithm, RFC 3394
00063 */
00064 
00065 AESKeyWrapContext * 
00066 AESKeyWrap_AllocateContext(void)
00067 {
00068     AESKeyWrapContext * cx = PORT_New(AESKeyWrapContext);
00069     return cx;
00070 }
00071 
00072 SECStatus  
00073 AESKeyWrap_InitContext(AESKeyWrapContext *cx, 
00074                      const unsigned char *key, 
00075                      unsigned int keylen,
00076                      const unsigned char *iv, 
00077                      int x1,
00078                      unsigned int encrypt,
00079                      unsigned int x2)
00080 {
00081     SECStatus rv = SECFailure;
00082     if (!cx) {
00083        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00084        return SECFailure;
00085     }
00086     if (iv) {
00087        memcpy(cx->iv, iv, sizeof cx->iv);
00088     } else {
00089        memset(cx->iv, 0xA6, sizeof cx->iv);
00090     }
00091     rv = AES_InitContext(&cx->aescx, key, keylen, NULL, NSS_AES, encrypt, 
00092                                   AES_BLOCK_SIZE);
00093     return rv;
00094 }
00095 
00096 /*
00097 ** Create a new AES context suitable for AES encryption/decryption.
00098 **     "key" raw key data
00099 **     "keylen" the number of bytes of key data (16, 24, or 32)
00100 */
00101 extern AESKeyWrapContext *
00102 AESKeyWrap_CreateContext(const unsigned char *key, const unsigned char *iv, 
00103                          int encrypt, unsigned int keylen)
00104 {
00105     SECStatus rv;
00106     AESKeyWrapContext * cx = AESKeyWrap_AllocateContext();
00107     if (!cx) 
00108        return NULL;  /* error is already set */
00109     rv = AESKeyWrap_InitContext(cx, key, keylen, iv, 0, encrypt, 0);
00110     if (rv != SECSuccess) {
00111         PORT_Free(cx);
00112        cx = NULL;    /* error should already be set */
00113     }
00114     return cx;
00115 }
00116 
00117 /*
00118 ** Destroy a AES KeyWrap context.
00119 **     "cx" the context
00120 **     "freeit" if PR_TRUE then free the object as well as its sub-objects
00121 */
00122 extern void 
00123 AESKeyWrap_DestroyContext(AESKeyWrapContext *cx, PRBool freeit)
00124 {
00125     if (cx) {
00126        AES_DestroyContext(&cx->aescx, PR_FALSE);
00127 /*     memset(cx, 0, sizeof *cx); */
00128        if (freeit)
00129            PORT_Free(cx);
00130     }
00131 }
00132 
00133 #if !BIG_ENDIAN_WITH_64_BIT_REGISTERS
00134 
00135 /* The AES Key Wrap algorithm has 64-bit values that are ALWAYS big-endian
00136 ** (Most significant byte first) in memory.  The only ALU operations done
00137 ** on them are increment, decrement, and XOR.  So, on little-endian CPUs,
00138 ** and on CPUs that lack 64-bit registers, these big-endian 64-bit operations
00139 ** are simulated in the following code.  This is thought to be faster and
00140 ** simpler than trying to convert the data to little-endian and back.
00141 */
00142 
00143 /* A and T point to two 64-bit values stored most signficant byte first
00144 ** (big endian).  This function increments the 64-bit value T, and then
00145 ** XORs it with A, changing A.
00146 */ 
00147 static void
00148 increment_and_xor(unsigned char *A, unsigned char *T)
00149 {
00150     if (!++T[7])
00151         if (!++T[6])
00152            if (!++T[5])
00153               if (!++T[4])
00154                   if (!++T[3])
00155                      if (!++T[2])
00156                          if (!++T[1])
00157                              ++T[0];
00158 
00159     A[0] ^= T[0];
00160     A[1] ^= T[1];
00161     A[2] ^= T[2];
00162     A[3] ^= T[3];
00163     A[4] ^= T[4];
00164     A[5] ^= T[5];
00165     A[6] ^= T[6];
00166     A[7] ^= T[7];
00167 }
00168 
00169 /* A and T point to two 64-bit values stored most signficant byte first
00170 ** (big endian).  This function XORs T with A, giving a new A, then 
00171 ** decrements the 64-bit value T.
00172 */ 
00173 static void
00174 xor_and_decrement(unsigned char *A, unsigned char *T)
00175 {
00176     A[0] ^= T[0];
00177     A[1] ^= T[1];
00178     A[2] ^= T[2];
00179     A[3] ^= T[3];
00180     A[4] ^= T[4];
00181     A[5] ^= T[5];
00182     A[6] ^= T[6];
00183     A[7] ^= T[7];
00184 
00185     if (!T[7]--)
00186         if (!T[6]--)
00187            if (!T[5]--)
00188               if (!T[4]--)
00189                   if (!T[3]--)
00190                      if (!T[2]--)
00191                          if (!T[1]--)
00192                              T[0]--;
00193 
00194 }
00195 
00196 /* Given an unsigned long t (in host byte order), store this value as a
00197 ** 64-bit big-endian value (MSB first) in *pt.
00198 */
00199 static void
00200 set_t(unsigned char *pt, unsigned long t)
00201 {
00202     pt[7] = (unsigned char)t; t >>= 8;
00203     pt[6] = (unsigned char)t; t >>= 8;
00204     pt[5] = (unsigned char)t; t >>= 8;
00205     pt[4] = (unsigned char)t; t >>= 8;
00206     pt[3] = (unsigned char)t; t >>= 8;
00207     pt[2] = (unsigned char)t; t >>= 8;
00208     pt[1] = (unsigned char)t; t >>= 8;
00209     pt[0] = (unsigned char)t;
00210 }
00211 
00212 #endif
00213 
00214 /*
00215 ** Perform AES key wrap.
00216 **     "cx" the context
00217 **     "output" the output buffer to store the encrypted data.
00218 **     "outputLen" how much data is stored in "output". Set by the routine
00219 **        after some data is stored in output.
00220 **     "maxOutputLen" the maximum amount of data that can ever be
00221 **        stored in "output"
00222 **     "input" the input data
00223 **     "inputLen" the amount of input data
00224 */
00225 extern SECStatus 
00226 AESKeyWrap_Encrypt(AESKeyWrapContext *cx, unsigned char *output,
00227             unsigned int *pOutputLen, unsigned int maxOutputLen,
00228             const unsigned char *input, unsigned int inputLen)
00229 {
00230     PRUint64 *     R          = NULL;
00231     unsigned int   nBlocks;
00232     unsigned int   i, j;
00233     unsigned int   aesLen     = AES_BLOCK_SIZE;
00234     unsigned int   outLen     = inputLen + AES_KEY_WRAP_BLOCK_SIZE;
00235     SECStatus      s          = SECFailure;
00236     /* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */
00237     PRUint64       t;
00238     PRUint64       B[2];
00239 
00240 #define A B[0]
00241 
00242     /* Check args */
00243     if (!inputLen || 0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) {
00244        PORT_SetError(SEC_ERROR_INPUT_LEN);
00245        return s;
00246     }
00247 #ifdef maybe
00248     if (!output && pOutputLen) {   /* caller is asking for output size */
00249        *pOutputLen = outLen;
00250        return SECSuccess;
00251     }
00252 #endif
00253     if (maxOutputLen < outLen) {
00254        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
00255        return s;
00256     }
00257     if (cx == NULL || output == NULL || input == NULL) {
00258        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00259        return s;
00260     }
00261     nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE;
00262     R = PORT_NewArray(PRUint64, nBlocks + 1);
00263     if (!R)
00264        return s;     /* error is already set. */
00265     /* 
00266     ** 1) Initialize variables.
00267     */
00268     memcpy(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
00269     memcpy(&R[1], input, inputLen);
00270 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
00271     t = 0;
00272 #else
00273     memset(&t, 0, sizeof t);
00274 #endif
00275     /* 
00276     ** 2) Calculate intermediate values.
00277     */
00278     for (j = 0; j < 6; ++j) {
00279        for (i = 1; i <= nBlocks; ++i) {
00280            B[1] = R[i];
00281            s = AES_Encrypt(&cx->aescx, (unsigned char *)B, &aesLen, 
00282                            sizeof B,  (unsigned char *)B, sizeof B);
00283            if (s != SECSuccess) 
00284                break;
00285            R[i] = B[1];
00286            /* here, increment t and XOR A with t (in big endian order); */
00287 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
00288            A ^= ++t; 
00289 #else
00290            increment_and_xor((unsigned char *)&A, (unsigned char *)&t);
00291 #endif
00292        }
00293     }
00294     /* 
00295     ** 3) Output the results.
00296     */
00297     if (s == SECSuccess) {
00298        R[0] =  A;
00299        memcpy(output, &R[0], outLen);
00300        if (pOutputLen)
00301            *pOutputLen = outLen;
00302     } else if (pOutputLen) {
00303        *pOutputLen = 0;
00304     }
00305     PORT_ZFree(R, outLen);
00306     return s;
00307 }
00308 #undef A
00309 
00310 /*
00311 ** Perform AES key unwrap.
00312 **     "cx" the context
00313 **     "output" the output buffer to store the decrypted data.
00314 **     "outputLen" how much data is stored in "output". Set by the routine
00315 **        after some data is stored in output.
00316 **     "maxOutputLen" the maximum amount of data that can ever be
00317 **        stored in "output"
00318 **     "input" the input data
00319 **     "inputLen" the amount of input data
00320 */
00321 extern SECStatus 
00322 AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output,
00323             unsigned int *pOutputLen, unsigned int maxOutputLen,
00324             const unsigned char *input, unsigned int inputLen)
00325 {
00326     PRUint64 *     R          = NULL;
00327     unsigned int   nBlocks;
00328     unsigned int   i, j;
00329     unsigned int   aesLen     = AES_BLOCK_SIZE;
00330     unsigned int   outLen;
00331     SECStatus      s          = SECFailure;
00332     /* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */
00333     PRUint64       t;
00334     PRUint64       B[2];
00335 
00336 #define A B[0]
00337 
00338     /* Check args */
00339     if (inputLen < 3 * AES_KEY_WRAP_BLOCK_SIZE || 
00340         0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) {
00341        PORT_SetError(SEC_ERROR_INPUT_LEN);
00342        return s;
00343     }
00344     outLen = inputLen - AES_KEY_WRAP_BLOCK_SIZE;
00345 #ifdef maybe
00346     if (!output && pOutputLen) {   /* caller is asking for output size */
00347        *pOutputLen = outLen;
00348        return SECSuccess;
00349     }
00350 #endif
00351     if (maxOutputLen < outLen) {
00352        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
00353        return s;
00354     }
00355     if (cx == NULL || output == NULL || input == NULL) {
00356        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00357        return s;
00358     }
00359     nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE;
00360     R = PORT_NewArray(PRUint64, nBlocks);
00361     if (!R)
00362        return s;     /* error is already set. */
00363     nBlocks--;
00364     /* 
00365     ** 1) Initialize variables.
00366     */
00367     memcpy(&R[0], input, inputLen);
00368     A = R[0];
00369 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
00370     t = 6UL * nBlocks;
00371 #else
00372     set_t((unsigned char *)&t, 6UL * nBlocks);
00373 #endif
00374     /* 
00375     ** 2) Calculate intermediate values.
00376     */
00377     for (j = 0; j < 6; ++j) {
00378        for (i = nBlocks; i; --i) {
00379            /* here, XOR A with t (in big endian order) and decrement t; */
00380 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
00381            A ^= t--; 
00382 #else
00383            xor_and_decrement((unsigned char *)&A, (unsigned char *)&t);
00384 #endif
00385            B[1] = R[i];
00386            s = AES_Decrypt(&cx->aescx, (unsigned char *)B, &aesLen, 
00387                            sizeof B,  (unsigned char *)B, sizeof B);
00388            if (s != SECSuccess) 
00389                break;
00390            R[i] = B[1];
00391        }
00392     }
00393     /* 
00394     ** 3) Output the results.
00395     */
00396     if (s == SECSuccess) {
00397        int bad = memcmp(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
00398        if (!bad) {
00399            memcpy(output, &R[1], outLen);
00400            if (pOutputLen)
00401               *pOutputLen = outLen;
00402        } else {
00403            PORT_SetError(SEC_ERROR_BAD_DATA);
00404            if (pOutputLen) 
00405               *pOutputLen = 0;
00406        }
00407     } else if (pOutputLen) {
00408        *pOutputLen = 0;
00409     }
00410     PORT_ZFree(R, inputLen);
00411     return s;
00412 }
00413 #undef A