Initial import from FreeBSD RELENG_4:
[dragonfly.git] / tools / tools / crypto / cryptotest.c
1 /* $FreeBSD: src/tools/tools/crypto/cryptotest.c,v 1.3.2.2 2003/06/03 00:14:44 sam Exp $ */
2
3 /*
4  * This program repeatedly encrypts and decrypts a buffer with the built-in
5  * iv and key, using hardware crypto.  At the end, it computes the data rate
6  * achieved.  invoke with the number of times to encrypt and the buffer size.
7  *
8  * For a test of how fast a crypto card is, use something like:
9  *      cryptotest -z 1024
10  * This will run a series of tests using the available crypto/cipher
11  * algorithms over a variety of buffer sizes.  The 1024 says to do 1024
12  * iterations.  Extra arguments can be used to specify one or more buffer
13  * sizes to use in doing tests.
14  *
15  * To fork multiple processes all doing the same work, specify -t X on the
16  * command line to get X "threads" running simultaneously.  No effort is made
17  * synchronize the threads or otherwise maximize load.
18  *
19  * If the kernel crypto code is built with CRYPTO_TIMING and you run as root,
20  * then you can specify the -p option to get a "profile" of the time spent
21  * processing crypto operations.  At present this data is only meaningful for
22  * symmetric operations.  To get meaningful numbers you must run on an idle
23  * machine.
24  *
25  * Expect ~400 Mb/s for a Broadcom 582x for 8K buffers on a reasonable CPU
26  * (64-bit PCI helps).  Hifn 7811 parts top out at ~110 Mb/s.
27  *
28  * This code originally came from openbsd; give them all the credit.
29  */
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <sys/ioctl.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <sys/wait.h>
38 #include <sys/mman.h>
39
40 #include <sys/sysctl.h>
41 #include <sys/time.h>
42 #include <crypto/cryptodev.h>
43
44 #define CHUNK   64      /* how much to display */
45 #define N(a)            (sizeof (a) / sizeof (a[0]))
46 #define streq(a,b)      (strcasecmp(a,b) == 0)
47
48 void    hexdump(char *, int);
49
50 int     cryptodev_fd;
51 int     fd;
52 struct  session_op session;
53 struct  crypt_op cryptop;
54 char    iv[8] = "00000000";
55 int     verbose = 0;
56 int     opflags = 0;
57 int     verify = 0;
58
59 struct alg {
60         const char* name;
61         int     ishash;
62         int     blocksize;
63         int     minkeylen;
64         int     maxkeylen;
65         int     code;
66 } algorithms[] = {
67 #ifdef CRYPTO_NULL_CBC
68         { "null",       0,      8,      1,      256,    CRYPTO_NULL_CBC },
69 #endif
70         { "des",        0,      8,      8,      8,      CRYPTO_DES_CBC },
71         { "3des",       0,      8,      24,     24,     CRYPTO_3DES_CBC },
72         { "blf",        0,      8,      5,      56,     CRYPTO_BLF_CBC },
73         { "cast",       0,      8,      5,      16,     CRYPTO_CAST_CBC },
74         { "skj",        0,      8,      10,     10,     CRYPTO_SKIPJACK_CBC },
75         { "aes",        0,      16,     16,     16,     CRYPTO_RIJNDAEL128_CBC},
76         { "aes192",     0,      16,     24,     24,     CRYPTO_RIJNDAEL128_CBC},
77         { "aes256",     0,      16,     32,     32,     CRYPTO_RIJNDAEL128_CBC},
78 #ifdef notdef
79         { "arc4",       0,      8,      1,      32,     CRYPTO_ARC4 },
80 #endif
81         { "md5",        1,      8,      16,     16,     CRYPTO_MD5_HMAC },
82         { "sha1",       1,      8,      20,     20,     CRYPTO_SHA1_HMAC },
83         { "sha256",     1,      8,      32,     32,     CRYPTO_SHA2_HMAC },
84         { "sha384",     1,      8,      48,     48,     CRYPTO_SHA2_HMAC },
85         { "sha512",     1,      8,      64,     64,     CRYPTO_SHA2_HMAC },
86 };
87
88 static void
89 usage(const char* cmd)
90 {
91         printf("usage: %s [-c] [-z] [-s] [-b] [-v] [-a algorithm] [count] [size ...]\n",
92                 cmd);
93         printf("where algorithm is one of:\n");
94         printf("    des 3des (default) blowfish cast skipjack\n");
95         printf("    aes (aka rijndael) aes192 aes256 arc4\n");
96         printf("count is the number of encrypt/decrypt ops to do\n");
97         printf("size is the number of bytes of text to encrypt+decrypt\n");
98         printf("\n");
99         printf("-c check the results (slows timing)\n");
100         printf("-z run all available algorithms on a variety of sizes\n");
101         printf("-v be verbose\n");
102         printf("-b mark operations for batching\n");
103         printf("-p profile kernel crypto operation (must be root)\n");
104         exit(-1);
105 }
106
107 static struct alg*
108 getalgbycode(int cipher)
109 {
110         int i;
111
112         for (i = 0; i < N(algorithms); i++)
113                 if (cipher == algorithms[i].code)
114                         return &algorithms[i];
115         return NULL;
116 }
117
118 static struct alg*
119 getalgbyname(const char* name)
120 {
121         int i;
122
123         for (i = 0; i < N(algorithms); i++)
124                 if (streq(name, algorithms[i].name))
125                         return &algorithms[i];
126         return NULL;
127 }
128
129 static void
130 runtest(struct alg *alg, int count, int size, int cmd, struct timeval *tv)
131 {
132         int i;
133         struct timeval start, stop, dt;
134         char *cleartext, *ciphertext, *originaltext;
135
136         if (ioctl(cryptodev_fd,CRIOGET,&fd) == -1)
137                 err(1, "CRIOGET failed");
138
139         bzero(&session, sizeof(session));
140         if (!alg->ishash) {
141                 session.keylen = (alg->minkeylen + alg->maxkeylen)/2;
142                 session.key = (char *) malloc(session.keylen);
143                 if (session.key == NULL)
144                         err(1, "malloc (key)");
145                 for (i = 0; i < session.keylen; i++)
146                         session.key[i] = '0' + (i%10);
147                 session.cipher = alg->code;
148         } else {
149                 session.mackeylen = (alg->minkeylen + alg->maxkeylen)/2;
150                 session.mackey = (char *) malloc(session.mackeylen);
151                 if (session.mackey == NULL)
152                         err(1, "malloc (mac)");
153                 for (i = 0; i < session.mackeylen; i++)
154                         session.mackey[i] = '0' + (i%10);
155                 session.mac = alg->code;
156         }
157         if (ioctl(fd, cmd, &session) == -1) {
158                 if (cmd == CIOCGSESSION) {
159                         close(fd);
160                         if (verbose) {
161                                 printf("cipher %s", alg->name);
162                                 if (alg->ishash)
163                                         printf(" mackeylen %u\n", session.mackeylen);
164                                 else
165                                         printf(" keylen %u\n", session.keylen);
166                                 perror("CIOCGSESSION");
167                         }
168                         /* hardware doesn't support algorithm; skip it */
169                         return;
170                 }
171                 printf("cipher %s keylen %u mackeylen %u\n",
172                         alg->name, session.keylen, session.mackeylen);
173                 err(1, "CIOCGSESSION failed");
174         }
175
176         if ((originaltext = (char *)malloc(size)) == NULL)
177                 err(1, "malloc (originaltext)");
178         if ((cleartext = (char *)malloc(size)) == NULL)
179                 err(1, "malloc (cleartext)");
180         if ((ciphertext = (char *)malloc(size)) == NULL)
181                 err(1, "malloc (ciphertext)");
182         for (i = 0; i < size; i++)
183                 cleartext[i] = 'a' + i%26;
184         memcpy(originaltext, cleartext, size);
185
186         if (verbose) {
187                 printf("session = 0x%x\n", session.ses);
188                 printf("count = %d, size = %d\n", count, size);
189                 cryptop.ses = session.ses;
190                 if (!alg->ishash) {
191                         printf("iv:");
192                         hexdump(iv, sizeof iv);
193                 }
194                 printf("cleartext:");
195                 hexdump(cleartext, MIN(size, CHUNK));
196         }
197
198         gettimeofday(&start, NULL);
199         if (!alg->ishash) {
200                 for (i = 0; i < count; i++) {
201                         cryptop.op = COP_ENCRYPT;
202                         cryptop.flags = opflags;
203                         cryptop.len = size;
204                         cryptop.src = cleartext;
205                         cryptop.dst = ciphertext;
206                         cryptop.mac = 0;
207                         cryptop.iv = iv;
208
209                         if (ioctl(fd, CIOCCRYPT, &cryptop) == -1)
210                                 err(1, "CIOCCRYPT failed");
211
212                         if (verify && bcmp(ciphertext, cleartext, size) == 0) {
213                                 printf("cipher text unchanged:");
214                                 hexdump(ciphertext, size);
215                         }
216
217                         memset(cleartext, 'x', MIN(size, CHUNK));
218                         cryptop.op = COP_DECRYPT;
219                         cryptop.flags = opflags;
220                         cryptop.len = size;
221                         cryptop.src = ciphertext;
222                         cryptop.dst = cleartext;
223                         cryptop.mac = 0;
224                         cryptop.iv = iv;
225
226                         if (ioctl(fd, CIOCCRYPT, &cryptop) == -1)
227                                 err(1, "CIOCCRYPT failed");
228
229                         if (verify && bcmp(cleartext, originaltext, size) != 0) {
230                                 printf("decrypt mismatch:\n");
231                                 printf("original:");
232                                 hexdump(originaltext, size);
233                                 printf("cleartext:");
234                                 hexdump(cleartext, size);
235                         }
236                 }
237         } else {
238                 for (i = 0; i < count; i++) {
239                         cryptop.op = 0;
240                         cryptop.flags = opflags;
241                         cryptop.len = size;
242                         cryptop.src = cleartext;
243                         cryptop.dst = 0;
244                         cryptop.mac = ciphertext;
245                         cryptop.iv = 0;
246
247                         if (ioctl(fd, CIOCCRYPT, &cryptop) == -1)
248                                 err(1, "CIOCCRYPT failed");
249                 }
250         }
251         gettimeofday(&stop, NULL);
252  
253         if (ioctl(fd, CIOCFSESSION, &session.ses) == -1)
254                 perror("CIOCFSESSION");
255
256         if (verbose) {
257                 printf("cleartext:");
258                 hexdump(cleartext, MIN(size, CHUNK));
259         }
260         timersub(&stop, &start, tv);
261
262         free(ciphertext);
263         free(cleartext);
264
265         close(fd);
266 }
267
268 #ifdef __FreeBSD__
269 static void
270 resetstats()
271 {
272         struct cryptostats stats;
273         size_t slen;
274
275         slen = sizeof (stats);
276         if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, NULL) < 0) {
277                 perror("kern.crypto_stats");
278                 return;
279         }
280         bzero(&stats.cs_invoke, sizeof (stats.cs_invoke));
281         bzero(&stats.cs_done, sizeof (stats.cs_done));
282         bzero(&stats.cs_cb, sizeof (stats.cs_cb));
283         bzero(&stats.cs_finis, sizeof (stats.cs_finis));
284         stats.cs_invoke.min.tv_sec = 10000;
285         stats.cs_done.min.tv_sec = 10000;
286         stats.cs_cb.min.tv_sec = 10000;
287         stats.cs_finis.min.tv_sec = 10000;
288         if (sysctlbyname("kern.crypto_stats", NULL, NULL, &stats, sizeof (stats)) < 0)
289                 perror("kern.cryptostats");
290 }
291
292 static void
293 printt(const char* tag, struct cryptotstat *ts)
294 {
295         uint64_t avg, min, max;
296
297         if (ts->count == 0)
298                 return;
299         avg = (1000000000LL*ts->acc.tv_sec + ts->acc.tv_nsec) / ts->count;
300         min = 1000000000LL*ts->min.tv_sec + ts->min.tv_nsec;
301         max = 1000000000LL*ts->max.tv_sec + ts->max.tv_nsec;
302         printf("%16.16s: avg %6llu ns : min %6llu ns : max %7llu ns [%u samps]\n",
303                 tag, avg, min, max, ts->count);
304 }
305 #endif
306
307 static void
308 runtests(struct alg *alg, int count, int size, int cmd, int threads, int profile)
309 {
310         int i, status;
311         double t;
312         void *region;
313         struct timeval *tvp;
314         struct timeval total;
315         int otiming;
316
317         if (size % alg->blocksize) {
318                 if (verbose)
319                         printf("skipping blocksize %u 'cuz not a multiple of "
320                                 "%s blocksize %u\n",
321                                 size, alg->name, alg->blocksize);
322                 return;
323         }
324
325         region = mmap(NULL, threads * sizeof (struct timeval),
326                         PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
327         if (region == MAP_FAILED) {
328                 perror("mmap");
329                 return;
330         }
331         tvp = (struct timeval *) region;
332 #ifdef __FreeBSD__
333         if (profile) {
334                 size_t tlen = sizeof (otiming);
335                 int timing = 1;
336
337                 resetstats();
338                 if (sysctlbyname("debug.crypto_timing", &otiming, &tlen,
339                                 &timing, sizeof (timing)) < 0)
340                         perror("debug.crypto_timing");
341         }
342 #endif
343
344         if (threads > 1) {
345                 for (i = 0; i < threads; i++)
346                         if (fork() == 0) {
347                                 runtest(alg, count, size, cmd, &tvp[i]);
348                                 exit(0);
349                         }
350                 while (waitpid(WAIT_MYPGRP, &status, 0) != -1)
351                         ;
352         } else
353                 runtest(alg, count, size, cmd, tvp);
354
355         t = 0;
356         for (i = 0; i < threads; i++)
357                 t += (((double)tvp[i].tv_sec * 1000000 + tvp[i].tv_usec) / 1000000);
358         if (t) {
359                 int nops = alg->ishash ? count : 2*count;
360
361                 t /= threads;
362                 printf("%6.3lf sec, %7d %6s crypts, %7d bytes, %8.0lf byte/sec, %7.1lf Mb/sec\n",
363                     t, nops, alg->name, size, (double)nops*size / t,
364                     (double)nops*size / t * 8 / 1024 / 1024);
365         }
366 #ifdef __FreeBSD__
367         if (profile) {
368                 struct cryptostats stats;
369                 size_t slen = sizeof (stats);
370
371                 if (sysctlbyname("debug.crypto_timing", NULL, NULL,
372                                 &otiming, sizeof (otiming)) < 0)
373                         perror("debug.crypto_timing");
374                 if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, NULL) < 0)
375                         perror("kern.cryptostats");
376                 if (stats.cs_invoke.count) {
377                         printt("dispatch->invoke", &stats.cs_invoke);
378                         printt("invoke->done", &stats.cs_done);
379                         printt("done->cb", &stats.cs_cb);
380                         printt("cb->finis", &stats.cs_finis);
381                 }
382         }
383 #endif
384         fflush(stdout);
385 }
386
387 int
388 main(int argc, char **argv)
389 {
390         struct alg *alg = NULL;
391         int count = 1;
392         int sizes[128], nsizes = 0;
393         int cmd = CIOCGSESSION;
394         int testall = 0;
395         int maxthreads = 1;
396         int profile = 0;
397         int i, ch;
398
399         while ((ch = getopt(argc, argv, "cpzsva:bt:")) != -1) {
400                 switch (ch) {
401 #ifdef CIOCGSSESSION
402                 case 's':
403                         cmd = CIOCGSSESSION;
404                         break;
405 #endif
406                 case 'v':
407                         verbose++;
408                         break;
409                 case 'a':
410                         alg = getalgbyname(optarg);
411                         if (alg == NULL) {
412                                 if (streq(optarg, "rijndael"))
413                                         alg = getalgbyname("aes");
414                                 else
415                                         usage(argv[0]);
416                         }
417                         break;
418                 case 't':
419                         maxthreads = atoi(optarg);
420                         break;
421                 case 'z':
422                         testall = 1;
423                         break;
424                 case 'p':
425                         profile = 1;
426                         break;
427                 case 'b':
428                         opflags |= COP_F_BATCH;
429                         break;
430                 case 'c':
431                         verify = 1;
432                         break;
433                 default:
434                         usage(argv[0]);
435                 }
436         }
437         argc -= optind, argv += optind;
438         if (argc > 0)
439                 count = atoi(argv[0]);
440         while (argc > 1) {
441                 int s = atoi(argv[1]);
442                 if (nsizes < N(sizes)) {
443                         sizes[nsizes++] = s;
444                 } else {
445                         printf("Too many sizes, ignoring %u\n", s);
446                 }
447                 argc--, argv++;
448         }
449         if (nsizes == 0) {
450                 if (alg)
451                         sizes[nsizes++] = alg->blocksize;
452                 else
453                         sizes[nsizes++] = 8;
454                 if (testall) {
455                         while (sizes[nsizes-1] < 8*1024) {
456                                 sizes[nsizes] = sizes[nsizes-1]<<1;
457                                 nsizes++;
458                         }
459                 }
460         }
461
462         if ((cryptodev_fd = open("/dev/crypto",O_RDWR,0)) < 0)
463                 err(1, "/dev/crypto");
464
465         if (testall) {
466                 for (i = 0; i < N(algorithms); i++) {
467                         int j;
468                         alg = &algorithms[i];
469                         for (j = 0; j < nsizes; j++)
470                                 runtests(alg, count, sizes[j], cmd, maxthreads, profile);
471                 }
472         } else {
473                 if (alg == NULL)
474                         alg = getalgbycode(CRYPTO_3DES_CBC);
475                 for (i = 0; i < nsizes; i++)
476                         runtests(alg, count, sizes[i], cmd, maxthreads, profile);
477         }
478
479         return (0);
480 }
481
482 void
483 hexdump(char *p, int n)
484 {
485         int i;
486         for (i = 0; i < n; i++) {
487                 if (i%16 == 0) {
488                         if (i != 0) {
489                                 int j;
490                                 char *l = p-16;
491                                 printf("  |");
492                                 for (j = 0; j < 16; j++,l++)
493                                         printf("%c", (((*l)&0xff)>0x1f && ((*l)&0xff)<0x7f) ? (*l)&0xff : '.');
494                                 printf("|");
495                         }
496                         printf("\n%04x: ", i);
497                 }
498                 printf(" %02x", (int)(*p++)&0xff);
499         }
500         printf("\n");
501 }