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