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