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
split_kv(char * line,char ** kp,char ** vp)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
parse_num(char * v,uint64_t * np)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 *
load_tests(const char * filepath,crypto_test_alg_t * algp)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
foreach_aes_ccm(alg_cb_t cb,void * arg,crypto_test_outmode_t outmode)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
foreach_aes_gcm(alg_cb_t cb,void * arg,crypto_test_outmode_t outmode)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
init_mech(crypto_mechanism_t * mech,crypto_test_alg_t alg,uint8_t * iv,size_t ivlen,uint8_t * aad,size_t aadlen,size_t msglen,size_t taglen,boolean_t decrypt)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
encrypt_one(crypto_mechanism_t * mech,const uint8_t * key,size_t keylen,const uint8_t * msg,size_t msglen,uint8_t * out,size_t outlen,uint64_t * usecp)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
decrypt_one(crypto_mechanism_t * mech,const uint8_t * key,size_t keylen,const uint8_t * ct,size_t ctlen,const uint8_t * tag,size_t taglen,uint8_t * out,size_t outlen,uint64_t * usecp)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
hexdump(const char * str,const uint8_t * src,uint_t len)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
test_result(const crypto_test_t * test,int encrypt_rv,uint8_t * encrypt_buf,int decrypt_rv,uint8_t * decrypt_buf,crypto_test_outmode_t outmode)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
run_tests(const char * alginfo,const crypto_test_t * tests,crypto_test_outmode_t outmode)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
run_test_alg_cb(const char * alginfo,void * arg)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
runtests_main(const char * filename,crypto_test_outmode_t outmode)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
fill_random(uint8_t * v,size_t sz)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
perf_alg_cb(const char * alginfo,void * arg)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
perf_main(const char * algname,crypto_test_outmode_t outmode)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
usage(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
main(int argc,char ** argv)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