Annotation of embedaddon/strongswan/scripts/aes-test.c, revision 1.1
1.1 ! misho 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>