cryptotest.c revision 329343
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: stable/11/tools/tools/crypto/cryptotest.c 329343 2018-02-15 21:27:24Z jhb $
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
86#include <sys/param.h>
87#include <sys/cpuset.h>
88#include <sys/ioctl.h>
89#include <sys/mman.h>
90#include <sys/sysctl.h>
91#include <sys/time.h>
92#include <sys/wait.h>
93
94#include <err.h>
95#include <fcntl.h>
96#include <paths.h>
97#include <stdio.h>
98#include <stdlib.h>
99#include <string.h>
100#include <sysexits.h>
101#include <unistd.h>
102
103#include <crypto/cryptodev.h>
104
105#define	CHUNK	64	/* how much to display */
106#define	streq(a,b)	(strcasecmp(a,b) == 0)
107
108void	hexdump(char *, int);
109
110int	verbose = 0;
111int	opflags = 0;
112int	verify = 0;
113int	crid = CRYPTO_FLAG_HARDWARE;
114
115struct alg {
116	const char* name;
117	int	ishash;
118	int	blocksize;
119	int	minkeylen;
120	int	maxkeylen;
121	int	code;
122} algorithms[] = {
123#ifdef CRYPTO_NULL_CBC
124	{ "null",	0,	8,	1,	256,	CRYPTO_NULL_CBC },
125#endif
126	{ "des",	0,	8,	8,	8,	CRYPTO_DES_CBC },
127	{ "3des",	0,	8,	24,	24,	CRYPTO_3DES_CBC },
128	{ "blf",	0,	8,	5,	56,	CRYPTO_BLF_CBC },
129	{ "cast",	0,	8,	5,	16,	CRYPTO_CAST_CBC },
130	{ "skj",	0,	8,	10,	10,	CRYPTO_SKIPJACK_CBC },
131	{ "rij",	0,	16,	16,	16,	CRYPTO_RIJNDAEL128_CBC},
132	{ "aes",	0,	16,	16,	16,	CRYPTO_AES_CBC},
133	{ "aes192",	0,	16,	24,	24,	CRYPTO_AES_CBC},
134	{ "aes256",	0,	16,	32,	32,	CRYPTO_AES_CBC},
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
142void
143usage(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("    null des 3des (default) blowfish cast skipjack rij\n");
149	printf("    aes aes192 aes256 md5 sha1 sha256 sha384 sha512\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, specify 'soft' for testing software implementations\n");
155	printf("\tNOTE: to use software you must set:\n\t sysctl kern.cryptodevallowsoft=1\n");
156	printf("-z run all available algorithms on a variety of sizes\n");
157	printf("-v be verbose\n");
158	printf("-b mark operations for batching\n");
159	printf("-p profile kernel crypto operation (must be root)\n");
160	printf("-t n for n threads and run tests concurrently\n");
161	exit(-1);
162}
163
164struct alg*
165getalgbycode(int cipher)
166{
167	int i;
168
169	for (i = 0; i < nitems(algorithms); i++)
170		if (cipher == algorithms[i].code)
171			return &algorithms[i];
172	return NULL;
173}
174
175struct alg*
176getalgbyname(const char* name)
177{
178	int i;
179
180	for (i = 0; i < nitems(algorithms); i++)
181		if (streq(name, algorithms[i].name))
182			return &algorithms[i];
183	return NULL;
184}
185
186int
187devcrypto(void)
188{
189	int fd = -1;
190
191	if (fd < 0) {
192		fd = open(_PATH_DEV "crypto", O_RDWR, 0);
193		if (fd < 0)
194			err(1, _PATH_DEV "crypto");
195		if (fcntl(fd, F_SETFD, 1) == -1)
196			err(1, "fcntl(F_SETFD) (devcrypto)");
197	}
198	return fd;
199}
200
201int
202crlookup(const char *devname)
203{
204	struct crypt_find_op find;
205
206	if (strncmp(devname, "soft", 4) == 0)
207		return CRYPTO_FLAG_SOFTWARE;
208
209	find.crid = -1;
210	strlcpy(find.name, devname, sizeof(find.name));
211	if (ioctl(devcrypto(), CIOCFINDDEV, &find) == -1)
212		err(1, "ioctl(CIOCFINDDEV)");
213	return find.crid;
214}
215
216const char *
217crfind(int crid)
218{
219	static struct crypt_find_op find;
220
221	bzero(&find, sizeof(find));
222	find.crid = crid;
223	if (ioctl(devcrypto(), CRIOFINDDEV, &find) == -1)
224		err(1, "ioctl(CIOCFINDDEV): crid %d", crid);
225	return find.name;
226}
227
228int
229crget(void)
230{
231	int fd;
232
233	if (ioctl(devcrypto(), CRIOGET, &fd) == -1)
234		err(1, "ioctl(CRIOGET)");
235	if (fcntl(fd, F_SETFD, 1) == -1)
236		err(1, "fcntl(F_SETFD) (crget)");
237	return fd;
238}
239
240char
241rdigit(void)
242{
243	const char a[] = {
244		0x10,0x54,0x11,0x48,0x45,0x12,0x4f,0x13,0x49,0x53,0x14,0x41,
245		0x15,0x16,0x4e,0x55,0x54,0x17,0x18,0x4a,0x4f,0x42,0x19,0x01
246	};
247	return 0x20+a[random()%nitems(a)];
248}
249
250void
251runtest(struct alg *alg, int count, int size, u_long cmd, struct timeval *tv)
252{
253	int i, fd = crget();
254	struct timeval start, stop, dt;
255	char *cleartext, *ciphertext, *originaltext, *key;
256	struct session2_op sop;
257	struct crypt_op cop;
258	char iv[EALG_MAX_BLOCK_LEN];
259
260	bzero(&sop, sizeof(sop));
261	if (!alg->ishash) {
262		sop.keylen = (alg->minkeylen + alg->maxkeylen)/2;
263		key = (char *) malloc(sop.keylen);
264		if (key == NULL)
265			err(1, "malloc (key)");
266		for (i = 0; i < sop.keylen; i++)
267			key[i] = rdigit();
268		sop.key = key;
269		sop.cipher = alg->code;
270	} else {
271		sop.mackeylen = (alg->minkeylen + alg->maxkeylen)/2;
272		key = (char *) malloc(sop.mackeylen);
273		if (key == NULL)
274			err(1, "malloc (mac)");
275		for (i = 0; i < sop.mackeylen; i++)
276			key[i] = rdigit();
277		sop.mackey = key;
278		sop.mac = alg->code;
279	}
280	sop.crid = crid;
281	if (ioctl(fd, cmd, &sop) < 0) {
282		if (cmd == CIOCGSESSION || cmd == CIOCGSESSION2) {
283			close(fd);
284			if (verbose) {
285				printf("cipher %s", alg->name);
286				if (alg->ishash)
287					printf(" mackeylen %u\n", sop.mackeylen);
288				else
289					printf(" keylen %u\n", sop.keylen);
290				perror("CIOCGSESSION");
291			}
292			/* hardware doesn't support algorithm; skip it */
293			return;
294		}
295		printf("cipher %s keylen %u mackeylen %u\n",
296			alg->name, sop.keylen, sop.mackeylen);
297		err(1, "CIOCGSESSION");
298	}
299
300	originaltext = malloc(3*size);
301	if (originaltext == NULL)
302		err(1, "malloc (text)");
303	cleartext = originaltext+size;
304	ciphertext = cleartext+size;
305	for (i = 0; i < size; i++)
306		cleartext[i] = rdigit();
307	memcpy(originaltext, cleartext, size);
308	for (i = 0; i < nitems(iv); i++)
309		iv[i] = rdigit();
310
311	if (verbose) {
312		printf("session = 0x%x\n", sop.ses);
313		printf("device = %s\n", crfind(sop.crid));
314		printf("count = %d, size = %d\n", count, size);
315		if (!alg->ishash) {
316			printf("iv:");
317			hexdump(iv, sizeof iv);
318		}
319		printf("cleartext:");
320		hexdump(cleartext, MIN(size, CHUNK));
321	}
322
323	gettimeofday(&start, NULL);
324	if (!alg->ishash) {
325		for (i = 0; i < count; i++) {
326			cop.ses = sop.ses;
327			cop.op = COP_ENCRYPT;
328			cop.flags = opflags;
329			cop.len = size;
330			cop.src = cleartext;
331			cop.dst = ciphertext;
332			cop.mac = 0;
333			cop.iv = iv;
334
335			if (ioctl(fd, CIOCCRYPT, &cop) < 0)
336				err(1, "ioctl(CIOCCRYPT)");
337
338			if (verify && bcmp(ciphertext, cleartext, size) == 0) {
339				printf("cipher text unchanged:");
340				hexdump(ciphertext, size);
341			}
342
343			memset(cleartext, 'x', MIN(size, CHUNK));
344			cop.ses = sop.ses;
345			cop.op = COP_DECRYPT;
346			cop.flags = opflags;
347			cop.len = size;
348			cop.src = ciphertext;
349			cop.dst = cleartext;
350			cop.mac = 0;
351			cop.iv = iv;
352
353			if (ioctl(fd, CIOCCRYPT, &cop) < 0)
354				err(1, "ioctl(CIOCCRYPT)");
355
356			if (verify && bcmp(cleartext, originaltext, size) != 0) {
357				printf("decrypt mismatch:\n");
358				printf("original:");
359				hexdump(originaltext, size);
360				printf("cleartext:");
361				hexdump(cleartext, size);
362			}
363		}
364	} else {
365		for (i = 0; i < count; i++) {
366			cop.ses = sop.ses;
367			cop.op = 0;
368			cop.flags = opflags;
369			cop.len = size;
370			cop.src = cleartext;
371			cop.dst = 0;
372			cop.mac = ciphertext;
373			cop.iv = 0;
374
375			if (ioctl(fd, CIOCCRYPT, &cop) < 0)
376				err(1, "ioctl(CIOCCRYPT)");
377		}
378	}
379	gettimeofday(&stop, NULL);
380
381	if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0)
382		perror("ioctl(CIOCFSESSION)");
383
384	if (verbose) {
385		printf("cleartext:");
386		hexdump(cleartext, MIN(size, CHUNK));
387	}
388	timersub(&stop, &start, tv);
389
390	free(originaltext);
391
392	close(fd);
393}
394
395#ifdef __FreeBSD__
396void
397resetstats()
398{
399	struct cryptostats stats;
400	size_t slen;
401
402	slen = sizeof (stats);
403	if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, 0) < 0) {
404		perror("kern.crypto_stats");
405		return;
406	}
407	bzero(&stats.cs_invoke, sizeof (stats.cs_invoke));
408	bzero(&stats.cs_done, sizeof (stats.cs_done));
409	bzero(&stats.cs_cb, sizeof (stats.cs_cb));
410	bzero(&stats.cs_finis, sizeof (stats.cs_finis));
411	stats.cs_invoke.min.tv_sec = 10000;
412	stats.cs_done.min.tv_sec = 10000;
413	stats.cs_cb.min.tv_sec = 10000;
414	stats.cs_finis.min.tv_sec = 10000;
415	if (sysctlbyname("kern.crypto_stats", NULL, NULL, &stats, sizeof (stats)) < 0)
416		perror("kern.cryptostats");
417}
418
419void
420printt(const char* tag, struct cryptotstat *ts)
421{
422	uint64_t avg, min, max;
423
424	if (ts->count == 0)
425		return;
426	avg = (1000000000LL*ts->acc.tv_sec + ts->acc.tv_nsec) / ts->count;
427	min = 1000000000LL*ts->min.tv_sec + ts->min.tv_nsec;
428	max = 1000000000LL*ts->max.tv_sec + ts->max.tv_nsec;
429	printf("%16.16s: avg %6llu ns : min %6llu ns : max %7llu ns [%u samps]\n",
430		tag, avg, min, max, ts->count);
431}
432#endif
433
434void
435runtests(struct alg *alg, int count, int size, u_long cmd, int threads, int profile)
436{
437	int i, status;
438	double t;
439	void *region;
440	struct timeval *tvp;
441	struct timeval total;
442	int otiming;
443
444	if (size % alg->blocksize) {
445		if (verbose)
446			printf("skipping blocksize %u 'cuz not a multiple of "
447				"%s blocksize %u\n",
448				size, alg->name, alg->blocksize);
449		return;
450	}
451
452	region = mmap(NULL, threads * sizeof (struct timeval),
453			PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
454	if (region == MAP_FAILED) {
455		perror("mmap");
456		return;
457	}
458	tvp = (struct timeval *) region;
459#ifdef __FreeBSD__
460	if (profile) {
461		size_t tlen = sizeof (otiming);
462		int timing = 1;
463
464		resetstats();
465		if (sysctlbyname("debug.crypto_timing", &otiming, &tlen,
466				&timing, sizeof (timing)) < 0)
467			perror("debug.crypto_timing");
468	}
469#endif
470
471	if (threads > 1) {
472		for (i = 0; i < threads; i++)
473			if (fork() == 0) {
474				cpuset_t mask;
475				CPU_ZERO(&mask);
476				CPU_SET(i, &mask);
477				cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
478				    -1, sizeof(mask), &mask);
479				runtest(alg, count, size, cmd, &tvp[i]);
480				exit(0);
481			}
482		while (waitpid(WAIT_MYPGRP, &status, 0) != -1)
483			;
484	} else
485		runtest(alg, count, size, cmd, tvp);
486
487	t = 0;
488	for (i = 0; i < threads; i++)
489		t += (((double)tvp[i].tv_sec * 1000000 + tvp[i].tv_usec) / 1000000);
490	if (t) {
491		int nops = alg->ishash ? count : 2*count;
492
493		nops *= threads;
494		printf("%8.3lf sec, %7d %6s crypts, %7d bytes, %8.0lf byte/sec, %7.1lf Mb/sec\n",
495		    t, nops, alg->name, size, (double)nops*size / t,
496		    (double)nops*size / t * 8 / 1024 / 1024);
497	}
498#ifdef __FreeBSD__
499	if (profile) {
500		struct cryptostats stats;
501		size_t slen = sizeof (stats);
502
503		if (sysctlbyname("debug.crypto_timing", NULL, NULL,
504				&otiming, sizeof (otiming)) < 0)
505			perror("debug.crypto_timing");
506		if (sysctlbyname("kern.crypto_stats", &stats, &slen, NULL, 0) < 0)
507			perror("kern.cryptostats");
508		if (stats.cs_invoke.count) {
509			printt("dispatch->invoke", &stats.cs_invoke);
510			printt("invoke->done", &stats.cs_done);
511			printt("done->cb", &stats.cs_cb);
512			printt("cb->finis", &stats.cs_finis);
513		}
514	}
515#endif
516	fflush(stdout);
517}
518
519int
520main(int argc, char **argv)
521{
522	struct alg *alg = NULL;
523	int count = 1;
524	int sizes[128], nsizes = 0;
525	u_long cmd = CIOCGSESSION2;
526	int testall = 0;
527	int maxthreads = 1;
528	int profile = 0;
529	int i, ch;
530
531	while ((ch = getopt(argc, argv, "cpzsva:bd:t:")) != -1) {
532		switch (ch) {
533#ifdef CIOCGSSESSION
534		case 's':
535			cmd = CIOCGSSESSION;
536			break;
537#endif
538		case 'v':
539			verbose++;
540			break;
541		case 'a':
542			alg = getalgbyname(optarg);
543			if (alg == NULL) {
544				if (streq(optarg, "rijndael"))
545					alg = getalgbyname("aes");
546				else
547					usage(argv[0]);
548			}
549			break;
550		case 'd':
551			crid = crlookup(optarg);
552			break;
553		case 't':
554			maxthreads = atoi(optarg);
555			break;
556		case 'z':
557			testall = 1;
558			break;
559		case 'p':
560			profile = 1;
561			break;
562		case 'b':
563			opflags |= COP_F_BATCH;
564			break;
565		case 'c':
566			verify = 1;
567			break;
568		default:
569			usage(argv[0]);
570		}
571	}
572	argc -= optind, argv += optind;
573	if (argc > 0)
574		count = atoi(argv[0]);
575	while (argc > 1) {
576		int s = atoi(argv[1]);
577		if (nsizes < nitems(sizes)) {
578			sizes[nsizes++] = s;
579		} else {
580			printf("Too many sizes, ignoring %u\n", s);
581		}
582		argc--, argv++;
583	}
584	if (maxthreads > CPU_SETSIZE)
585		errx(EX_USAGE, "Too many threads, %d, choose fewer.", maxthreads);
586
587	if (nsizes == 0) {
588		if (alg)
589			sizes[nsizes++] = alg->blocksize;
590		else
591			sizes[nsizes++] = 8;
592		if (testall) {
593			while (sizes[nsizes-1] < 8*1024) {
594				sizes[nsizes] = sizes[nsizes-1]<<1;
595				nsizes++;
596			}
597		}
598	}
599
600	if (testall) {
601		for (i = 0; i < nitems(algorithms); i++) {
602			int j;
603			alg = &algorithms[i];
604			for (j = 0; j < nsizes; j++)
605				runtests(alg, count, sizes[j], cmd, maxthreads, profile);
606		}
607	} else {
608		if (alg == NULL)
609			alg = getalgbycode(CRYPTO_3DES_CBC);
610		for (i = 0; i < nsizes; i++)
611			runtests(alg, count, sizes[i], cmd, maxthreads, profile);
612	}
613
614	return (0);
615}
616
617void
618hexdump(char *p, int n)
619{
620	int i, off;
621
622	for (off = 0; n > 0; off += 16, n -= 16) {
623		printf("%s%04x:", off == 0 ? "\n" : "", off);
624		i = (n >= 16 ? 16 : n);
625		do {
626			printf(" %02x", *p++ & 0xff);
627		} while (--i);
628		printf("\n");
629	}
630}
631