xref: /dragonfly/tools/tools/crypto/cryptotest.c (revision c8860c9a)
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
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 }
623