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 { "x86_64", "avx2" }, 533 { "aesni", "avx2" }, 534 }; 535 536 /* signature of function to call after setting implementation params */ 537 typedef void (*alg_cb_t)(const char *alginfo, void *arg); 538 539 /* loop over each AES-CCM implementation */ 540 static void 541 foreach_aes_ccm(alg_cb_t cb, void *arg, crypto_test_outmode_t outmode) 542 { 543 char alginfo[64]; 544 545 for (int i = 0; i < ARRAY_SIZE(aes_impl); i++) { 546 snprintf(alginfo, sizeof (alginfo), "AES-CCM [%s]", 547 aes_impl[i]); 548 549 int err = -aes_impl_set(aes_impl[i]); 550 if (err != 0 && outmode != OUT_SUMMARY) 551 printf("W: %s couldn't enable AES impl '%s': %s\n", 552 alginfo, aes_impl[i], strerror(err)); 553 554 cb(alginfo, (err == 0) ? arg : NULL); 555 } 556 } 557 558 /* loop over each AES-GCM implementation */ 559 static void 560 foreach_aes_gcm(alg_cb_t cb, void *arg, crypto_test_outmode_t outmode) 561 { 562 char alginfo[64]; 563 564 for (int i = 0; i < ARRAY_SIZE(aes_gcm_impl); i++) { 565 const char *aes_impl = aes_gcm_impl[i][0]; 566 const char *gcm_impl = aes_gcm_impl[i][1]; 567 568 snprintf(alginfo, sizeof (alginfo), "AES-GCM [%s+%s]", 569 aes_impl, gcm_impl); 570 571 int err = -aes_impl_set(aes_impl); 572 if (err != 0 && outmode != OUT_SUMMARY) 573 printf("W: %s couldn't enable AES impl '%s': %s\n", 574 alginfo, aes_impl, strerror(err)); 575 576 if (err == 0) { 577 err = -gcm_impl_set(gcm_impl); 578 if (err != 0 && outmode != OUT_SUMMARY) { 579 printf("W: %s couldn't enable " 580 "GCM impl '%s': %s\n", 581 alginfo, gcm_impl, strerror(err)); 582 } 583 } 584 585 cb(alginfo, (err == 0) ? arg : NULL); 586 } 587 } 588 589 /* ========== */ 590 591 /* ICP lowlevel drivers */ 592 593 /* 594 * initialise the mechanism (algorithm description) with the wanted parameters 595 * for the next operation. 596 * 597 * mech must be allocated and mech->cm_params point to space large enough 598 * to hold the parameters for the given algorithm. 599 * 600 * decrypt is true if setting up for decryption, false for encryption. 601 */ 602 static void 603 init_mech(crypto_mechanism_t *mech, crypto_test_alg_t alg, 604 uint8_t *iv, size_t ivlen, 605 uint8_t *aad, size_t aadlen, 606 size_t msglen, size_t taglen, 607 boolean_t decrypt) 608 { 609 switch (alg) { 610 case ALG_AES_GCM: { 611 mech->cm_type = crypto_mech2id(SUN_CKM_AES_GCM); 612 mech->cm_param_len = sizeof (CK_AES_GCM_PARAMS); 613 CK_AES_GCM_PARAMS *p = (CK_AES_GCM_PARAMS *)mech->cm_param; 614 p->pIv = (uchar_t *)iv; 615 p->ulIvLen = ivlen; 616 p->ulIvBits = ivlen << 3; 617 p->pAAD = aad; 618 p->ulAADLen = aadlen; 619 p->ulTagBits = taglen << 3; 620 break; 621 } 622 case ALG_AES_CCM: { 623 mech->cm_type = crypto_mech2id(SUN_CKM_AES_CCM); 624 mech->cm_param_len = sizeof (CK_AES_CCM_PARAMS); 625 CK_AES_CCM_PARAMS *p = (CK_AES_CCM_PARAMS *)mech->cm_param; 626 p->nonce = iv; 627 p->ulNonceSize = ivlen; 628 p->authData = aad; 629 p->ulAuthDataSize = aadlen; 630 p->ulMACSize = taglen; 631 /* 632 * ICP CCM needs the MAC len in the data size for decrypt, 633 * even if the buffer isn't that big. 634 */ 635 p->ulDataSize = msglen + (decrypt ? taglen : 0); 636 break; 637 } 638 default: 639 abort(); 640 } 641 } 642 643 /* 644 * call crypto_encrypt() with the given inputs. 645 * 646 * mech: previously initialised by init_mech 647 * key, keylen: raw data and length of key 648 * msg, msglen: raw data and length of message 649 * out, outlen: buffer to write output to (min msglen+taglen) 650 * usecp: if not NULL, recieves microseconds in crypto_encrypt() 651 */ 652 static int 653 encrypt_one(crypto_mechanism_t *mech, 654 const uint8_t *key, size_t keylen, 655 const uint8_t *msg, size_t msglen, 656 uint8_t *out, size_t outlen, 657 uint64_t *usecp) 658 { 659 crypto_key_t k = { 660 .ck_data = (uint8_t *)key, 661 .ck_length = keylen << 3, 662 }; 663 664 crypto_data_t i = { 665 .cd_format = CRYPTO_DATA_RAW, 666 .cd_offset = 0, 667 .cd_length = msglen, 668 .cd_raw = { 669 .iov_base = (char *)msg, 670 .iov_len = msglen, 671 }, 672 }; 673 674 crypto_data_t o = { 675 .cd_format = CRYPTO_DATA_RAW, 676 .cd_offset = 0, 677 .cd_length = outlen, 678 .cd_raw = { 679 .iov_base = (char *)out, 680 .iov_len = outlen, 681 }, 682 }; 683 684 struct timeval start, end, diff; 685 if (usecp != NULL) 686 gettimeofday(&start, NULL); 687 688 int rv = crypto_encrypt(mech, &i, &k, NULL, &o); 689 690 if (usecp != NULL) { 691 gettimeofday(&end, NULL); 692 timersub(&end, &start, &diff); 693 *usecp = 694 ((uint64_t)diff.tv_sec) * 1000000 + (uint64_t)diff.tv_usec; 695 } 696 697 return (rv); 698 } 699 700 /* 701 * call crypto_decrypt() with the given inputs. 702 * 703 * mech: previously initialised by init_mech 704 * key, keylen: raw data and length of key 705 * ct, ctlen: raw data and length of ciphertext 706 * tag, taglen: raw data and length of tag (MAC) 707 * out, outlen: buffer to write output to (min ctlen) 708 * usecp: if not NULL, recieves microseconds in crypto_decrypt() 709 */ 710 static int 711 decrypt_one(crypto_mechanism_t *mech, 712 const uint8_t *key, size_t keylen, 713 const uint8_t *ct, size_t ctlen, 714 const uint8_t *tag, size_t taglen, 715 uint8_t *out, size_t outlen, 716 uint64_t *usecp) 717 { 718 uint8_t inbuf[1024]; 719 720 crypto_key_t k = { 721 .ck_data = (uint8_t *)key, 722 .ck_length = keylen << 3, 723 }; 724 725 memcpy(inbuf, ct, ctlen); 726 memcpy(inbuf + ctlen, tag, taglen); 727 crypto_data_t i = { 728 .cd_format = CRYPTO_DATA_RAW, 729 .cd_offset = 0, 730 .cd_length = ctlen + taglen, 731 .cd_raw = { 732 .iov_base = (char *)inbuf, 733 .iov_len = ctlen + taglen, 734 }, 735 }; 736 737 crypto_data_t o = { 738 .cd_format = CRYPTO_DATA_RAW, 739 .cd_offset = 0, 740 .cd_length = outlen, 741 .cd_raw = { 742 .iov_base = (char *)out, 743 .iov_len = outlen 744 }, 745 }; 746 747 struct timeval start, end, diff; 748 if (usecp != NULL) 749 gettimeofday(&start, NULL); 750 751 int rv = crypto_decrypt(mech, &i, &k, NULL, &o); 752 753 if (usecp != NULL) { 754 gettimeofday(&end, NULL); 755 timersub(&start, &end, &diff); 756 *usecp = 757 ((uint64_t)diff.tv_sec) * 1000000 + (uint64_t)diff.tv_usec; 758 } 759 760 return (rv); 761 } 762 763 /* ========== */ 764 765 /* correctness tests */ 766 767 /* 768 * helper; dump the provided data as hex, with a string prefix 769 */ 770 static void 771 hexdump(const char *str, const uint8_t *src, uint_t len) 772 { 773 printf("%12s:", str); 774 int i = 0; 775 while (i < len) { 776 if (i % 4 == 0) 777 printf(" "); 778 printf("%02x", src[i]); 779 i++; 780 if (i % 16 == 0 && i < len) { 781 printf("\n"); 782 if (i < len) 783 printf(" "); 784 } 785 } 786 printf("\n"); 787 } 788 789 /* 790 * analyse test result and on failure, print useful output for debugging. 791 * 792 * test: the test we ran 793 * encrypt_rv: return value from crypto_encrypt() 794 * encrypt_buf: the output buffer from crypto_encrypt() 795 * decrypt_rv: return value from crypto_decrypt() 796 * decrypt_buf: the output buffer from crypto_decrypt() 797 * outmode: output mode (summary, fail, all) 798 */ 799 static boolean_t 800 test_result(const crypto_test_t *test, int encrypt_rv, uint8_t *encrypt_buf, 801 int decrypt_rv, uint8_t *decrypt_buf, crypto_test_outmode_t outmode) 802 { 803 boolean_t ct_match = B_FALSE, tag_match = B_FALSE, msg_match = B_FALSE; 804 boolean_t encrypt_pass = B_FALSE, decrypt_pass = B_FALSE; 805 boolean_t pass = B_FALSE; 806 807 /* check if the encrypt output matches the expected ciphertext */ 808 if (memcmp(encrypt_buf, test->ct.val, test->msg.len) == 0) 809 ct_match = B_TRUE; 810 811 /* 812 * check if the tag at the end of the encrypt output matches the 813 * expected tag 814 */ 815 if (memcmp(encrypt_buf + test->msg.len, test->tag.val, 816 test->tag.len) == 0) 817 tag_match = B_TRUE; 818 819 /* check if the decrypt output matches the expected plaintext */ 820 if (memcmp(decrypt_buf, test->msg.val, test->msg.len) == 0) 821 msg_match = B_TRUE; 822 823 if (test->result == RS_VALID) { 824 /* 825 * a "valid" test is where the params describe an 826 * encrypt/decrypt cycle that should succeed. we consider 827 * these to have passed the test if crypto_encrypt() and 828 * crypto_decrypt() return success, and the output data 829 * matches the expected values from the test params. 830 */ 831 if (encrypt_rv == CRYPTO_SUCCESS) { 832 if (ct_match && tag_match) 833 encrypt_pass = B_TRUE; 834 } 835 if (decrypt_rv == CRYPTO_SUCCESS) { 836 if (msg_match) 837 decrypt_pass = B_TRUE; 838 } 839 } else { 840 /* 841 * an "invalid" test is where the params describe an 842 * encrypt/decrypt cycle that should _not_ succeed. 843 * 844 * for decrypt, we only need to check the result from 845 * crypto_decrypt(), because decrypt checks the the tag (MAC) 846 * as part of its operation. 847 * 848 * for encrypt, the tag (MAC) is an output of the encryption 849 * function, so if encryption succeeds, we have to check that 850 * the returned tag matches the expected tag. 851 */ 852 if (encrypt_rv != CRYPTO_SUCCESS || !tag_match) 853 encrypt_pass = B_TRUE; 854 if (decrypt_rv != CRYPTO_SUCCESS) 855 decrypt_pass = B_TRUE; 856 } 857 858 /* the test as a whole passed if both encrypt and decrypt passed */ 859 pass = (encrypt_pass && decrypt_pass); 860 861 /* if the test passed we may not have to output anything */ 862 if (outmode == OUT_SUMMARY || (outmode == OUT_FAIL && pass)) 863 return (pass); 864 865 /* print summary of test result */ 866 printf("%s[%ju]: encrypt=%s decrypt=%s\n", test->fileloc, 867 (uintmax_t)test->id, 868 encrypt_pass ? "PASS" : "FAIL", 869 decrypt_pass ? "PASS" : "FAIL"); 870 871 if (!pass) { 872 /* 873 * if the test didn't pass, print any comment or flags field 874 * from the test params, which if present can help 875 * understanding what the ICP did wrong 876 */ 877 if (test->comment != NULL) 878 printf(" comment: %s\n", test->comment); 879 if (test->flags != NULL) 880 printf(" flags: %s\n", test->flags); 881 } 882 883 if (!encrypt_pass) { 884 /* encrypt failed */ 885 886 /* print return value from crypto_encrypt() */ 887 printf(" encrypt rv = 0x%02x [%s]\n", encrypt_rv, 888 crypto_errstr[encrypt_rv] ? 889 crypto_errstr[encrypt_rv] : "???"); 890 891 /* print mismatched ciphertext */ 892 if (!ct_match) { 893 printf(" ciphertexts don't match:\n"); 894 hexdump("got", encrypt_buf, test->msg.len); 895 hexdump("expected", test->ct.val, test->msg.len); 896 } 897 898 /* print mistmatched tag (MAC) */ 899 if (!tag_match) { 900 printf(" tags don't match:\n"); 901 hexdump("got", encrypt_buf + test->msg.len, 902 test->tag.len); 903 hexdump("expected", test->tag.val, test->tag.len); 904 } 905 } 906 907 if (!decrypt_pass) { 908 /* decrypt failed */ 909 910 /* print return value from crypto_decrypt() */ 911 printf(" decrypt rv = 0x%02x [%s]\n", decrypt_rv, 912 crypto_errstr[decrypt_rv] ? 913 crypto_errstr[decrypt_rv] : "???"); 914 915 /* print mismatched plaintext */ 916 if (!msg_match) { 917 printf(" plaintexts don't match:\n"); 918 hexdump("got", decrypt_buf, test->msg.len); 919 hexdump("expected", test->msg.val, test->msg.len); 920 } 921 } 922 923 if (!pass) 924 printf("\n"); 925 926 return (pass); 927 } 928 929 /* 930 * run the given list of tests. 931 * 932 * alginfo: a prefix for the test summary, showing the ICP algo implementation 933 * in use for this run. 934 * tests: first test in test list 935 * outmode: output mode, passed to test_result() 936 */ 937 static int 938 run_tests(const char *alginfo, const crypto_test_t *tests, 939 crypto_test_outmode_t outmode) 940 { 941 int ntests = 0, npass = 0; 942 943 /* 944 * allocate space for the mechanism description, and alg-specific 945 * params, and hook them up. 946 */ 947 crypto_mechanism_t mech = {}; 948 union { 949 CK_AES_GCM_PARAMS gcm; 950 CK_AES_CCM_PARAMS ccm; 951 } params = {}; 952 mech.cm_param = (caddr_t)¶ms; 953 954 /* space for encrypt/decrypt output */ 955 uint8_t encrypt_buf[1024]; 956 uint8_t decrypt_buf[1024]; 957 958 for (const crypto_test_t *test = tests; test != NULL; 959 test = test->next) { 960 ntests++; 961 962 /* setup mechanism description for encrypt, then encrypt */ 963 init_mech(&mech, test->alg, test->iv.val, test->iv.len, 964 test->aad.val, test->aad.len, test->msg.len, test->tag.len, 965 B_FALSE); 966 int encrypt_rv = encrypt_one(&mech, 967 test->key.val, test->key.len, 968 test->msg.val, test->msg.len, 969 encrypt_buf, test->msg.len + test->tag.len, NULL); 970 971 /* setup mechanism description for decrypt, then decrypt */ 972 init_mech(&mech, test->alg, test->iv.val, test->iv.len, 973 test->aad.val, test->aad.len, test->msg.len, test->tag.len, 974 B_TRUE); 975 int decrypt_rv = decrypt_one(&mech, 976 test->key.val, test->key.len, 977 test->ct.val, test->ct.len, 978 test->tag.val, test->tag.len, 979 decrypt_buf, test->ct.len, NULL); 980 981 /* consider results and if it passed, count it */ 982 if (test_result(test, encrypt_rv, encrypt_buf, 983 decrypt_rv, decrypt_buf, outmode)) 984 npass++; 985 } 986 987 printf("%s: tests=%d: passed=%d failed=%d\n", 988 alginfo, ntests, npass, ntests-npass); 989 990 return (ntests != npass); 991 } 992 993 /* args for run_test_alg_cb */ 994 typedef struct { 995 crypto_test_t *tests; 996 crypto_test_outmode_t outmode; 997 int failed; 998 } run_test_alg_args_t; 999 1000 /* per-alg-impl function for correctness test runs */ 1001 static void 1002 run_test_alg_cb(const char *alginfo, void *arg) 1003 { 1004 if (arg == NULL) { 1005 printf("%s: [not supported on this platform]\n", alginfo); 1006 return; 1007 } 1008 run_test_alg_args_t *args = arg; 1009 args->failed += run_tests(alginfo, args->tests, args->outmode); 1010 } 1011 1012 /* main function for correctness tests */ 1013 static int 1014 runtests_main(const char *filename, crypto_test_outmode_t outmode) 1015 { 1016 crypto_test_alg_t alg = ALG_NONE; 1017 crypto_test_t *tests = load_tests(filename, &alg); 1018 if (tests == NULL) 1019 return (1); 1020 1021 icp_init(); 1022 1023 run_test_alg_args_t args = { 1024 .tests = tests, 1025 .outmode = outmode, 1026 .failed = 0, 1027 }; 1028 1029 switch (alg) { 1030 case ALG_AES_CCM: 1031 foreach_aes_ccm(run_test_alg_cb, &args, outmode); 1032 break; 1033 case ALG_AES_GCM: 1034 foreach_aes_gcm(run_test_alg_cb, &args, outmode); 1035 break; 1036 default: 1037 abort(); 1038 } 1039 1040 icp_fini(); 1041 1042 return (args.failed); 1043 } 1044 1045 /* ========== */ 1046 1047 /* performance tests */ 1048 1049 /* helper; fill the given buffer with random data */ 1050 static int 1051 fill_random(uint8_t *v, size_t sz) 1052 { 1053 int fd = open("/dev/urandom", O_RDONLY); 1054 if (fd < 0) 1055 return (errno); 1056 1057 while (sz > 0) { 1058 ssize_t r = read(fd, v, sz); 1059 if (r < 0) { 1060 close(fd); 1061 return (errno); 1062 } 1063 v += r; 1064 sz -= r; 1065 } 1066 1067 close(fd); 1068 1069 return (0); 1070 } 1071 1072 /* args for perf_alg_cb */ 1073 typedef struct { 1074 crypto_test_alg_t alg; 1075 uint8_t *msg; 1076 uint8_t *out; 1077 uint8_t key[32]; 1078 uint8_t iv[12]; 1079 } perf_alg_args_t; 1080 1081 #define PERF_MSG_SHIFT_MIN (10) /* min test size 2^10 == 1K */ 1082 #define PERF_MSG_SHIFT_MAX (19) /* max test size 2^19 == 512K */ 1083 #define PERF_ROUNDS (1000) /* 1000 rounds per test */ 1084 1085 /* per-alg-impl function for performance test runs */ 1086 static void 1087 perf_alg_cb(const char *alginfo, void *arg) 1088 { 1089 char buf[10]; 1090 printf("%-28s", alginfo); 1091 1092 if (arg == NULL) { 1093 printf("[not supported on this platform]\n"); 1094 return; 1095 } 1096 1097 perf_alg_args_t *args = arg; 1098 1099 /* space for mechanism description */ 1100 crypto_mechanism_t mech = {}; 1101 union { 1102 CK_AES_GCM_PARAMS gcm; 1103 CK_AES_CCM_PARAMS ccm; 1104 } params = {}; 1105 mech.cm_param = (caddr_t)¶ms; 1106 1107 /* loop for each power-2 input size */ 1108 for (int i = PERF_MSG_SHIFT_MIN; i <= PERF_MSG_SHIFT_MAX; i++) { 1109 /* size of input */ 1110 size_t sz = 1<<i; 1111 1112 /* initialise mechanism */ 1113 init_mech(&mech, args->alg, args->iv, sizeof (args->iv), 1114 val_empty, 0, sz, 16, B_FALSE); 1115 1116 /* run N rounds and accumulate total time */ 1117 uint64_t total = 0; 1118 for (int round = 0; round < PERF_ROUNDS; round++) { 1119 uint64_t usec; 1120 encrypt_one(&mech, args->key, sizeof (args->key), 1121 args->msg, sz, args->out, sz+16, &usec); 1122 total += usec; 1123 } 1124 1125 /* 1126 * print avg time per round. zfs_nicetime expects nanoseconds, 1127 * so we multiply first 1128 */ 1129 zfs_nicetime((total*1000)/PERF_ROUNDS, buf, sizeof (buf)); 1130 printf(" %5s", buf); 1131 } 1132 1133 printf("\n"); 1134 } 1135 1136 /* main function for performance tests */ 1137 static int 1138 perf_main(const char *algname, crypto_test_outmode_t outmode) 1139 { 1140 perf_alg_args_t args; 1141 1142 if (strcmp(algname, "AES-CCM") == 0) 1143 args.alg = ALG_AES_CCM; 1144 else if (strcmp(algname, "AES-GCM") == 0) 1145 args.alg = ALG_AES_GCM; 1146 else { 1147 fprintf(stderr, "E: unknown algorithm: %s\n", algname); 1148 return (1); 1149 } 1150 1151 /* 1152 * test runs are often slow, but the very first ones won't be. by 1153 * disabling buffering, we can display results immediately, and 1154 * the user quickly gets an idea of what to expect 1155 */ 1156 setvbuf(stdout, NULL, _IONBF, 0); 1157 1158 /* allocate random data for encrypt input */ 1159 size_t maxsz = (1<<PERF_MSG_SHIFT_MAX); 1160 args.msg = malloc(maxsz); 1161 VERIFY0(fill_random(args.msg, maxsz)); 1162 1163 /* allocate space for output, +16 bytes for tag */ 1164 args.out = malloc(maxsz+16); 1165 1166 /* fill key and iv */ 1167 VERIFY0(fill_random(args.key, sizeof (args.key))); 1168 VERIFY0(fill_random(args.iv, sizeof (args.iv))); 1169 1170 icp_init(); 1171 1172 /* print header */ 1173 char buf[10]; 1174 printf("avg encrypt (%4d rounds) ", PERF_ROUNDS); 1175 for (int i = PERF_MSG_SHIFT_MIN; i <= PERF_MSG_SHIFT_MAX; i++) { 1176 zfs_nicebytes(1<<i, buf, sizeof (buf)); 1177 printf(" %5s", buf); 1178 } 1179 printf("\n"); 1180 1181 /* loop over all implementations of the wanted algorithm */ 1182 switch (args.alg) { 1183 case ALG_AES_CCM: 1184 foreach_aes_ccm(perf_alg_cb, &args, outmode); 1185 break; 1186 case ALG_AES_GCM: 1187 foreach_aes_gcm(perf_alg_cb, &args, outmode); 1188 break; 1189 default: 1190 abort(); 1191 } 1192 1193 icp_fini(); 1194 1195 return (0); 1196 } 1197 1198 /* ========== */ 1199 1200 /* main entry */ 1201 1202 static void 1203 usage(void) 1204 { 1205 fprintf(stderr, 1206 "usage: crypto_test [-v] < -c <testfile> | -p <alg> >\n"); 1207 exit(1); 1208 } 1209 1210 int 1211 main(int argc, char **argv) 1212 { 1213 crypto_test_outmode_t outmode = OUT_SUMMARY; 1214 const char *filename = NULL; 1215 const char *algname = NULL; 1216 1217 int c; 1218 while ((c = getopt(argc, argv, "c:p:v")) != -1) { 1219 switch (c) { 1220 case 'c': 1221 filename = optarg; 1222 break; 1223 case 'p': 1224 algname = optarg; 1225 break; 1226 case 'v': 1227 outmode = (outmode == OUT_SUMMARY) ? OUT_FAIL : OUT_ALL; 1228 break; 1229 case '?': 1230 usage(); 1231 } 1232 } 1233 1234 argc -= optind; 1235 argv += optind; 1236 1237 if (filename != NULL && algname != NULL) { 1238 fprintf(stderr, "E: can't use -c and -p together\n"); 1239 usage(); 1240 } 1241 1242 if (argc != 0) 1243 usage(); 1244 1245 if (filename) 1246 return (runtests_main(filename, outmode)); 1247 1248 return (perf_main(algname, outmode)); 1249 } 1250