|
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 |
|
31 struct intel_AES_GCMContextStr{ |
|
32 unsigned char Htbl[16*AES_BLOCK_SIZE]; |
|
33 unsigned char X0[AES_BLOCK_SIZE]; |
|
34 unsigned char T[AES_BLOCK_SIZE]; |
|
35 unsigned char CTR[AES_BLOCK_SIZE]; |
|
36 AESContext *aes_context; |
|
37 unsigned long tagBits; |
|
38 unsigned long Alen; |
|
39 unsigned long Mlen; |
|
40 }; |
|
41 |
|
42 intel_AES_GCMContext *intel_AES_GCM_CreateContext(void *context, |
|
43 freeblCipherFunc cipher, |
|
44 const unsigned char *params, |
|
45 unsigned int blocksize) |
|
46 { |
|
47 intel_AES_GCMContext *gcm = NULL; |
|
48 AESContext *aes = (AESContext*)context; |
|
49 const CK_GCM_PARAMS *gcmParams = (const CK_GCM_PARAMS *)params; |
|
50 unsigned char buff[AES_BLOCK_SIZE]; /* aux buffer */ |
|
51 |
|
52 unsigned long IV_whole_len = gcmParams->ulIvLen & (~0xful); |
|
53 unsigned int IV_remainder_len = gcmParams->ulIvLen & 0xful; |
|
54 unsigned long AAD_whole_len = gcmParams->ulAADLen & (~0xful); |
|
55 unsigned int AAD_remainder_len = gcmParams->ulAADLen & 0xful; |
|
56 |
|
57 __m128i BSWAP_MASK = _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); |
|
58 __m128i ONE = _mm_set_epi32(0,0,0,1); |
|
59 unsigned int j; |
|
60 SECStatus rv; |
|
61 |
|
62 if (blocksize != AES_BLOCK_SIZE) { |
|
63 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
|
64 return NULL; |
|
65 } |
|
66 gcm = PORT_ZNew(intel_AES_GCMContext); |
|
67 |
|
68 if (gcm == NULL) { |
|
69 return NULL; |
|
70 } |
|
71 |
|
72 /* initialize context fields */ |
|
73 gcm->aes_context = aes; |
|
74 gcm->tagBits = gcmParams->ulTagBits; |
|
75 gcm->Alen = 0; |
|
76 gcm->Mlen = 0; |
|
77 |
|
78 /* first prepare H and its derivatives for ghash */ |
|
79 intel_aes_gcmINIT(gcm->Htbl, (unsigned char*)aes->expandedKey, aes->Nr); |
|
80 |
|
81 /* Initial TAG value is zero */ |
|
82 _mm_storeu_si128((__m128i*)gcm->T, _mm_setzero_si128()); |
|
83 _mm_storeu_si128((__m128i*)gcm->X0, _mm_setzero_si128()); |
|
84 |
|
85 /* Init the counter */ |
|
86 if (gcmParams->ulIvLen == 12) { |
|
87 _mm_storeu_si128((__m128i*)gcm->CTR, |
|
88 _mm_setr_epi32(((unsigned int*)gcmParams->pIv)[0], |
|
89 ((unsigned int*)gcmParams->pIv)[1], |
|
90 ((unsigned int*)gcmParams->pIv)[2], |
|
91 0x01000000)); |
|
92 } else { |
|
93 /* If IV size is not 96 bits, then the initial counter value is GHASH |
|
94 * of the IV */ |
|
95 intel_aes_gcmAAD(gcm->Htbl, gcmParams->pIv, IV_whole_len, gcm->T); |
|
96 |
|
97 /* Partial block */ |
|
98 if (IV_remainder_len) { |
|
99 PORT_Memset(buff, 0, AES_BLOCK_SIZE); |
|
100 PORT_Memcpy(buff, gcmParams->pIv + IV_whole_len, IV_remainder_len); |
|
101 intel_aes_gcmAAD(gcm->Htbl, buff, AES_BLOCK_SIZE, gcm->T); |
|
102 } |
|
103 |
|
104 intel_aes_gcmTAG( |
|
105 gcm->Htbl, |
|
106 gcm->T, |
|
107 gcmParams->ulIvLen, |
|
108 0, |
|
109 gcm->X0, |
|
110 gcm->CTR); |
|
111 |
|
112 /* TAG should be zero again */ |
|
113 _mm_storeu_si128((__m128i*)gcm->T, _mm_setzero_si128()); |
|
114 } |
|
115 |
|
116 /* Encrypt the initial counter, will be used to encrypt the GHASH value, |
|
117 * in the end */ |
|
118 rv = (*cipher)(context, gcm->X0, &j, AES_BLOCK_SIZE, gcm->CTR, |
|
119 AES_BLOCK_SIZE, AES_BLOCK_SIZE); |
|
120 if (rv != SECSuccess) { |
|
121 goto loser; |
|
122 } |
|
123 |
|
124 /* Promote the counter by 1 */ |
|
125 _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)); |
|
126 |
|
127 /* Now hash AAD - it would actually make sense to seperate the context |
|
128 * creation from the AAD, because that would allow to reuse the H, which |
|
129 * only changes when the AES key changes, and not every package, like the |
|
130 * IV and AAD */ |
|
131 intel_aes_gcmAAD(gcm->Htbl, gcmParams->pAAD, AAD_whole_len, gcm->T); |
|
132 if (AAD_remainder_len) { |
|
133 PORT_Memset(buff, 0, AES_BLOCK_SIZE); |
|
134 PORT_Memcpy(buff, gcmParams->pAAD + AAD_whole_len, AAD_remainder_len); |
|
135 intel_aes_gcmAAD(gcm->Htbl, buff, AES_BLOCK_SIZE, gcm->T); |
|
136 } |
|
137 gcm->Alen += gcmParams->ulAADLen; |
|
138 return gcm; |
|
139 |
|
140 loser: |
|
141 if (gcm) { |
|
142 PORT_Free(gcm); |
|
143 } |
|
144 return NULL; |
|
145 } |
|
146 |
|
147 void intel_AES_GCM_DestroyContext(intel_AES_GCMContext *gcm, PRBool freeit) |
|
148 { |
|
149 if (freeit) { |
|
150 PORT_Free(gcm); |
|
151 } |
|
152 } |
|
153 |
|
154 SECStatus intel_AES_GCM_EncryptUpdate(intel_AES_GCMContext *gcm, |
|
155 unsigned char *outbuf, |
|
156 unsigned int *outlen, unsigned int maxout, |
|
157 const unsigned char *inbuf, unsigned int inlen, |
|
158 unsigned int blocksize) |
|
159 { |
|
160 unsigned int tagBytes; |
|
161 unsigned char T[AES_BLOCK_SIZE]; |
|
162 unsigned int j; |
|
163 |
|
164 tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; |
|
165 if (UINT_MAX - inlen < tagBytes) { |
|
166 PORT_SetError(SEC_ERROR_INPUT_LEN); |
|
167 return SECFailure; |
|
168 } |
|
169 if (maxout < inlen + tagBytes) { |
|
170 *outlen = inlen + tagBytes; |
|
171 PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
|
172 return SECFailure; |
|
173 } |
|
174 |
|
175 intel_aes_gcmENC( |
|
176 inbuf, |
|
177 outbuf, |
|
178 gcm, |
|
179 inlen); |
|
180 |
|
181 gcm->Mlen += inlen; |
|
182 |
|
183 intel_aes_gcmTAG( |
|
184 gcm->Htbl, |
|
185 gcm->T, |
|
186 gcm->Mlen, |
|
187 gcm->Alen, |
|
188 gcm->X0, |
|
189 T); |
|
190 |
|
191 *outlen = inlen + tagBytes; |
|
192 |
|
193 for (j = 0; j < tagBytes; j++) { |
|
194 outbuf[inlen + j] = T[j]; |
|
195 } |
|
196 return SECSuccess; |
|
197 } |
|
198 |
|
199 SECStatus intel_AES_GCM_DecryptUpdate(intel_AES_GCMContext *gcm, |
|
200 unsigned char *outbuf, |
|
201 unsigned int *outlen, unsigned int maxout, |
|
202 const unsigned char *inbuf, unsigned int inlen, |
|
203 unsigned int blocksize) |
|
204 { |
|
205 unsigned int tagBytes; |
|
206 unsigned char T[AES_BLOCK_SIZE]; |
|
207 const unsigned char *intag; |
|
208 |
|
209 tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; |
|
210 |
|
211 /* get the authentication block */ |
|
212 if (inlen < tagBytes) { |
|
213 PORT_SetError(SEC_ERROR_INPUT_LEN); |
|
214 return SECFailure; |
|
215 } |
|
216 |
|
217 inlen -= tagBytes; |
|
218 intag = inbuf + inlen; |
|
219 |
|
220 if (maxout < inlen) { |
|
221 *outlen = inlen; |
|
222 PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
|
223 return SECFailure; |
|
224 } |
|
225 |
|
226 intel_aes_gcmDEC( |
|
227 inbuf, |
|
228 outbuf, |
|
229 gcm, |
|
230 inlen); |
|
231 |
|
232 gcm->Mlen += inlen; |
|
233 intel_aes_gcmTAG( |
|
234 gcm->Htbl, |
|
235 gcm->T, |
|
236 gcm->Mlen, |
|
237 gcm->Alen, |
|
238 gcm->X0, |
|
239 T); |
|
240 |
|
241 if (NSS_SecureMemcmp(T, intag, tagBytes) != 0) { |
|
242 memset(outbuf, 0, inlen); |
|
243 *outlen = 0; |
|
244 /* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */ |
|
245 PORT_SetError(SEC_ERROR_BAD_DATA); |
|
246 return SECFailure; |
|
247 } |
|
248 *outlen = inlen; |
|
249 |
|
250 return SECSuccess; |
|
251 } |
|
252 |
|
253 #endif |