1 /*-
2 * Copyright (c) 2004 Sam Leffler, Errno Consulting
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
18 *
19 * NO WARRANTY
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, MERCHANTABILITY
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.
31 *
32 * $FreeBSD$
33 */
34
35 /*
36 * Simple tool for testing hardware/system crypto support.
37 *
38 * cryptotest [-czsbv] [-a algorithm] [count] [size ...]
39 *
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
45 * -v be verbose
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:
50 * null null cbc
51 * des des cbc
52 * 3des 3des cbc
53 * blf blowfish cbc
54 * cast cast cbc
55 * skj skipjack cbc
56 * aes rijndael/aes 128-bit cbc
57 * aes192 rijndael/aes 192-bit cbc
58 * aes256 rijndael/aes 256-bit cbc
59 * md5 md5 hmac
60 * sha1 sha1 hmac
61 * sha256 256-bit sha2 hmac
62 * sha384 384-bit sha2 hmac
63 * sha512 512--bit sha2 hmac
64 *
65 * For a test of how fast a crypto card is, use something like:
66 * cryptotest -z 1024
67 * This will run a series of tests using the available crypto/cipher
68 * algorithms over a variety of buffer sizes. The 1024 says to do 1024
69 * iterations. Extra arguments can be used to specify one or more buffer
70 * sizes to use in doing tests.
71 *
72 * To fork multiple processes all doing the same work, specify -t X on the
73 * command line to get X "threads" running simultaneously. No effort is made
74 * to synchronize the threads or otherwise maximize load.
75 *
76 * If the kernel crypto code is built with CRYPTO_TIMING and you run as root,
77 * then you can specify the -p option to get a "profile" of the time spent
78 * processing crypto operations. At present this data is only meaningful for
79 * symmetric operations. To get meaningful numbers you must run on an idle
80 * machine.
81 *
82 * Expect ~400 Mb/s for a Broadcom 582x for 8K buffers on a reasonable CPU
83 * (64-bit PCI helps). Hifn 7811 parts top out at ~110 Mb/s.
84 */
85 #include <sys/types.h>
86 #include <sys/param.h>
87 #include <sys/time.h>
88 #include <sys/ioctl.h>
89 #include <stdio.h>
90 #include <fcntl.h>
91 #include <unistd.h>
92 #include <sys/wait.h>
93 #include <sys/mman.h>
94 #include <paths.h>
95 #include <stdlib.h>
96 #include <string.h>
97
98 #include <sys/sysctl.h>
99 #include <sys/time.h>
100 #include <crypto/cryptodev.h>
101
102 #define CHUNK 64 /* how much to display */
103 #define N(a) (sizeof (a) / sizeof (a[0]))
104 #define streq(a,b) (strcasecmp(a,b) == 0)
105
106 void hexdump(char *, int);
107
108 int verbose = 0;
109 int opflags = 0;
110 int verify = 0;
111 int crid = CRYPTO_FLAG_HARDWARE;
112
113 struct alg {
114 const char* name;
115 int ishash;
116 int blocksize;
117 int minkeylen;
118 int maxkeylen;
119 int code;
120 } algorithms[] = {
121 #ifdef CRYPTO_NULL_CBC
122 { "null", 0, 8, 1, 256, CRYPTO_NULL_CBC },
123 #endif
124 { "des", 0, 8, 8, 8, CRYPTO_DES_CBC },
125 { "3des", 0, 8, 24, 24, CRYPTO_3DES_CBC },
126 { "blf", 0, 8, 5, 56, CRYPTO_BLF_CBC },
127 { "cast", 0, 8, 5, 16, CRYPTO_CAST_CBC },
128 { "skj", 0, 8, 10, 10, CRYPTO_SKIPJACK_CBC },
129 { "aes", 0, 16, 16, 16, CRYPTO_RIJNDAEL128_CBC},
130 { "aes192", 0, 16, 24, 24, CRYPTO_RIJNDAEL128_CBC},
131 { "aes256", 0, 16, 32, 32, CRYPTO_RIJNDAEL128_CBC},
132 #ifdef notdef
133 { "arc4", 0, 8, 1, 32, CRYPTO_ARC4 },
134 #endif
135 { "md5", 1, 8, 16, 16, CRYPTO_MD5_HMAC },
136 { "sha1", 1, 8, 20, 20, CRYPTO_SHA1_HMAC },
137 { "sha256", 1, 8, 32, 32, CRYPTO_SHA2_256_HMAC },
138 { "sha384", 1, 8, 48, 48, CRYPTO_SHA2_384_HMAC },
139 { "sha512", 1, 8, 64, 64, CRYPTO_SHA2_512_HMAC },
140 };
141
142 static void
usage(const char * cmd)143 usage(const char* cmd)
144 {
145 printf("usage: %s [-czsbv] [-d dev] [-a algorithm] [count] [size ...]\n",
146 cmd);
147 printf("where algorithm is one of:\n");
148 printf(" des 3des (default) blowfish cast skipjack\n");
149 printf(" aes (aka rijndael) aes192 aes256 arc4\n");
150 printf("count is the number of encrypt/decrypt ops to do\n");
151 printf("size is the number of bytes of text to encrypt+decrypt\n");
152 printf("\n");
153 printf("-c check the results (slows timing)\n");
154 printf("-d use specific device\n");
155 printf("-z run all available algorithms on a variety of sizes\n");
156 printf("-v be verbose\n");
157 printf("-b mark operations for batching\n");
158 printf("-p profile kernel crypto operation (must be root)\n");
159 exit(-1);
160 }
161
162 static struct alg*
getalgbycode(int cipher)163 getalgbycode(int cipher)
164 {
165 int i;
166
167 for (i = 0; i < N(algorithms); i++)
168 if (cipher == algorithms[i].code)
169 return &algorithms[i];
170 return NULL;
171 }
172
173 static struct alg*
getalgbyname(const char * name)174 getalgbyname(const char* name)
175 {
176 int i;
177
178 for (i = 0; i < N(algorithms); i++)
179 if (streq(name, algorithms[i].name))
180 return &algorithms[i];
181 return NULL;
182 }
183
184 static int
devcrypto(void)185 devcrypto(void)
186 {
187 static int fd = -1;
188
189 if (fd < 0) {
190 fd = open(_PATH_DEV "crypto", O_RDWR, 0);
191 if (fd < 0)
192 err(1, _PATH_DEV "crypto");
193 if (fcntl(fd, F_SETFD, 1) == -1)
194 err(1, "fcntl(F_SETFD) (devcrypto)");
195 }
196 return fd;
197 }
198
199 static int
crlookup(const char * devname)200 crlookup(const char *devname)
201 {
202 struct crypt_find_op find;
203
204 find.crid = -1;
205 strlcpy(find.name, devname, sizeof(find.name));
206 if (ioctl(devcrypto(), CIOCFINDDEV, &find) == -1)
207 err(1, "ioctl(CIOCFINDDEV)");
208 return find.crid;
209 }
210
211 static const char *
crfind(int crid)212 crfind(int crid)
213 {
214 static struct crypt_find_op find;
215
216 bzero(&find, sizeof(find));
217 find.crid = crid;
218 if (ioctl(devcrypto(), CRIOFINDDEV, &find) == -1)
219 err(1, "ioctl(CIOCFINDDEV): crid %d", crid);
220 return find.name;
221 }
222
223 static int
crget(void)224 crget(void)
225 {
226 int fd;
227
228 if (ioctl(devcrypto(), CRIOGET, &fd) == -1)
229 err(1, "ioctl(CRIOGET)");
230 if (fcntl(fd, F_SETFD, 1) == -1)
231 err(1, "fcntl(F_SETFD) (crget)");
232 return fd;
233 }
234
235 static char
rdigit(void)236 rdigit(void)
237 {
238 const char a[] = {
239 0x10,0x54,0x11,0x48,0x45,0x12,0x4f,0x13,0x49,0x53,0x14,0x41,
240 0x15,0x16,0x4e,0x55,0x54,0x17,0x18,0x4a,0x4f,0x42,0x19,0x01
241 };
242 return 0x20+a[random()%N(a)];
243 }
244
245 static void
runtest(struct alg * alg,int count,int size,u_long cmd,struct timeval * tv)246 runtest(struct alg *alg, int count, int size, u_long cmd, struct timeval *tv)
247 {
248 int i, fd = crget();
249 struct timeval start, stop, dt;
250 char *cleartext, *ciphertext, *originaltext;
251 struct session2_op sop;
252 struct crypt_op cop;
253 char iv[EALG_MAX_BLOCK_LEN];
254
255 bzero(&sop, sizeof(sop));
256 if (!alg->ishash) {
257 sop.keylen = (alg->minkeylen + alg->maxkeylen)/2;
258 sop.key = (char *) malloc(sop.keylen);
259 if (sop.key == NULL)
260 err(1, "malloc (key)");
261 for (i = 0; i < sop.keylen; i++)
262 sop.key[i] = rdigit();
263 sop.cipher = alg->code;
264 } else {
265 sop.mackeylen = (alg->minkeylen + alg->maxkeylen)/2;
266 sop.mackey = (char *) malloc(sop.mackeylen);
267 if (sop.mackey == NULL)
268 err(1, "malloc (mac)");
269 for (i = 0; i < sop.mackeylen; i++)
270 sop.mackey[i] = rdigit();
271 sop.mac = alg->code;
272 }
273 sop.crid = crid;
274 if (ioctl(fd, cmd, &sop) < 0) {
275 if (cmd == CIOCGSESSION || cmd == CIOCGSESSION2) {
276 close(fd);
277 if (verbose) {
278 printf("cipher %s", alg->name);
279 if (alg->ishash)
280 printf(" mackeylen %u\n", sop.mackeylen);
281 else
282 printf(" keylen %u\n", sop.keylen);
283 perror("CIOCGSESSION");
284 }
285 /* hardware doesn't support algorithm; skip it */
286 return;
287 }
288 printf("cipher %s keylen %u mackeylen %u\n",
289 alg->name, sop.keylen, sop.mackeylen);
290 err(1, "CIOCGSESSION");
291 }
292
293 originaltext = malloc(3*size);
294 if (originaltext == NULL)
295 err(1, "malloc (text)");
296 cleartext = originaltext+size;
297 ciphertext = cleartext+size;
298 for (i = 0; i < size; i++)
299 cleartext[i] = rdigit();
300 memcpy(originaltext, cleartext, size);
301 for (i = 0; i < N(iv); i++)
302 iv[i] = rdigit();
303
304 if (verbose) {
305 printf("session = 0x%x\n", sop.ses);
306 printf("device = %s\n", crfind(sop.crid));
307 printf("count = %d, size = %d\n", count, size);
308 if (!alg->ishash) {
309 printf("iv:");
310 hexdump(iv, sizeof iv);
311 }
312 printf("cleartext:");
313 hexdump(cleartext, MIN(size, CHUNK));
314 }
315
316 gettimeofday(&start, NULL);
317 if (!alg->ishash) {
318 for (i = 0; i < count; i++) {
319 cop.ses = sop.ses;
320 cop.op = COP_ENCRYPT;
321 cop.flags = opflags;
322 cop.len = size;
323 cop.src = cleartext;
324 cop.dst = ciphertext;
325 cop.mac = 0;
326 cop.iv = iv;
327
328 if (ioctl(fd, CIOCCRYPT, &cop) < 0)
329 err(1, "ioctl(CIOCCRYPT)");
330
331 if (verify && bcmp(ciphertext, cleartext, size) == 0) {
332 printf("cipher text unchanged:");
333 hexdump(ciphertext, size);
334 }
335
336 memset(cleartext, 'x', MIN(size, CHUNK));
337 cop.ses = sop.ses;
338 cop.op = COP_DECRYPT;
339 cop.flags = opflags;
340 cop.len = size;
341 cop.src = ciphertext;
342 cop.dst = cleartext;
343 cop.mac = 0;
344 cop.iv = iv;
345
346 if (ioctl(fd, CIOCCRYPT, &cop) < 0)
347 err(1, "ioctl(CIOCCRYPT)");
348
349 if (verify && bcmp(cleartext, originaltext, size) != 0) {
350 printf("decrypt mismatch:\n");
351 printf("original:");
352 hexdump(originaltext, size);
353 printf("cleartext:");
354 hexdump(cleartext, size);
355 }
356 }
357 } else {
358 for (i = 0; i < count; i++) {
359 cop.ses = sop.ses;
360 cop.op = 0;
361 cop.flags = opflags;
362 cop.len = size;
363 cop.src = cleartext;
364 cop.dst = 0;
365 cop.mac = ciphertext;
366 cop.iv = 0;
367
368 if (ioctl(fd, CIOCCRYPT, &cop) < 0)
369 err(1, "ioctl(CIOCCRYPT)");
370 }
371 }
372 gettimeofday(&stop, NULL);
373
374 if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0)
375 perror("ioctl(CIOCFSESSION)");
376
377 if (verbose) {
378 printf("cleartext:");
379 hexdump(cleartext, MIN(size, CHUNK));
380 }
381 timersub(&stop, &start, tv);
382
383 free(originaltext);
384
385 close(fd);
386 }
387
388 #ifdef __FreeBSD__
389 static void
resetstats()390 resetstats()
391 {
392 struct cryptostats stats;
393 size_t slen;
394
395 slen = sizeof (stats);
396 if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, 0) < 0) {
397 perror("kern.crypto_stats");
398 return;
399 }
400 bzero(&stats.cs_invoke, sizeof (stats.cs_invoke));
401 bzero(&stats.cs_done, sizeof (stats.cs_done));
402 bzero(&stats.cs_cb, sizeof (stats.cs_cb));
403 bzero(&stats.cs_finis, sizeof (stats.cs_finis));
404 stats.cs_invoke.min.tv_sec = 10000;
405 stats.cs_done.min.tv_sec = 10000;
406 stats.cs_cb.min.tv_sec = 10000;
407 stats.cs_finis.min.tv_sec = 10000;
408 if (sysctlbyname("kern.crypto_stats", NULL, NULL, &stats, sizeof (stats)) < 0)
409 perror("kern.cryptostats");
410 }
411
412 static void
printt(const char * tag,struct cryptotstat * ts)413 printt(const char* tag, struct cryptotstat *ts)
414 {
415 uint64_t avg, min, max;
416
417 if (ts->count == 0)
418 return;
419 avg = (1000000000LL*ts->acc.tv_sec + ts->acc.tv_nsec) / ts->count;
420 min = 1000000000LL*ts->min.tv_sec + ts->min.tv_nsec;
421 max = 1000000000LL*ts->max.tv_sec + ts->max.tv_nsec;
422 printf("%16.16s: avg %6llu ns : min %6llu ns : max %7llu ns [%u samps]\n",
423 tag, avg, min, max, ts->count);
424 }
425 #endif
426
427 static void
runtests(struct alg * alg,int count,int size,u_long cmd,int threads,int profile)428 runtests(struct alg *alg, int count, int size, u_long cmd, int threads, int profile)
429 {
430 int i, status;
431 double t;
432 void *region;
433 struct timeval *tvp;
434 struct timeval total;
435 int otiming;
436
437 if (size % alg->blocksize) {
438 if (verbose)
439 printf("skipping blocksize %u 'cuz not a multiple of "
440 "%s blocksize %u\n",
441 size, alg->name, alg->blocksize);
442 return;
443 }
444
445 region = mmap(NULL, threads * sizeof (struct timeval),
446 PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
447 if (region == MAP_FAILED) {
448 perror("mmap");
449 return;
450 }
451 tvp = (struct timeval *) region;
452 #ifdef __FreeBSD__
453 if (profile) {
454 size_t tlen = sizeof (otiming);
455 int timing = 1;
456
457 resetstats();
458 if (sysctlbyname("debug.crypto_timing", &otiming, &tlen,
459 &timing, sizeof (timing)) < 0)
460 perror("debug.crypto_timing");
461 }
462 #endif
463
464 if (threads > 1) {
465 for (i = 0; i < threads; i++)
466 if (fork() == 0) {
467 runtest(alg, count, size, cmd, &tvp[i]);
468 exit(0);
469 }
470 while (waitpid(WAIT_MYPGRP, &status, 0) != -1)
471 ;
472 } else
473 runtest(alg, count, size, cmd, tvp);
474
475 t = 0;
476 for (i = 0; i < threads; i++)
477 t += (((double)tvp[i].tv_sec * 1000000 + tvp[i].tv_usec) / 1000000);
478 if (t) {
479 int nops = alg->ishash ? count : 2*count;
480
481 #if 0
482 t /= threads;
483 printf("%6.3lf sec, %7d %6s crypts, %7d bytes, %8.0lf byte/sec, %7.1lf Mb/sec\n",
484 t, nops, alg->name, size, (double)nops*size / t,
485 (double)nops*size / t * 8 / 1024 / 1024);
486 #else
487 nops *= threads;
488 printf("%8.3lf sec, %7d %6s crypts, %7d bytes, %8.0lf byte/sec, %7.1lf Mb/sec\n",
489 t, nops, alg->name, size, (double)nops*size / t,
490 (double)nops*size / t * 8 / 1024 / 1024);
491 #endif
492 }
493 #ifdef __FreeBSD__
494 if (profile) {
495 struct cryptostats stats;
496 size_t slen = sizeof (stats);
497
498 if (sysctlbyname("debug.crypto_timing", NULL, NULL,
499 &otiming, sizeof (otiming)) < 0)
500 perror("debug.crypto_timing");
501 if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, 0) < 0)
502 perror("kern.cryptostats");
503 if (stats.cs_invoke.count) {
504 printt("dispatch->invoke", &stats.cs_invoke);
505 printt("invoke->done", &stats.cs_done);
506 printt("done->cb", &stats.cs_cb);
507 printt("cb->finis", &stats.cs_finis);
508 }
509 }
510 #endif
511 fflush(stdout);
512 }
513
514 int
main(int argc,char ** argv)515 main(int argc, char **argv)
516 {
517 struct alg *alg = NULL;
518 int count = 1;
519 int sizes[128], nsizes = 0;
520 u_long cmd = CIOCGSESSION2;
521 int testall = 0;
522 int maxthreads = 1;
523 int profile = 0;
524 int i, ch;
525
526 while ((ch = getopt(argc, argv, "cpzsva:bd:t:")) != -1) {
527 switch (ch) {
528 #ifdef CIOCGSSESSION
529 case 's':
530 cmd = CIOCGSSESSION;
531 break;
532 #endif
533 case 'v':
534 verbose++;
535 break;
536 case 'a':
537 alg = getalgbyname(optarg);
538 if (alg == NULL) {
539 if (streq(optarg, "rijndael"))
540 alg = getalgbyname("aes");
541 else
542 usage(argv[0]);
543 }
544 break;
545 case 'd':
546 crid = crlookup(optarg);
547 break;
548 case 't':
549 maxthreads = atoi(optarg);
550 break;
551 case 'z':
552 testall = 1;
553 break;
554 case 'p':
555 profile = 1;
556 break;
557 case 'b':
558 opflags |= COP_F_BATCH;
559 break;
560 case 'c':
561 verify = 1;
562 break;
563 default:
564 usage(argv[0]);
565 }
566 }
567 argc -= optind, argv += optind;
568 if (argc > 0)
569 count = atoi(argv[0]);
570 while (argc > 1) {
571 int s = atoi(argv[1]);
572 if (nsizes < N(sizes)) {
573 sizes[nsizes++] = s;
574 } else {
575 printf("Too many sizes, ignoring %u\n", s);
576 }
577 argc--, argv++;
578 }
579 if (nsizes == 0) {
580 if (alg)
581 sizes[nsizes++] = alg->blocksize;
582 else
583 sizes[nsizes++] = 8;
584 if (testall) {
585 while (sizes[nsizes-1] < 8*1024) {
586 sizes[nsizes] = sizes[nsizes-1]<<1;
587 nsizes++;
588 }
589 }
590 }
591
592 if (testall) {
593 for (i = 0; i < N(algorithms); i++) {
594 int j;
595 alg = &algorithms[i];
596 for (j = 0; j < nsizes; j++)
597 runtests(alg, count, sizes[j], cmd, maxthreads, profile);
598 }
599 } else {
600 if (alg == NULL)
601 alg = getalgbycode(CRYPTO_3DES_CBC);
602 for (i = 0; i < nsizes; i++)
603 runtests(alg, count, sizes[i], cmd, maxthreads, profile);
604 }
605
606 return (0);
607 }
608
609 void
hexdump(char * p,int n)610 hexdump(char *p, int n)
611 {
612 int i, off;
613
614 for (off = 0; n > 0; off += 16, n -= 16) {
615 printf("%s%04x:", off == 0 ? "\n" : "", off);
616 i = (n >= 16 ? 16 : n);
617 do {
618 printf(" %02x", *p++ & 0xff);
619 } while (--i);
620 printf("\n");
621 }
622 }
623