ppc-gcm-wrap.c (13457B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 /* Copyright(c) 2013, Intel Corp. */ 5 6 /* Wrapper functions for PowerPC optimized implementation of AES-GCM */ 7 8 #ifdef FREEBL_NO_DEPEND 9 #include "stubs.h" 10 #endif 11 12 #include "blapii.h" 13 #include "blapit.h" 14 #include "gcm.h" 15 #include "ctr.h" 16 #include "secerr.h" 17 #include "prtypes.h" 18 #include "pkcs11t.h" 19 20 #include <limits.h> 21 #include <stdio.h> 22 23 #include "ppc-gcm.h" 24 #include "rijndael.h" 25 26 struct ppc_AES_GCMContextStr { 27 unsigned char Htbl[8 * AES_BLOCK_SIZE]; 28 unsigned char X0[AES_BLOCK_SIZE]; 29 unsigned char T[AES_BLOCK_SIZE]; 30 unsigned char CTR[AES_BLOCK_SIZE]; 31 AESContext *aes_context; 32 unsigned long tagBits; 33 unsigned long Alen; 34 unsigned long Mlen; 35 freeblCipherFunc cipher; 36 PRBool ctr_context_init; 37 gcmIVContext gcm_iv; 38 }; 39 40 SECStatus ppc_aes_gcmInitCounter(ppc_AES_GCMContext *gcm, 41 const unsigned char *iv, 42 unsigned long ivLen, unsigned long tagBits, 43 const unsigned char *aad, unsigned long aadLen); 44 45 ppc_AES_GCMContext * 46 ppc_AES_GCM_CreateContext(void *context, 47 freeblCipherFunc cipher, 48 const unsigned char *params) 49 { 50 ppc_AES_GCMContext *gcm = NULL; 51 AESContext *aes = (AESContext *)context; 52 const CK_NSS_GCM_PARAMS *gcmParams = (const CK_NSS_GCM_PARAMS *)params; 53 SECStatus rv; 54 55 gcm = PORT_ZNew(ppc_AES_GCMContext); 56 if (gcm == NULL) { 57 return NULL; 58 } 59 60 /* initialize context fields */ 61 gcm->aes_context = aes; 62 gcm->cipher = cipher; 63 gcm->Alen = 0; 64 gcm->Mlen = 0; 65 gcm->ctr_context_init = PR_FALSE; 66 67 /* first prepare H and its derivatives for ghash */ 68 ppc_aes_gcmINIT(gcm->Htbl, aes->k.expandedKey, aes->Nr); 69 70 gcm_InitIVContext(&gcm->gcm_iv); 71 72 /* if gcmParams is NULL, then we are creating an PKCS #11 MESSAGE 73 * style context, in which we initialize the key once, then do separate 74 * iv/aad's for each message. If we are doing that kind of operation, 75 * we've finished with init here. We'll init the Counter in each AEAD 76 * call */ 77 if (gcmParams == NULL) { 78 return gcm; 79 } 80 81 rv = ppc_aes_gcmInitCounter(gcm, gcmParams->pIv, 82 gcmParams->ulIvLen, gcmParams->ulTagBits, 83 gcmParams->pAAD, gcmParams->ulAADLen); 84 if (rv != SECSuccess) { 85 PORT_Free(gcm); 86 return NULL; 87 } 88 gcm->ctr_context_init = PR_TRUE; 89 90 return gcm; 91 } 92 93 SECStatus 94 ppc_aes_gcmInitCounter(ppc_AES_GCMContext *gcm, 95 const unsigned char *iv, unsigned long ivLen, 96 unsigned long tagBits, 97 const unsigned char *aad, unsigned long aadLen) 98 { 99 unsigned int j; 100 SECStatus rv; 101 102 if (ivLen == 0) { 103 PORT_SetError(SEC_ERROR_INVALID_ARGS); 104 return SECFailure; 105 } 106 107 if (tagBits != 128 && tagBits != 120 && tagBits != 112 && 108 tagBits != 104 && tagBits != 96 && tagBits != 64 && 109 tagBits != 32) { 110 PORT_SetError(SEC_ERROR_INVALID_ARGS); 111 return SECFailure; 112 } 113 gcm->tagBits = tagBits; 114 115 /* reset the aad and message length counters */ 116 gcm->Alen = 0; 117 gcm->Mlen = 0; 118 119 /* Initial TAG value is zero */ 120 PORT_Memset(gcm->T, 0, AES_BLOCK_SIZE); 121 PORT_Memset(gcm->X0, 0, AES_BLOCK_SIZE); 122 123 /* Init the counter */ 124 if (ivLen == 12) { 125 PORT_Memcpy(gcm->CTR, iv, AES_BLOCK_SIZE - 4); 126 gcm->CTR[12] = 0; 127 gcm->CTR[13] = 0; 128 gcm->CTR[14] = 0; 129 gcm->CTR[15] = 1; 130 } else { 131 /* If IV size is not 96 bits, then the initial counter value is GHASH 132 * of the IV */ 133 ppc_aes_gcmHASH(gcm->Htbl, iv, ivLen, gcm->T); 134 135 ppc_aes_gcmTAG( 136 gcm->Htbl, 137 gcm->T, 138 ivLen, 139 0, 140 gcm->X0, 141 gcm->CTR); 142 143 /* TAG should be zero again */ 144 PORT_Memset(gcm->T, 0, AES_BLOCK_SIZE); 145 } 146 147 /* Encrypt the initial counter, will be used to encrypt the GHASH value, 148 * in the end */ 149 rv = (*gcm->cipher)(gcm->aes_context, gcm->X0, &j, AES_BLOCK_SIZE, gcm->CTR, 150 AES_BLOCK_SIZE, AES_BLOCK_SIZE); 151 if (rv != SECSuccess) { 152 return SECFailure; 153 } 154 155 /* Promote the counter by 1 */ 156 gcm->CTR[14] += !(++gcm->CTR[15]); 157 gcm->CTR[13] += !(gcm->CTR[15]) && !(gcm->CTR[14]); 158 gcm->CTR[12] += !(gcm->CTR[15]) && !(gcm->CTR[14]) && !(gcm->CTR[13]); 159 160 /* Now hash AAD - it would actually make sense to seperate the context 161 * creation from the AAD, because that would allow to reuse the H, which 162 * only changes when the AES key changes, and not every package, like the 163 * IV and AAD */ 164 ppc_aes_gcmHASH(gcm->Htbl, aad, aadLen, gcm->T); 165 gcm->Alen += aadLen; 166 return SECSuccess; 167 } 168 169 void 170 ppc_AES_GCM_DestroyContext(ppc_AES_GCMContext *gcm, PRBool freeit) 171 { 172 PORT_SafeZero(gcm, sizeof(ppc_AES_GCMContext)); 173 if (freeit) { 174 PORT_Free(gcm); 175 } 176 } 177 178 SECStatus 179 ppc_AES_GCM_EncryptUpdate(ppc_AES_GCMContext *gcm, 180 unsigned char *outbuf, 181 unsigned int *outlen, unsigned int maxout, 182 const unsigned char *inbuf, unsigned int inlen, 183 unsigned int blocksize) 184 { 185 unsigned int tagBytes; 186 unsigned char T[AES_BLOCK_SIZE]; 187 unsigned int j; 188 189 // GCM has a 16 octet block, with a 32-bit block counter 190 // Limit in accordance with SP800-38D 191 if (sizeof(inlen) > 4 && 192 inlen >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) { 193 PORT_SetError(SEC_ERROR_INPUT_LEN); 194 return SECFailure; 195 } 196 197 if (!gcm->ctr_context_init) { 198 PORT_SetError(SEC_ERROR_NOT_INITIALIZED); 199 return SECFailure; 200 } 201 202 tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; 203 if (UINT_MAX - inlen < tagBytes) { 204 PORT_SetError(SEC_ERROR_INPUT_LEN); 205 return SECFailure; 206 } 207 if (maxout < inlen + tagBytes) { 208 *outlen = inlen + tagBytes; 209 PORT_SetError(SEC_ERROR_OUTPUT_LEN); 210 return SECFailure; 211 } 212 213 ppc_aes_gcmCRYPT( 214 inbuf, 215 outbuf, 216 inlen, 217 gcm->CTR, 218 gcm->aes_context->k.expandedKey, 219 gcm->aes_context->Nr); 220 ppc_aes_gcmHASH( 221 gcm->Htbl, 222 outbuf, 223 inlen, 224 gcm->T); 225 226 gcm->Mlen += inlen; 227 228 ppc_aes_gcmTAG( 229 gcm->Htbl, 230 gcm->T, 231 gcm->Mlen, 232 gcm->Alen, 233 gcm->X0, 234 T); 235 236 *outlen = inlen + tagBytes; 237 238 for (j = 0; j < tagBytes; j++) { 239 outbuf[inlen + j] = T[j]; 240 } 241 return SECSuccess; 242 } 243 244 SECStatus 245 ppc_AES_GCM_DecryptUpdate(ppc_AES_GCMContext *gcm, 246 unsigned char *outbuf, 247 unsigned int *outlen, unsigned int maxout, 248 const unsigned char *inbuf, unsigned int inlen, 249 unsigned int blocksize) 250 { 251 unsigned int tagBytes; 252 unsigned char T[AES_BLOCK_SIZE]; 253 const unsigned char *intag; 254 255 if (!gcm->ctr_context_init) { 256 PORT_SetError(SEC_ERROR_NOT_INITIALIZED); 257 return SECFailure; 258 } 259 260 tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; 261 262 /* get the authentication block */ 263 if (inlen < tagBytes) { 264 PORT_SetError(SEC_ERROR_INPUT_LEN); 265 return SECFailure; 266 } 267 268 inlen -= tagBytes; 269 intag = inbuf + inlen; 270 271 // GCM has a 16 octet block, with a 32-bit block counter 272 // Limit in accordance with SP800-38D 273 if (sizeof(inlen) > 4 && 274 inlen >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) { 275 PORT_SetError(SEC_ERROR_INPUT_LEN); 276 return SECFailure; 277 } 278 279 if (maxout < inlen) { 280 *outlen = inlen; 281 PORT_SetError(SEC_ERROR_OUTPUT_LEN); 282 return SECFailure; 283 } 284 285 ppc_aes_gcmHASH( 286 gcm->Htbl, 287 inbuf, 288 inlen, 289 gcm->T); 290 ppc_aes_gcmCRYPT( 291 inbuf, 292 outbuf, 293 inlen, 294 gcm->CTR, 295 gcm->aes_context->k.expandedKey, 296 gcm->aes_context->Nr); 297 298 gcm->Mlen += inlen; 299 ppc_aes_gcmTAG( 300 gcm->Htbl, 301 gcm->T, 302 gcm->Mlen, 303 gcm->Alen, 304 gcm->X0, 305 T); 306 307 if (NSS_SecureMemcmp(T, intag, tagBytes) != 0) { 308 memset(outbuf, 0, inlen); 309 *outlen = 0; 310 /* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */ 311 PORT_SetError(SEC_ERROR_BAD_DATA); 312 return SECFailure; 313 } 314 *outlen = inlen; 315 316 return SECSuccess; 317 } 318 319 SECStatus 320 ppc_AES_GCM_EncryptAEAD(ppc_AES_GCMContext *gcm, 321 unsigned char *outbuf, 322 unsigned int *outlen, unsigned int maxout, 323 const unsigned char *inbuf, unsigned int inlen, 324 void *params, unsigned int paramLen, 325 const unsigned char *aad, unsigned int aadLen, 326 unsigned int blocksize) 327 { 328 unsigned int tagBytes; 329 unsigned char T[AES_BLOCK_SIZE]; 330 const CK_GCM_MESSAGE_PARAMS *gcmParams = 331 (const CK_GCM_MESSAGE_PARAMS *)params; 332 SECStatus rv; 333 334 // GCM has a 16 octet block, with a 32-bit block counter 335 // Limit in accordance with SP800-38D 336 if (sizeof(inlen) > 4 && 337 inlen >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) { 338 PORT_SetError(SEC_ERROR_INPUT_LEN); 339 return SECFailure; 340 } 341 /* paramLen comes all the way from the application layer, make sure 342 * it's correct */ 343 if (paramLen != sizeof(CK_GCM_MESSAGE_PARAMS)) { 344 PORT_SetError(SEC_ERROR_INVALID_ARGS); 345 return SECFailure; 346 } 347 348 /* if we were initialized with the C_EncryptInit, we shouldn't be in this 349 * function */ 350 if (gcm->ctr_context_init) { 351 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 352 return SECFailure; 353 } 354 355 if (maxout < inlen) { 356 *outlen = inlen; 357 PORT_SetError(SEC_ERROR_OUTPUT_LEN); 358 return SECFailure; 359 } 360 361 rv = gcm_GenerateIV(&gcm->gcm_iv, gcmParams->pIv, gcmParams->ulIvLen, 362 gcmParams->ulIvFixedBits, gcmParams->ivGenerator); 363 if (rv != SECSuccess) { 364 return SECFailure; 365 } 366 367 rv = ppc_aes_gcmInitCounter(gcm, gcmParams->pIv, gcmParams->ulIvLen, 368 gcmParams->ulTagBits, aad, aadLen); 369 if (rv != SECSuccess) { 370 return SECFailure; 371 } 372 373 tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; 374 375 ppc_aes_gcmCRYPT(inbuf, outbuf, inlen, gcm->CTR, gcm->aes_context->k.expandedKey, 376 gcm->aes_context->Nr); 377 ppc_aes_gcmHASH(gcm->Htbl, outbuf, inlen, gcm->T); 378 379 gcm->Mlen += inlen; 380 381 ppc_aes_gcmTAG(gcm->Htbl, gcm->T, gcm->Mlen, gcm->Alen, gcm->X0, T); 382 383 *outlen = inlen; 384 PORT_Memcpy(gcmParams->pTag, T, tagBytes); 385 return SECSuccess; 386 } 387 388 SECStatus 389 ppc_AES_GCM_DecryptAEAD(ppc_AES_GCMContext *gcm, 390 unsigned char *outbuf, 391 unsigned int *outlen, unsigned int maxout, 392 const unsigned char *inbuf, unsigned int inlen, 393 void *params, unsigned int paramLen, 394 const unsigned char *aad, unsigned int aadLen, 395 unsigned int blocksize) 396 { 397 unsigned int tagBytes; 398 unsigned char T[AES_BLOCK_SIZE]; 399 const unsigned char *intag; 400 const CK_GCM_MESSAGE_PARAMS *gcmParams = 401 (const CK_GCM_MESSAGE_PARAMS *)params; 402 SECStatus rv; 403 404 /* paramLen comes all the way from the application layer, make sure 405 * it's correct */ 406 if (paramLen != sizeof(CK_GCM_MESSAGE_PARAMS)) { 407 PORT_SetError(SEC_ERROR_INVALID_ARGS); 408 return SECFailure; 409 } 410 /* if we were initialized with the C_DecryptInit, we shouldn't be in this 411 * function */ 412 if (gcm->ctr_context_init) { 413 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 414 return SECFailure; 415 } 416 417 // GCM has a 16 octet block, with a 32-bit block counter 418 // Limit in accordance with SP800-38D 419 if (sizeof(inlen) > 4 && 420 inlen >= ((1ULL << 32) - 2) * AES_BLOCK_SIZE) { 421 PORT_SetError(SEC_ERROR_INPUT_LEN); 422 return SECFailure; 423 } 424 425 if (maxout < inlen) { 426 *outlen = inlen; 427 PORT_SetError(SEC_ERROR_OUTPUT_LEN); 428 return SECFailure; 429 } 430 431 rv = ppc_aes_gcmInitCounter(gcm, gcmParams->pIv, gcmParams->ulIvLen, 432 gcmParams->ulTagBits, aad, aadLen); 433 if (rv != SECSuccess) { 434 return SECFailure; 435 } 436 437 tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; 438 intag = gcmParams->pTag; 439 PORT_Assert(tagBytes != 0); 440 441 ppc_aes_gcmHASH(gcm->Htbl, inbuf, inlen, gcm->T); 442 ppc_aes_gcmCRYPT(inbuf, outbuf, inlen, gcm->CTR, gcm->aes_context->k.expandedKey, 443 gcm->aes_context->Nr); 444 445 gcm->Mlen += inlen; 446 ppc_aes_gcmTAG(gcm->Htbl, gcm->T, gcm->Mlen, gcm->Alen, gcm->X0, T); 447 448 if (NSS_SecureMemcmp(T, intag, tagBytes) != 0) { 449 memset(outbuf, 0, inlen); 450 *outlen = 0; 451 /* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */ 452 PORT_SetError(SEC_ERROR_BAD_DATA); 453 return SECFailure; 454 } 455 *outlen = inlen; 456 457 return SECSuccess; 458 }