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