Merge remote-tracking branch 'origin/vendor/LIBRESSL'
[dragonfly.git] / tools / tools / crypto / cryptotest.c
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, 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.
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
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*
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*
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
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
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 *
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
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
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
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
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
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
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
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
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 }