]> git.ipfire.org Git - people/ms/strongswan.git/blob - scripts/aes-test.c
Merge branch 'utils-split'
[people/ms/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 = chunk_empty;
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 if (param != PARAM_UNKNOWN)
269 { /* could be that the file ended with a complete test vector */
270 return TRUE;
271 }
272 return FALSE;
273 }
274
275 static bool verify_test_vector(test_vector_t *test)
276 {
277 if (ctx.use_gcm)
278 {
279 if (ctx.decrypt)
280 {
281 return test->key.ptr && test->iv.ptr && test->cipher.ptr &&
282 test->icv.ptr;
283 }
284 return test->key.ptr && test->plain.ptr;
285 }
286 if (ctx.decrypt)
287 {
288 return test->key.ptr && test->iv.ptr && test->cipher.ptr;
289 }
290 return test->key.ptr && test->iv.ptr && test->plain.ptr;
291 }
292
293 static bool do_test_gcm(test_vector_t *test)
294 {
295 encryption_algorithm_t alg;
296 chunk_t key, iv;
297 aead_t *aead;
298 size_t saltlen, ivlen;
299
300 switch (ctx.icvlen / 8)
301 {
302 case 8:
303 alg = ENCR_AES_GCM_ICV8;
304 break;
305 case 12:
306 alg = ENCR_AES_GCM_ICV12;
307 break;
308 case 16:
309 alg = ENCR_AES_GCM_ICV16;
310 break;
311 default:
312 DBG1(DBG_APP, "unsupported ICV length: %d", ctx.icvlen);
313 return FALSE;
314 }
315
316 aead = lib->crypto->create_aead(lib->crypto, alg, test->key.len, 4);
317 if (!aead)
318 {
319 DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
320 encryption_algorithm_names, alg, test->key.len * 8);
321 return FALSE;
322 }
323 /* our API is quite RFC 4106 specific, that is, part of the IV is provided
324 * at the end of the key. */
325 saltlen = aead->get_key_size(aead) - test->key.len;
326 ivlen = aead->get_iv_size(aead);
327 if (ctx.ivlen / 8 != saltlen + ivlen)
328 {
329 DBG1(DBG_APP, "unsupported IV length: %d", ctx.ivlen);
330 aead->destroy(aead);
331 return FALSE;
332 }
333 if (!test->external_iv)
334 {
335 rng_t *rng;
336
337 /* the IV consists of saltlen random bytes (usually additional keymat)
338 * followed by a counter, zero here */
339 test->iv = chunk_alloc(saltlen + ivlen);
340 memset(test->iv.ptr, 0, test->iv.len);
341 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
342 if (!rng || !rng->get_bytes(rng, saltlen, test->iv.ptr))
343 {
344 DBG1(DBG_APP, "failed to generate IV");
345 DESTROY_IF(rng);
346 aead->destroy(aead);
347 return FALSE;
348 }
349 rng->destroy(rng);
350 }
351 key = chunk_alloca(test->key.len + saltlen);
352 memcpy(key.ptr, test->key.ptr, test->key.len);
353 memcpy(key.ptr + test->key.len, test->iv.ptr, saltlen);
354 iv = chunk_alloca(ivlen);
355 memcpy(iv.ptr, test->iv.ptr + saltlen, iv.len);
356 if (!aead->set_key(aead, key))
357 {
358 DBG1(DBG_APP, "failed to set key");
359 aead->destroy(aead);
360 return FALSE;
361 }
362 if (ctx.decrypt)
363 {
364 /* the ICV is expected to follow the cipher text */
365 chunk_t cipher = chunk_cata("cc", test->cipher, test->icv);
366 /* store if the verification of the ICV verification is successful */
367 test->success = aead->decrypt(aead, cipher, test->aad, iv,
368 &test->plain);
369 }
370 else
371 {
372 if (!aead->encrypt(aead, test->plain, test->aad, iv, &test->cipher))
373 {
374 DBG1(DBG_APP, "encryption failed");
375 aead->destroy(aead);
376 return FALSE;
377 }
378 /* copy ICV from the end of the cipher text */
379 test->icv = chunk_alloc(ctx.icvlen / 8);
380 test->cipher.len -= test->icv.len;
381 memcpy(test->icv.ptr, test->cipher.ptr + test->cipher.len,
382 test->icv.len);
383 }
384 aead->destroy(aead);
385 return TRUE;
386 }
387
388 static bool do_crypt(crypter_t *crypter, test_vector_t *test)
389 {
390 if (ctx.decrypt)
391 {
392 if (!crypter->decrypt(crypter, test->cipher, test->iv, &test->plain))
393 {
394 DBG1(DBG_APP, "decryption failed");
395 return FALSE;
396 }
397 }
398 else
399 {
400 if (!crypter->encrypt(crypter, test->plain, test->iv, &test->cipher))
401 {
402 DBG1(DBG_APP, "encryption failed");
403 return FALSE;
404 }
405 }
406 return TRUE;
407 }
408
409 static bool do_test_cbc(test_vector_t *test)
410 {
411 crypter_t *crypter;
412
413 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
414 test->key.len);
415 if (!crypter)
416 {
417 DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
418 encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
419 return FALSE;
420 }
421 if (!crypter->set_key(crypter, test->key))
422 {
423 DBG1(DBG_APP, "failed to set key");
424 crypter->destroy(crypter);
425 return FALSE;
426 }
427 if (!do_crypt(crypter, test))
428 {
429 crypter->destroy(crypter);
430 return FALSE;
431 }
432 crypter->destroy(crypter);
433 return TRUE;
434 }
435
436 static bool do_test_mct(test_vector_t *test)
437 {
438 crypter_t *crypter;
439 chunk_t prev, *input, *output;
440 int i, j;
441
442 crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
443 test->key.len);
444 if (!crypter)
445 {
446 DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
447 encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
448 return FALSE;
449 }
450 input = ctx.decrypt ? &test->cipher : &test->plain;
451 output = ctx.decrypt ? &test->plain : &test->cipher;
452 if (crypter->get_block_size(crypter) != input->len)
453 {
454 DBG1(DBG_APP, "MCT only works for input with a length of one block");
455 crypter->destroy(crypter);
456 return FALSE;
457 }
458 prev = chunk_alloca(input->len);
459 /* assume initial IV as previous output */
460 *output = chunk_clone(test->iv);
461 for (i = 0; i < 100; i++)
462 {
463 if (i > 0)
464 { /* we copied the original lines already */
465 fprintf(ctx.out, "COUNT = %d\n", i);
466 fprintf(ctx.out, "KEY = %+B\n", &test->key);
467 fprintf(ctx.out, "IV = %+B\n", &test->iv);
468 fprintf(ctx.out, "%s = %+B\n",
469 ctx.decrypt ? "CIPHERTEXT" : "PLAINTEXT", input);
470 }
471 if (!crypter->set_key(crypter, test->key))
472 {
473 DBG1(DBG_APP, "failed to set key");
474 return FALSE;
475 }
476 for (j = 0; j < 1000; j++)
477 {
478 /* store previous output as it is used as input after next */
479 memcpy(prev.ptr, output->ptr, prev.len);
480 chunk_free(output);
481 if (!do_crypt(crypter, test))
482 {
483 crypter->destroy(crypter);
484 return FALSE;
485 }
486 /* prepare the next IV (our API does not allow incremental calls) */
487 if (ctx.decrypt)
488 {
489 memcpy(test->iv.ptr, input->ptr, test->iv.len);
490 }
491 else
492 {
493 memcpy(test->iv.ptr, output->ptr, test->iv.len);
494 }
495 /* the previous output is the next input */
496 memcpy(input->ptr, prev.ptr, input->len);
497 }
498 fprintf(ctx.out, "%s = %+B\n\n",
499 ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT", output);
500 /* derive key for next round */
501 switch (test->key.len)
502 {
503 case 16:
504 memxor(test->key.ptr, output->ptr, output->len);
505 break;
506 case 24:
507 memxor(test->key.ptr, prev.ptr + 8, 8);
508 memxor(test->key.ptr + 8, output->ptr, output->len);
509 break;
510 case 32:
511 memxor(test->key.ptr, prev.ptr, prev.len);
512 memxor(test->key.ptr + prev.len, output->ptr, output->len);
513 break;
514 }
515 /* the current output is used as IV for the next round */
516 memcpy(test->iv.ptr, output->ptr, test->iv.len);
517 }
518 crypter->destroy(crypter);
519 /* we return FALSE as we print the output ourselves */
520 return FALSE;
521 }
522
523 static bool do_test(test_vector_t *test)
524 {
525 if (ctx.use_gcm)
526 {
527 return do_test_gcm(test);
528 }
529 if (ctx.use_mct)
530 {
531 return do_test_mct(test);
532 }
533 return do_test_cbc(test);
534 }
535
536 static void usage(FILE *out, char *name)
537 {
538 fprintf(out, "Test AES implementation according to the AES Algorithm Validation Suite (AESAVS)\n");
539 fprintf(out, "and the GCM Validation System (GCMVS)\n\n");
540 fprintf(out, "%s [OPTIONS]\n\n", name);
541 fprintf(out, "Options:\n");
542 fprintf(out, " -h, --help print this help.\n");
543 fprintf(out, " -d, --debug=LEVEL set debug level (default 1).\n");
544 fprintf(out, " -m, --mode=MODE mode to test, either CBC or GCM (default CBC).\n");
545 fprintf(out, " -t, --mct run Monte Carlo Test (MCT), only for CBC.\n");
546 fprintf(out, " -x, --decrypt test decryption (not needed for CBC as files contain control directives).\n");
547 fprintf(out, " -i, --in=FILE request file (default STDIN).\n");
548 fprintf(out, " -o, --out=FILE response file (default STDOUT).\n");
549 fprintf(out, "\n");
550 }
551
552 int main(int argc, char *argv[])
553 {
554 test_vector_t test;
555
556 ctx.in = stdin;
557 ctx.out = stdout;
558
559 library_init(NULL, "aes-test");
560 atexit(library_deinit);
561
562 while (true)
563 {
564 struct option long_opts[] = {
565 {"help", no_argument, NULL, 'h' },
566 {"debug", required_argument, NULL, 'd' },
567 {"mode", required_argument, NULL, 'm' },
568 {"mct", no_argument, NULL, 't' },
569 {"decrypt", no_argument, NULL, 'x' },
570 {"in", required_argument, NULL, 'i' },
571 {"out", required_argument, NULL, 'o' },
572 {0,0,0,0 },
573 };
574 switch (getopt_long(argc, argv, "hd:m:txi:o:", long_opts, NULL))
575 {
576 case EOF:
577 break;
578 case 'h':
579 usage(stdout, argv[0]);
580 return 0;
581 case 'd':
582 dbg_default_set_level(atoi(optarg));
583 continue;
584 case 'm':
585 if (strcaseeq(optarg, "GCM"))
586 {
587 ctx.use_gcm = TRUE;
588 }
589 else if (!strcaseeq(optarg, "CBC"))
590 {
591 usage(stderr, argv[0]);
592 return 1;
593 }
594 continue;
595 case 't':
596 ctx.use_mct = TRUE;
597 continue;
598 case 'x':
599 ctx.decrypt = TRUE;
600 continue;
601 case 'i':
602 ctx.in = fopen(optarg, "r");
603 if (!ctx.in)
604 {
605 fprintf(stderr, "failed to open '%s': %s\n", optarg,
606 strerror(errno));
607 usage(stderr, argv[0]);
608 return 1;
609 }
610 continue;
611 case 'o':
612 ctx.out = fopen(optarg, "w");
613 if (!ctx.out)
614 {
615 fprintf(stderr, "failed to open '%s': %s\n", optarg,
616 strerror(errno));
617 usage(stderr, argv[0]);
618 return 1;
619 }
620 continue;
621 default:
622 usage(stderr, argv[0]);
623 return 1;
624 }
625 break;
626 }
627 /* TODO: maybe make plugins configurable */
628 lib->plugins->load(lib->plugins, PLUGINS);
629 lib->plugins->status(lib->plugins, LEVEL_CTRL);
630
631 while (get_next_test_vector(&test))
632 {
633 if (verify_test_vector(&test))
634 {
635 if (do_test(&test))
636 {
637 print_result(&test);
638 }
639 }
640 else
641 {
642 DBG1(DBG_APP, "test vector with missing data encountered");
643 }
644 fprintf(ctx.out, "\n");
645 test_vector_free(&test);
646 }
647
648 if (ctx.in != stdin)
649 {
650 fclose(ctx.in);
651 }
652 if (ctx.out != stdout)
653 {
654 fclose(ctx.out);
655 }
656 return 0;
657 }