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