1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/freebl/gcm.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,846 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#ifdef FREEBL_NO_DEPEND 1.9 +#include "stubs.h" 1.10 +#endif 1.11 +#include "blapii.h" 1.12 +#include "blapit.h" 1.13 +#include "gcm.h" 1.14 +#include "ctr.h" 1.15 +#include "secerr.h" 1.16 +#include "prtypes.h" 1.17 +#include "pkcs11t.h" 1.18 + 1.19 +#include <limits.h> 1.20 + 1.21 +/************************************************************************** 1.22 + * First implement the Galois hash function of GCM (gcmHash) * 1.23 + **************************************************************************/ 1.24 +#define GCM_HASH_LEN_LEN 8 /* gcm hash defines lengths to be 64 bits */ 1.25 + 1.26 +typedef struct gcmHashContextStr gcmHashContext; 1.27 + 1.28 +static SECStatus gcmHash_InitContext(gcmHashContext *hash, 1.29 + const unsigned char *H, 1.30 + unsigned int blocksize); 1.31 +static void gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit); 1.32 +static SECStatus gcmHash_Update(gcmHashContext *ghash, 1.33 + const unsigned char *buf, unsigned int len, 1.34 + unsigned int blocksize); 1.35 +static SECStatus gcmHash_Sync(gcmHashContext *ghash, unsigned int blocksize); 1.36 +static SECStatus gcmHash_Final(gcmHashContext *gcm, unsigned char *outbuf, 1.37 + unsigned int *outlen, unsigned int maxout, 1.38 + unsigned int blocksize); 1.39 +static SECStatus gcmHash_Reset(gcmHashContext *ghash, 1.40 + const unsigned char *inbuf, 1.41 + unsigned int inbufLen, unsigned int blocksize); 1.42 + 1.43 +/* compile time defines to select how the GF2 multiply is calculated. 1.44 + * There are currently 2 algorithms implemented here: MPI and ALGORITHM_1. 1.45 + * 1.46 + * MPI uses the GF2m implemented in mpi to support GF2 ECC. 1.47 + * ALGORITHM_1 is the Algorithm 1 in both NIST SP 800-38D and 1.48 + * "The Galois/Counter Mode of Operation (GCM)", McGrew & Viega. 1.49 + */ 1.50 +#if !defined(GCM_USE_ALGORITHM_1) && !defined(GCM_USE_MPI) 1.51 +#define GCM_USE_MPI 1 /* MPI is about 5x faster with the 1.52 + * same or less complexity. It's possible to use 1.53 + * tables to speed things up even more */ 1.54 +#endif 1.55 + 1.56 +/* GCM defines the bit string to be LSB first, which is exactly 1.57 + * opposite everyone else, including hardware. build array 1.58 + * to reverse everything. */ 1.59 +static const unsigned char gcm_byte_rev[256] = { 1.60 + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 1.61 + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 1.62 + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 1.63 + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 1.64 + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 1.65 + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 1.66 + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 1.67 + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 1.68 + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 1.69 + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 1.70 + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 1.71 + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 1.72 + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 1.73 + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 1.74 + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 1.75 + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 1.76 + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 1.77 + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 1.78 + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 1.79 + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 1.80 + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 1.81 + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 1.82 + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 1.83 + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 1.84 + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 1.85 + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 1.86 + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 1.87 + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 1.88 + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 1.89 + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 1.90 + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 1.91 + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff 1.92 +}; 1.93 + 1.94 + 1.95 +#ifdef GCM_TRACE 1.96 +#include <stdio.h> 1.97 + 1.98 +#define GCM_TRACE_X(ghash,label) { \ 1.99 + unsigned char _X[MAX_BLOCK_SIZE]; int i; \ 1.100 + gcm_getX(ghash, _X, blocksize); \ 1.101 + printf(label,(ghash)->m); \ 1.102 + for (i=0; i < blocksize; i++) printf("%02x",_X[i]); \ 1.103 + printf("\n"); } 1.104 +#define GCM_TRACE_BLOCK(label,buf,blocksize) {\ 1.105 + printf(label); \ 1.106 + for (i=0; i < blocksize; i++) printf("%02x",buf[i]); \ 1.107 + printf("\n"); } 1.108 +#else 1.109 +#define GCM_TRACE_X(ghash,label) 1.110 +#define GCM_TRACE_BLOCK(label,buf,blocksize) 1.111 +#endif 1.112 + 1.113 +#ifdef GCM_USE_MPI 1.114 + 1.115 +#ifdef GCM_USE_ALGORITHM_1 1.116 +#error "Only define one of GCM_USE_MPI, GCM_USE_ALGORITHM_1" 1.117 +#endif 1.118 +/* use the MPI functions to calculate Xn = (Xn-1^C_i)*H mod poly */ 1.119 +#include "mpi.h" 1.120 +#include "secmpi.h" 1.121 +#include "mplogic.h" 1.122 +#include "mp_gf2m.h" 1.123 + 1.124 +/* state needed to handle GCM Hash function */ 1.125 +struct gcmHashContextStr { 1.126 + mp_int H; 1.127 + mp_int X; 1.128 + mp_int C_i; 1.129 + const unsigned int *poly; 1.130 + unsigned char buffer[MAX_BLOCK_SIZE]; 1.131 + unsigned int bufLen; 1.132 + int m; /* XXX what is m? */ 1.133 + unsigned char counterBuf[2*GCM_HASH_LEN_LEN]; 1.134 + PRUint64 cLen; 1.135 +}; 1.136 + 1.137 +/* f = x^128 + x^7 + x^2 + x + 1 */ 1.138 +static const unsigned int poly_128[] = { 128, 7, 2, 1, 0 }; 1.139 + 1.140 +/* sigh, GCM defines the bit strings exactly backwards from everything else */ 1.141 +static void 1.142 +gcm_reverse(unsigned char *target, const unsigned char *src, 1.143 + unsigned int blocksize) 1.144 +{ 1.145 + unsigned int i; 1.146 + for (i=0; i < blocksize; i++) { 1.147 + target[blocksize-i-1] = gcm_byte_rev[src[i]]; 1.148 + } 1.149 +} 1.150 + 1.151 +/* Initialize a gcmHashContext */ 1.152 +static SECStatus 1.153 +gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, 1.154 + unsigned int blocksize) 1.155 +{ 1.156 + mp_err err = MP_OKAY; 1.157 + unsigned char H_rev[MAX_BLOCK_SIZE]; 1.158 + 1.159 + MP_DIGITS(&ghash->H) = 0; 1.160 + MP_DIGITS(&ghash->X) = 0; 1.161 + MP_DIGITS(&ghash->C_i) = 0; 1.162 + CHECK_MPI_OK( mp_init(&ghash->H) ); 1.163 + CHECK_MPI_OK( mp_init(&ghash->X) ); 1.164 + CHECK_MPI_OK( mp_init(&ghash->C_i) ); 1.165 + 1.166 + mp_zero(&ghash->X); 1.167 + gcm_reverse(H_rev, H, blocksize); 1.168 + CHECK_MPI_OK( mp_read_unsigned_octets(&ghash->H, H_rev, blocksize) ); 1.169 + 1.170 + /* set the irreducible polynomial. Each blocksize has its own polynomial. 1.171 + * for now only blocksize 16 (=128 bits) is defined */ 1.172 + switch (blocksize) { 1.173 + case 16: /* 128 bits */ 1.174 + ghash->poly = poly_128; 1.175 + break; 1.176 + default: 1.177 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.178 + goto cleanup; 1.179 + } 1.180 + ghash->cLen = 0; 1.181 + ghash->bufLen = 0; 1.182 + ghash->m = 0; 1.183 + PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf)); 1.184 + return SECSuccess; 1.185 +cleanup: 1.186 + gcmHash_DestroyContext(ghash, PR_FALSE); 1.187 + return SECFailure; 1.188 +} 1.189 + 1.190 +/* Destroy a HashContext (Note we zero the digits so this function 1.191 + * is idempotent if called with freeit == PR_FALSE */ 1.192 +static void 1.193 +gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit) 1.194 +{ 1.195 + mp_clear(&ghash->H); 1.196 + mp_clear(&ghash->X); 1.197 + mp_clear(&ghash->C_i); 1.198 + MP_DIGITS(&ghash->H) = 0; 1.199 + MP_DIGITS(&ghash->X) = 0; 1.200 + MP_DIGITS(&ghash->C_i) = 0; 1.201 + if (freeit) { 1.202 + PORT_Free(ghash); 1.203 + } 1.204 +} 1.205 + 1.206 +static SECStatus 1.207 +gcm_getX(gcmHashContext *ghash, unsigned char *T, unsigned int blocksize) 1.208 +{ 1.209 + int len; 1.210 + mp_err err; 1.211 + unsigned char tmp_buf[MAX_BLOCK_SIZE]; 1.212 + unsigned char *X; 1.213 + 1.214 + len = mp_unsigned_octet_size(&ghash->X); 1.215 + if (len <= 0) { 1.216 + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 1.217 + return SECFailure; 1.218 + } 1.219 + X = tmp_buf; 1.220 + PORT_Assert((unsigned int)len <= blocksize); 1.221 + if ((unsigned int)len > blocksize) { 1.222 + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 1.223 + return SECFailure; 1.224 + } 1.225 + /* zero pad the result */ 1.226 + if (len != blocksize) { 1.227 + PORT_Memset(X,0,blocksize-len); 1.228 + X += blocksize-len; 1.229 + } 1.230 + 1.231 + err = mp_to_unsigned_octets(&ghash->X, X, len); 1.232 + if (err < 0) { 1.233 + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 1.234 + return SECFailure; 1.235 + } 1.236 + gcm_reverse(T, tmp_buf, blocksize); 1.237 + return SECSuccess; 1.238 +} 1.239 + 1.240 +static SECStatus 1.241 +gcm_HashMult(gcmHashContext *ghash, const unsigned char *buf, 1.242 + unsigned int count, unsigned int blocksize) 1.243 +{ 1.244 + SECStatus rv = SECFailure; 1.245 + mp_err err = MP_OKAY; 1.246 + unsigned char tmp_buf[MAX_BLOCK_SIZE]; 1.247 + unsigned int i; 1.248 + 1.249 + for (i=0; i < count; i++, buf += blocksize) { 1.250 + ghash->m++; 1.251 + gcm_reverse(tmp_buf, buf, blocksize); 1.252 + CHECK_MPI_OK(mp_read_unsigned_octets(&ghash->C_i, tmp_buf, blocksize)); 1.253 + CHECK_MPI_OK(mp_badd(&ghash->X, &ghash->C_i, &ghash->C_i)); 1.254 + /* 1.255 + * Looking to speed up GCM, this the the place to do it. 1.256 + * There are two areas that can be exploited to speed up this code. 1.257 + * 1.258 + * 1) H is a constant in this multiply. We can precompute H * (0 - 255) 1.259 + * at init time and this becomes an blockize xors of our table lookup. 1.260 + * 1.261 + * 2) poly is a constant for each blocksize. We can calculate the 1.262 + * modulo reduction by a series of adds and shifts. 1.263 + * 1.264 + * For now we are after functionality, so we will go ahead and use 1.265 + * the builtin bmulmod from mpi 1.266 + */ 1.267 + CHECK_MPI_OK(mp_bmulmod(&ghash->C_i, &ghash->H, 1.268 + ghash->poly, &ghash->X)); 1.269 + GCM_TRACE_X(ghash, "X%d = ") 1.270 + } 1.271 + rv = SECSuccess; 1.272 +cleanup: 1.273 + if (rv != SECSuccess) { 1.274 + MP_TO_SEC_ERROR(err); 1.275 + } 1.276 + return rv; 1.277 +} 1.278 + 1.279 +static void 1.280 +gcm_zeroX(gcmHashContext *ghash) 1.281 +{ 1.282 + mp_zero(&ghash->X); 1.283 + ghash->m = 0; 1.284 +} 1.285 + 1.286 +#endif 1.287 + 1.288 +#ifdef GCM_USE_ALGORITHM_1 1.289 +/* use algorithm 1 of McGrew & Viega "The Galois/Counter Mode of Operation" */ 1.290 + 1.291 +#define GCM_ARRAY_SIZE (MAX_BLOCK_SIZE/sizeof(unsigned long)) 1.292 + 1.293 +struct gcmHashContextStr { 1.294 + unsigned long H[GCM_ARRAY_SIZE]; 1.295 + unsigned long X[GCM_ARRAY_SIZE]; 1.296 + unsigned long R; 1.297 + unsigned char buffer[MAX_BLOCK_SIZE]; 1.298 + unsigned int bufLen; 1.299 + int m; 1.300 + unsigned char counterBuf[2*GCM_HASH_LEN_LEN]; 1.301 + PRUint64 cLen; 1.302 +}; 1.303 + 1.304 +static void 1.305 +gcm_bytes_to_longs(unsigned long *l, const unsigned char *c, unsigned int len) 1.306 +{ 1.307 + int i,j; 1.308 + int array_size = len/sizeof(unsigned long); 1.309 + 1.310 + PORT_Assert(len % sizeof(unsigned long) == 0); 1.311 + for (i=0; i < array_size; i++) { 1.312 + unsigned long tmp = 0; 1.313 + int byte_offset = i * sizeof(unsigned long); 1.314 + for (j=sizeof(unsigned long)-1; j >= 0; j--) { 1.315 + tmp = (tmp << PR_BITS_PER_BYTE) | gcm_byte_rev[c[byte_offset+j]]; 1.316 + } 1.317 + l[i] = tmp; 1.318 + } 1.319 +} 1.320 + 1.321 +static void 1.322 +gcm_longs_to_bytes(const unsigned long *l, unsigned char *c, unsigned int len) 1.323 +{ 1.324 + int i,j; 1.325 + int array_size = len/sizeof(unsigned long); 1.326 + 1.327 + PORT_Assert(len % sizeof(unsigned long) == 0); 1.328 + for (i=0; i < array_size; i++) { 1.329 + unsigned long tmp = l[i]; 1.330 + int byte_offset = i * sizeof(unsigned long); 1.331 + for (j=0; j < sizeof(unsigned long); j++) { 1.332 + c[byte_offset+j] = gcm_byte_rev[tmp & 0xff]; 1.333 + tmp = (tmp >> PR_BITS_PER_BYTE); 1.334 + } 1.335 + } 1.336 +} 1.337 + 1.338 + 1.339 +/* Initialize a gcmHashContext */ 1.340 +static SECStatus 1.341 +gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H, 1.342 + unsigned int blocksize) 1.343 +{ 1.344 + PORT_Memset(ghash->X, 0, sizeof(ghash->X)); 1.345 + PORT_Memset(ghash->H, 0, sizeof(ghash->H)); 1.346 + gcm_bytes_to_longs(ghash->H, H, blocksize); 1.347 + 1.348 + /* set the irreducible polynomial. Each blocksize has its own polynommial 1.349 + * for now only blocksize 16 (=128 bits) is defined */ 1.350 + switch (blocksize) { 1.351 + case 16: /* 128 bits */ 1.352 + ghash->R = (unsigned long) 0x87; /* x^7 + x^2 + x +1 */ 1.353 + break; 1.354 + default: 1.355 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.356 + goto cleanup; 1.357 + } 1.358 + ghash->cLen = 0; 1.359 + ghash->bufLen = 0; 1.360 + ghash->m = 0; 1.361 + PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf)); 1.362 + return SECSuccess; 1.363 +cleanup: 1.364 + return SECFailure; 1.365 +} 1.366 + 1.367 +/* Destroy a HashContext (Note we zero the digits so this function 1.368 + * is idempotent if called with freeit == PR_FALSE */ 1.369 +static void 1.370 +gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit) 1.371 +{ 1.372 + if (freeit) { 1.373 + PORT_Free(ghash); 1.374 + } 1.375 +} 1.376 + 1.377 +static unsigned long 1.378 +gcm_shift_one(unsigned long *t, unsigned int count) 1.379 +{ 1.380 + unsigned long carry = 0; 1.381 + unsigned long nextcarry = 0; 1.382 + unsigned int i; 1.383 + for (i=0; i < count; i++) { 1.384 + nextcarry = t[i] >> ((sizeof(unsigned long)*PR_BITS_PER_BYTE)-1); 1.385 + t[i] = (t[i] << 1) | carry; 1.386 + carry = nextcarry; 1.387 + } 1.388 + return carry; 1.389 +} 1.390 + 1.391 +static SECStatus 1.392 +gcm_getX(gcmHashContext *ghash, unsigned char *T, unsigned int blocksize) 1.393 +{ 1.394 + gcm_longs_to_bytes(ghash->X, T, blocksize); 1.395 + return SECSuccess; 1.396 +} 1.397 + 1.398 +#define GCM_XOR(t, s, len) \ 1.399 + for (l=0; l < len; l++) t[l] ^= s[l] 1.400 + 1.401 +static SECStatus 1.402 +gcm_HashMult(gcmHashContext *ghash, const unsigned char *buf, 1.403 + unsigned int count, unsigned int blocksize) 1.404 +{ 1.405 + unsigned long C_i[GCM_ARRAY_SIZE]; 1.406 + unsigned int arraysize = blocksize/sizeof(unsigned long); 1.407 + unsigned int i, j, k, l; 1.408 + 1.409 + for (i=0; i < count; i++, buf += blocksize) { 1.410 + ghash->m++; 1.411 + gcm_bytes_to_longs(C_i, buf, blocksize); 1.412 + GCM_XOR(C_i, ghash->X, arraysize); 1.413 + /* multiply X = C_i * H */ 1.414 + PORT_Memset(ghash->X, 0, sizeof(ghash->X)); 1.415 + for (j=0; j < arraysize; j++) { 1.416 + unsigned long H = ghash->H[j]; 1.417 + for (k=0; k < sizeof(unsigned long)*PR_BITS_PER_BYTE; k++) { 1.418 + if (H & 1) { 1.419 + GCM_XOR(ghash->X, C_i, arraysize); 1.420 + } 1.421 + if (gcm_shift_one(C_i, arraysize)) { 1.422 + C_i[0] = C_i[0] ^ ghash->R; 1.423 + } 1.424 + H = H >> 1; 1.425 + } 1.426 + } 1.427 + GCM_TRACE_X(ghash, "X%d = ") 1.428 + } 1.429 + return SECSuccess; 1.430 +} 1.431 + 1.432 + 1.433 +static void 1.434 +gcm_zeroX(gcmHashContext *ghash) 1.435 +{ 1.436 + PORT_Memset(ghash->X, 0, sizeof(ghash->X)); 1.437 + ghash->m = 0; 1.438 +} 1.439 +#endif 1.440 + 1.441 +/* 1.442 + * implement GCM GHASH using the freebl GHASH function. The gcm_HashMult 1.443 + * function always takes blocksize lengths of data. gcmHash_Update will 1.444 + * format the data properly. 1.445 + */ 1.446 +static SECStatus 1.447 +gcmHash_Update(gcmHashContext *ghash, const unsigned char *buf, 1.448 + unsigned int len, unsigned int blocksize) 1.449 +{ 1.450 + unsigned int blocks; 1.451 + SECStatus rv; 1.452 + 1.453 + ghash->cLen += (len*PR_BITS_PER_BYTE); 1.454 + 1.455 + /* first deal with the current buffer of data. Try to fill it out so 1.456 + * we can hash it */ 1.457 + if (ghash->bufLen) { 1.458 + unsigned int needed = PR_MIN(len, blocksize - ghash->bufLen); 1.459 + if (needed != 0) { 1.460 + PORT_Memcpy(ghash->buffer+ghash->bufLen, buf, needed); 1.461 + } 1.462 + buf += needed; 1.463 + len -= needed; 1.464 + ghash->bufLen += needed; 1.465 + if (len == 0) { 1.466 + /* didn't add enough to hash the data, nothing more do do */ 1.467 + return SECSuccess; 1.468 + } 1.469 + PORT_Assert(ghash->bufLen == blocksize); 1.470 + /* hash the buffer and clear it */ 1.471 + rv = gcm_HashMult(ghash, ghash->buffer, 1, blocksize); 1.472 + PORT_Memset(ghash->buffer, 0, blocksize); 1.473 + ghash->bufLen = 0; 1.474 + if (rv != SECSuccess) { 1.475 + return SECFailure; 1.476 + } 1.477 + } 1.478 + /* now hash any full blocks remaining in the data stream */ 1.479 + blocks = len/blocksize; 1.480 + if (blocks) { 1.481 + rv = gcm_HashMult(ghash, buf, blocks, blocksize); 1.482 + if (rv != SECSuccess) { 1.483 + return SECFailure; 1.484 + } 1.485 + buf += blocks*blocksize; 1.486 + len -= blocks*blocksize; 1.487 + } 1.488 + 1.489 + /* save any remainder in the buffer to be hashed with the next call */ 1.490 + if (len != 0) { 1.491 + PORT_Memcpy(ghash->buffer, buf, len); 1.492 + ghash->bufLen = len; 1.493 + } 1.494 + return SECSuccess; 1.495 +} 1.496 + 1.497 +/* 1.498 + * write out any partial blocks zero padded through the GHASH engine, 1.499 + * save the lengths for the final completion of the hash 1.500 + */ 1.501 +static SECStatus 1.502 +gcmHash_Sync(gcmHashContext *ghash, unsigned int blocksize) 1.503 +{ 1.504 + int i; 1.505 + SECStatus rv; 1.506 + 1.507 + /* copy the previous counter to the upper block */ 1.508 + PORT_Memcpy(ghash->counterBuf, &ghash->counterBuf[GCM_HASH_LEN_LEN], 1.509 + GCM_HASH_LEN_LEN); 1.510 + /* copy the current counter in the lower block */ 1.511 + for (i=0; i < GCM_HASH_LEN_LEN; i++) { 1.512 + ghash->counterBuf[GCM_HASH_LEN_LEN+i] = 1.513 + (ghash->cLen >> ((GCM_HASH_LEN_LEN-1-i)*PR_BITS_PER_BYTE)) & 0xff; 1.514 + } 1.515 + ghash->cLen = 0; 1.516 + 1.517 + /* now zero fill the buffer and hash the last block */ 1.518 + if (ghash->bufLen) { 1.519 + PORT_Memset(ghash->buffer+ghash->bufLen, 0, blocksize - ghash->bufLen); 1.520 + rv = gcm_HashMult(ghash, ghash->buffer, 1, blocksize); 1.521 + PORT_Memset(ghash->buffer, 0, blocksize); 1.522 + ghash->bufLen = 0; 1.523 + if (rv != SECSuccess) { 1.524 + return SECFailure; 1.525 + } 1.526 + } 1.527 + return SECSuccess; 1.528 +} 1.529 + 1.530 +/* 1.531 + * This does the final sync, hashes the lengths, then returns 1.532 + * "T", the hashed output. 1.533 + */ 1.534 +static SECStatus 1.535 +gcmHash_Final(gcmHashContext *ghash, unsigned char *outbuf, 1.536 + unsigned int *outlen, unsigned int maxout, 1.537 + unsigned int blocksize) 1.538 +{ 1.539 + unsigned char T[MAX_BLOCK_SIZE]; 1.540 + SECStatus rv; 1.541 + 1.542 + rv = gcmHash_Sync(ghash, blocksize); 1.543 + if (rv != SECSuccess) { 1.544 + return SECFailure; 1.545 + } 1.546 + 1.547 + rv = gcm_HashMult(ghash, ghash->counterBuf, (GCM_HASH_LEN_LEN*2)/blocksize, 1.548 + blocksize); 1.549 + if (rv != SECSuccess) { 1.550 + return SECFailure; 1.551 + } 1.552 + 1.553 + GCM_TRACE_X(ghash, "GHASH(H,A,C) = ") 1.554 + 1.555 + rv = gcm_getX(ghash, T, blocksize); 1.556 + if (rv != SECSuccess) { 1.557 + return SECFailure; 1.558 + } 1.559 + 1.560 + if (maxout > blocksize) maxout = blocksize; 1.561 + PORT_Memcpy(outbuf, T, maxout); 1.562 + *outlen = maxout; 1.563 + return SECSuccess; 1.564 +} 1.565 + 1.566 +SECStatus 1.567 +gcmHash_Reset(gcmHashContext *ghash, const unsigned char *AAD, 1.568 + unsigned int AADLen, unsigned int blocksize) 1.569 +{ 1.570 + SECStatus rv; 1.571 + 1.572 + ghash->cLen = 0; 1.573 + PORT_Memset(ghash->counterBuf, 0, GCM_HASH_LEN_LEN*2); 1.574 + ghash->bufLen = 0; 1.575 + gcm_zeroX(ghash); 1.576 + 1.577 + /* now kick things off by hashing the Additional Authenticated Data */ 1.578 + if (AADLen != 0) { 1.579 + rv = gcmHash_Update(ghash, AAD, AADLen, blocksize); 1.580 + if (rv != SECSuccess) { 1.581 + return SECFailure; 1.582 + } 1.583 + rv = gcmHash_Sync(ghash, blocksize); 1.584 + if (rv != SECSuccess) { 1.585 + return SECFailure; 1.586 + } 1.587 + } 1.588 + return SECSuccess; 1.589 +} 1.590 + 1.591 +/************************************************************************** 1.592 + * Now implement the GCM using gcmHash and CTR * 1.593 + **************************************************************************/ 1.594 + 1.595 +/* state to handle the full GCM operation (hash and counter) */ 1.596 +struct GCMContextStr { 1.597 + gcmHashContext ghash_context; 1.598 + CTRContext ctr_context; 1.599 + unsigned long tagBits; 1.600 + unsigned char tagKey[MAX_BLOCK_SIZE]; 1.601 +}; 1.602 + 1.603 +GCMContext * 1.604 +GCM_CreateContext(void *context, freeblCipherFunc cipher, 1.605 + const unsigned char *params, unsigned int blocksize) 1.606 +{ 1.607 + GCMContext *gcm = NULL; 1.608 + gcmHashContext *ghash; 1.609 + unsigned char H[MAX_BLOCK_SIZE]; 1.610 + unsigned int tmp; 1.611 + PRBool freeCtr = PR_FALSE; 1.612 + PRBool freeHash = PR_FALSE; 1.613 + const CK_GCM_PARAMS *gcmParams = (const CK_GCM_PARAMS *)params; 1.614 + CK_AES_CTR_PARAMS ctrParams; 1.615 + SECStatus rv; 1.616 + 1.617 + if (blocksize > MAX_BLOCK_SIZE || blocksize > sizeof(ctrParams.cb)) { 1.618 + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); 1.619 + return NULL; 1.620 + } 1.621 + gcm = PORT_ZNew(GCMContext); 1.622 + if (gcm == NULL) { 1.623 + return NULL; 1.624 + } 1.625 + /* first fill in the ghash context */ 1.626 + ghash = &gcm->ghash_context; 1.627 + PORT_Memset(H, 0, blocksize); 1.628 + rv = (*cipher)(context, H, &tmp, blocksize, H, blocksize, blocksize); 1.629 + if (rv != SECSuccess) { 1.630 + goto loser; 1.631 + } 1.632 + rv = gcmHash_InitContext(ghash, H, blocksize); 1.633 + if (rv != SECSuccess) { 1.634 + goto loser; 1.635 + } 1.636 + freeHash = PR_TRUE; 1.637 + 1.638 + /* fill in the Counter context */ 1.639 + ctrParams.ulCounterBits = 32; 1.640 + PORT_Memset(ctrParams.cb, 0, sizeof(ctrParams.cb)); 1.641 + if ((blocksize == 16) && (gcmParams->ulIvLen == 12)) { 1.642 + PORT_Memcpy(ctrParams.cb, gcmParams->pIv, gcmParams->ulIvLen); 1.643 + ctrParams.cb[blocksize-1] = 1; 1.644 + } else { 1.645 + rv = gcmHash_Update(ghash, gcmParams->pIv, gcmParams->ulIvLen, 1.646 + blocksize); 1.647 + if (rv != SECSuccess) { 1.648 + goto loser; 1.649 + } 1.650 + rv = gcmHash_Final(ghash, ctrParams.cb, &tmp, blocksize, blocksize); 1.651 + if (rv != SECSuccess) { 1.652 + goto loser; 1.653 + } 1.654 + } 1.655 + rv = CTR_InitContext(&gcm->ctr_context, context, cipher, 1.656 + (unsigned char *)&ctrParams, blocksize); 1.657 + if (rv != SECSuccess) { 1.658 + goto loser; 1.659 + } 1.660 + freeCtr = PR_TRUE; 1.661 + 1.662 + /* fill in the gcm structure */ 1.663 + gcm->tagBits = gcmParams->ulTagBits; /* save for final step */ 1.664 + /* calculate the final tag key. NOTE: gcm->tagKey is zero to start with. 1.665 + * if this assumption changes, we would need to explicitly clear it here */ 1.666 + rv = CTR_Update(&gcm->ctr_context, gcm->tagKey, &tmp, blocksize, 1.667 + gcm->tagKey, blocksize, blocksize); 1.668 + if (rv != SECSuccess) { 1.669 + goto loser; 1.670 + } 1.671 + 1.672 + /* finally mix in the AAD data */ 1.673 + rv = gcmHash_Reset(ghash, gcmParams->pAAD, gcmParams->ulAADLen, blocksize); 1.674 + if (rv != SECSuccess) { 1.675 + goto loser; 1.676 + } 1.677 + 1.678 + return gcm; 1.679 + 1.680 +loser: 1.681 + if (freeCtr) { 1.682 + CTR_DestroyContext(&gcm->ctr_context, PR_FALSE); 1.683 + } 1.684 + if (freeHash) { 1.685 + gcmHash_DestroyContext(&gcm->ghash_context, PR_FALSE); 1.686 + } 1.687 + if (gcm) { 1.688 + PORT_Free(gcm); 1.689 + } 1.690 + return NULL; 1.691 +} 1.692 + 1.693 +void 1.694 +GCM_DestroyContext(GCMContext *gcm, PRBool freeit) 1.695 +{ 1.696 + /* these two are statically allocated and will be freed when we free 1.697 + * gcm. call their destroy functions to free up any locally 1.698 + * allocated data (like mp_int's) */ 1.699 + CTR_DestroyContext(&gcm->ctr_context, PR_FALSE); 1.700 + gcmHash_DestroyContext(&gcm->ghash_context, PR_FALSE); 1.701 + if (freeit) { 1.702 + PORT_Free(gcm); 1.703 + } 1.704 +} 1.705 + 1.706 +static SECStatus 1.707 +gcm_GetTag(GCMContext *gcm, unsigned char *outbuf, 1.708 + unsigned int *outlen, unsigned int maxout, 1.709 + unsigned int blocksize) 1.710 +{ 1.711 + unsigned int tagBytes; 1.712 + unsigned int extra; 1.713 + unsigned int i; 1.714 + SECStatus rv; 1.715 + 1.716 + tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE; 1.717 + extra = tagBytes*PR_BITS_PER_BYTE - gcm->tagBits; 1.718 + 1.719 + if (outbuf == NULL) { 1.720 + *outlen = tagBytes; 1.721 + PORT_SetError(SEC_ERROR_OUTPUT_LEN); 1.722 + return SECFailure; 1.723 + } 1.724 + 1.725 + if (maxout < tagBytes) { 1.726 + *outlen = tagBytes; 1.727 + PORT_SetError(SEC_ERROR_OUTPUT_LEN); 1.728 + return SECFailure; 1.729 + } 1.730 + maxout = tagBytes; 1.731 + rv = gcmHash_Final(&gcm->ghash_context, outbuf, outlen, maxout, blocksize); 1.732 + if (rv != SECSuccess) { 1.733 + return SECFailure; 1.734 + } 1.735 + 1.736 + GCM_TRACE_BLOCK("GHASH=", outbuf, blocksize); 1.737 + GCM_TRACE_BLOCK("Y0=", gcm->tagKey, blocksize); 1.738 + for (i=0; i < *outlen; i++) { 1.739 + outbuf[i] ^= gcm->tagKey[i]; 1.740 + } 1.741 + GCM_TRACE_BLOCK("Y0=", gcm->tagKey, blocksize); 1.742 + GCM_TRACE_BLOCK("T=", outbuf, blocksize); 1.743 + /* mask off any extra bits we got */ 1.744 + if (extra) { 1.745 + outbuf[tagBytes-1] &= ~((1 << extra)-1); 1.746 + } 1.747 + return SECSuccess; 1.748 +} 1.749 + 1.750 + 1.751 +/* 1.752 + * See The Galois/Counter Mode of Operation, McGrew and Viega. 1.753 + * GCM is basically counter mode with a specific initialization and 1.754 + * built in macing operation. 1.755 + */ 1.756 +SECStatus 1.757 +GCM_EncryptUpdate(GCMContext *gcm, unsigned char *outbuf, 1.758 + unsigned int *outlen, unsigned int maxout, 1.759 + const unsigned char *inbuf, unsigned int inlen, 1.760 + unsigned int blocksize) 1.761 +{ 1.762 + SECStatus rv; 1.763 + unsigned int tagBytes; 1.764 + unsigned int len; 1.765 + 1.766 + tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE; 1.767 + if (UINT_MAX - inlen < tagBytes) { 1.768 + PORT_SetError(SEC_ERROR_INPUT_LEN); 1.769 + return SECFailure; 1.770 + } 1.771 + if (maxout < inlen + tagBytes) { 1.772 + *outlen = inlen + tagBytes; 1.773 + PORT_SetError(SEC_ERROR_OUTPUT_LEN); 1.774 + return SECFailure; 1.775 + } 1.776 + 1.777 + rv = CTR_Update(&gcm->ctr_context, outbuf, outlen, maxout, 1.778 + inbuf, inlen, blocksize); 1.779 + if (rv != SECSuccess) { 1.780 + return SECFailure; 1.781 + } 1.782 + rv = gcmHash_Update(&gcm->ghash_context, outbuf, *outlen, blocksize); 1.783 + if (rv != SECSuccess) { 1.784 + PORT_Memset(outbuf, 0, *outlen); /* clear the output buffer */ 1.785 + *outlen = 0; 1.786 + return SECFailure; 1.787 + } 1.788 + rv = gcm_GetTag(gcm, outbuf + *outlen, &len, maxout - *outlen, blocksize); 1.789 + if (rv != SECSuccess) { 1.790 + PORT_Memset(outbuf, 0, *outlen); /* clear the output buffer */ 1.791 + *outlen = 0; 1.792 + return SECFailure; 1.793 + }; 1.794 + *outlen += len; 1.795 + return SECSuccess; 1.796 +} 1.797 + 1.798 +/* 1.799 + * See The Galois/Counter Mode of Operation, McGrew and Viega. 1.800 + * GCM is basically counter mode with a specific initialization and 1.801 + * built in macing operation. NOTE: the only difference between Encrypt 1.802 + * and Decrypt is when we calculate the mac. That is because the mac must 1.803 + * always be calculated on the cipher text, not the plain text, so for 1.804 + * encrypt, we do the CTR update first and for decrypt we do the mac first. 1.805 + */ 1.806 +SECStatus 1.807 +GCM_DecryptUpdate(GCMContext *gcm, unsigned char *outbuf, 1.808 + unsigned int *outlen, unsigned int maxout, 1.809 + const unsigned char *inbuf, unsigned int inlen, 1.810 + unsigned int blocksize) 1.811 +{ 1.812 + SECStatus rv; 1.813 + unsigned int tagBytes; 1.814 + unsigned char tag[MAX_BLOCK_SIZE]; 1.815 + const unsigned char *intag; 1.816 + unsigned int len; 1.817 + 1.818 + tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE; 1.819 + 1.820 + /* get the authentication block */ 1.821 + if (inlen < tagBytes) { 1.822 + PORT_SetError(SEC_ERROR_INPUT_LEN); 1.823 + return SECFailure; 1.824 + } 1.825 + 1.826 + inlen -= tagBytes; 1.827 + intag = inbuf + inlen; 1.828 + 1.829 + /* verify the block */ 1.830 + rv = gcmHash_Update(&gcm->ghash_context, inbuf, inlen, blocksize); 1.831 + if (rv != SECSuccess) { 1.832 + return SECFailure; 1.833 + } 1.834 + rv = gcm_GetTag(gcm, tag, &len, blocksize, blocksize); 1.835 + if (rv != SECSuccess) { 1.836 + return SECFailure; 1.837 + } 1.838 + /* Don't decrypt if we can't authenticate the encrypted data! 1.839 + * This assumes that if tagBits is not a multiple of 8, intag will 1.840 + * preserve the masked off missing bits. */ 1.841 + if (NSS_SecureMemcmp(tag, intag, tagBytes) != 0) { 1.842 + /* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */ 1.843 + PORT_SetError(SEC_ERROR_BAD_DATA); 1.844 + return SECFailure; 1.845 + } 1.846 + /* finish the decryption */ 1.847 + return CTR_Update(&gcm->ctr_context, outbuf, outlen, maxout, 1.848 + inbuf, inlen, blocksize); 1.849 +}