1: /*
2: * Copyright (C) 2013 Tobias Brunner
3: * HSR 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>