]> git.ipfire.org Git - thirdparty/strongswan.git/blob - scripts/aes-test.c
aes-test: Add script to test AES implementations according to AESAVS/GCMVS
[thirdparty/strongswan.git] / scripts / aes-test.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <getopt.h>
21 #include <errno.h>
22
23 #include <library.h>
24
25 /** plugins to load */
26 #undef PLUGINS
27 #define PLUGINS "openssl"
28
29 /**
30 * Context
31 */
32 static struct {
33 /** input file */
34 FILE *in;
35 /** output file */
36 FILE *out;
37 /** whether to use GCM or CBC */
38 bool use_gcm;
39 /** whether to run the Monte Carlo Test */
40 bool use_mct;
41 /** whether to test encryption or decryption */
42 bool decrypt;
43 /** IV length in bits in case of GCM */
44 int ivlen;
45 /** ICV length in bits in case of GCM */
46 int icvlen;
47 } ctx;
48
49 /**
50 * Types of parameters of a test vector
51 */
52 typedef enum {
53 PARAM_UNKNOWN,
54 PARAM_COUNT,
55 PARAM_KEY,
56 PARAM_IV,
57 PARAM_PLAINTEXT,
58 PARAM_CIPHERTEXT,
59 PARAM_AAD,
60 PARAM_ICV,
61 } param_t;
62
63 static param_t parse_parameter(char *param)
64 {
65 if (strcaseeq(param, "COUNT"))
66 {
67 return PARAM_COUNT;
68 }
69 if (strcaseeq(param, "KEY"))
70 {
71 return PARAM_KEY;
72 }
73 if (strcaseeq(param, "IV"))
74 {
75 return PARAM_IV;
76 }
77 if (strcaseeq(param, "PLAINTEXT") ||
78 strcaseeq(param, "PT"))
79 {
80 return PARAM_PLAINTEXT;
81 }
82 if (strcaseeq(param, "CIPHERTEXT") ||
83 strcaseeq(param, "CT"))
84 {
85 return PARAM_CIPHERTEXT;
86 }
87 if (strcaseeq(param, "AAD"))
88 {
89 return PARAM_AAD;
90 }
91 if (strcaseeq(param, "TAG"))
92 {
93 return PARAM_ICV;
94 }
95 return PARAM_UNKNOWN;
96 }
97
98 /**
99 * Test vector
100 */
101 typedef struct {
102 /** encryption/decryption key */
103 chunk_t key;
104 /** initialization vector */
105 chunk_t iv;
106 /** plain text */
107 chunk_t plain;
108 /** cipher text */
109 chunk_t cipher;
110 /** associated data */
111 chunk_t aad;
112 /** ICV/tag */
113 chunk_t icv;
114 /** whether the IV was provided */
115 bool external_iv;
116 /** whether the decryption/verification in GCM mode was successful */
117 bool success;
118 } test_vector_t;
119
120 static void test_vector_free(test_vector_t *test)
121 {
122 chunk_free(&test->key);
123 chunk_free(&test->iv);
124 chunk_free(&test->plain);
125 chunk_free(&test->cipher);
126 chunk_free(&test->aad);
127 chunk_free(&test->icv);
128 }
129
130 static void print_result(test_vector_t *test)
131 {
132 if (ctx.use_gcm)
133 {
134 if (ctx.decrypt)
135 {
136 if (test->success)
137 {
138 fprintf(ctx.out, "PT = %+B\n", &test->plain);
139 }
140 else
141 {
142 fprintf(ctx.out, "FAIL\n");
143 }
144 return;
145 }
146 if (!test->external_iv)
147 {
148 fprintf(ctx.out, "IV = %+B\n", &test->iv);
149 }
150 fprintf(ctx.out, "CT = %+B\n", &test->cipher);
151 fprintf(ctx.out, "Tag = %+B\n", &test->icv);
152 }
153 else
154 {
155 fprintf(ctx.out, "%s = %+B\n", ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT",
156 ctx.decrypt ? &test->plain : &test->cipher);
157 }
158 }
159
160 static bool get_next_test_vector(test_vector_t *test)
161 {
162 param_t param = PARAM_UNKNOWN;
163 char line[512];
164
165 memset(test, 0, sizeof(test_vector_t));
166
167 while (fgets(line, sizeof(line), ctx.in))
168 {
169 enumerator_t *enumerator;
170 chunk_t value;
171 char *token;
172 int i;
173
174 switch (line[0])
175 {
176 case '\n':
177 case '\r':
178 case '#':
179 case '\0':
180 /* copy comments, empty lines etc. directly to the output */
181 if (param != PARAM_UNKNOWN)
182 { /* seems we got a complete test vector */
183 return TRUE;
184 }
185 fputs(line, ctx.out);
186 continue;
187 case '[':
188 /* control directives */
189 fputs(line, ctx.out);
190 if (strpfx(line, "[ENCRYPT]"))
191 {
192 ctx.decrypt = FALSE;
193 }
194 else if (strpfx(line, "[DECRYPT]"))
195 {
196 ctx.decrypt = TRUE;
197 }
198 else if (strcasepfx(line, "[IVlen = "))
199 {
200 ctx.ivlen = atoi(line + strlen("[IVlen = "));
201 }
202 else if (strcasepfx(line, "[Taglen = "))
203 {
204 ctx.icvlen = atoi(line + strlen("[Taglen = "));
205 }
206 continue;
207 default:
208 /* we assume the rest of the lines are PARAM = VALUE pairs*/
209 fputs(line, ctx.out);
210 break;
211 }
212
213 i = 0;
214 enumerator = enumerator_create_token(line, "=", " \n\r");
215 while (enumerator->enumerate(enumerator, &token))
216 {
217 switch (i++)
218 {
219 case 0: /* PARAM */
220 param = parse_parameter(token);
221 continue;
222 case 1: /* VALUE */
223 if (param != PARAM_UNKNOWN && param != PARAM_COUNT)
224 {
225 value = chunk_from_hex(chunk_from_str(token), NULL);
226 }
227 else
228 {
229 value = chunk_empty;
230 }
231 continue;
232 default:
233 break;
234 }
235 break;
236 }
237 enumerator->destroy(enumerator);
238 if (i < 2)
239 {
240 value = chunk_empty;
241 }
242 switch (param)
243 {
244 case PARAM_KEY:
245 test->key = value;
246 break;
247 case PARAM_IV:
248 test->iv = value;
249 test->external_iv = TRUE;
250 break;
251 case PARAM_PLAINTEXT:
252 test->plain = value;
253 break;
254 case PARAM_CIPHERTEXT:
255 test->cipher = value;
256 break;
257 case PARAM_AAD:
258 test->aad = value;
259 break;
260 case PARAM_ICV:
261 test->icv = value;
262 break;
263 default:
264 chunk_free(&value);
265 break;
266 }
267 }
268 return FALSE;
269 }
270
271 static bool verify_test_vector(test_vector_t *test)
272 {
273 if (ctx.use_gcm)
274 {
275 if (ctx.decrypt)
276 {
277 return test->key.ptr && test->iv.ptr && test->cipher.ptr &&
278 test->icv.ptr;
279 }
280 return test->key.ptr && test->plain.ptr;
281 }
282 if (ctx.decrypt)
283 {
284 return test->key.ptr && test->iv.ptr && test->cipher.ptr;
285 }
286 return test->key.ptr && test->iv.ptr && test->plain.ptr;
287 }
288
289 static bool do_test_gcm(test_vector_t *test)
290 {
291 encryption_algorithm_t alg;
292 chunk_t key, iv;
293 aead_t *aead;
294 size_t saltlen, ivlen;
295
296 switch (ctx.icvlen / 8)
297 {
298 case 8:
299 alg = ENCR_AES_GCM_ICV8;
300 break;
301 case 12:
302 alg = ENCR_AES_GCM_ICV12;
303 break;
304 case 16:
305 alg = ENCR_AES_GCM_ICV16;
306 break;
307 default:
308 DBG1(DBG_APP, "unsupported ICV length: %d", ctx.icvlen);
309 return FALSE;
310 }
311
312 aead = lib->crypto->create_aead(lib->crypto, alg, test->key.len);
313 if (!aead)
314 {
315 DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
316 encryption_algorithm_names, alg, test->key.len * 8);
317 return FALSE;
318 }
319 /* our API is quite RFC 4106 specific, that is, part of the IV is provided
320 * at the end of the key. */
321 saltlen = aead->get_key_size(aead) - test->key.len;
322 ivlen = aead->get_iv_size(aead);
323 if (ctx.ivlen / 8 != saltlen + ivlen)
324 {
325 DBG1(DBG_APP, "unsupported IV length: %d", ctx.ivlen);
326 aead->destroy(aead);
327 return FALSE;
328 }
329 if (!test->external_iv)
330 {
331 rng_t *rng;
332
333 /* the IV consists of saltlen random bytes (usually additional keymat)
334 * followed by a counter, zero here */
335 test->iv = chunk_alloc(saltlen + ivlen);
336 memset(test->iv.ptr, 0, test->iv.len);
337 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
338 if (!rng || !rng->get_bytes(rng, saltlen, test->iv.ptr))
339 {
340 DBG1(DBG_APP, "failed to generate IV");
341 DESTROY_IF(rng);
342 aead->destroy(aead);
343 return FALSE;
344 }
345 rng->destroy(rng);
346 }
347 key = chunk_alloca(test->key.len + saltlen);
348 memcpy(key.ptr, test->key.ptr, test->key.len);
349 memcpy(key.ptr + test->key.len, test->iv.ptr, saltlen);
350 iv = chunk_alloca(ivlen);
351 memcpy(iv.ptr, test->iv.ptr + saltlen, iv.len);
352 if (!aead->set_key(aead, key))
353 {
354 DBG1(DBG_APP, "failed to set key");
355 aead->destroy(aead);
356 return FALSE;
357 }
358 if (ctx.decrypt)
359 {
360 /* the ICV is expected to follow the cipher text */
361 chunk_t cipher = chunk_cata("cc", test->cipher, test->icv);
362 /* store if the verification of the ICV verification is successful */
363 test->success = aead->decrypt(aead, cipher, test->aad, iv,
364 &test->plain);
365 }
366 else
367 {
368 if (!aead->encrypt(aead, test->plain, test->aad, iv, &test->cipher))
369 {
370 DBG1(DBG_APP, "encryption failed");
371 aead->destroy(aead);
372 return FALSE;
373 }
374 /* copy ICV from the end of the cipher text */
375 test->icv = chunk_alloc(ctx.icvlen / 8);
376 test->cipher.len -= test->icv.len;
377 memcpy(test->icv.ptr, test->cipher.ptr + test->cipher.len,
378 test->icv.len);
379 }
380 aead->destroy(aead);
381 return TRUE;
382 }
383
384 static bool crypt(crypter_t *crypter, test_vector_t *test)
385 {
386 if (ctx.decrypt)
387 {
388 if (!crypter->decrypt(crypter, test->cipher, test->iv, &test->plain))
389 {
390 DBG1(DBG_APP, "decryption failed");
391 return FALSE;
392 }
393 }
394 else
395 {
396 if (!crypter->encrypt(crypter, test->plain, test->iv, &test->cipher))
397 {
398 DBG1(DBG_APP, "encryption failed");
399 return FALSE;
400 }
401 }
402 return TRUE;
403 }
404
405 static bool do_test_cbc(test_vector_t *test)
406 {
407 crypter_t *crypter;
408
409 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
410 test->key.len);
411 if (!crypter)
412 {
413 DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
414 encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
415 return FALSE;
416 }
417 if (!crypter->set_key(crypter, test->key))
418 {
419 DBG1(DBG_APP, "failed to set key");
420 crypter->destroy(crypter);
421 return FALSE;
422 }
423 if (!crypt(crypter, test))
424 {
425 crypter->destroy(crypter);
426 return FALSE;
427 }
428 crypter->destroy(crypter);
429 return TRUE;
430 }
431
432 static bool do_test_mct(test_vector_t *test)
433 {
434 crypter_t *crypter;
435 chunk_t prev, *input, *output;
436 int i, j;
437
438 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
439 test->key.len);
440 if (!crypter)
441 {
442 DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
443 encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
444 return FALSE;
445 }
446 input = ctx.decrypt ? &test->cipher : &test->plain;
447 output = ctx.decrypt ? &test->plain : &test->cipher;
448 if (crypter->get_block_size(crypter) != input->len)
449 {
450 DBG1(DBG_APP, "MCT only works for input with a length of one block");
451 crypter->destroy(crypter);
452 return FALSE;
453 }
454 prev = chunk_alloca(input->len);
455 /* assume initial IV as previous output */
456 *output = chunk_clone(test->iv);
457 for (i = 0; i < 100; i++)
458 {
459 if (i > 0)
460 { /* we copied the original lines already */
461 fprintf(ctx.out, "COUNT = %d\n", i);
462 fprintf(ctx.out, "KEY = %+B\n", &test->key);
463 fprintf(ctx.out, "IV = %+B\n", &test->iv);
464 fprintf(ctx.out, "%s = %+B\n",
465 ctx.decrypt ? "CIPHERTEXT" : "PLAINTEXT", input);
466 }
467 if (!crypter->set_key(crypter, test->key))
468 {
469 DBG1(DBG_APP, "failed to set key");
470 return FALSE;
471 }
472 for (j = 0; j < 1000; j++)
473 {
474 /* store previous output as it is used as input after next */
475 memcpy(prev.ptr, output->ptr, prev.len);
476 chunk_free(output);
477 if (!crypt(crypter, test))
478 {
479 crypter->destroy(crypter);
480 return FALSE;
481 }
482 /* prepare the next IV (our API does not allow incremental calls) */
483 if (ctx.decrypt)
484 {
485 memcpy(test->iv.ptr, input->ptr, test->iv.len);
486 }
487 else
488 {
489 memcpy(test->iv.ptr, output->ptr, test->iv.len);
490 }
491 /* the previous output is the next input */
492 memcpy(input->ptr, prev.ptr, input->len);
493 }
494 fprintf(ctx.out, "%s = %+B\n\n",
495 ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT", output);
496 /* derive key for next round */
497 switch (test->key.len)
498 {
499 case 16:
500 memxor(test->key.ptr, output->ptr, output->len);
501 break;
502 case 24:
503 memxor(test->key.ptr, prev.ptr + 8, 8);
504 memxor(test->key.ptr + 8, output->ptr, output->len);
505 break;
506 case 32:
507 memxor(test->key.ptr, prev.ptr, prev.len);
508 memxor(test->key.ptr + prev.len, output->ptr, output->len);
509 break;
510 }
511 /* the current output is used as IV for the next round */
512 memcpy(test->iv.ptr, output->ptr, test->iv.len);
513 }
514 crypter->destroy(crypter);
515 /* we return FALSE as we print the output ourselves */
516 return FALSE;
517 }
518
519 static bool do_test(test_vector_t *test)
520 {
521 if (ctx.use_gcm)
522 {
523 return do_test_gcm(test);
524 }
525 if (ctx.use_mct)
526 {
527 return do_test_mct(test);
528 }
529 return do_test_cbc(test);
530 }
531
532 static void usage(FILE *out, char *name)
533 {
534 fprintf(out, "Test AES implementation according to the AES Algorithm Validation Suite (AESAVS)\n");
535 fprintf(out, "and the GCM Validation System (GCMVS)\n\n");
536 fprintf(out, "%s [OPTIONS]\n\n", name);
537 fprintf(out, "Options:\n");
538 fprintf(out, " -h, --help print this help.\n");
539 fprintf(out, " -d, --debug=LEVEL set debug level (default 1).\n");
540 fprintf(out, " -m, --mode=MODE mode to test, either CBC or GCM (default CBC).\n");
541 fprintf(out, " -t, --mct run Monte Carlo Test (MCT), only for CBC.\n");
542 fprintf(out, " -x, --decrypt test decryption (not needed for CBC as files contain control directives).\n");
543 fprintf(out, " -i, --in=FILE request file (default STDIN).\n");
544 fprintf(out, " -o, --out=FILE response file (default STDOUT).\n");
545 fprintf(out, "\n");
546 }
547
548 int main(int argc, char *argv[])
549 {
550 test_vector_t test;
551
552 ctx.in = stdin;
553 ctx.out = stdout;
554
555 library_init(NULL);
556 atexit(library_deinit);
557
558 while (true)
559 {
560 struct option long_opts[] = {
561 {"help", no_argument, NULL, 'h' },
562 {"debug", required_argument, NULL, 'd' },
563 {"mode", required_argument, NULL, 'm' },
564 {"mct", no_argument, NULL, 't' },
565 {"decrypt", no_argument, NULL, 'x' },
566 {"in", required_argument, NULL, 'i' },
567 {"out", required_argument, NULL, 'o' },
568 {0,0,0,0 },
569 };
570 switch (getopt_long(argc, argv, "hd:m:txi:o:", long_opts, NULL))
571 {
572 case EOF:
573 break;
574 case 'h':
575 usage(stdout, argv[0]);
576 return 0;
577 case 'd':
578 dbg_default_set_level(atoi(optarg));
579 continue;
580 case 'm':
581 if (strcaseeq(optarg, "GCM"))
582 {
583 ctx.use_gcm = TRUE;
584 }
585 else if (!strcaseeq(optarg, "CBC"))
586 {
587 usage(stderr, argv[0]);
588 return 1;
589 }
590 continue;
591 case 't':
592 ctx.use_mct = TRUE;
593 continue;
594 case 'x':
595 ctx.decrypt = TRUE;
596 continue;
597 case 'i':
598 ctx.in = fopen(optarg, "r");
599 if (!ctx.in)
600 {
601 fprintf(stderr, "failed to open '%s': %s\n", optarg,
602 strerror(errno));
603 usage(stderr, argv[0]);
604 return 1;
605 }
606 continue;
607 case 'o':
608 ctx.out = fopen(optarg, "w");
609 if (!ctx.out)
610 {
611 fprintf(stderr, "failed to open '%s': %s\n", optarg,
612 strerror(errno));
613 usage(stderr, argv[0]);
614 return 1;
615 }
616 continue;
617 default:
618 usage(stderr, argv[0]);
619 return 1;
620 }
621 break;
622 }
623 /* TODO: maybe make plugins configurable */
624 lib->plugins->load(lib->plugins, PLUGINS);
625 lib->plugins->status(lib->plugins, LEVEL_CTRL);
626
627 while (get_next_test_vector(&test))
628 {
629 if (verify_test_vector(&test))
630 {
631 if (do_test(&test))
632 {
633 print_result(&test);
634 }
635 }
636 else
637 {
638 DBG1(DBG_APP, "test vector with missing data encountered");
639 }
640 fprintf(ctx.out, "\n");
641 test_vector_free(&test);
642 }
643
644 if (ctx.in != stdin)
645 {
646 fclose(ctx.in);
647 }
648 if (ctx.out != stdout)
649 {
650 fclose(ctx.out);
651 }
652 return 0;
653 }