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