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[%lu]: encrypt=%s decrypt=%s\n", test->fileloc, test->id, 867 encrypt_pass ? "PASS" : "FAIL", 868 decrypt_pass ? "PASS" : "FAIL"); 869 870 if (!pass) { 871 /* 872 * if the test didn't pass, print any comment or flags field 873 * from the test params, which if present can help 874 * understanding what the ICP did wrong 875 */ 876 if (test->comment != NULL) 877 printf(" comment: %s\n", test->comment); 878 if (test->flags != NULL) 879 printf(" flags: %s\n", test->flags); 880 } 881 882 if (!encrypt_pass) { 883 /* encrypt failed */ 884 885 /* print return value from crypto_encrypt() */ 886 printf(" encrypt rv = 0x%02x [%s]\n", encrypt_rv, 887 crypto_errstr[encrypt_rv] ? 888 crypto_errstr[encrypt_rv] : "???"); 889 890 /* print mismatched ciphertext */ 891 if (!ct_match) { 892 printf(" ciphertexts don't match:\n"); 893 hexdump("got", encrypt_buf, test->msg.len); 894 hexdump("expected", test->ct.val, test->msg.len); 895 } 896 897 /* print mistmatched tag (MAC) */ 898 if (!tag_match) { 899 printf(" tags don't match:\n"); 900 hexdump("got", encrypt_buf + test->msg.len, 901 test->tag.len); 902 hexdump("expected", test->tag.val, test->tag.len); 903 } 904 } 905 906 if (!decrypt_pass) { 907 /* decrypt failed */ 908 909 /* print return value from crypto_decrypt() */ 910 printf(" decrypt rv = 0x%02x [%s]\n", decrypt_rv, 911 crypto_errstr[decrypt_rv] ? 912 crypto_errstr[decrypt_rv] : "???"); 913 914 /* print mismatched plaintext */ 915 if (!msg_match) { 916 printf(" plaintexts don't match:\n"); 917 hexdump("got", decrypt_buf, test->msg.len); 918 hexdump("expected", test->msg.val, test->msg.len); 919 } 920 } 921 922 if (!pass) 923 printf("\n"); 924 925 return (pass); 926 } 927 928 /* 929 * run the given list of tests. 930 * 931 * alginfo: a prefix for the test summary, showing the ICP algo implementation 932 * in use for this run. 933 * tests: first test in test list 934 * outmode: output mode, passed to test_result() 935 */ 936 static int 937 run_tests(const char *alginfo, const crypto_test_t *tests, 938 crypto_test_outmode_t outmode) 939 { 940 int ntests = 0, npass = 0; 941 942 /* 943 * allocate space for the mechanism description, and alg-specific 944 * params, and hook them up. 945 */ 946 crypto_mechanism_t mech = {}; 947 union { 948 CK_AES_GCM_PARAMS gcm; 949 CK_AES_CCM_PARAMS ccm; 950 } params = {}; 951 mech.cm_param = (caddr_t)¶ms; 952 953 /* space for encrypt/decrypt output */ 954 uint8_t encrypt_buf[1024]; 955 uint8_t decrypt_buf[1024]; 956 957 for (const crypto_test_t *test = tests; test != NULL; 958 test = test->next) { 959 ntests++; 960 961 /* setup mechanism description for encrypt, then encrypt */ 962 init_mech(&mech, test->alg, test->iv.val, test->iv.len, 963 test->aad.val, test->aad.len, test->msg.len, test->tag.len, 964 B_FALSE); 965 int encrypt_rv = encrypt_one(&mech, 966 test->key.val, test->key.len, 967 test->msg.val, test->msg.len, 968 encrypt_buf, test->msg.len + test->tag.len, NULL); 969 970 /* setup mechanism description for decrypt, then decrypt */ 971 init_mech(&mech, test->alg, test->iv.val, test->iv.len, 972 test->aad.val, test->aad.len, test->msg.len, test->tag.len, 973 B_TRUE); 974 int decrypt_rv = decrypt_one(&mech, 975 test->key.val, test->key.len, 976 test->ct.val, test->ct.len, 977 test->tag.val, test->tag.len, 978 decrypt_buf, test->ct.len, NULL); 979 980 /* consider results and if it passed, count it */ 981 if (test_result(test, encrypt_rv, encrypt_buf, 982 decrypt_rv, decrypt_buf, outmode)) 983 npass++; 984 } 985 986 printf("%s: tests=%d: passed=%d failed=%d\n", 987 alginfo, ntests, npass, ntests-npass); 988 989 return (ntests != npass); 990 } 991 992 /* args for run_test_alg_cb */ 993 typedef struct { 994 crypto_test_t *tests; 995 crypto_test_outmode_t outmode; 996 int failed; 997 } run_test_alg_args_t; 998 999 /* per-alg-impl function for correctness test runs */ 1000 static void 1001 run_test_alg_cb(const char *alginfo, void *arg) 1002 { 1003 if (arg == NULL) { 1004 printf("%s: [not supported on this platform]\n", alginfo); 1005 return; 1006 } 1007 run_test_alg_args_t *args = arg; 1008 args->failed += run_tests(alginfo, args->tests, args->outmode); 1009 } 1010 1011 /* main function for correctness tests */ 1012 static int 1013 runtests_main(const char *filename, crypto_test_outmode_t outmode) 1014 { 1015 crypto_test_alg_t alg = ALG_NONE; 1016 crypto_test_t *tests = load_tests(filename, &alg); 1017 if (tests == NULL) 1018 return (1); 1019 1020 icp_init(); 1021 1022 run_test_alg_args_t args = { 1023 .tests = tests, 1024 .outmode = outmode, 1025 .failed = 0, 1026 }; 1027 1028 switch (alg) { 1029 case ALG_AES_CCM: 1030 foreach_aes_ccm(run_test_alg_cb, &args, outmode); 1031 break; 1032 case ALG_AES_GCM: 1033 foreach_aes_gcm(run_test_alg_cb, &args, outmode); 1034 break; 1035 default: 1036 abort(); 1037 } 1038 1039 icp_fini(); 1040 1041 return (args.failed); 1042 } 1043 1044 /* ========== */ 1045 1046 /* performance tests */ 1047 1048 /* helper; fill the given buffer with random data */ 1049 static int 1050 fill_random(uint8_t *v, size_t sz) 1051 { 1052 int fd = open("/dev/urandom", O_RDONLY); 1053 if (fd < 0) 1054 return (errno); 1055 1056 while (sz > 0) { 1057 ssize_t r = read(fd, v, sz); 1058 if (r < 0) { 1059 close(fd); 1060 return (errno); 1061 } 1062 v += r; 1063 sz -= r; 1064 } 1065 1066 close(fd); 1067 1068 return (0); 1069 } 1070 1071 /* args for perf_alg_cb */ 1072 typedef struct { 1073 crypto_test_alg_t alg; 1074 uint8_t *msg; 1075 uint8_t *out; 1076 uint8_t key[32]; 1077 uint8_t iv[12]; 1078 } perf_alg_args_t; 1079 1080 #define PERF_MSG_SHIFT_MIN (10) /* min test size 2^10 == 1K */ 1081 #define PERF_MSG_SHIFT_MAX (19) /* max test size 2^19 == 512K */ 1082 #define PERF_ROUNDS (1000) /* 1000 rounds per test */ 1083 1084 /* per-alg-impl function for performance test runs */ 1085 static void 1086 perf_alg_cb(const char *alginfo, void *arg) 1087 { 1088 char buf[10]; 1089 printf("%-28s", alginfo); 1090 1091 if (arg == NULL) { 1092 printf("[not supported on this platform]\n"); 1093 return; 1094 } 1095 1096 perf_alg_args_t *args = arg; 1097 1098 /* space for mechanism description */ 1099 crypto_mechanism_t mech = {}; 1100 union { 1101 CK_AES_GCM_PARAMS gcm; 1102 CK_AES_CCM_PARAMS ccm; 1103 } params = {}; 1104 mech.cm_param = (caddr_t)¶ms; 1105 1106 /* loop for each power-2 input size */ 1107 for (int i = PERF_MSG_SHIFT_MIN; i <= PERF_MSG_SHIFT_MAX; i++) { 1108 /* size of input */ 1109 size_t sz = 1<<i; 1110 1111 /* initialise mechanism */ 1112 init_mech(&mech, args->alg, args->iv, sizeof (args->iv), 1113 val_empty, 0, sz, 16, B_FALSE); 1114 1115 /* run N rounds and accumulate total time */ 1116 uint64_t total = 0; 1117 for (int round = 0; round < PERF_ROUNDS; round++) { 1118 uint64_t usec; 1119 encrypt_one(&mech, args->key, sizeof (args->key), 1120 args->msg, sz, args->out, sz+16, &usec); 1121 total += usec; 1122 } 1123 1124 /* 1125 * print avg time per round. zfs_nicetime expects nanoseconds, 1126 * so we multiply first 1127 */ 1128 zfs_nicetime((total*1000)/PERF_ROUNDS, buf, sizeof (buf)); 1129 printf(" %5s", buf); 1130 } 1131 1132 printf("\n"); 1133 } 1134 1135 /* main function for performance tests */ 1136 static int 1137 perf_main(const char *algname, crypto_test_outmode_t outmode) 1138 { 1139 perf_alg_args_t args; 1140 1141 if (strcmp(algname, "AES-CCM") == 0) 1142 args.alg = ALG_AES_CCM; 1143 else if (strcmp(algname, "AES-GCM") == 0) 1144 args.alg = ALG_AES_GCM; 1145 else { 1146 fprintf(stderr, "E: unknown algorithm: %s\n", algname); 1147 return (1); 1148 } 1149 1150 /* 1151 * test runs are often slow, but the very first ones won't be. by 1152 * disabling buffering, we can display results immediately, and 1153 * the user quickly gets an idea of what to expect 1154 */ 1155 setvbuf(stdout, NULL, _IONBF, 0); 1156 1157 /* allocate random data for encrypt input */ 1158 size_t maxsz = (1<<PERF_MSG_SHIFT_MAX); 1159 args.msg = malloc(maxsz); 1160 VERIFY0(fill_random(args.msg, maxsz)); 1161 1162 /* allocate space for output, +16 bytes for tag */ 1163 args.out = malloc(maxsz+16); 1164 1165 /* fill key and iv */ 1166 VERIFY0(fill_random(args.key, sizeof (args.key))); 1167 VERIFY0(fill_random(args.iv, sizeof (args.iv))); 1168 1169 icp_init(); 1170 1171 /* print header */ 1172 char buf[10]; 1173 printf("avg encrypt (%4d rounds) ", PERF_ROUNDS); 1174 for (int i = PERF_MSG_SHIFT_MIN; i <= PERF_MSG_SHIFT_MAX; i++) { 1175 zfs_nicebytes(1<<i, buf, sizeof (buf)); 1176 printf(" %5s", buf); 1177 } 1178 printf("\n"); 1179 1180 /* loop over all implementations of the wanted algorithm */ 1181 switch (args.alg) { 1182 case ALG_AES_CCM: 1183 foreach_aes_ccm(perf_alg_cb, &args, outmode); 1184 break; 1185 case ALG_AES_GCM: 1186 foreach_aes_gcm(perf_alg_cb, &args, outmode); 1187 break; 1188 default: 1189 abort(); 1190 } 1191 1192 icp_fini(); 1193 1194 return (0); 1195 } 1196 1197 /* ========== */ 1198 1199 /* main entry */ 1200 1201 static void 1202 usage(void) 1203 { 1204 fprintf(stderr, 1205 "usage: crypto_test [-v] < -c <testfile> | -p <alg> >\n"); 1206 exit(1); 1207 } 1208 1209 int 1210 main(int argc, char **argv) 1211 { 1212 crypto_test_outmode_t outmode = OUT_SUMMARY; 1213 const char *filename = NULL; 1214 const char *algname = NULL; 1215 1216 int c; 1217 while ((c = getopt(argc, argv, "c:p:v")) != -1) { 1218 switch (c) { 1219 case 'c': 1220 filename = optarg; 1221 break; 1222 case 'p': 1223 algname = optarg; 1224 break; 1225 case 'v': 1226 outmode = (outmode == OUT_SUMMARY) ? OUT_FAIL : OUT_ALL; 1227 break; 1228 case '?': 1229 usage(); 1230 } 1231 } 1232 1233 argc -= optind; 1234 argv += optind; 1235 1236 if (filename != NULL && algname != NULL) { 1237 fprintf(stderr, "E: can't use -c and -p together\n"); 1238 usage(); 1239 } 1240 1241 if (argc != 0) 1242 usage(); 1243 1244 if (filename) 1245 return (runtests_main(filename, outmode)); 1246 1247 return (perf_main(algname, outmode)); 1248 } 1249