|
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 |
|
5 #include <stdio.h> |
|
6 #include <stdlib.h> |
|
7 #include <ctype.h> |
|
8 |
|
9 #include "pk11pub.h" |
|
10 #include "secerr.h" |
|
11 #include "nss.h" |
|
12 |
|
13 static SECStatus |
|
14 hex_to_byteval(const char *c2, unsigned char *byteval) |
|
15 { |
|
16 int i; |
|
17 unsigned char offset; |
|
18 *byteval = 0; |
|
19 for (i=0; i<2; i++) { |
|
20 if (c2[i] >= '0' && c2[i] <= '9') { |
|
21 offset = c2[i] - '0'; |
|
22 *byteval |= offset << 4*(1-i); |
|
23 } else if (c2[i] >= 'a' && c2[i] <= 'f') { |
|
24 offset = c2[i] - 'a'; |
|
25 *byteval |= (offset + 10) << 4*(1-i); |
|
26 } else if (c2[i] >= 'A' && c2[i] <= 'F') { |
|
27 offset = c2[i] - 'A'; |
|
28 *byteval |= (offset + 10) << 4*(1-i); |
|
29 } else { |
|
30 return SECFailure; |
|
31 } |
|
32 } |
|
33 return SECSuccess; |
|
34 } |
|
35 |
|
36 static SECStatus |
|
37 aes_encrypt_buf( |
|
38 const unsigned char *key, unsigned int keysize, |
|
39 const unsigned char *iv, unsigned int ivsize, |
|
40 unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen, |
|
41 const unsigned char *input, unsigned int inputlen, |
|
42 const unsigned char *aad, unsigned int aadlen, unsigned int tagsize) |
|
43 { |
|
44 SECStatus rv = SECFailure; |
|
45 SECItem key_item; |
|
46 PK11SlotInfo* slot = NULL; |
|
47 PK11SymKey *symKey = NULL; |
|
48 CK_GCM_PARAMS gcm_params; |
|
49 SECItem param; |
|
50 |
|
51 /* Import key into NSS. */ |
|
52 key_item.type = siBuffer; |
|
53 key_item.data = (unsigned char *) key; /* const cast */ |
|
54 key_item.len = keysize; |
|
55 slot = PK11_GetInternalSlot(); |
|
56 symKey = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap, |
|
57 CKA_ENCRYPT, &key_item, NULL); |
|
58 PK11_FreeSlot(slot); |
|
59 slot = NULL; |
|
60 if (!symKey) { |
|
61 fprintf(stderr, "PK11_ImportSymKey failed\n"); |
|
62 goto loser; |
|
63 } |
|
64 |
|
65 gcm_params.pIv = (unsigned char *) iv; /* const cast */ |
|
66 gcm_params.ulIvLen = ivsize; |
|
67 gcm_params.pAAD = (unsigned char *) aad; /* const cast */ |
|
68 gcm_params.ulAADLen = aadlen; |
|
69 gcm_params.ulTagBits = tagsize * 8; |
|
70 |
|
71 param.type = siBuffer; |
|
72 param.data = (unsigned char *) &gcm_params; |
|
73 param.len = sizeof(gcm_params); |
|
74 |
|
75 if (PK11_Encrypt(symKey, CKM_AES_GCM, ¶m, |
|
76 output, outputlen, maxoutputlen, |
|
77 input, inputlen) != SECSuccess) { |
|
78 fprintf(stderr, "PK11_Encrypt failed\n"); |
|
79 goto loser; |
|
80 } |
|
81 |
|
82 rv = SECSuccess; |
|
83 |
|
84 loser: |
|
85 if (symKey != NULL) { |
|
86 PK11_FreeSymKey(symKey); |
|
87 } |
|
88 return rv; |
|
89 } |
|
90 |
|
91 static SECStatus |
|
92 aes_decrypt_buf( |
|
93 const unsigned char *key, unsigned int keysize, |
|
94 const unsigned char *iv, unsigned int ivsize, |
|
95 unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen, |
|
96 const unsigned char *input, unsigned int inputlen, |
|
97 const unsigned char *aad, unsigned int aadlen, |
|
98 const unsigned char *tag, unsigned int tagsize) |
|
99 { |
|
100 SECStatus rv = SECFailure; |
|
101 unsigned char concatenated[11*16]; /* 1 to 11 blocks */ |
|
102 SECItem key_item; |
|
103 PK11SlotInfo *slot = NULL; |
|
104 PK11SymKey *symKey = NULL; |
|
105 CK_GCM_PARAMS gcm_params; |
|
106 SECItem param; |
|
107 |
|
108 if (inputlen + tagsize > sizeof(concatenated)) { |
|
109 fprintf(stderr, "aes_decrypt_buf: local buffer too small\n"); |
|
110 goto loser; |
|
111 } |
|
112 memcpy(concatenated, input, inputlen); |
|
113 memcpy(concatenated + inputlen, tag, tagsize); |
|
114 |
|
115 /* Import key into NSS. */ |
|
116 key_item.type = siBuffer; |
|
117 key_item.data = (unsigned char *) key; /* const cast */ |
|
118 key_item.len = keysize; |
|
119 slot = PK11_GetInternalSlot(); |
|
120 symKey = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap, |
|
121 CKA_DECRYPT, &key_item, NULL); |
|
122 PK11_FreeSlot(slot); |
|
123 slot = NULL; |
|
124 if (!symKey) { |
|
125 fprintf(stderr, "PK11_ImportSymKey failed\n"); |
|
126 goto loser; |
|
127 } |
|
128 |
|
129 gcm_params.pIv = (unsigned char *) iv; |
|
130 gcm_params.ulIvLen = ivsize; |
|
131 gcm_params.pAAD = (unsigned char *) aad; |
|
132 gcm_params.ulAADLen = aadlen; |
|
133 gcm_params.ulTagBits = tagsize * 8; |
|
134 |
|
135 param.type = siBuffer; |
|
136 param.data = (unsigned char *) &gcm_params; |
|
137 param.len = sizeof(gcm_params); |
|
138 |
|
139 if (PK11_Decrypt(symKey, CKM_AES_GCM, ¶m, |
|
140 output, outputlen, maxoutputlen, |
|
141 concatenated, inputlen + tagsize) != SECSuccess) { |
|
142 goto loser; |
|
143 } |
|
144 |
|
145 rv = SECSuccess; |
|
146 |
|
147 loser: |
|
148 if (symKey != NULL) { |
|
149 PK11_FreeSymKey(symKey); |
|
150 } |
|
151 return rv; |
|
152 } |
|
153 |
|
154 /* |
|
155 * Perform the AES Known Answer Test (KAT) in Galois Counter Mode (GCM). |
|
156 * |
|
157 * respfn is the pathname of the RESPONSE file. |
|
158 */ |
|
159 static void |
|
160 aes_gcm_kat(const char *respfn) |
|
161 { |
|
162 char buf[512]; /* holds one line from the input REQUEST file. |
|
163 * needs to be large enough to hold the longest |
|
164 * line "CIPHERTEXT = <320 hex digits>\n". |
|
165 */ |
|
166 FILE *aesresp; /* input stream from the RESPONSE file */ |
|
167 int i, j; |
|
168 unsigned int test_group = 0; |
|
169 unsigned int num_tests; |
|
170 PRBool is_encrypt; |
|
171 unsigned char key[32]; /* 128, 192, or 256 bits */ |
|
172 unsigned int keysize; |
|
173 unsigned char iv[10*16]; /* 1 to 10 blocks */ |
|
174 unsigned int ivsize; |
|
175 unsigned char plaintext[10*16]; /* 1 to 10 blocks */ |
|
176 unsigned int plaintextlen = 0; |
|
177 unsigned char aad[10*16]; /* 1 to 10 blocks */ |
|
178 unsigned int aadlen = 0; |
|
179 unsigned char ciphertext[10*16]; /* 1 to 10 blocks */ |
|
180 unsigned int ciphertextlen; |
|
181 unsigned char tag[16]; |
|
182 unsigned int tagsize; |
|
183 unsigned char output[10*16]; /* 1 to 10 blocks */ |
|
184 unsigned int outputlen; |
|
185 |
|
186 unsigned int expected_keylen = 0; |
|
187 unsigned int expected_ivlen = 0; |
|
188 unsigned int expected_ptlen = 0; |
|
189 unsigned int expected_aadlen = 0; |
|
190 unsigned int expected_taglen = 0; |
|
191 SECStatus rv; |
|
192 |
|
193 if (strstr(respfn, "Encrypt") != NULL) { |
|
194 is_encrypt = PR_TRUE; |
|
195 } else if (strstr(respfn, "Decrypt") != NULL) { |
|
196 is_encrypt = PR_FALSE; |
|
197 } else { |
|
198 fprintf(stderr, "Input file name must contain Encrypt or Decrypt\n"); |
|
199 exit(1); |
|
200 } |
|
201 aesresp = fopen(respfn, "r"); |
|
202 if (aesresp == NULL) { |
|
203 fprintf(stderr, "Cannot open input file %s\n", respfn); |
|
204 exit(1); |
|
205 } |
|
206 while (fgets(buf, sizeof buf, aesresp) != NULL) { |
|
207 /* a comment or blank line */ |
|
208 if (buf[0] == '#' || buf[0] == '\n') { |
|
209 continue; |
|
210 } |
|
211 /* [Keylen = ...], [IVlen = ...], etc. */ |
|
212 if (buf[0] == '[') { |
|
213 if (strncmp(&buf[1], "Keylen = ", 9) == 0) { |
|
214 expected_keylen = atoi(&buf[10]); |
|
215 } else if (strncmp(&buf[1], "IVlen = ", 8) == 0) { |
|
216 expected_ivlen = atoi(&buf[9]); |
|
217 } else if (strncmp(&buf[1], "PTlen = ", 8) == 0) { |
|
218 expected_ptlen = atoi(&buf[9]); |
|
219 } else if (strncmp(&buf[1], "AADlen = ", 9) == 0) { |
|
220 expected_aadlen = atoi(&buf[10]); |
|
221 } else if (strncmp(&buf[1], "Taglen = ", 9) == 0) { |
|
222 expected_taglen = atoi(&buf[10]); |
|
223 |
|
224 test_group++; |
|
225 if (test_group > 1) { |
|
226 /* Report num_tests for the previous test group. */ |
|
227 printf("%u tests\n", num_tests); |
|
228 } |
|
229 num_tests = 0; |
|
230 printf("Keylen = %u, IVlen = %u, PTlen = %u, AADlen = %u, " |
|
231 "Taglen = %u: ", expected_keylen, expected_ivlen, |
|
232 expected_ptlen, expected_aadlen, expected_taglen); |
|
233 /* Convert lengths in bits to lengths in bytes. */ |
|
234 PORT_Assert(expected_keylen % 8 == 0); |
|
235 expected_keylen /= 8; |
|
236 PORT_Assert(expected_ivlen % 8 == 0); |
|
237 expected_ivlen /= 8; |
|
238 PORT_Assert(expected_ptlen % 8 == 0); |
|
239 expected_ptlen /= 8; |
|
240 PORT_Assert(expected_aadlen % 8 == 0); |
|
241 expected_aadlen /= 8; |
|
242 PORT_Assert(expected_taglen % 8 == 0); |
|
243 expected_taglen /= 8; |
|
244 } else { |
|
245 fprintf(stderr, "Unexpected input line: %s\n", buf); |
|
246 exit(1); |
|
247 } |
|
248 continue; |
|
249 } |
|
250 /* "Count = x" begins a new data set */ |
|
251 if (strncmp(buf, "Count", 5) == 0) { |
|
252 /* zeroize the variables for the test with this data set */ |
|
253 memset(key, 0, sizeof key); |
|
254 keysize = 0; |
|
255 memset(iv, 0, sizeof iv); |
|
256 ivsize = 0; |
|
257 memset(plaintext, 0, sizeof plaintext); |
|
258 plaintextlen = 0; |
|
259 memset(aad, 0, sizeof aad); |
|
260 aadlen = 0; |
|
261 memset(ciphertext, 0, sizeof ciphertext); |
|
262 ciphertextlen = 0; |
|
263 memset(output, 0, sizeof output); |
|
264 outputlen = 0; |
|
265 num_tests++; |
|
266 continue; |
|
267 } |
|
268 /* Key = ... */ |
|
269 if (strncmp(buf, "Key", 3) == 0) { |
|
270 i = 3; |
|
271 while (isspace(buf[i]) || buf[i] == '=') { |
|
272 i++; |
|
273 } |
|
274 for (j=0; isxdigit(buf[i]); i+=2,j++) { |
|
275 hex_to_byteval(&buf[i], &key[j]); |
|
276 } |
|
277 keysize = j; |
|
278 if (keysize != expected_keylen) { |
|
279 fprintf(stderr, "Unexpected key length: %u vs. %u\n", |
|
280 keysize, expected_keylen); |
|
281 exit(1); |
|
282 } |
|
283 continue; |
|
284 } |
|
285 /* IV = ... */ |
|
286 if (strncmp(buf, "IV", 2) == 0) { |
|
287 i = 2; |
|
288 while (isspace(buf[i]) || buf[i] == '=') { |
|
289 i++; |
|
290 } |
|
291 for (j=0; isxdigit(buf[i]); i+=2,j++) { |
|
292 hex_to_byteval(&buf[i], &iv[j]); |
|
293 } |
|
294 ivsize = j; |
|
295 if (ivsize != expected_ivlen) { |
|
296 fprintf(stderr, "Unexpected IV length: %u vs. %u\n", |
|
297 ivsize, expected_ivlen); |
|
298 exit(1); |
|
299 } |
|
300 continue; |
|
301 } |
|
302 /* PT = ... */ |
|
303 if (strncmp(buf, "PT", 2) == 0) { |
|
304 i = 2; |
|
305 while (isspace(buf[i]) || buf[i] == '=') { |
|
306 i++; |
|
307 } |
|
308 for (j=0; isxdigit(buf[i]); i+=2,j++) { |
|
309 hex_to_byteval(&buf[i], &plaintext[j]); |
|
310 } |
|
311 plaintextlen = j; |
|
312 if (plaintextlen != expected_ptlen) { |
|
313 fprintf(stderr, "Unexpected PT length: %u vs. %u\n", |
|
314 plaintextlen, expected_ptlen); |
|
315 exit(1); |
|
316 } |
|
317 |
|
318 if (!is_encrypt) { |
|
319 rv = aes_decrypt_buf(key, keysize, iv, ivsize, |
|
320 output, &outputlen, sizeof output, |
|
321 ciphertext, ciphertextlen, aad, aadlen, tag, tagsize); |
|
322 if (rv != SECSuccess) { |
|
323 fprintf(stderr, "aes_decrypt_buf failed\n"); |
|
324 goto loser; |
|
325 } |
|
326 if (outputlen != plaintextlen) { |
|
327 fprintf(stderr, "aes_decrypt_buf: wrong output size\n"); |
|
328 goto loser; |
|
329 } |
|
330 if (memcmp(output, plaintext, plaintextlen) != 0) { |
|
331 fprintf(stderr, "aes_decrypt_buf: wrong plaintext\n"); |
|
332 goto loser; |
|
333 } |
|
334 } |
|
335 continue; |
|
336 } |
|
337 /* FAIL */ |
|
338 if (strncmp(buf, "FAIL", 4) == 0) { |
|
339 plaintextlen = 0; |
|
340 |
|
341 PORT_Assert(!is_encrypt); |
|
342 rv = aes_decrypt_buf(key, keysize, iv, ivsize, |
|
343 output, &outputlen, sizeof output, |
|
344 ciphertext, ciphertextlen, aad, aadlen, tag, tagsize); |
|
345 if (rv != SECFailure) { |
|
346 fprintf(stderr, "aes_decrypt_buf succeeded unexpectedly\n"); |
|
347 goto loser; |
|
348 } |
|
349 if (PORT_GetError() != SEC_ERROR_BAD_DATA) { |
|
350 fprintf(stderr, "aes_decrypt_buf failed with incorrect " |
|
351 "error code\n"); |
|
352 goto loser; |
|
353 } |
|
354 continue; |
|
355 } |
|
356 /* AAD = ... */ |
|
357 if (strncmp(buf, "AAD", 3) == 0) { |
|
358 i = 3; |
|
359 while (isspace(buf[i]) || buf[i] == '=') { |
|
360 i++; |
|
361 } |
|
362 for (j=0; isxdigit(buf[i]); i+=2,j++) { |
|
363 hex_to_byteval(&buf[i], &aad[j]); |
|
364 } |
|
365 aadlen = j; |
|
366 if (aadlen != expected_aadlen) { |
|
367 fprintf(stderr, "Unexpected AAD length: %u vs. %u\n", |
|
368 aadlen, expected_aadlen); |
|
369 exit(1); |
|
370 } |
|
371 continue; |
|
372 } |
|
373 /* CT = ... */ |
|
374 if (strncmp(buf, "CT", 2) == 0) { |
|
375 i = 2; |
|
376 while (isspace(buf[i]) || buf[i] == '=') { |
|
377 i++; |
|
378 } |
|
379 for (j=0; isxdigit(buf[i]); i+=2,j++) { |
|
380 hex_to_byteval(&buf[i], &ciphertext[j]); |
|
381 } |
|
382 ciphertextlen = j; |
|
383 if (ciphertextlen != expected_ptlen) { |
|
384 fprintf(stderr, "Unexpected CT length: %u vs. %u\n", |
|
385 ciphertextlen, expected_ptlen); |
|
386 exit(1); |
|
387 } |
|
388 continue; |
|
389 } |
|
390 /* Tag = ... */ |
|
391 if (strncmp(buf, "Tag", 3) == 0) { |
|
392 i = 3; |
|
393 while (isspace(buf[i]) || buf[i] == '=') { |
|
394 i++; |
|
395 } |
|
396 for (j=0; isxdigit(buf[i]); i+=2,j++) { |
|
397 hex_to_byteval(&buf[i], &tag[j]); |
|
398 } |
|
399 tagsize = j; |
|
400 if (tagsize != expected_taglen) { |
|
401 fprintf(stderr, "Unexpected tag length: %u vs. %u\n", |
|
402 tagsize, expected_taglen); |
|
403 exit(1); |
|
404 } |
|
405 |
|
406 if (is_encrypt) { |
|
407 rv = aes_encrypt_buf(key, keysize, iv, ivsize, |
|
408 output, &outputlen, sizeof output, |
|
409 plaintext, plaintextlen, aad, aadlen, tagsize); |
|
410 if (rv != SECSuccess) { |
|
411 fprintf(stderr, "aes_encrypt_buf failed\n"); |
|
412 goto loser; |
|
413 } |
|
414 if (outputlen != plaintextlen + tagsize) { |
|
415 fprintf(stderr, "aes_encrypt_buf: wrong output size\n"); |
|
416 goto loser; |
|
417 } |
|
418 if (memcmp(output, ciphertext, plaintextlen) != 0) { |
|
419 fprintf(stderr, "aes_encrypt_buf: wrong ciphertext\n"); |
|
420 goto loser; |
|
421 } |
|
422 if (memcmp(output + plaintextlen, tag, tagsize) != 0) { |
|
423 fprintf(stderr, "aes_encrypt_buf: wrong tag\n"); |
|
424 goto loser; |
|
425 } |
|
426 } |
|
427 continue; |
|
428 } |
|
429 } |
|
430 /* Report num_tests for the last test group. */ |
|
431 printf("%u tests\n", num_tests); |
|
432 printf("%u test groups\n", test_group); |
|
433 printf("PASS\n"); |
|
434 loser: |
|
435 fclose(aesresp); |
|
436 } |
|
437 |
|
438 int main(int argc, char **argv) |
|
439 { |
|
440 if (argc < 2) exit(1); |
|
441 |
|
442 NSS_NoDB_Init(NULL); |
|
443 |
|
444 /*************/ |
|
445 /* AES */ |
|
446 /*************/ |
|
447 if (strcmp(argv[1], "aes") == 0) { |
|
448 /* argv[2]=kat argv[3]=gcm argv[4]=<test name>.rsp */ |
|
449 if (strcmp(argv[2], "kat") == 0) { |
|
450 /* Known Answer Test (KAT) */ |
|
451 aes_gcm_kat(argv[4]); |
|
452 } |
|
453 } |
|
454 |
|
455 NSS_Shutdown(); |
|
456 return 0; |
|
457 } |