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