1 /* 2 * cipher_driver.c 3 * 4 * A driver for the generic cipher type 5 * 6 * David A. McGrew 7 * Cisco Systems, Inc. 8 */ 9 10 /* 11 * 12 * Copyright (c) 2001-2006, Cisco Systems, Inc. 13 * All rights reserved. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 19 * Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 22 * Redistributions in binary form must reproduce the above 23 * copyright notice, this list of conditions and the following 24 * disclaimer in the documentation and/or other materials provided 25 * with the distribution. 26 * 27 * Neither the name of the Cisco Systems, Inc. nor the names of its 28 * contributors may be used to endorse or promote products derived 29 * from this software without specific prior written permission. 30 * 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 34 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 35 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 36 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 37 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 38 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 42 * OF THE POSSIBILITY OF SUCH DAMAGE. 43 * 44 */ 45 46 #include <stdio.h> /* for printf() */ 47 #include <stdlib.h> /* for rand() */ 48 #include <string.h> /* for memset() */ 49 #include <unistd.h> /* for getopt() */ 50 #include "cipher.h" 51 #include "aes_icm.h" 52 #include "null_cipher.h" 53 54 #define PRINT_DEBUG 0 55 56 void 57 cipher_driver_test_throughput(cipher_t *c); 58 59 err_status_t 60 cipher_driver_self_test(cipher_type_t *ct); 61 62 63 /* 64 * cipher_driver_test_buffering(ct) tests the cipher's output 65 * buffering for correctness by checking the consistency of succesive 66 * calls 67 */ 68 69 err_status_t 70 cipher_driver_test_buffering(cipher_t *c); 71 72 73 /* 74 * functions for testing cipher cache thrash 75 */ 76 err_status_t 77 cipher_driver_test_array_throughput(cipher_type_t *ct, 78 int klen, int num_cipher); 79 80 void 81 cipher_array_test_throughput(cipher_t *ca[], int num_cipher); 82 83 uint64_t 84 cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 85 unsigned octets_in_buffer, int num_trials); 86 87 err_status_t 88 cipher_array_delete(cipher_t *cipher_array[], int num_cipher); 89 90 err_status_t 91 cipher_array_alloc_init(cipher_t ***cipher_array, int num_ciphers, 92 cipher_type_t *ctype, int klen); 93 94 void 95 usage(char *prog_name) { 96 printf("usage: %s [ -t | -v | -a ]\n", prog_name); 97 exit(255); 98 } 99 100 void 101 check_status(err_status_t s) { 102 if (s) { 103 printf("error (code %d)\n", s); 104 exit(s); 105 } 106 return; 107 } 108 109 /* 110 * null_cipher, aes_icm, and aes_cbc are the cipher meta-objects 111 * defined in the files in crypto/cipher subdirectory. these are 112 * declared external so that we can use these cipher types here 113 */ 114 115 extern cipher_type_t null_cipher; 116 extern cipher_type_t aes_icm; 117 extern cipher_type_t aes_cbc; 118 119 int 120 main(int argc, char *argv[]) { 121 cipher_t *c = NULL; 122 err_status_t status; 123 unsigned char test_key[48] = { 124 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 125 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 126 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 127 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 128 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 129 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 130 }; 131 int q; 132 unsigned do_timing_test = 0; 133 unsigned do_validation = 0; 134 unsigned do_array_timing_test = 0; 135 136 /* process input arguments */ 137 while (1) { 138 q = getopt(argc, argv, "tva"); 139 if (q == -1) 140 break; 141 switch (q) { 142 case 't': 143 do_timing_test = 1; 144 break; 145 case 'v': 146 do_validation = 1; 147 break; 148 case 'a': 149 do_array_timing_test = 1; 150 break; 151 default: 152 usage(argv[0]); 153 } 154 } 155 156 printf("cipher test driver\n" 157 "David A. McGrew\n" 158 "Cisco Systems, Inc.\n"); 159 160 if (!do_validation && !do_timing_test && !do_array_timing_test) 161 usage(argv[0]); 162 163 /* arry timing (cache thrash) test */ 164 if (do_array_timing_test) { 165 int max_num_cipher = 1 << 16; /* number of ciphers in cipher_array */ 166 int num_cipher; 167 168 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 169 cipher_driver_test_array_throughput(&null_cipher, 0, num_cipher); 170 171 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 172 cipher_driver_test_array_throughput(&aes_icm, 30, num_cipher); 173 174 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 175 cipher_driver_test_array_throughput(&aes_icm, 46, num_cipher); 176 177 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 178 cipher_driver_test_array_throughput(&aes_cbc, 16, num_cipher); 179 180 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 181 cipher_driver_test_array_throughput(&aes_cbc, 32, num_cipher); 182 } 183 184 if (do_validation) { 185 cipher_driver_self_test(&null_cipher); 186 cipher_driver_self_test(&aes_icm); 187 cipher_driver_self_test(&aes_cbc); 188 } 189 190 /* do timing and/or buffer_test on null_cipher */ 191 status = cipher_type_alloc(&null_cipher, &c, 0); 192 check_status(status); 193 194 status = cipher_init(c, NULL, direction_encrypt); 195 check_status(status); 196 197 if (do_timing_test) 198 cipher_driver_test_throughput(c); 199 if (do_validation) { 200 status = cipher_driver_test_buffering(c); 201 check_status(status); 202 } 203 status = cipher_dealloc(c); 204 check_status(status); 205 206 207 /* run the throughput test on the aes_icm cipher (128-bit key) */ 208 status = cipher_type_alloc(&aes_icm, &c, 30); 209 if (status) { 210 fprintf(stderr, "error: can't allocate cipher\n"); 211 exit(status); 212 } 213 214 status = cipher_init(c, test_key, direction_encrypt); 215 check_status(status); 216 217 if (do_timing_test) 218 cipher_driver_test_throughput(c); 219 220 if (do_validation) { 221 status = cipher_driver_test_buffering(c); 222 check_status(status); 223 } 224 225 status = cipher_dealloc(c); 226 check_status(status); 227 228 /* repeat the tests with 256-bit keys */ 229 status = cipher_type_alloc(&aes_icm, &c, 46); 230 if (status) { 231 fprintf(stderr, "error: can't allocate cipher\n"); 232 exit(status); 233 } 234 235 status = cipher_init(c, test_key, direction_encrypt); 236 check_status(status); 237 238 if (do_timing_test) 239 cipher_driver_test_throughput(c); 240 241 if (do_validation) { 242 status = cipher_driver_test_buffering(c); 243 check_status(status); 244 } 245 246 status = cipher_dealloc(c); 247 check_status(status); 248 249 return 0; 250 } 251 252 void 253 cipher_driver_test_throughput(cipher_t *c) { 254 int i; 255 int min_enc_len = 32; 256 int max_enc_len = 2048; /* should be a power of two */ 257 int num_trials = 1000000; 258 259 printf("timing %s throughput, key length %d:\n", c->type->description, c->key_len); 260 fflush(stdout); 261 for (i=min_enc_len; i <= max_enc_len; i = i * 2) 262 printf("msg len: %d\tgigabits per second: %f\n", 263 i, cipher_bits_per_second(c, i, num_trials) / 1e9); 264 265 } 266 267 err_status_t 268 cipher_driver_self_test(cipher_type_t *ct) { 269 err_status_t status; 270 271 printf("running cipher self-test for %s...", ct->description); 272 status = cipher_type_self_test(ct); 273 if (status) { 274 printf("failed with error code %d\n", status); 275 exit(status); 276 } 277 printf("passed\n"); 278 279 return err_status_ok; 280 } 281 282 /* 283 * cipher_driver_test_buffering(ct) tests the cipher's output 284 * buffering for correctness by checking the consistency of succesive 285 * calls 286 */ 287 288 err_status_t 289 cipher_driver_test_buffering(cipher_t *c) { 290 int i, j, num_trials = 1000; 291 unsigned len, buflen = 1024; 292 uint8_t buffer0[buflen], buffer1[buflen], *current, *end; 293 uint8_t idx[16] = { 294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34 296 }; 297 err_status_t status; 298 299 printf("testing output buffering for cipher %s...", 300 c->type->description); 301 302 for (i=0; i < num_trials; i++) { 303 304 /* set buffers to zero */ 305 for (j=0; j < buflen; j++) 306 buffer0[j] = buffer1[j] = 0; 307 308 /* initialize cipher */ 309 status = cipher_set_iv(c, idx); 310 if (status) 311 return status; 312 313 /* generate 'reference' value by encrypting all at once */ 314 status = cipher_encrypt(c, buffer0, &buflen); 315 if (status) 316 return status; 317 318 /* re-initialize cipher */ 319 status = cipher_set_iv(c, idx); 320 if (status) 321 return status; 322 323 /* now loop over short lengths until buffer1 is encrypted */ 324 current = buffer1; 325 end = buffer1 + buflen; 326 while (current < end) { 327 328 /* choose a short length */ 329 len = rand() & 0x01f; 330 331 /* make sure that len doesn't cause us to overreach the buffer */ 332 if (current + len > end) 333 len = end - current; 334 335 status = cipher_encrypt(c, current, &len); 336 if (status) 337 return status; 338 339 /* advance pointer into buffer1 to reflect encryption */ 340 current += len; 341 342 /* if buffer1 is all encrypted, break out of loop */ 343 if (current == end) 344 break; 345 } 346 347 /* compare buffers */ 348 for (j=0; j < buflen; j++) 349 if (buffer0[j] != buffer1[j]) { 350 #if PRINT_DEBUG 351 printf("test case %d failed at byte %d\n", i, j); 352 printf("computed: %s\n", octet_string_hex_string(buffer1, buflen)); 353 printf("expected: %s\n", octet_string_hex_string(buffer0, buflen)); 354 #endif 355 return err_status_algo_fail; 356 } 357 } 358 359 printf("passed\n"); 360 361 return err_status_ok; 362 } 363 364 365 /* 366 * The function cipher_test_throughput_array() tests the effect of CPU 367 * cache thrash on cipher throughput. 368 * 369 * cipher_array_alloc_init(ctype, array, num_ciphers) creates an array 370 * of cipher_t of type ctype 371 */ 372 373 err_status_t 374 cipher_array_alloc_init(cipher_t ***ca, int num_ciphers, 375 cipher_type_t *ctype, int klen) { 376 int i, j; 377 err_status_t status; 378 uint8_t *key; 379 cipher_t **cipher_array; 380 /* pad klen allocation, to handle aes_icm reading 16 bytes for the 381 14-byte salt */ 382 int klen_pad = ((klen + 15) >> 4) << 4; 383 384 /* allocate array of pointers to ciphers */ 385 cipher_array = (cipher_t **) malloc(sizeof(cipher_t *) * num_ciphers); 386 if (cipher_array == NULL) 387 return err_status_alloc_fail; 388 389 /* set ca to location of cipher_array */ 390 *ca = cipher_array; 391 392 /* allocate key */ 393 key = crypto_alloc(klen_pad); 394 if (key == NULL) { 395 free(cipher_array); 396 return err_status_alloc_fail; 397 } 398 399 /* allocate and initialize an array of ciphers */ 400 for (i=0; i < num_ciphers; i++) { 401 402 /* allocate cipher */ 403 status = cipher_type_alloc(ctype, cipher_array, klen); 404 if (status) 405 return status; 406 407 /* generate random key and initialize cipher */ 408 for (j=0; j < klen; j++) 409 key[j] = (uint8_t) rand(); 410 for (; j < klen_pad; j++) 411 key[j] = 0; 412 status = cipher_init(*cipher_array, key, direction_encrypt); 413 if (status) 414 return status; 415 416 /* printf("%dth cipher is at %p\n", i, *cipher_array); */ 417 /* printf("%dth cipher description: %s\n", i, */ 418 /* (*cipher_array)->type->description); */ 419 420 /* advance cipher array pointer */ 421 cipher_array++; 422 } 423 424 crypto_free(key); 425 426 return err_status_ok; 427 } 428 429 err_status_t 430 cipher_array_delete(cipher_t *cipher_array[], int num_cipher) { 431 int i; 432 433 for (i=0; i < num_cipher; i++) { 434 cipher_dealloc(cipher_array[i]); 435 } 436 437 free(cipher_array); 438 439 return err_status_ok; 440 } 441 442 443 /* 444 * cipher_array_bits_per_second(c, l, t) computes (an estimate of) the 445 * number of bits that a cipher implementation can encrypt in a second 446 * when distinct keys are used to encrypt distinct messages 447 * 448 * c is a cipher (which MUST be allocated an initialized already), l 449 * is the length in octets of the test data to be encrypted, and t is 450 * the number of trials 451 * 452 * if an error is encountered, the value 0 is returned 453 */ 454 455 uint64_t 456 cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 457 unsigned octets_in_buffer, int num_trials) { 458 int i; 459 v128_t nonce; 460 clock_t timer; 461 unsigned char *enc_buf; 462 int cipher_index = rand() % num_cipher; 463 464 /* Over-alloc, for NIST CBC padding */ 465 enc_buf = crypto_alloc(octets_in_buffer+17); 466 if (enc_buf == NULL) 467 return 0; /* indicate bad parameters by returning null */ 468 memset(enc_buf, 0, octets_in_buffer); 469 470 /* time repeated trials */ 471 v128_set_to_zero(&nonce); 472 timer = clock(); 473 for(i=0; i < num_trials; i++, nonce.v32[3] = i) { 474 /* length parameter to cipher_encrypt is in/out -- out is total, padded 475 * length -- so reset it each time. */ 476 unsigned octets_to_encrypt = octets_in_buffer; 477 478 /* encrypt buffer with cipher */ 479 cipher_set_iv(cipher_array[cipher_index], &nonce); 480 cipher_encrypt(cipher_array[cipher_index], enc_buf, &octets_to_encrypt); 481 482 /* choose a cipher at random from the array*/ 483 cipher_index = (*((uint32_t *)enc_buf)) % num_cipher; 484 } 485 timer = clock() - timer; 486 487 free(enc_buf); 488 489 if (timer == 0) { 490 /* Too fast! */ 491 return 0; 492 } 493 494 return (uint64_t)CLOCKS_PER_SEC * num_trials * 8 * octets_in_buffer / timer; 495 } 496 497 void 498 cipher_array_test_throughput(cipher_t *ca[], int num_cipher) { 499 int i; 500 int min_enc_len = 16; 501 int max_enc_len = 2048; /* should be a power of two */ 502 int num_trials = 1000000; 503 504 printf("timing %s throughput with key length %d, array size %d:\n", 505 (ca[0])->type->description, (ca[0])->key_len, num_cipher); 506 fflush(stdout); 507 for (i=min_enc_len; i <= max_enc_len; i = i * 4) 508 printf("msg len: %d\tgigabits per second: %f\n", i, 509 cipher_array_bits_per_second(ca, num_cipher, i, num_trials) / 1e9); 510 511 } 512 513 err_status_t 514 cipher_driver_test_array_throughput(cipher_type_t *ct, 515 int klen, int num_cipher) { 516 cipher_t **ca = NULL; 517 err_status_t status; 518 519 status = cipher_array_alloc_init(&ca, num_cipher, ct, klen); 520 if (status) { 521 printf("error: cipher_array_alloc_init() failed with error code %d\n", 522 status); 523 return status; 524 } 525 526 cipher_array_test_throughput(ca, num_cipher); 527 528 cipher_array_delete(ca, num_cipher); 529 530 return err_status_ok; 531 } 532