2 * Copyright (c) 2004 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
15 * 3. Neither the names of the above-listed copyright holders nor the names
16 * of any contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
23 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
24 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
25 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
28 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGES.
36 * Simple tool for testing hardware/system crypto support.
38 * cryptotest [-czsbv] [-a algorithm] [count] [size ...]
40 * Run count iterations of a crypt+decrypt or mac operation on a buffer of
41 * size bytes. A random key and iv are used. Options:
42 * -c check the results
43 * -d dev pin work on device dev
44 * -z run all available algorithms on a variety of buffer sizes
46 * -b mark operations for batching
47 * -p profile kernel crypto operations (must be root)
48 * -t n fork n threads and run tests concurrently
49 * Known algorithms are:
56 * aes rijndael/aes 128-bit cbc
57 * aes192 rijndael/aes 192-bit cbc
58 * aes256 rijndael/aes 256-bit cbc
59 * chacha20 Chacha20 stream cipher
64 * sha256 256-bit sha2 hmac
65 * sha384 384-bit sha2 hmac
66 * sha512 512--bit sha2 hmac
68 * For a test of how fast a crypto card is, use something like:
70 * This will run a series of tests using the available crypto/cipher
71 * algorithms over a variety of buffer sizes. The 1024 says to do 1024
72 * iterations. Extra arguments can be used to specify one or more buffer
73 * sizes to use in doing tests.
75 * To fork multiple processes all doing the same work, specify -t X on the
76 * command line to get X "threads" running simultaneously. No effort is made
77 * to synchronize the threads or otherwise maximize load.
79 * If the kernel crypto code is built with CRYPTO_TIMING and you run as root,
80 * then you can specify the -p option to get a "profile" of the time spent
81 * processing crypto operations. At present this data is only meaningful for
82 * symmetric operations. To get meaningful numbers you must run on an idle
85 * Expect ~400 Mb/s for a Broadcom 582x for 8K buffers on a reasonable CPU
86 * (64-bit PCI helps). Hifn 7811 parts top out at ~110 Mb/s.
89 #include <sys/param.h>
90 #include <sys/cpuset.h>
91 #include <sys/ioctl.h>
93 #include <sys/sysctl.h>
103 #include <sysexits.h>
106 #include <crypto/cryptodev.h>
108 #define CHUNK 64 /* how much to display */
109 #define streq(a,b) (strcasecmp(a,b) == 0)
111 void hexdump(char *, int);
116 int crid = CRYPTO_FLAG_HARDWARE;
126 #ifdef CRYPTO_NULL_CBC
127 { "null", 0, 8, 1, 256, CRYPTO_NULL_CBC },
129 { "des", 0, 8, 8, 8, CRYPTO_DES_CBC },
130 { "3des", 0, 8, 24, 24, CRYPTO_3DES_CBC },
131 { "blf", 0, 8, 5, 56, CRYPTO_BLF_CBC },
132 { "cast", 0, 8, 5, 16, CRYPTO_CAST_CBC },
133 { "skj", 0, 8, 10, 10, CRYPTO_SKIPJACK_CBC },
134 { "rij", 0, 16, 16, 16, CRYPTO_RIJNDAEL128_CBC},
135 { "aes", 0, 16, 16, 16, CRYPTO_AES_CBC},
136 { "aes192", 0, 16, 24, 24, CRYPTO_AES_CBC},
137 { "aes256", 0, 16, 32, 32, CRYPTO_AES_CBC},
138 { "chacha20", 0, 1, 32, 32, CRYPTO_CHACHA20},
139 { "blake2b", 1, 128, 64, 64, CRYPTO_BLAKE2B },
140 { "blake2s", 1, 64, 32, 32, CRYPTO_BLAKE2S },
141 { "md5", 1, 8, 16, 16, CRYPTO_MD5_HMAC },
142 { "sha1", 1, 8, 20, 20, CRYPTO_SHA1_HMAC },
143 { "sha256", 1, 8, 32, 32, CRYPTO_SHA2_256_HMAC },
144 { "sha384", 1, 8, 48, 48, CRYPTO_SHA2_384_HMAC },
145 { "sha512", 1, 8, 64, 64, CRYPTO_SHA2_512_HMAC },
149 usage(const char* cmd)
151 printf("usage: %s [-czsbv] [-d dev] [-a algorithm] [count] [size ...]\n",
153 printf("where algorithm is one of:\n");
154 printf(" null des 3des (default) blowfish cast skipjack rij\n");
155 printf(" aes aes192 aes256 chacha20 md5 sha1 sha256 sha384 sha512\n");
156 printf(" blake2b blake2s\n");
157 printf(" or an encryption algorithm concatented with authentication\n");
158 printf(" algorithm with '+' in the middle, e.g., aes+sha1.\n");
159 printf("count is the number of encrypt/decrypt ops to do\n");
160 printf("size is the number of bytes of text to encrypt+decrypt\n");
162 printf("-c check the results (slows timing)\n");
163 printf("-d use specific device, specify 'soft' for testing software implementations\n");
164 printf("\tNOTE: to use software you must set:\n\t sysctl kern.cryptodevallowsoft=1\n");
165 printf("-z run all available algorithms on a variety of sizes\n");
166 printf("-v be verbose\n");
167 printf("-b mark operations for batching\n");
168 printf("-p profile kernel crypto operation (must be root)\n");
169 printf("-t n for n threads and run tests concurrently\n");
174 getalgbycode(int cipher)
178 for (i = 0; i < nitems(algorithms); i++)
179 if (cipher == algorithms[i].code)
180 return &algorithms[i];
185 getalgbyname(const char* name)
189 for (i = 0; i < nitems(algorithms); i++)
190 if (streq(name, algorithms[i].name))
191 return &algorithms[i];
201 fd = open(_PATH_DEV "crypto", O_RDWR, 0);
203 err(1, _PATH_DEV "crypto");
204 if (fcntl(fd, F_SETFD, 1) == -1)
205 err(1, "fcntl(F_SETFD) (devcrypto)");
211 crlookup(const char *devname)
213 struct crypt_find_op find;
215 if (strncmp(devname, "soft", 4) == 0)
216 return CRYPTO_FLAG_SOFTWARE;
219 strlcpy(find.name, devname, sizeof(find.name));
220 if (ioctl(devcrypto(), CIOCFINDDEV, &find) == -1)
221 err(1, "ioctl(CIOCFINDDEV)");
228 static struct crypt_find_op find;
230 bzero(&find, sizeof(find));
232 if (ioctl(devcrypto(), CRIOFINDDEV, &find) == -1)
233 err(1, "ioctl(CIOCFINDDEV): crid %d", crid);
242 if (ioctl(devcrypto(), CRIOGET, &fd) == -1)
243 err(1, "ioctl(CRIOGET)");
244 if (fcntl(fd, F_SETFD, 1) == -1)
245 err(1, "fcntl(F_SETFD) (crget)");
253 0x10,0x54,0x11,0x48,0x45,0x12,0x4f,0x13,0x49,0x53,0x14,0x41,
254 0x15,0x16,0x4e,0x55,0x54,0x17,0x18,0x4a,0x4f,0x42,0x19,0x01
256 return 0x20+a[random()%nitems(a)];
260 runtest(struct alg *ealg, struct alg *alg, int count, int size, u_long cmd, struct timeval *tv)
263 struct timeval start, stop, dt;
264 char *cleartext, *ciphertext, *originaltext, *key;
265 struct session2_op sop;
267 char iv[EALG_MAX_BLOCK_LEN];
270 /* Canonicalize 'ealg' to crypt alg and 'alg' to authentication alg. */
271 if (ealg == NULL && !alg->ishash) {
276 bzero(&sop, sizeof(sop));
278 sop.keylen = (ealg->minkeylen + ealg->maxkeylen)/2;
279 key = (char *) malloc(sop.keylen);
281 err(1, "malloc (key)");
282 for (i = 0; i < sop.keylen; i++)
285 sop.cipher = ealg->code;
288 sop.mackeylen = (alg->minkeylen + alg->maxkeylen)/2;
289 key = (char *) malloc(sop.mackeylen);
291 err(1, "malloc (mac)");
292 for (i = 0; i < sop.mackeylen; i++)
299 if (ioctl(fd, cmd, &sop) < 0) {
300 if (cmd == CIOCGSESSION || cmd == CIOCGSESSION2) {
303 printf("cipher %s%s%s", ealg? ealg->name : "",
304 (ealg && alg) ? "+" : "",
305 alg? alg->name : "");
308 printf(" mackeylen %u\n", sop.mackeylen);
310 printf(" keylen %u\n", sop.keylen);
311 perror("CIOCGSESSION");
313 /* hardware doesn't support algorithm; skip it */
316 printf("cipher %s%s%s keylen %u mackeylen %u\n",
317 ealg? ealg->name : "", (ealg && alg) ? "+" : "",
318 alg? alg->name : "", sop.keylen, sop.mackeylen);
319 err(1, "CIOCGSESSION");
322 originaltext = malloc(3*size);
323 if (originaltext == NULL)
324 err(1, "malloc (text)");
325 cleartext = originaltext+size;
326 ciphertext = cleartext+size;
327 for (i = 0; i < size; i++)
328 cleartext[i] = rdigit();
329 memcpy(originaltext, cleartext, size);
330 for (i = 0; i < nitems(iv); i++)
334 printf("session = 0x%x\n", sop.ses);
335 printf("device = %s\n", crfind(sop.crid));
336 printf("count = %d, size = %d\n", count, size);
339 hexdump(iv, sizeof iv);
341 printf("cleartext:");
342 hexdump(cleartext, MIN(size, CHUNK));
345 gettimeofday(&start, NULL);
347 for (i = 0; i < count; i++) {
349 cop.op = COP_ENCRYPT;
350 cop.flags = opflags | COP_F_CIPHER_FIRST;
353 cop.dst = ciphertext;
360 if (ioctl(fd, CIOCCRYPT, &cop) < 0)
361 err(1, "ioctl(CIOCCRYPT)");
363 if (verify && bcmp(ciphertext, cleartext, size) == 0) {
364 printf("cipher text unchanged:");
365 hexdump(ciphertext, size);
368 memset(cleartext, 'x', MIN(size, CHUNK));
370 cop.op = COP_DECRYPT;
373 cop.src = ciphertext;
381 if (ioctl(fd, CIOCCRYPT, &cop) < 0)
382 err(1, "ioctl(CIOCCRYPT)");
384 if (verify && bcmp(cleartext, originaltext, size) != 0) {
385 printf("decrypt mismatch:\n");
387 hexdump(originaltext, size);
388 printf("cleartext:");
389 hexdump(cleartext, size);
393 for (i = 0; i < count; i++) {
400 cop.mac = ciphertext;
403 if (ioctl(fd, CIOCCRYPT, &cop) < 0)
404 err(1, "ioctl(CIOCCRYPT)");
407 gettimeofday(&stop, NULL);
409 if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0)
410 perror("ioctl(CIOCFSESSION)");
413 printf("cleartext:");
414 hexdump(cleartext, MIN(size, CHUNK));
416 timersub(&stop, &start, tv);
427 struct cryptostats stats;
430 slen = sizeof (stats);
431 if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, 0) < 0) {
432 perror("kern.crypto_stats");
435 bzero(&stats.cs_invoke, sizeof (stats.cs_invoke));
436 bzero(&stats.cs_done, sizeof (stats.cs_done));
437 bzero(&stats.cs_cb, sizeof (stats.cs_cb));
438 bzero(&stats.cs_finis, sizeof (stats.cs_finis));
439 stats.cs_invoke.min.tv_sec = 10000;
440 stats.cs_done.min.tv_sec = 10000;
441 stats.cs_cb.min.tv_sec = 10000;
442 stats.cs_finis.min.tv_sec = 10000;
443 if (sysctlbyname("kern.crypto_stats", NULL, NULL, &stats, sizeof (stats)) < 0)
444 perror("kern.cryptostats");
448 printt(const char* tag, struct cryptotstat *ts)
450 uint64_t avg, min, max;
454 avg = (1000000000LL*ts->acc.tv_sec + ts->acc.tv_nsec) / ts->count;
455 min = 1000000000LL*ts->min.tv_sec + ts->min.tv_nsec;
456 max = 1000000000LL*ts->max.tv_sec + ts->max.tv_nsec;
457 printf("%16.16s: avg %6llu ns : min %6llu ns : max %7llu ns [%u samps]\n",
458 tag, avg, min, max, ts->count);
463 runtests(struct alg *ealg, struct alg *alg, int count, int size, u_long cmd, int threads, int profile)
469 struct timeval total;
472 if (size % alg->blocksize || (ealg && size % ealg->blocksize)) {
474 printf("skipping blocksize %u 'cuz not a multiple of "
475 "%s blocksize %u (or %s blocksize %u)\n",
476 size, alg->name, alg->blocksize,
477 ealg ? ealg->name : "n/a",
478 ealg ? ealg->blocksize : 0);
482 region = mmap(NULL, threads * sizeof (struct timeval),
483 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
484 if (region == MAP_FAILED) {
488 tvp = (struct timeval *) region;
491 size_t tlen = sizeof (otiming);
495 if (sysctlbyname("debug.crypto_timing", &otiming, &tlen,
496 &timing, sizeof (timing)) < 0)
497 perror("debug.crypto_timing");
502 for (i = 0; i < threads; i++)
507 cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
508 -1, sizeof(mask), &mask);
509 runtest(ealg, alg, count, size, cmd, &tvp[i]);
512 while (waitpid(WAIT_MYPGRP, &status, 0) != -1)
515 runtest(ealg, alg, count, size, cmd, tvp);
518 for (i = 0; i < threads; i++)
519 t += (((double)tvp[i].tv_sec * 1000000 + tvp[i].tv_usec) / 1000000);
521 int nops = alg->ishash ? count : 2*count;
524 printf("%8.3lf sec, %7d %6s%s%6s crypts, %7d bytes, %8.0lf byte/sec, %7.1lf Mb/sec\n",
525 t, nops, alg->name, ealg? "+" : "", ealg? ealg->name : "",
526 size, (double)nops*size / t,
527 (double)nops*size / t * 8 / 1024 / 1024);
531 struct cryptostats stats;
532 size_t slen = sizeof (stats);
534 if (sysctlbyname("debug.crypto_timing", NULL, NULL,
535 &otiming, sizeof (otiming)) < 0)
536 perror("debug.crypto_timing");
537 if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, 0) < 0)
538 perror("kern.cryptostats");
539 if (stats.cs_invoke.count) {
540 printt("dispatch->invoke", &stats.cs_invoke);
541 printt("invoke->done", &stats.cs_done);
542 printt("done->cb", &stats.cs_cb);
543 printt("cb->finis", &stats.cs_finis);
551 main(int argc, char **argv)
553 struct alg *alg = NULL, *ealg = NULL;
556 int sizes[128], nsizes = 0;
557 u_long cmd = CIOCGSESSION2;
563 while ((ch = getopt(argc, argv, "cpzsva:bd:t:")) != -1) {
574 tmp = strchr(optarg, '+');
577 ealg = getalgbyname(optarg);
578 if (ealg == NULL || ealg->ishash)
583 alg = getalgbyname(optarg);
585 if (streq(optarg, "rijndael"))
586 alg = getalgbyname("aes");
589 } else if (ealg != NULL && !alg->ishash)
593 crid = crlookup(optarg);
596 maxthreads = atoi(optarg);
605 opflags |= COP_F_BATCH;
614 argc -= optind, argv += optind;
616 count = atoi(argv[0]);
618 int s = atoi(argv[1]);
619 if (nsizes < nitems(sizes)) {
622 printf("Too many sizes, ignoring %u\n", s);
626 if (maxthreads > CPU_SETSIZE)
627 errx(EX_USAGE, "Too many threads, %d, choose fewer.", maxthreads);
631 sizes[nsizes++] = alg->blocksize;
635 while (sizes[nsizes-1] < 8*1024) {
636 sizes[nsizes] = sizes[nsizes-1]<<1;
643 for (i = 0; i < nitems(algorithms); i++) {
645 alg = &algorithms[i];
646 for (j = 0; j < nsizes; j++)
647 runtests(ealg, alg, count, sizes[j], cmd, maxthreads, profile);
651 alg = getalgbycode(CRYPTO_3DES_CBC);
652 for (i = 0; i < nsizes; i++)
653 runtests(ealg, alg, count, sizes[i], cmd, maxthreads, profile);
660 hexdump(char *p, int n)
664 for (off = 0; n > 0; off += 16, n -= 16) {
665 printf("%s%04x:", off == 0 ? "\n" : "", off);
666 i = (n >= 16 ? 16 : n);
668 printf(" %02x", *p++ & 0xff);