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