xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/crypto_test.c (revision 53a2e2635ab2d17bed1de7b4e0d782dd23ceb6ea)
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 	{ "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
foreach_aes_ccm(alg_cb_t cb,void * arg,crypto_test_outmode_t outmode)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
foreach_aes_gcm(alg_cb_t cb,void * arg,crypto_test_outmode_t outmode)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
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)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
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)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
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)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
hexdump(const char * str,const uint8_t * src,uint_t len)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
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)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
run_tests(const char * alginfo,const crypto_test_t * tests,crypto_test_outmode_t outmode)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)&params;
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
run_test_alg_cb(const char * alginfo,void * arg)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
runtests_main(const char * filename,crypto_test_outmode_t outmode)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
fill_random(uint8_t * v,size_t sz)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
perf_alg_cb(const char * alginfo,void * arg)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)&params;
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
perf_main(const char * algname,crypto_test_outmode_t outmode)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
usage(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
main(int argc,char ** argv)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