security/nss/lib/freebl/aeskeywrap.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /*
michael@0 2 * aeskeywrap.c - implement AES Key Wrap algorithm from RFC 3394
michael@0 3 *
michael@0 4 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 7
michael@0 8 #ifdef FREEBL_NO_DEPEND
michael@0 9 #include "stubs.h"
michael@0 10 #endif
michael@0 11
michael@0 12 #include "prcpucfg.h"
michael@0 13 #if defined(IS_LITTLE_ENDIAN) || defined(SHA_NO_LONG_LONG)
michael@0 14 #define BIG_ENDIAN_WITH_64_BIT_REGISTERS 0
michael@0 15 #else
michael@0 16 #define BIG_ENDIAN_WITH_64_BIT_REGISTERS 1
michael@0 17 #endif
michael@0 18 #include "prtypes.h" /* for PRUintXX */
michael@0 19 #include "secport.h" /* for PORT_XXX */
michael@0 20 #include "secerr.h"
michael@0 21 #include "blapi.h" /* for AES_ functions */
michael@0 22 #include "rijndael.h"
michael@0 23
michael@0 24 struct AESKeyWrapContextStr {
michael@0 25 unsigned char iv[AES_KEY_WRAP_IV_BYTES];
michael@0 26 AESContext aescx;
michael@0 27 };
michael@0 28
michael@0 29 /******************************************/
michael@0 30 /*
michael@0 31 ** AES key wrap algorithm, RFC 3394
michael@0 32 */
michael@0 33
michael@0 34 AESKeyWrapContext *
michael@0 35 AESKeyWrap_AllocateContext(void)
michael@0 36 {
michael@0 37 AESKeyWrapContext * cx = PORT_New(AESKeyWrapContext);
michael@0 38 return cx;
michael@0 39 }
michael@0 40
michael@0 41 SECStatus
michael@0 42 AESKeyWrap_InitContext(AESKeyWrapContext *cx,
michael@0 43 const unsigned char *key,
michael@0 44 unsigned int keylen,
michael@0 45 const unsigned char *iv,
michael@0 46 int x1,
michael@0 47 unsigned int encrypt,
michael@0 48 unsigned int x2)
michael@0 49 {
michael@0 50 SECStatus rv = SECFailure;
michael@0 51 if (!cx) {
michael@0 52 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 53 return SECFailure;
michael@0 54 }
michael@0 55 if (iv) {
michael@0 56 memcpy(cx->iv, iv, sizeof cx->iv);
michael@0 57 } else {
michael@0 58 memset(cx->iv, 0xA6, sizeof cx->iv);
michael@0 59 }
michael@0 60 rv = AES_InitContext(&cx->aescx, key, keylen, NULL, NSS_AES, encrypt,
michael@0 61 AES_BLOCK_SIZE);
michael@0 62 return rv;
michael@0 63 }
michael@0 64
michael@0 65 /*
michael@0 66 ** Create a new AES context suitable for AES encryption/decryption.
michael@0 67 ** "key" raw key data
michael@0 68 ** "keylen" the number of bytes of key data (16, 24, or 32)
michael@0 69 */
michael@0 70 extern AESKeyWrapContext *
michael@0 71 AESKeyWrap_CreateContext(const unsigned char *key, const unsigned char *iv,
michael@0 72 int encrypt, unsigned int keylen)
michael@0 73 {
michael@0 74 SECStatus rv;
michael@0 75 AESKeyWrapContext * cx = AESKeyWrap_AllocateContext();
michael@0 76 if (!cx)
michael@0 77 return NULL; /* error is already set */
michael@0 78 rv = AESKeyWrap_InitContext(cx, key, keylen, iv, 0, encrypt, 0);
michael@0 79 if (rv != SECSuccess) {
michael@0 80 PORT_Free(cx);
michael@0 81 cx = NULL; /* error should already be set */
michael@0 82 }
michael@0 83 return cx;
michael@0 84 }
michael@0 85
michael@0 86 /*
michael@0 87 ** Destroy a AES KeyWrap context.
michael@0 88 ** "cx" the context
michael@0 89 ** "freeit" if PR_TRUE then free the object as well as its sub-objects
michael@0 90 */
michael@0 91 extern void
michael@0 92 AESKeyWrap_DestroyContext(AESKeyWrapContext *cx, PRBool freeit)
michael@0 93 {
michael@0 94 if (cx) {
michael@0 95 AES_DestroyContext(&cx->aescx, PR_FALSE);
michael@0 96 /* memset(cx, 0, sizeof *cx); */
michael@0 97 if (freeit)
michael@0 98 PORT_Free(cx);
michael@0 99 }
michael@0 100 }
michael@0 101
michael@0 102 #if !BIG_ENDIAN_WITH_64_BIT_REGISTERS
michael@0 103
michael@0 104 /* The AES Key Wrap algorithm has 64-bit values that are ALWAYS big-endian
michael@0 105 ** (Most significant byte first) in memory. The only ALU operations done
michael@0 106 ** on them are increment, decrement, and XOR. So, on little-endian CPUs,
michael@0 107 ** and on CPUs that lack 64-bit registers, these big-endian 64-bit operations
michael@0 108 ** are simulated in the following code. This is thought to be faster and
michael@0 109 ** simpler than trying to convert the data to little-endian and back.
michael@0 110 */
michael@0 111
michael@0 112 /* A and T point to two 64-bit values stored most signficant byte first
michael@0 113 ** (big endian). This function increments the 64-bit value T, and then
michael@0 114 ** XORs it with A, changing A.
michael@0 115 */
michael@0 116 static void
michael@0 117 increment_and_xor(unsigned char *A, unsigned char *T)
michael@0 118 {
michael@0 119 if (!++T[7])
michael@0 120 if (!++T[6])
michael@0 121 if (!++T[5])
michael@0 122 if (!++T[4])
michael@0 123 if (!++T[3])
michael@0 124 if (!++T[2])
michael@0 125 if (!++T[1])
michael@0 126 ++T[0];
michael@0 127
michael@0 128 A[0] ^= T[0];
michael@0 129 A[1] ^= T[1];
michael@0 130 A[2] ^= T[2];
michael@0 131 A[3] ^= T[3];
michael@0 132 A[4] ^= T[4];
michael@0 133 A[5] ^= T[5];
michael@0 134 A[6] ^= T[6];
michael@0 135 A[7] ^= T[7];
michael@0 136 }
michael@0 137
michael@0 138 /* A and T point to two 64-bit values stored most signficant byte first
michael@0 139 ** (big endian). This function XORs T with A, giving a new A, then
michael@0 140 ** decrements the 64-bit value T.
michael@0 141 */
michael@0 142 static void
michael@0 143 xor_and_decrement(unsigned char *A, unsigned char *T)
michael@0 144 {
michael@0 145 A[0] ^= T[0];
michael@0 146 A[1] ^= T[1];
michael@0 147 A[2] ^= T[2];
michael@0 148 A[3] ^= T[3];
michael@0 149 A[4] ^= T[4];
michael@0 150 A[5] ^= T[5];
michael@0 151 A[6] ^= T[6];
michael@0 152 A[7] ^= T[7];
michael@0 153
michael@0 154 if (!T[7]--)
michael@0 155 if (!T[6]--)
michael@0 156 if (!T[5]--)
michael@0 157 if (!T[4]--)
michael@0 158 if (!T[3]--)
michael@0 159 if (!T[2]--)
michael@0 160 if (!T[1]--)
michael@0 161 T[0]--;
michael@0 162
michael@0 163 }
michael@0 164
michael@0 165 /* Given an unsigned long t (in host byte order), store this value as a
michael@0 166 ** 64-bit big-endian value (MSB first) in *pt.
michael@0 167 */
michael@0 168 static void
michael@0 169 set_t(unsigned char *pt, unsigned long t)
michael@0 170 {
michael@0 171 pt[7] = (unsigned char)t; t >>= 8;
michael@0 172 pt[6] = (unsigned char)t; t >>= 8;
michael@0 173 pt[5] = (unsigned char)t; t >>= 8;
michael@0 174 pt[4] = (unsigned char)t; t >>= 8;
michael@0 175 pt[3] = (unsigned char)t; t >>= 8;
michael@0 176 pt[2] = (unsigned char)t; t >>= 8;
michael@0 177 pt[1] = (unsigned char)t; t >>= 8;
michael@0 178 pt[0] = (unsigned char)t;
michael@0 179 }
michael@0 180
michael@0 181 #endif
michael@0 182
michael@0 183 /*
michael@0 184 ** Perform AES key wrap.
michael@0 185 ** "cx" the context
michael@0 186 ** "output" the output buffer to store the encrypted data.
michael@0 187 ** "outputLen" how much data is stored in "output". Set by the routine
michael@0 188 ** after some data is stored in output.
michael@0 189 ** "maxOutputLen" the maximum amount of data that can ever be
michael@0 190 ** stored in "output"
michael@0 191 ** "input" the input data
michael@0 192 ** "inputLen" the amount of input data
michael@0 193 */
michael@0 194 extern SECStatus
michael@0 195 AESKeyWrap_Encrypt(AESKeyWrapContext *cx, unsigned char *output,
michael@0 196 unsigned int *pOutputLen, unsigned int maxOutputLen,
michael@0 197 const unsigned char *input, unsigned int inputLen)
michael@0 198 {
michael@0 199 PRUint64 * R = NULL;
michael@0 200 unsigned int nBlocks;
michael@0 201 unsigned int i, j;
michael@0 202 unsigned int aesLen = AES_BLOCK_SIZE;
michael@0 203 unsigned int outLen = inputLen + AES_KEY_WRAP_BLOCK_SIZE;
michael@0 204 SECStatus s = SECFailure;
michael@0 205 /* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */
michael@0 206 PRUint64 t;
michael@0 207 PRUint64 B[2];
michael@0 208
michael@0 209 #define A B[0]
michael@0 210
michael@0 211 /* Check args */
michael@0 212 if (!inputLen || 0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) {
michael@0 213 PORT_SetError(SEC_ERROR_INPUT_LEN);
michael@0 214 return s;
michael@0 215 }
michael@0 216 #ifdef maybe
michael@0 217 if (!output && pOutputLen) { /* caller is asking for output size */
michael@0 218 *pOutputLen = outLen;
michael@0 219 return SECSuccess;
michael@0 220 }
michael@0 221 #endif
michael@0 222 if (maxOutputLen < outLen) {
michael@0 223 PORT_SetError(SEC_ERROR_OUTPUT_LEN);
michael@0 224 return s;
michael@0 225 }
michael@0 226 if (cx == NULL || output == NULL || input == NULL) {
michael@0 227 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 228 return s;
michael@0 229 }
michael@0 230 nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE;
michael@0 231 R = PORT_NewArray(PRUint64, nBlocks + 1);
michael@0 232 if (!R)
michael@0 233 return s; /* error is already set. */
michael@0 234 /*
michael@0 235 ** 1) Initialize variables.
michael@0 236 */
michael@0 237 memcpy(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
michael@0 238 memcpy(&R[1], input, inputLen);
michael@0 239 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
michael@0 240 t = 0;
michael@0 241 #else
michael@0 242 memset(&t, 0, sizeof t);
michael@0 243 #endif
michael@0 244 /*
michael@0 245 ** 2) Calculate intermediate values.
michael@0 246 */
michael@0 247 for (j = 0; j < 6; ++j) {
michael@0 248 for (i = 1; i <= nBlocks; ++i) {
michael@0 249 B[1] = R[i];
michael@0 250 s = AES_Encrypt(&cx->aescx, (unsigned char *)B, &aesLen,
michael@0 251 sizeof B, (unsigned char *)B, sizeof B);
michael@0 252 if (s != SECSuccess)
michael@0 253 break;
michael@0 254 R[i] = B[1];
michael@0 255 /* here, increment t and XOR A with t (in big endian order); */
michael@0 256 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
michael@0 257 A ^= ++t;
michael@0 258 #else
michael@0 259 increment_and_xor((unsigned char *)&A, (unsigned char *)&t);
michael@0 260 #endif
michael@0 261 }
michael@0 262 }
michael@0 263 /*
michael@0 264 ** 3) Output the results.
michael@0 265 */
michael@0 266 if (s == SECSuccess) {
michael@0 267 R[0] = A;
michael@0 268 memcpy(output, &R[0], outLen);
michael@0 269 if (pOutputLen)
michael@0 270 *pOutputLen = outLen;
michael@0 271 } else if (pOutputLen) {
michael@0 272 *pOutputLen = 0;
michael@0 273 }
michael@0 274 PORT_ZFree(R, outLen);
michael@0 275 return s;
michael@0 276 }
michael@0 277 #undef A
michael@0 278
michael@0 279 /*
michael@0 280 ** Perform AES key unwrap.
michael@0 281 ** "cx" the context
michael@0 282 ** "output" the output buffer to store the decrypted data.
michael@0 283 ** "outputLen" how much data is stored in "output". Set by the routine
michael@0 284 ** after some data is stored in output.
michael@0 285 ** "maxOutputLen" the maximum amount of data that can ever be
michael@0 286 ** stored in "output"
michael@0 287 ** "input" the input data
michael@0 288 ** "inputLen" the amount of input data
michael@0 289 */
michael@0 290 extern SECStatus
michael@0 291 AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output,
michael@0 292 unsigned int *pOutputLen, unsigned int maxOutputLen,
michael@0 293 const unsigned char *input, unsigned int inputLen)
michael@0 294 {
michael@0 295 PRUint64 * R = NULL;
michael@0 296 unsigned int nBlocks;
michael@0 297 unsigned int i, j;
michael@0 298 unsigned int aesLen = AES_BLOCK_SIZE;
michael@0 299 unsigned int outLen;
michael@0 300 SECStatus s = SECFailure;
michael@0 301 /* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */
michael@0 302 PRUint64 t;
michael@0 303 PRUint64 B[2];
michael@0 304
michael@0 305 #define A B[0]
michael@0 306
michael@0 307 /* Check args */
michael@0 308 if (inputLen < 3 * AES_KEY_WRAP_BLOCK_SIZE ||
michael@0 309 0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) {
michael@0 310 PORT_SetError(SEC_ERROR_INPUT_LEN);
michael@0 311 return s;
michael@0 312 }
michael@0 313 outLen = inputLen - AES_KEY_WRAP_BLOCK_SIZE;
michael@0 314 #ifdef maybe
michael@0 315 if (!output && pOutputLen) { /* caller is asking for output size */
michael@0 316 *pOutputLen = outLen;
michael@0 317 return SECSuccess;
michael@0 318 }
michael@0 319 #endif
michael@0 320 if (maxOutputLen < outLen) {
michael@0 321 PORT_SetError(SEC_ERROR_OUTPUT_LEN);
michael@0 322 return s;
michael@0 323 }
michael@0 324 if (cx == NULL || output == NULL || input == NULL) {
michael@0 325 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 326 return s;
michael@0 327 }
michael@0 328 nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE;
michael@0 329 R = PORT_NewArray(PRUint64, nBlocks);
michael@0 330 if (!R)
michael@0 331 return s; /* error is already set. */
michael@0 332 nBlocks--;
michael@0 333 /*
michael@0 334 ** 1) Initialize variables.
michael@0 335 */
michael@0 336 memcpy(&R[0], input, inputLen);
michael@0 337 A = R[0];
michael@0 338 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
michael@0 339 t = 6UL * nBlocks;
michael@0 340 #else
michael@0 341 set_t((unsigned char *)&t, 6UL * nBlocks);
michael@0 342 #endif
michael@0 343 /*
michael@0 344 ** 2) Calculate intermediate values.
michael@0 345 */
michael@0 346 for (j = 0; j < 6; ++j) {
michael@0 347 for (i = nBlocks; i; --i) {
michael@0 348 /* here, XOR A with t (in big endian order) and decrement t; */
michael@0 349 #if BIG_ENDIAN_WITH_64_BIT_REGISTERS
michael@0 350 A ^= t--;
michael@0 351 #else
michael@0 352 xor_and_decrement((unsigned char *)&A, (unsigned char *)&t);
michael@0 353 #endif
michael@0 354 B[1] = R[i];
michael@0 355 s = AES_Decrypt(&cx->aescx, (unsigned char *)B, &aesLen,
michael@0 356 sizeof B, (unsigned char *)B, sizeof B);
michael@0 357 if (s != SECSuccess)
michael@0 358 break;
michael@0 359 R[i] = B[1];
michael@0 360 }
michael@0 361 }
michael@0 362 /*
michael@0 363 ** 3) Output the results.
michael@0 364 */
michael@0 365 if (s == SECSuccess) {
michael@0 366 int bad = memcmp(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
michael@0 367 if (!bad) {
michael@0 368 memcpy(output, &R[1], outLen);
michael@0 369 if (pOutputLen)
michael@0 370 *pOutputLen = outLen;
michael@0 371 } else {
michael@0 372 s = SECFailure;
michael@0 373 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 374 if (pOutputLen)
michael@0 375 *pOutputLen = 0;
michael@0 376 }
michael@0 377 } else if (pOutputLen) {
michael@0 378 *pOutputLen = 0;
michael@0 379 }
michael@0 380 PORT_ZFree(R, inputLen);
michael@0 381 return s;
michael@0 382 }
michael@0 383 #undef A

mercurial