1 /* 2 * SPDX-License-Identifier: MIT 3 * 4 * Copyright (c) 2025, Rob Norris <robn@despairlabs.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25 /* 26 * This is a userspace test driver for the ICP. It has two modes: 27 * 28 * "correctness" (-c <testfile>): 29 * Load a file full of test vectors. For each implementation of the named 30 * algorithm, loop over the tests, and run encrypt and decrypt with the 31 * provided parameters and confirm they either do (result=valid) or do not 32 * (result=invalid) succeed. 33 * 34 * "performance" (-p <alg>) 35 * For each implementation of the named algorithm, run 1000 rounds of 36 * encrypt() on a range of power-2 sizes of input data from 2^10 (1K) to 37 * 2^19 (512K). 38 */ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <errno.h> 44 #include <getopt.h> 45 46 #include <sys/crypto/icp.h> 47 #include <sys/crypto/api.h> 48 49 /* for zfs_nicenum, zfs_nicebytes */ 50 #include <libzutil.h> 51 52 /* ========== */ 53 54 /* types and data for both modes */ 55 56 /* valid test algorithms */ 57 typedef enum { 58 ALG_NONE, 59 ALG_AES_GCM, 60 ALG_AES_CCM, 61 } crypto_test_alg_t; 62 63 /* 64 * Generally the ICP expects zero-length data to still require a valid 65 * (non-NULL) pointer, even though it will never read from it. This is a 66 * convenient valid item for tjat case. 67 */ 68 static uint8_t val_empty[1] = {0}; 69 70 /* Strings for error returns */ 71 static const char *crypto_errstr[] = { 72 [CRYPTO_SUCCESS] = "CRYPTO_SUCCESS", 73 [CRYPTO_HOST_MEMORY] = "CRYPTO_HOST_MEMORY", 74 [CRYPTO_FAILED] = "CRYPTO_FAILED", 75 [CRYPTO_ARGUMENTS_BAD] = "CRYPTO_ARGUMENTS_BAD", 76 [CRYPTO_DATA_LEN_RANGE] = "CRYPTO_DATA_LEN_RANGE", 77 [CRYPTO_ENCRYPTED_DATA_LEN_RANGE] = "CRYPTO_ENCRYPTED_DATA_LEN_RANGE", 78 [CRYPTO_KEY_SIZE_RANGE] = "CRYPTO_KEY_SIZE_RANGE", 79 [CRYPTO_KEY_TYPE_INCONSISTENT] = "CRYPTO_KEY_TYPE_INCONSISTENT", 80 [CRYPTO_MECHANISM_INVALID] = "CRYPTO_MECHANISM_INVALID", 81 [CRYPTO_MECHANISM_PARAM_INVALID] = "CRYPTO_MECHANISM_PARAM_INVALID", 82 [CRYPTO_SIGNATURE_INVALID] = "CRYPTO_SIGNATURE_INVALID", 83 [CRYPTO_BUFFER_TOO_SMALL] = "CRYPTO_BUFFER_TOO_SMALL", 84 [CRYPTO_NOT_SUPPORTED] = "CRYPTO_NOT_SUPPORTED", 85 [CRYPTO_INVALID_CONTEXT] = "CRYPTO_INVALID_CONTEXT", 86 [CRYPTO_INVALID_MAC] = "CRYPTO_INVALID_MAC", 87 [CRYPTO_MECH_NOT_SUPPORTED] = "CRYPTO_MECH_NOT_SUPPORTED", 88 [CRYPTO_INVALID_PROVIDER_ID] = "CRYPTO_INVALID_PROVIDER_ID", 89 [CRYPTO_BUSY] = "CRYPTO_BUSY", 90 [CRYPTO_UNKNOWN_PROVIDER] = "CRYPTO_UNKNOWN_PROVIDER", 91 }; 92 93 /* what to output; driven by -v switch */ 94 typedef enum { 95 OUT_SUMMARY, 96 OUT_FAIL, 97 OUT_ALL, 98 } crypto_test_outmode_t; 99 100 101 /* ========== */ 102 103 /* types and data for correctness tests */ 104 105 /* most ICP inputs are separate val & len */ 106 typedef struct { 107 uint8_t *val; 108 size_t len; 109 } crypto_test_val_t; 110 111 /* tests can be expected to pass (valid) or expected to fail (invalid) */ 112 typedef enum { 113 RS_NONE = 0, 114 RS_VALID, 115 RS_INVALID, 116 } crypto_test_result_t; 117 118 /* a single test, loaded from the test file */ 119 typedef struct crypto_test crypto_test_t; 120 struct crypto_test { 121 crypto_test_t *next; /* ptr to next test */ 122 char *fileloc; /* file:line of test in file */ 123 crypto_test_alg_t alg; /* alg, for convenience */ 124 125 /* id, comment and flags are for output */ 126 uint64_t id; 127 char *comment; 128 char *flags; 129 130 /* 131 * raw test params. these are hex strings in the test file, which 132 * we convert on load. 133 */ 134 crypto_test_val_t iv; 135 crypto_test_val_t key; 136 crypto_test_val_t msg; 137 crypto_test_val_t ct; 138 crypto_test_val_t aad; 139 crypto_test_val_t tag; 140 141 /* expected result */ 142 crypto_test_result_t result; 143 }; 144 145 /* ========== */ 146 147 /* test file loader */ 148 149 /* 150 * helper; split a 'key: value\n' line into separate key and value. original 151 * line is modified; \0 will be inserted at end of key and end of value. 152 */ 153 static boolean_t 154 split_kv(char *line, char **kp, char **vp) 155 { 156 char *c = strstr(line, ":"); 157 if (c == NULL) 158 return (B_FALSE); 159 160 161 *c++ = '\0'; 162 while (*c == ' ') 163 c++; 164 165 char *v = c; 166 c = strchr(v, '\n'); 167 if (c != NULL) { 168 *c++ = '\0'; 169 if (*c != '\0') 170 return (B_FALSE); 171 } 172 173 *kp = line; 174 *vp = v; 175 return (B_TRUE); 176 } 177 178 /* 179 * helper; parse decimal number to uint64 180 */ 181 static boolean_t 182 parse_num(char *v, uint64_t *np) 183 { 184 char *c = NULL; 185 errno = 0; 186 uint64_t n = strtoull(v, &c, 10); 187 if (*v == '\0' || *c != '\0' || errno != 0 || 188 n >= UINT32_MAX || n == 0) 189 return (B_FALSE); 190 *np = n; 191 return (B_TRUE); 192 } 193 194 /* 195 * load tests from the test file. returns a linked list of tests, and the 196 * test algorithm in *algp. 197 */ 198 static crypto_test_t * 199 load_tests(const char *filepath, crypto_test_alg_t *algp) 200 { 201 crypto_test_t *tests = NULL, *tail = NULL; 202 char *buf = NULL; 203 size_t buflen = 0; 204 FILE *fh = NULL; 205 206 if ((fh = fopen(filepath, "r")) == NULL) { 207 fprintf(stderr, "E: couldn't open %s: %s\n", 208 filepath, strerror(errno)); 209 goto err; 210 } 211 212 /* extract the filename part from the path, for nicer output */ 213 const char *filename = &filepath[strlen(filepath)-1]; 214 while (filename != filepath) { 215 if (*filename == '/') { 216 filename++; 217 break; 218 } 219 filename--; 220 } 221 222 int lineno = 0; 223 224 crypto_test_alg_t alg = ALG_NONE; 225 uint64_t ntests = 0; 226 crypto_test_t *test = NULL; 227 uint64_t ncommitted = 0; 228 229 char *k, *v; 230 231 ssize_t nread; 232 while ((nread = getline(&buf, &buflen, fh)) != -1 || errno == 0) { 233 /* track line number for output and for test->fileloc */ 234 lineno++; 235 236 if (nread < 2 && test != NULL) { 237 /* 238 * blank line or end of file; close out any test in 239 * progress and commit it. 240 */ 241 if (test->id == 0 || 242 test->iv.val == NULL || 243 test->key.val == NULL || 244 test->msg.val == NULL || 245 test->ct.val == NULL || 246 test->aad.val == NULL || 247 test->tag.val == NULL || 248 test->result == RS_NONE) { 249 fprintf(stderr, "E: incomplete test [%s:%d]\n", 250 filename, lineno); 251 goto err; 252 } 253 254 /* commit the test, ie, add it to the list */ 255 if (tail == NULL) 256 tests = tail = test; 257 else { 258 tail->next = test; 259 tail = test; 260 } 261 ncommitted++; 262 263 test = NULL; 264 } 265 266 if (nread == -1) 267 /* end of file and tests finished, done */ 268 break; 269 270 if (nread < 2 && ncommitted == 0) { 271 /* 272 * blank line after header, make sure the header is 273 * complete. 274 */ 275 if (alg == ALG_NONE || ntests == 0) { 276 fprintf(stderr, "E: incomplete header " 277 "[%s:%d]\n", filename, lineno); 278 goto err; 279 } 280 } 281 282 if (nread < 2) { 283 /* 284 * blank line and the header is committed, and no 285 * current test, so the next test will start on the 286 * next line. 287 */ 288 test = calloc(1, sizeof (crypto_test_t)); 289 int len = strlen(filename) + 10; 290 test->fileloc = calloc(len, 1); 291 snprintf(test->fileloc, len, "%s:%d", 292 filename, lineno+1); 293 test->alg = alg; 294 continue; 295 } 296 297 /* 298 * must be a k:v line. if there is a current test, then this 299 * line is part of it, otherwise it's a header line 300 */ 301 if (!split_kv(buf, &k, &v)) { 302 fprintf(stderr, "E: malformed line [%s:%d]\n", 303 filename, lineno); 304 goto err; 305 } 306 307 if (test == NULL) { 308 /* no current test, so a header key */ 309 310 /* 311 * typical header: 312 * 313 * algorithm: AES-GCM 314 * tests: 316 315 */ 316 if (strcmp(k, "algorithm") == 0) { 317 if (alg != ALG_NONE) 318 goto err_dup_key; 319 if (strcmp(v, "AES-GCM") == 0) 320 alg = ALG_AES_GCM; 321 else if (strcmp(v, "AES-CCM") == 0) 322 alg = ALG_AES_CCM; 323 else { 324 fprintf(stderr, 325 "E: unknown algorithm [%s:%d]: " 326 "%s\n", filename, lineno, v); 327 goto err; 328 } 329 } else if (strcmp(k, "tests") == 0) { 330 if (ntests > 0) 331 goto err_dup_key; 332 if (!parse_num(v, &ntests)) { 333 fprintf(stderr, 334 "E: invalid number of tests " 335 "[%s:%d]: %s\n", filename, lineno, 336 v); 337 goto err; 338 } 339 } else { 340 fprintf(stderr, "E: unknown header key " 341 "[%s:%d]: %s\n", filename, lineno, k); 342 goto err; 343 } 344 continue; 345 } 346 347 /* test key */ 348 349 /* 350 * typical test: 351 * 352 * id: 48 353 * comment: Flipped bit 63 in tag 354 * flags: ModifiedTag 355 * iv: 505152535455565758595a5b 356 * key: 000102030405060708090a0b0c0d0e0f 357 * msg: 202122232425262728292a2b2c2d2e2f 358 * ct: eb156d081ed6b6b55f4612f021d87b39 359 * aad: 360 * tag: d8847dbc326a066988c77ad3863e6083 361 * result: invalid 362 */ 363 if (strcmp(k, "id") == 0) { 364 if (test->id > 0) 365 goto err_dup_key; 366 if (!parse_num(v, &test->id)) { 367 fprintf(stderr, 368 "E: invalid test id [%s:%d]: %s\n", 369 filename, lineno, v); 370 goto err; 371 } 372 continue; 373 } else if (strcmp(k, "comment") == 0) { 374 if (test->comment != NULL) 375 goto err_dup_key; 376 test->comment = strdup(v); 377 continue; 378 } else if (strcmp(k, "flags") == 0) { 379 if (test->flags != NULL) 380 goto err_dup_key; 381 test->flags = strdup(v); 382 continue; 383 } else if (strcmp(k, "result") == 0) { 384 if (test->result != RS_NONE) 385 goto err_dup_key; 386 if (strcmp(v, "valid") == 0) 387 test->result = RS_VALID; 388 else if (strcmp(v, "invalid") == 0) 389 test->result = RS_INVALID; 390 else { 391 fprintf(stderr, 392 "E: unknown test result [%s:%d]: %s\n", 393 filename, lineno, v); 394 goto err; 395 } 396 continue; 397 } 398 399 /* 400 * for the test param keys, we set up a pointer to the right 401 * field in the test struct, and then work through that 402 * pointer. 403 */ 404 crypto_test_val_t *vp = NULL; 405 if (strcmp(buf, "iv") == 0) 406 vp = &test->iv; 407 else if (strcmp(buf, "key") == 0) 408 vp = &test->key; 409 else if (strcmp(buf, "msg") == 0) 410 vp = &test->msg; 411 else if (strcmp(buf, "ct") == 0) 412 vp = &test->ct; 413 else if (strcmp(buf, "aad") == 0) 414 vp = &test->aad; 415 else if (strcmp(buf, "tag") == 0) 416 vp = &test->tag; 417 else { 418 fprintf(stderr, "E: unknown key [%s:%d]: %s\n", 419 filename, lineno, buf); 420 goto err; 421 } 422 423 if (vp->val != NULL) 424 goto err_dup_key; 425 426 /* sanity; these are hex bytes so must be two chars per byte. */ 427 size_t vlen = strlen(v); 428 if ((vlen & 1) == 1) { 429 fprintf(stderr, "E: value length not even " 430 "[%s:%d]: %s\n", filename, lineno, buf); 431 goto err; 432 } 433 434 /* 435 * zero-length params are allowed, but ICP requires a non-NULL 436 * value pointer, so we give it one and also use that as 437 * a marker for us to know that we've filled this value. 438 */ 439 if (vlen == 0) { 440 vp->val = val_empty; 441 continue; 442 } 443 444 /* 445 * convert incoming value from hex to raw. allocate space 446 * half as long as the length, then loop the chars and 447 * convert from ascii to 4-bit values, shifting or or-ing 448 * as appropriate. 449 */ 450 vp->len = vlen/2; 451 vp->val = calloc(vp->len, 1); 452 453 for (int i = 0; i < vlen; i++) { 454 char c = v[i]; 455 if (!((c >= '0' && c <= '9') || 456 (c >= 'a' && c <= 'f'))) { 457 fprintf(stderr, "E: invalid hex char " 458 "[%s:%d]: %c\n", filename, lineno, c); 459 goto err; 460 } 461 462 uint8_t n = ((c <= '9') ? (c-0x30) : (c-0x57)) & 0xf; 463 if ((i & 1) == 0) 464 vp->val[i/2] = n << 4; 465 else 466 vp->val[i/2] |= n; 467 } 468 } 469 470 if (errno != 0) { 471 fprintf(stderr, "E: couldn't read %s: %s\n", 472 filepath, strerror(errno)); 473 goto err; 474 } 475 476 free(buf); 477 fclose(fh); 478 479 if (tests == NULL) 480 fprintf(stderr, "E: no tests in %s\n", filepath); 481 482 *algp = alg; 483 return (tests); 484 485 /* 486 * jump target for duplicate key error. this is so common that it's easier 487 * to just have a single error point. 488 */ 489 err_dup_key: 490 fprintf(stderr, "E: duplicate key [%s:%d]: %s\n", filename, lineno, k); 491 492 err: 493 if (buf != NULL) 494 free(buf); 495 if (fh != NULL) 496 fclose(fh); 497 498 /* 499 * XXX we should probably free all the tests here, but the test file 500 * is generated and this is a one-shot program, so it's really 501 * not worth the effort today 502 */ 503 504 return (NULL); 505 } 506 507 /* ========== */ 508 509 /* ICP algorithm implementation selection */ 510 511 /* 512 * It's currently not really possible to query the ICP for which 513 * implementations it supports. Also, not all GCM implementations work 514 * with all AES implementations. For now, we keep a hardcoded list of 515 * valid combinations. 516 */ 517 static const char *aes_impl[] = { 518 "generic", 519 "x86_64", 520 "aesni", 521 }; 522 523 static const char *aes_gcm_impl[][2] = { 524 { "generic", "generic" }, 525 { "x86_64", "generic" }, 526 { "aesni", "generic" }, 527 { "generic", "pclmulqdq" }, 528 { "x86_64", "pclmulqdq" }, 529 { "aesni", "pclmulqdq" }, 530 { "x86_64", "avx" }, 531 { "aesni", "avx" }, 532 }; 533 534 /* signature of function to call after setting implementation params */ 535 typedef void (*alg_cb_t)(const char *alginfo, void *arg); 536 537 /* loop over each AES-CCM implementation */ 538 static void 539 foreach_aes_ccm(alg_cb_t cb, void *arg, crypto_test_outmode_t outmode) 540 { 541 char alginfo[64]; 542 543 for (int i = 0; i < ARRAY_SIZE(aes_impl); i++) { 544 snprintf(alginfo, sizeof (alginfo), "AES-CCM [%s]", 545 aes_impl[i]); 546 547 int err = -aes_impl_set(aes_impl[i]); 548 if (err != 0 && outmode != OUT_SUMMARY) 549 printf("W: %s couldn't enable AES impl '%s': %s\n", 550 alginfo, aes_impl[i], strerror(err)); 551 552 cb(alginfo, (err == 0) ? arg : NULL); 553 } 554 } 555 556 /* loop over each AES-GCM implementation */ 557 static void 558 foreach_aes_gcm(alg_cb_t cb, void *arg, crypto_test_outmode_t outmode) 559 { 560 char alginfo[64]; 561 562 for (int i = 0; i < ARRAY_SIZE(aes_gcm_impl); i++) { 563 const char *aes_impl = aes_gcm_impl[i][0]; 564 const char *gcm_impl = aes_gcm_impl[i][1]; 565 566 snprintf(alginfo, sizeof (alginfo), "AES-GCM [%s+%s]", 567 aes_impl, gcm_impl); 568 569 int err = -aes_impl_set(aes_impl); 570 if (err != 0 && outmode != OUT_SUMMARY) 571 printf("W: %s couldn't enable AES impl '%s': %s\n", 572 alginfo, aes_impl, strerror(err)); 573 574 if (err == 0) { 575 err = -gcm_impl_set(gcm_impl); 576 if (err != 0 && outmode != OUT_SUMMARY) { 577 printf("W: %s couldn't enable " 578 "GCM impl '%s': %s\n", 579 alginfo, gcm_impl, strerror(err)); 580 } 581 } 582 583 cb(alginfo, (err == 0) ? arg : NULL); 584 } 585 } 586 587 /* ========== */ 588 589 /* ICP lowlevel drivers */ 590 591 /* 592 * initialise the mechanism (algorithm description) with the wanted parameters 593 * for the next operation. 594 * 595 * mech must be allocated and mech->cm_params point to space large enough 596 * to hold the parameters for the given algorithm. 597 * 598 * decrypt is true if setting up for decryption, false for encryption. 599 */ 600 static void 601 init_mech(crypto_mechanism_t *mech, crypto_test_alg_t alg, 602 uint8_t *iv, size_t ivlen, 603 uint8_t *aad, size_t aadlen, 604 size_t msglen, size_t taglen, 605 boolean_t decrypt) 606 { 607 switch (alg) { 608 case ALG_AES_GCM: { 609 mech->cm_type = crypto_mech2id(SUN_CKM_AES_GCM); 610 mech->cm_param_len = sizeof (CK_AES_GCM_PARAMS); 611 CK_AES_GCM_PARAMS *p = (CK_AES_GCM_PARAMS *)mech->cm_param; 612 p->pIv = (uchar_t *)iv; 613 p->ulIvLen = ivlen; 614 p->ulIvBits = ivlen << 3; 615 p->pAAD = aad; 616 p->ulAADLen = aadlen; 617 p->ulTagBits = taglen << 3; 618 break; 619 } 620 case ALG_AES_CCM: { 621 mech->cm_type = crypto_mech2id(SUN_CKM_AES_CCM); 622 mech->cm_param_len = sizeof (CK_AES_CCM_PARAMS); 623 CK_AES_CCM_PARAMS *p = (CK_AES_CCM_PARAMS *)mech->cm_param; 624 p->nonce = iv; 625 p->ulNonceSize = ivlen; 626 p->authData = aad; 627 p->ulAuthDataSize = aadlen; 628 p->ulMACSize = taglen; 629 /* 630 * ICP CCM needs the MAC len in the data size for decrypt, 631 * even if the buffer isn't that big. 632 */ 633 p->ulDataSize = msglen + (decrypt ? taglen : 0); 634 break; 635 } 636 default: 637 abort(); 638 } 639 } 640 641 /* 642 * call crypto_encrypt() with the given inputs. 643 * 644 * mech: previously initialised by init_mech 645 * key, keylen: raw data and length of key 646 * msg, msglen: raw data and length of message 647 * out, outlen: buffer to write output to (min msglen+taglen) 648 * usecp: if not NULL, recieves microseconds in crypto_encrypt() 649 */ 650 static int 651 encrypt_one(crypto_mechanism_t *mech, 652 const uint8_t *key, size_t keylen, 653 const uint8_t *msg, size_t msglen, 654 uint8_t *out, size_t outlen, 655 uint64_t *usecp) 656 { 657 crypto_key_t k = { 658 .ck_data = (uint8_t *)key, 659 .ck_length = keylen << 3, 660 }; 661 662 crypto_data_t i = { 663 .cd_format = CRYPTO_DATA_RAW, 664 .cd_offset = 0, 665 .cd_length = msglen, 666 .cd_raw = { 667 .iov_base = (char *)msg, 668 .iov_len = msglen, 669 }, 670 }; 671 672 crypto_data_t o = { 673 .cd_format = CRYPTO_DATA_RAW, 674 .cd_offset = 0, 675 .cd_length = outlen, 676 .cd_raw = { 677 .iov_base = (char *)out, 678 .iov_len = outlen, 679 }, 680 }; 681 682 struct timeval start, end, diff; 683 if (usecp != NULL) 684 gettimeofday(&start, NULL); 685 686 int rv = crypto_encrypt(mech, &i, &k, NULL, &o); 687 688 if (usecp != NULL) { 689 gettimeofday(&end, NULL); 690 timersub(&end, &start, &diff); 691 *usecp = 692 ((uint64_t)diff.tv_sec) * 1000000 + (uint64_t)diff.tv_usec; 693 } 694 695 return (rv); 696 } 697 698 /* 699 * call crypto_decrypt() with the given inputs. 700 * 701 * mech: previously initialised by init_mech 702 * key, keylen: raw data and length of key 703 * ct, ctlen: raw data and length of ciphertext 704 * tag, taglen: raw data and length of tag (MAC) 705 * out, outlen: buffer to write output to (min ctlen) 706 * usecp: if not NULL, recieves microseconds in crypto_decrypt() 707 */ 708 static int 709 decrypt_one(crypto_mechanism_t *mech, 710 const uint8_t *key, size_t keylen, 711 const uint8_t *ct, size_t ctlen, 712 const uint8_t *tag, size_t taglen, 713 uint8_t *out, size_t outlen, 714 uint64_t *usecp) 715 { 716 uint8_t inbuf[1024]; 717 718 crypto_key_t k = { 719 .ck_data = (uint8_t *)key, 720 .ck_length = keylen << 3, 721 }; 722 723 memcpy(inbuf, ct, ctlen); 724 memcpy(inbuf + ctlen, tag, taglen); 725 crypto_data_t i = { 726 .cd_format = CRYPTO_DATA_RAW, 727 .cd_offset = 0, 728 .cd_length = ctlen + taglen, 729 .cd_raw = { 730 .iov_base = (char *)inbuf, 731 .iov_len = ctlen + taglen, 732 }, 733 }; 734 735 crypto_data_t o = { 736 .cd_format = CRYPTO_DATA_RAW, 737 .cd_offset = 0, 738 .cd_length = outlen, 739 .cd_raw = { 740 .iov_base = (char *)out, 741 .iov_len = outlen 742 }, 743 }; 744 745 struct timeval start, end, diff; 746 if (usecp != NULL) 747 gettimeofday(&start, NULL); 748 749 int rv = crypto_decrypt(mech, &i, &k, NULL, &o); 750 751 if (usecp != NULL) { 752 gettimeofday(&end, NULL); 753 timersub(&start, &end, &diff); 754 *usecp = 755 ((uint64_t)diff.tv_sec) * 1000000 + (uint64_t)diff.tv_usec; 756 } 757 758 return (rv); 759 } 760 761 /* ========== */ 762 763 /* correctness tests */ 764 765 /* 766 * helper; dump the provided data as hex, with a string prefix 767 */ 768 static void 769 hexdump(const char *str, const uint8_t *src, uint_t len) 770 { 771 printf("%12s:", str); 772 int i = 0; 773 while (i < len) { 774 if (i % 4 == 0) 775 printf(" "); 776 printf("%02x", src[i]); 777 i++; 778 if (i % 16 == 0 && i < len) { 779 printf("\n"); 780 if (i < len) 781 printf(" "); 782 } 783 } 784 printf("\n"); 785 } 786 787 /* 788 * analyse test result and on failure, print useful output for debugging. 789 * 790 * test: the test we ran 791 * encrypt_rv: return value from crypto_encrypt() 792 * encrypt_buf: the output buffer from crypto_encrypt() 793 * decrypt_rv: return value from crypto_decrypt() 794 * decrypt_buf: the output buffer from crypto_decrypt() 795 * outmode: output mode (summary, fail, all) 796 */ 797 static boolean_t 798 test_result(const crypto_test_t *test, int encrypt_rv, uint8_t *encrypt_buf, 799 int decrypt_rv, uint8_t *decrypt_buf, crypto_test_outmode_t outmode) 800 { 801 boolean_t ct_match = B_FALSE, tag_match = B_FALSE, msg_match = B_FALSE; 802 boolean_t encrypt_pass = B_FALSE, decrypt_pass = B_FALSE; 803 boolean_t pass = B_FALSE; 804 805 /* check if the encrypt output matches the expected ciphertext */ 806 if (memcmp(encrypt_buf, test->ct.val, test->msg.len) == 0) 807 ct_match = B_TRUE; 808 809 /* 810 * check if the tag at the end of the encrypt output matches the 811 * expected tag 812 */ 813 if (memcmp(encrypt_buf + test->msg.len, test->tag.val, 814 test->tag.len) == 0) 815 tag_match = B_TRUE; 816 817 /* check if the decrypt output matches the expected plaintext */ 818 if (memcmp(decrypt_buf, test->msg.val, test->msg.len) == 0) 819 msg_match = B_TRUE; 820 821 if (test->result == RS_VALID) { 822 /* 823 * a "valid" test is where the params describe an 824 * encrypt/decrypt cycle that should succeed. we consider 825 * these to have passed the test if crypto_encrypt() and 826 * crypto_decrypt() return success, and the output data 827 * matches the expected values from the test params. 828 */ 829 if (encrypt_rv == CRYPTO_SUCCESS) { 830 if (ct_match && tag_match) 831 encrypt_pass = B_TRUE; 832 } 833 if (decrypt_rv == CRYPTO_SUCCESS) { 834 if (msg_match) 835 decrypt_pass = B_TRUE; 836 } 837 } else { 838 /* 839 * an "invalid" test is where the params describe an 840 * encrypt/decrypt cycle that should _not_ succeed. 841 * 842 * for decrypt, we only need to check the result from 843 * crypto_decrypt(), because decrypt checks the the tag (MAC) 844 * as part of its operation. 845 * 846 * for encrypt, the tag (MAC) is an output of the encryption 847 * function, so if encryption succeeds, we have to check that 848 * the returned tag matches the expected tag. 849 */ 850 if (encrypt_rv != CRYPTO_SUCCESS || !tag_match) 851 encrypt_pass = B_TRUE; 852 if (decrypt_rv != CRYPTO_SUCCESS) 853 decrypt_pass = B_TRUE; 854 } 855 856 /* the test as a whole passed if both encrypt and decrypt passed */ 857 pass = (encrypt_pass && decrypt_pass); 858 859 /* if the test passed we may not have to output anything */ 860 if (outmode == OUT_SUMMARY || (outmode == OUT_FAIL && pass)) 861 return (pass); 862 863 /* print summary of test result */ 864 printf("%s[%lu]: encrypt=%s decrypt=%s\n", test->fileloc, test->id, 865 encrypt_pass ? "PASS" : "FAIL", 866 decrypt_pass ? "PASS" : "FAIL"); 867 868 if (!pass) { 869 /* 870 * if the test didn't pass, print any comment or flags field 871 * from the test params, which if present can help 872 * understanding what the ICP did wrong 873 */ 874 if (test->comment != NULL) 875 printf(" comment: %s\n", test->comment); 876 if (test->flags != NULL) 877 printf(" flags: %s\n", test->flags); 878 } 879 880 if (!encrypt_pass) { 881 /* encrypt failed */ 882 883 /* print return value from crypto_encrypt() */ 884 printf(" encrypt rv = 0x%02x [%s]\n", encrypt_rv, 885 crypto_errstr[encrypt_rv] ? 886 crypto_errstr[encrypt_rv] : "???"); 887 888 /* print mismatched ciphertext */ 889 if (!ct_match) { 890 printf(" ciphertexts don't match:\n"); 891 hexdump("got", encrypt_buf, test->msg.len); 892 hexdump("expected", test->ct.val, test->msg.len); 893 } 894 895 /* print mistmatched tag (MAC) */ 896 if (!tag_match) { 897 printf(" tags don't match:\n"); 898 hexdump("got", encrypt_buf + test->msg.len, 899 test->tag.len); 900 hexdump("expected", test->tag.val, test->tag.len); 901 } 902 } 903 904 if (!decrypt_pass) { 905 /* decrypt failed */ 906 907 /* print return value from crypto_decrypt() */ 908 printf(" decrypt rv = 0x%02x [%s]\n", decrypt_rv, 909 crypto_errstr[decrypt_rv] ? 910 crypto_errstr[decrypt_rv] : "???"); 911 912 /* print mismatched plaintext */ 913 if (!msg_match) { 914 printf(" plaintexts don't match:\n"); 915 hexdump("got", decrypt_buf, test->msg.len); 916 hexdump("expected", test->msg.val, test->msg.len); 917 } 918 } 919 920 if (!pass) 921 printf("\n"); 922 923 return (pass); 924 } 925 926 /* 927 * run the given list of tests. 928 * 929 * alginfo: a prefix for the test summary, showing the ICP algo implementation 930 * in use for this run. 931 * tests: first test in test list 932 * outmode: output mode, passed to test_result() 933 */ 934 static int 935 run_tests(const char *alginfo, const crypto_test_t *tests, 936 crypto_test_outmode_t outmode) 937 { 938 int ntests = 0, npass = 0; 939 940 /* 941 * allocate space for the mechanism description, and alg-specific 942 * params, and hook them up. 943 */ 944 crypto_mechanism_t mech = {}; 945 union { 946 CK_AES_GCM_PARAMS gcm; 947 CK_AES_CCM_PARAMS ccm; 948 } params = {}; 949 mech.cm_param = (caddr_t)¶ms; 950 951 /* space for encrypt/decrypt output */ 952 uint8_t encrypt_buf[1024]; 953 uint8_t decrypt_buf[1024]; 954 955 for (const crypto_test_t *test = tests; test != NULL; 956 test = test->next) { 957 ntests++; 958 959 /* setup mechanism description for encrypt, then encrypt */ 960 init_mech(&mech, test->alg, test->iv.val, test->iv.len, 961 test->aad.val, test->aad.len, test->msg.len, test->tag.len, 962 B_FALSE); 963 int encrypt_rv = encrypt_one(&mech, 964 test->key.val, test->key.len, 965 test->msg.val, test->msg.len, 966 encrypt_buf, test->msg.len + test->tag.len, NULL); 967 968 /* setup mechanism description for decrypt, then decrypt */ 969 init_mech(&mech, test->alg, test->iv.val, test->iv.len, 970 test->aad.val, test->aad.len, test->msg.len, test->tag.len, 971 B_TRUE); 972 int decrypt_rv = decrypt_one(&mech, 973 test->key.val, test->key.len, 974 test->ct.val, test->ct.len, 975 test->tag.val, test->tag.len, 976 decrypt_buf, test->ct.len, NULL); 977 978 /* consider results and if it passed, count it */ 979 if (test_result(test, encrypt_rv, encrypt_buf, 980 decrypt_rv, decrypt_buf, outmode)) 981 npass++; 982 } 983 984 printf("%s: tests=%d: passed=%d failed=%d\n", 985 alginfo, ntests, npass, ntests-npass); 986 987 return (ntests != npass); 988 } 989 990 /* args for run_test_alg_cb */ 991 typedef struct { 992 crypto_test_t *tests; 993 crypto_test_outmode_t outmode; 994 int failed; 995 } run_test_alg_args_t; 996 997 /* per-alg-impl function for correctness test runs */ 998 static void 999 run_test_alg_cb(const char *alginfo, void *arg) 1000 { 1001 if (arg == NULL) { 1002 printf("%s: [not supported on this platform]\n", alginfo); 1003 return; 1004 } 1005 run_test_alg_args_t *args = arg; 1006 args->failed += run_tests(alginfo, args->tests, args->outmode); 1007 } 1008 1009 /* main function for correctness tests */ 1010 static int 1011 runtests_main(const char *filename, crypto_test_outmode_t outmode) 1012 { 1013 crypto_test_alg_t alg = ALG_NONE; 1014 crypto_test_t *tests = load_tests(filename, &alg); 1015 if (tests == NULL) 1016 return (1); 1017 1018 icp_init(); 1019 1020 run_test_alg_args_t args = { 1021 .tests = tests, 1022 .outmode = outmode, 1023 .failed = 0, 1024 }; 1025 1026 switch (alg) { 1027 case ALG_AES_CCM: 1028 foreach_aes_ccm(run_test_alg_cb, &args, outmode); 1029 break; 1030 case ALG_AES_GCM: 1031 foreach_aes_gcm(run_test_alg_cb, &args, outmode); 1032 break; 1033 default: 1034 abort(); 1035 } 1036 1037 icp_fini(); 1038 1039 return (args.failed); 1040 } 1041 1042 /* ========== */ 1043 1044 /* performance tests */ 1045 1046 /* helper; fill the given buffer with random data */ 1047 static int 1048 fill_random(uint8_t *v, size_t sz) 1049 { 1050 int fd = open("/dev/urandom", O_RDONLY); 1051 if (fd < 0) 1052 return (errno); 1053 1054 while (sz > 0) { 1055 ssize_t r = read(fd, v, sz); 1056 if (r < 0) { 1057 close(fd); 1058 return (errno); 1059 } 1060 v += r; 1061 sz -= r; 1062 } 1063 1064 close(fd); 1065 1066 return (0); 1067 } 1068 1069 /* args for perf_alg_cb */ 1070 typedef struct { 1071 crypto_test_alg_t alg; 1072 uint8_t *msg; 1073 uint8_t *out; 1074 uint8_t key[32]; 1075 uint8_t iv[12]; 1076 } perf_alg_args_t; 1077 1078 #define PERF_MSG_SHIFT_MIN (10) /* min test size 2^10 == 1K */ 1079 #define PERF_MSG_SHIFT_MAX (19) /* max test size 2^19 == 512K */ 1080 #define PERF_ROUNDS (1000) /* 1000 rounds per test */ 1081 1082 /* per-alg-impl function for performance test runs */ 1083 static void 1084 perf_alg_cb(const char *alginfo, void *arg) 1085 { 1086 char buf[10]; 1087 printf("%-28s", alginfo); 1088 1089 if (arg == NULL) { 1090 printf("[not supported on this platform]\n"); 1091 return; 1092 } 1093 1094 perf_alg_args_t *args = arg; 1095 1096 /* space for mechanism description */ 1097 crypto_mechanism_t mech = {}; 1098 union { 1099 CK_AES_GCM_PARAMS gcm; 1100 CK_AES_CCM_PARAMS ccm; 1101 } params = {}; 1102 mech.cm_param = (caddr_t)¶ms; 1103 1104 /* loop for each power-2 input size */ 1105 for (int i = PERF_MSG_SHIFT_MIN; i <= PERF_MSG_SHIFT_MAX; i++) { 1106 /* size of input */ 1107 size_t sz = 1<<i; 1108 1109 /* initialise mechanism */ 1110 init_mech(&mech, args->alg, args->iv, sizeof (args->iv), 1111 val_empty, 0, sz, 16, B_FALSE); 1112 1113 /* run N rounds and accumulate total time */ 1114 uint64_t total = 0; 1115 for (int round = 0; round < PERF_ROUNDS; round++) { 1116 uint64_t usec; 1117 encrypt_one(&mech, args->key, sizeof (args->key), 1118 args->msg, sz, args->out, sz+16, &usec); 1119 total += usec; 1120 } 1121 1122 /* 1123 * print avg time per round. zfs_nicetime expects nanoseconds, 1124 * so we multiply first 1125 */ 1126 zfs_nicetime((total*1000)/PERF_ROUNDS, buf, sizeof (buf)); 1127 printf(" %5s", buf); 1128 } 1129 1130 printf("\n"); 1131 } 1132 1133 /* main function for performance tests */ 1134 static int 1135 perf_main(const char *algname, crypto_test_outmode_t outmode) 1136 { 1137 perf_alg_args_t args; 1138 1139 if (strcmp(algname, "AES-CCM") == 0) 1140 args.alg = ALG_AES_CCM; 1141 else if (strcmp(algname, "AES-GCM") == 0) 1142 args.alg = ALG_AES_GCM; 1143 else { 1144 fprintf(stderr, "E: unknown algorithm: %s\n", algname); 1145 return (1); 1146 } 1147 1148 /* 1149 * test runs are often slow, but the very first ones won't be. by 1150 * disabling buffering, we can display results immediately, and 1151 * the user quickly gets an idea of what to expect 1152 */ 1153 setvbuf(stdout, NULL, _IONBF, 0); 1154 1155 /* allocate random data for encrypt input */ 1156 size_t maxsz = (1<<PERF_MSG_SHIFT_MAX); 1157 args.msg = malloc(maxsz); 1158 VERIFY0(fill_random(args.msg, maxsz)); 1159 1160 /* allocate space for output, +16 bytes for tag */ 1161 args.out = malloc(maxsz+16); 1162 1163 /* fill key and iv */ 1164 VERIFY0(fill_random(args.key, sizeof (args.key))); 1165 VERIFY0(fill_random(args.iv, sizeof (args.iv))); 1166 1167 icp_init(); 1168 1169 /* print header */ 1170 char buf[10]; 1171 printf("avg encrypt (%4d rounds) ", PERF_ROUNDS); 1172 for (int i = PERF_MSG_SHIFT_MIN; i <= PERF_MSG_SHIFT_MAX; i++) { 1173 zfs_nicebytes(1<<i, buf, sizeof (buf)); 1174 printf(" %5s", buf); 1175 } 1176 printf("\n"); 1177 1178 /* loop over all implementations of the wanted algorithm */ 1179 switch (args.alg) { 1180 case ALG_AES_CCM: 1181 foreach_aes_ccm(perf_alg_cb, &args, outmode); 1182 break; 1183 case ALG_AES_GCM: 1184 foreach_aes_gcm(perf_alg_cb, &args, outmode); 1185 break; 1186 default: 1187 abort(); 1188 } 1189 1190 icp_fini(); 1191 1192 return (0); 1193 } 1194 1195 /* ========== */ 1196 1197 /* main entry */ 1198 1199 static void 1200 usage(void) 1201 { 1202 fprintf(stderr, 1203 "usage: crypto_test [-v] < -c <testfile> | -p <alg> >\n"); 1204 exit(1); 1205 } 1206 1207 int 1208 main(int argc, char **argv) 1209 { 1210 crypto_test_outmode_t outmode = OUT_SUMMARY; 1211 const char *filename = NULL; 1212 const char *algname = NULL; 1213 1214 int c; 1215 while ((c = getopt(argc, argv, "c:p:v")) != -1) { 1216 switch (c) { 1217 case 'c': 1218 filename = optarg; 1219 break; 1220 case 'p': 1221 algname = optarg; 1222 break; 1223 case 'v': 1224 outmode = (outmode == OUT_SUMMARY) ? OUT_FAIL : OUT_ALL; 1225 break; 1226 case '?': 1227 usage(); 1228 } 1229 } 1230 1231 argc -= optind; 1232 argv += optind; 1233 1234 if (filename != NULL && algname != NULL) { 1235 fprintf(stderr, "E: can't use -c and -p together\n"); 1236 usage(); 1237 } 1238 1239 if (argc != 0) 1240 usage(); 1241 1242 if (filename) 1243 return (runtests_main(filename, outmode)); 1244 1245 return (perf_main(algname, outmode)); 1246 } 1247