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$ 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 108 void hexdump(char *, int); 109 110 int verbose = 0; 111 int opflags = 0; 112 int verify = 0; 113 int crid = CRYPTO_FLAG_HARDWARE; 114 115 struct 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 142 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(" 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 164 struct alg* 165 getalgbycode(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 175 struct alg* 176 getalgbyname(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 186 int 187 devcrypto(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 201 int 202 crlookup(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 216 const char * 217 crfind(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 228 int 229 crget(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 240 char 241 rdigit(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 250 void 251 runtest(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__ 396 void 397 resetstats() 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 419 void 420 printt(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 434 void 435 runtests(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 519 int 520 main(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 617 void 618 hexdump(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