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 56void 57cipher_driver_test_throughput(cipher_t *c); 58 59err_status_t 60cipher_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 69err_status_t 70cipher_driver_test_buffering(cipher_t *c); 71 72 73/* 74 * functions for testing cipher cache thrash 75 */ 76err_status_t 77cipher_driver_test_array_throughput(cipher_type_t *ct, 78 int klen, int num_cipher); 79 80void 81cipher_array_test_throughput(cipher_t *ca[], int num_cipher); 82 83uint64_t 84cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 85 unsigned octets_in_buffer, int num_trials); 86 87err_status_t 88cipher_array_delete(cipher_t *cipher_array[], int num_cipher); 89 90err_status_t 91cipher_array_alloc_init(cipher_t ***cipher_array, int num_ciphers, 92 cipher_type_t *ctype, int klen); 93 94void 95usage(char *prog_name) { 96 printf("usage: %s [ -t | -v | -a ]\n", prog_name); 97 exit(255); 98} 99 100void 101check_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 115extern cipher_type_t null_cipher; 116extern cipher_type_t aes_icm; 117extern cipher_type_t aes_cbc; 118 119int 120main(int argc, char *argv[]) { 121 cipher_t *c = NULL; 122 err_status_t status; 123 unsigned char test_key[20] = { 124 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 125 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 126 0x10, 0x11, 0x12, 0x13 127 }; 128 int q; 129 unsigned do_timing_test = 0; 130 unsigned do_validation = 0; 131 unsigned do_array_timing_test = 0; 132 133 /* process input arguments */ 134 while (1) { 135 q = getopt(argc, argv, "tva"); 136 if (q == -1) 137 break; 138 switch (q) { 139 case 't': 140 do_timing_test = 1; 141 break; 142 case 'v': 143 do_validation = 1; 144 break; 145 case 'a': 146 do_array_timing_test = 1; 147 break; 148 default: 149 usage(argv[0]); 150 } 151 } 152 153 printf("cipher test driver\n" 154 "David A. McGrew\n" 155 "Cisco Systems, Inc.\n"); 156 157 if (!do_validation && !do_timing_test && !do_array_timing_test) 158 usage(argv[0]); 159 160 /* arry timing (cache thrash) test */ 161 if (do_array_timing_test) { 162 int max_num_cipher = 1 << 16; /* number of ciphers in cipher_array */ 163 int num_cipher; 164 165 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 166 cipher_driver_test_array_throughput(&null_cipher, 0, num_cipher); 167 168 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 169 cipher_driver_test_array_throughput(&aes_icm, 30, num_cipher); 170 171 for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 172 cipher_driver_test_array_throughput(&aes_cbc, 16, num_cipher); 173 174 } 175 176 if (do_validation) { 177 cipher_driver_self_test(&null_cipher); 178 cipher_driver_self_test(&aes_icm); 179 cipher_driver_self_test(&aes_cbc); 180 } 181 182 /* do timing and/or buffer_test on null_cipher */ 183 status = cipher_type_alloc(&null_cipher, &c, 0); 184 check_status(status); 185 186 status = cipher_init(c, NULL, direction_encrypt); 187 check_status(status); 188 189 if (do_timing_test) 190 cipher_driver_test_throughput(c); 191 if (do_validation) { 192 status = cipher_driver_test_buffering(c); 193 check_status(status); 194 } 195 status = cipher_dealloc(c); 196 check_status(status); 197 198 199 /* run the throughput test on the aes_icm cipher */ 200 status = cipher_type_alloc(&aes_icm, &c, 30); 201 if (status) { 202 fprintf(stderr, "error: can't allocate cipher\n"); 203 exit(status); 204 } 205 206 status = cipher_init(c, test_key, direction_encrypt); 207 check_status(status); 208 209 if (do_timing_test) 210 cipher_driver_test_throughput(c); 211 212 if (do_validation) { 213 status = cipher_driver_test_buffering(c); 214 check_status(status); 215 } 216 217 status = cipher_dealloc(c); 218 check_status(status); 219 220 return 0; 221} 222 223void 224cipher_driver_test_throughput(cipher_t *c) { 225 int i; 226 int min_enc_len = 32; 227 int max_enc_len = 2048; /* should be a power of two */ 228 int num_trials = 100000; 229 230 printf("timing %s throughput:\n", c->type->description); 231 fflush(stdout); 232 for (i=min_enc_len; i <= max_enc_len; i = i * 2) 233 printf("msg len: %d\tgigabits per second: %f\n", 234 i, cipher_bits_per_second(c, i, num_trials) / 1e9); 235 236} 237 238err_status_t 239cipher_driver_self_test(cipher_type_t *ct) { 240 err_status_t status; 241 242 printf("running cipher self-test for %s...", ct->description); 243 status = cipher_type_self_test(ct); 244 if (status) { 245 printf("failed with error code %d\n", status); 246 exit(status); 247 } 248 printf("passed\n"); 249 250 return err_status_ok; 251} 252 253/* 254 * cipher_driver_test_buffering(ct) tests the cipher's output 255 * buffering for correctness by checking the consistency of succesive 256 * calls 257 */ 258 259err_status_t 260cipher_driver_test_buffering(cipher_t *c) { 261 int i, j, num_trials = 1000; 262 unsigned len, buflen = 1024; 263 uint8_t buffer0[buflen], buffer1[buflen], *current, *end; 264 uint8_t idx[16] = { 265 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34 267 }; 268 err_status_t status; 269 270 printf("testing output buffering for cipher %s...", 271 c->type->description); 272 273 for (i=0; i < num_trials; i++) { 274 275 /* set buffers to zero */ 276 for (j=0; j < buflen; j++) 277 buffer0[j] = buffer1[j] = 0; 278 279 /* initialize cipher */ 280 status = cipher_set_iv(c, idx); 281 if (status) 282 return status; 283 284 /* generate 'reference' value by encrypting all at once */ 285 status = cipher_encrypt(c, buffer0, &buflen); 286 if (status) 287 return status; 288 289 /* re-initialize cipher */ 290 status = cipher_set_iv(c, idx); 291 if (status) 292 return status; 293 294 /* now loop over short lengths until buffer1 is encrypted */ 295 current = buffer1; 296 end = buffer1 + buflen; 297 while (current < end) { 298 299 /* choose a short length */ 300 len = rand() & 0x01f; 301 302 /* make sure that len doesn't cause us to overreach the buffer */ 303 if (current + len > end) 304 len = end - current; 305 306 status = cipher_encrypt(c, current, &len); 307 if (status) 308 return status; 309 310 /* advance pointer into buffer1 to reflect encryption */ 311 current += len; 312 313 /* if buffer1 is all encrypted, break out of loop */ 314 if (current == end) 315 break; 316 } 317 318 /* compare buffers */ 319 for (j=0; j < buflen; j++) 320 if (buffer0[j] != buffer1[j]) { 321#if PRINT_DEBUG 322 printf("test case %d failed at byte %d\n", i, j); 323 printf("computed: %s\n", octet_string_hex_string(buffer1, buflen)); 324 printf("expected: %s\n", octet_string_hex_string(buffer0, buflen)); 325#endif 326 return err_status_algo_fail; 327 } 328 } 329 330 printf("passed\n"); 331 332 return err_status_ok; 333} 334 335 336/* 337 * The function cipher_test_throughput_array() tests the effect of CPU 338 * cache thrash on cipher throughput. 339 * 340 * cipher_array_alloc_init(ctype, array, num_ciphers) creates an array 341 * of cipher_t of type ctype 342 */ 343 344err_status_t 345cipher_array_alloc_init(cipher_t ***ca, int num_ciphers, 346 cipher_type_t *ctype, int klen) { 347 int i, j; 348 err_status_t status; 349 uint8_t *key; 350 cipher_t **cipher_array; 351 352 /* allocate array of pointers to ciphers */ 353 cipher_array = (cipher_t **) malloc(sizeof(cipher_t *) * num_ciphers); 354 if (cipher_array == NULL) 355 return err_status_alloc_fail; 356 357 /* set ca to location of cipher_array */ 358 *ca = cipher_array; 359 360 /* allocate key */ 361 key = crypto_alloc(klen); 362 if (key == NULL) { 363 free(cipher_array); 364 return err_status_alloc_fail; 365 } 366 367 /* allocate and initialize an array of ciphers */ 368 for (i=0; i < num_ciphers; i++) { 369 370 /* allocate cipher */ 371 status = cipher_type_alloc(ctype, cipher_array, klen); 372 if (status) 373 return status; 374 375 /* generate random key and initialize cipher */ 376 for (j=0; j < klen; j++) 377 key[j] = (uint8_t) rand(); 378 status = cipher_init(*cipher_array, key, direction_encrypt); 379 if (status) 380 return status; 381 382/* printf("%dth cipher is at %p\n", i, *cipher_array); */ 383/* printf("%dth cipher description: %s\n", i, */ 384/* (*cipher_array)->type->description); */ 385 386 /* advance cipher array pointer */ 387 cipher_array++; 388 } 389 390 return err_status_ok; 391} 392 393err_status_t 394cipher_array_delete(cipher_t *cipher_array[], int num_cipher) { 395 int i; 396 397 for (i=0; i < num_cipher; i++) { 398 cipher_dealloc(cipher_array[i]); 399 } 400 401 free(cipher_array); 402 403 return err_status_ok; 404} 405 406 407/* 408 * cipher_array_bits_per_second(c, l, t) computes (an estimate of) the 409 * number of bits that a cipher implementation can encrypt in a second 410 * when distinct keys are used to encrypt distinct messages 411 * 412 * c is a cipher (which MUST be allocated an initialized already), l 413 * is the length in octets of the test data to be encrypted, and t is 414 * the number of trials 415 * 416 * if an error is encountered, the value 0 is returned 417 */ 418 419uint64_t 420cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 421 unsigned octets_in_buffer, int num_trials) { 422 int i; 423 v128_t nonce; 424 clock_t timer; 425 unsigned char *enc_buf; 426 int cipher_index = 0; 427 428 429 enc_buf = crypto_alloc(octets_in_buffer); 430 if (enc_buf == NULL) 431 return 0; /* indicate bad parameters by returning null */ 432 433 /* time repeated trials */ 434 v128_set_to_zero(&nonce); 435 timer = clock(); 436 for(i=0; i < num_trials; i++, nonce.v32[3] = i) { 437 438 /* choose a cipher at random from the array*/ 439 cipher_index = (*((uint32_t *)enc_buf)) % num_cipher; 440 441 /* encrypt buffer with cipher */ 442 cipher_set_iv(cipher_array[cipher_index], &nonce); 443 cipher_encrypt(cipher_array[cipher_index], enc_buf, &octets_in_buffer); 444 } 445 timer = clock() - timer; 446 447 free(enc_buf); 448 449 if (timer == 0) { 450 /* Too fast! */ 451 return 0; 452 } 453 454 return CLOCKS_PER_SEC * num_trials * 8 * octets_in_buffer / timer; 455} 456 457void 458cipher_array_test_throughput(cipher_t *ca[], int num_cipher) { 459 int i; 460 int min_enc_len = 16; 461 int max_enc_len = 2048; /* should be a power of two */ 462 int num_trials = 10000; 463 464 printf("timing %s throughput with array size %d:\n", 465 (ca[0])->type->description, num_cipher); 466 fflush(stdout); 467 for (i=min_enc_len; i <= max_enc_len; i = i * 4) 468 printf("msg len: %d\tgigabits per second: %f\n", i, 469 cipher_array_bits_per_second(ca, num_cipher, i, num_trials) / 1e9); 470 471} 472 473err_status_t 474cipher_driver_test_array_throughput(cipher_type_t *ct, 475 int klen, int num_cipher) { 476 cipher_t **ca = NULL; 477 err_status_t status; 478 479 status = cipher_array_alloc_init(&ca, num_cipher, ct, klen); 480 if (status) { 481 printf("error: cipher_array_alloc_init() failed with error code %d\n", 482 status); 483 return status; 484 } 485 486 cipher_array_test_throughput(ca, num_cipher); 487 488 cipher_array_delete(ca, num_cipher); 489 490 return err_status_ok; 491} 492