cipher_driver.c (19030B)
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-2017 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 #ifdef HAVE_CONFIG_H 47 #include <config.h> 48 #endif 49 50 #include <stdio.h> /* for printf() */ 51 #include "getopt_s.h" 52 #include "cipher.h" 53 #include "cipher_priv.h" 54 #include "datatypes.h" 55 56 #define PRINT_DEBUG 0 57 58 void cipher_driver_test_throughput(srtp_cipher_t *c); 59 60 srtp_err_status_t cipher_driver_self_test(srtp_cipher_type_t *ct); 61 62 /* 63 * cipher_driver_test_buffering(ct) tests the cipher's output 64 * buffering for correctness by checking the consistency of succesive 65 * calls 66 */ 67 68 srtp_err_status_t cipher_driver_test_buffering(srtp_cipher_t *c); 69 70 /* 71 * functions for testing cipher cache thrash 72 */ 73 srtp_err_status_t cipher_driver_test_array_throughput(srtp_cipher_type_t *ct, 74 int klen, 75 int num_cipher); 76 77 void cipher_array_test_throughput(srtp_cipher_t *ca[], int num_cipher); 78 79 uint64_t cipher_array_bits_per_second(srtp_cipher_t *cipher_array[], 80 int num_cipher, 81 unsigned octets_in_buffer, 82 int num_trials); 83 84 srtp_err_status_t cipher_array_delete(srtp_cipher_t *cipher_array[], 85 int num_cipher); 86 87 srtp_err_status_t cipher_array_alloc_init(srtp_cipher_t ***cipher_array, 88 int num_ciphers, 89 srtp_cipher_type_t *ctype, 90 int klen); 91 92 void usage(char *prog_name) 93 { 94 printf("usage: %s [ -t | -v | -a ]\n", prog_name); 95 exit(255); 96 } 97 98 void check_status(srtp_err_status_t s) 99 { 100 if (s) { 101 printf("error (code %d)\n", s); 102 exit(s); 103 } 104 return; 105 } 106 107 /* 108 * null_cipher and srtp_aes_icm are the cipher meta-objects 109 * defined in the files in crypto/cipher subdirectory. these are 110 * declared external so that we can use these cipher types here 111 */ 112 113 extern srtp_cipher_type_t srtp_null_cipher; 114 extern srtp_cipher_type_t srtp_aes_icm_128; 115 extern srtp_cipher_type_t srtp_aes_icm_256; 116 #ifdef GCM 117 extern srtp_cipher_type_t srtp_aes_icm_192; 118 extern srtp_cipher_type_t srtp_aes_gcm_128; 119 extern srtp_cipher_type_t srtp_aes_gcm_256; 120 #endif 121 122 int main(int argc, char *argv[]) 123 { 124 srtp_cipher_t *c = NULL; 125 srtp_err_status_t status; 126 /* clang-format off */ 127 unsigned char test_key[48] = { 128 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 129 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 130 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 131 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 132 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 133 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 134 }; 135 /* clang-format on */ 136 int q; 137 unsigned do_timing_test = 0; 138 unsigned do_validation = 0; 139 unsigned do_array_timing_test = 0; 140 141 /* process input arguments */ 142 while (1) { 143 q = getopt_s(argc, argv, "tva"); 144 if (q == -1) 145 break; 146 switch (q) { 147 case 't': 148 do_timing_test = 1; 149 break; 150 case 'v': 151 do_validation = 1; 152 break; 153 case 'a': 154 do_array_timing_test = 1; 155 break; 156 default: 157 usage(argv[0]); 158 } 159 } 160 161 printf("cipher test driver\n" 162 "David A. McGrew\n" 163 "Cisco Systems, Inc.\n"); 164 165 if (!do_validation && !do_timing_test && !do_array_timing_test) 166 usage(argv[0]); 167 168 /* arry timing (cache thrash) test */ 169 if (do_array_timing_test) { 170 int max_num_cipher = 1 << 16; /* number of ciphers in cipher_array */ 171 int num_cipher; 172 173 for (num_cipher = 1; num_cipher < max_num_cipher; num_cipher *= 8) 174 cipher_driver_test_array_throughput(&srtp_null_cipher, 0, 175 num_cipher); 176 177 for (num_cipher = 1; num_cipher < max_num_cipher; num_cipher *= 8) 178 cipher_driver_test_array_throughput( 179 &srtp_aes_icm_128, SRTP_AES_ICM_128_KEY_LEN_WSALT, num_cipher); 180 181 for (num_cipher = 1; num_cipher < max_num_cipher; num_cipher *= 8) 182 cipher_driver_test_array_throughput( 183 &srtp_aes_icm_256, SRTP_AES_ICM_256_KEY_LEN_WSALT, num_cipher); 184 185 #ifdef GCM 186 for (num_cipher = 1; num_cipher < max_num_cipher; num_cipher *= 8) 187 cipher_driver_test_array_throughput( 188 &srtp_aes_icm_192, SRTP_AES_ICM_192_KEY_LEN_WSALT, num_cipher); 189 190 for (num_cipher = 1; num_cipher < max_num_cipher; num_cipher *= 8) { 191 cipher_driver_test_array_throughput( 192 &srtp_aes_gcm_128, SRTP_AES_GCM_128_KEY_LEN_WSALT, num_cipher); 193 } 194 195 for (num_cipher = 1; num_cipher < max_num_cipher; num_cipher *= 8) { 196 cipher_driver_test_array_throughput( 197 &srtp_aes_gcm_256, SRTP_AES_GCM_256_KEY_LEN_WSALT, num_cipher); 198 } 199 #endif 200 } 201 202 if (do_validation) { 203 cipher_driver_self_test(&srtp_null_cipher); 204 cipher_driver_self_test(&srtp_aes_icm_128); 205 cipher_driver_self_test(&srtp_aes_icm_256); 206 #ifdef GCM 207 cipher_driver_self_test(&srtp_aes_icm_192); 208 cipher_driver_self_test(&srtp_aes_gcm_128); 209 cipher_driver_self_test(&srtp_aes_gcm_256); 210 #endif 211 } 212 213 /* do timing and/or buffer_test on srtp_null_cipher */ 214 status = srtp_cipher_type_alloc(&srtp_null_cipher, &c, 0, 0); 215 check_status(status); 216 217 status = srtp_cipher_init(c, NULL); 218 check_status(status); 219 220 if (do_timing_test) 221 cipher_driver_test_throughput(c); 222 if (do_validation) { 223 status = cipher_driver_test_buffering(c); 224 check_status(status); 225 } 226 status = srtp_cipher_dealloc(c); 227 check_status(status); 228 229 /* run the throughput test on the aes_icm cipher (128-bit key) */ 230 status = srtp_cipher_type_alloc(&srtp_aes_icm_128, &c, 231 SRTP_AES_ICM_128_KEY_LEN_WSALT, 0); 232 if (status) { 233 fprintf(stderr, "error: can't allocate cipher\n"); 234 exit(status); 235 } 236 237 status = srtp_cipher_init(c, test_key); 238 check_status(status); 239 240 if (do_timing_test) 241 cipher_driver_test_throughput(c); 242 243 if (do_validation) { 244 status = cipher_driver_test_buffering(c); 245 check_status(status); 246 } 247 248 status = srtp_cipher_dealloc(c); 249 check_status(status); 250 251 /* repeat the tests with 256-bit keys */ 252 status = srtp_cipher_type_alloc(&srtp_aes_icm_256, &c, 253 SRTP_AES_ICM_256_KEY_LEN_WSALT, 0); 254 if (status) { 255 fprintf(stderr, "error: can't allocate cipher\n"); 256 exit(status); 257 } 258 259 status = srtp_cipher_init(c, test_key); 260 check_status(status); 261 262 if (do_timing_test) 263 cipher_driver_test_throughput(c); 264 265 if (do_validation) { 266 status = cipher_driver_test_buffering(c); 267 check_status(status); 268 } 269 270 status = srtp_cipher_dealloc(c); 271 check_status(status); 272 273 #ifdef GCM 274 /* run the throughput test on the aes_gcm_128 cipher */ 275 status = srtp_cipher_type_alloc(&srtp_aes_gcm_128, &c, 276 SRTP_AES_GCM_128_KEY_LEN_WSALT, 8); 277 if (status) { 278 fprintf(stderr, "error: can't allocate GCM 128 cipher\n"); 279 exit(status); 280 } 281 status = srtp_cipher_init(c, test_key); 282 check_status(status); 283 if (do_timing_test) { 284 cipher_driver_test_throughput(c); 285 } 286 287 // GCM ciphers don't do buffering; they're "one shot" 288 289 status = srtp_cipher_dealloc(c); 290 check_status(status); 291 292 /* run the throughput test on the aes_gcm_256 cipher */ 293 status = srtp_cipher_type_alloc(&srtp_aes_gcm_256, &c, 294 SRTP_AES_GCM_256_KEY_LEN_WSALT, 16); 295 if (status) { 296 fprintf(stderr, "error: can't allocate GCM 256 cipher\n"); 297 exit(status); 298 } 299 status = srtp_cipher_init(c, test_key); 300 check_status(status); 301 if (do_timing_test) { 302 cipher_driver_test_throughput(c); 303 } 304 305 // GCM ciphers don't do buffering; they're "one shot" 306 307 status = srtp_cipher_dealloc(c); 308 check_status(status); 309 #endif 310 311 return 0; 312 } 313 314 void cipher_driver_test_throughput(srtp_cipher_t *c) 315 { 316 int i; 317 int min_enc_len = 32; 318 int max_enc_len = 2048; /* should be a power of two */ 319 int num_trials = 1000000; 320 321 printf("timing %s throughput, key length %d:\n", c->type->description, 322 c->key_len); 323 fflush(stdout); 324 for (i = min_enc_len; i <= max_enc_len; i = i * 2) 325 printf("msg len: %d\tgigabits per second: %f\n", i, 326 srtp_cipher_bits_per_second(c, i, num_trials) / 1e9); 327 } 328 329 srtp_err_status_t cipher_driver_self_test(srtp_cipher_type_t *ct) 330 { 331 srtp_err_status_t status; 332 333 printf("running cipher self-test for %s...", ct->description); 334 status = srtp_cipher_type_self_test(ct); 335 if (status) { 336 printf("failed with error code %d\n", status); 337 exit(status); 338 } 339 printf("passed\n"); 340 341 return srtp_err_status_ok; 342 } 343 344 /* 345 * cipher_driver_test_buffering(ct) tests the cipher's output 346 * buffering for correctness by checking the consistency of succesive 347 * calls 348 */ 349 350 #define INITIAL_BUFLEN 1024 351 srtp_err_status_t cipher_driver_test_buffering(srtp_cipher_t *c) 352 { 353 int i, j, num_trials = 1000; 354 unsigned len, buflen = INITIAL_BUFLEN; 355 uint8_t buffer0[INITIAL_BUFLEN], buffer1[INITIAL_BUFLEN], *current, *end; 356 uint8_t idx[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34 }; 358 srtp_err_status_t status; 359 360 printf("testing output buffering for cipher %s...", c->type->description); 361 362 for (i = 0; i < num_trials; i++) { 363 /* set buffers to zero */ 364 for (j = 0; j < (int)buflen; j++) { 365 buffer0[j] = buffer1[j] = 0; 366 } 367 368 /* initialize cipher */ 369 status = srtp_cipher_set_iv(c, (uint8_t *)idx, srtp_direction_encrypt); 370 if (status) 371 return status; 372 373 /* generate 'reference' value by encrypting all at once */ 374 status = srtp_cipher_encrypt(c, buffer0, &buflen); 375 if (status) 376 return status; 377 378 /* re-initialize cipher */ 379 status = srtp_cipher_set_iv(c, (uint8_t *)idx, srtp_direction_encrypt); 380 if (status) 381 return status; 382 383 /* now loop over short lengths until buffer1 is encrypted */ 384 current = buffer1; 385 end = buffer1 + buflen; 386 while (current < end) { 387 /* choose a short length */ 388 len = srtp_cipher_rand_u32_for_tests() & 0x01f; 389 390 /* make sure that len doesn't cause us to overreach the buffer */ 391 if (current + len > end) 392 len = (unsigned)(end - current); 393 394 status = srtp_cipher_encrypt(c, current, &len); 395 if (status) 396 return status; 397 398 /* advance pointer into buffer1 to reflect encryption */ 399 current += len; 400 401 /* if buffer1 is all encrypted, break out of loop */ 402 if (current == end) 403 break; 404 } 405 406 /* compare buffers */ 407 for (j = 0; j < (int)buflen; j++) { 408 if (buffer0[j] != buffer1[j]) { 409 #if PRINT_DEBUG 410 printf("test case %d failed at byte %d\n", i, j); 411 printf("computed: %s\n", 412 octet_string_hex_string(buffer1, buflen)); 413 printf("expected: %s\n", 414 octet_string_hex_string(buffer0, buflen)); 415 #endif 416 return srtp_err_status_algo_fail; 417 } 418 } 419 } 420 421 printf("passed\n"); 422 423 return srtp_err_status_ok; 424 } 425 426 /* 427 * The function cipher_test_throughput_array() tests the effect of CPU 428 * cache thrash on cipher throughput. 429 * 430 * cipher_array_alloc_init(ctype, array, num_ciphers) creates an array 431 * of srtp_cipher_t of type ctype 432 */ 433 434 srtp_err_status_t cipher_array_alloc_init(srtp_cipher_t ***ca, 435 int num_ciphers, 436 srtp_cipher_type_t *ctype, 437 int klen) 438 { 439 int i, j; 440 srtp_err_status_t status; 441 uint8_t *key = NULL; 442 srtp_cipher_t **cipher_array; 443 /* pad klen allocation, to handle aes_icm reading 16 bytes for the 444 14-byte salt */ 445 int klen_pad = ((klen + 15) >> 4) << 4; 446 447 /* allocate array of pointers to ciphers */ 448 cipher_array = (srtp_cipher_t **)srtp_crypto_alloc(sizeof(srtp_cipher_t *) * 449 num_ciphers); 450 if (cipher_array == NULL) 451 return srtp_err_status_alloc_fail; 452 453 /* set ca to location of cipher_array */ 454 *ca = cipher_array; 455 456 /* allocate key , allow zero key for example null cipher */ 457 if (klen_pad > 0) { 458 key = srtp_crypto_alloc(klen_pad); 459 if (key == NULL) { 460 srtp_crypto_free(cipher_array); 461 return srtp_err_status_alloc_fail; 462 } 463 } 464 465 /* allocate and initialize an array of ciphers */ 466 for (i = 0; i < num_ciphers; i++) { 467 /* allocate cipher */ 468 status = srtp_cipher_type_alloc(ctype, cipher_array, klen, 16); 469 if (status) 470 return status; 471 472 /* generate random key and initialize cipher */ 473 srtp_cipher_rand_for_tests(key, klen); 474 for (j = klen; j < klen_pad; j++) 475 key[j] = 0; 476 status = srtp_cipher_init(*cipher_array, key); 477 if (status) 478 return status; 479 480 /* printf("%dth cipher is at %p\n", i, *cipher_array); */ 481 /* printf("%dth cipher description: %s\n", i, */ 482 /* (*cipher_array)->type->description); */ 483 484 /* advance cipher array pointer */ 485 cipher_array++; 486 } 487 488 srtp_crypto_free(key); 489 490 return srtp_err_status_ok; 491 } 492 493 srtp_err_status_t cipher_array_delete(srtp_cipher_t *cipher_array[], 494 int num_cipher) 495 { 496 int i; 497 498 for (i = 0; i < num_cipher; i++) { 499 srtp_cipher_dealloc(cipher_array[i]); 500 } 501 502 srtp_crypto_free(cipher_array); 503 504 return srtp_err_status_ok; 505 } 506 507 /* 508 * cipher_array_bits_per_second(c, l, t) computes (an estimate of) the 509 * number of bits that a cipher implementation can encrypt in a second 510 * when distinct keys are used to encrypt distinct messages 511 * 512 * c is a cipher (which MUST be allocated an initialized already), l 513 * is the length in octets of the test data to be encrypted, and t is 514 * the number of trials 515 * 516 * if an error is encountered, the value 0 is returned 517 */ 518 519 uint64_t cipher_array_bits_per_second(srtp_cipher_t *cipher_array[], 520 int num_cipher, 521 unsigned octets_in_buffer, 522 int num_trials) 523 { 524 int i; 525 v128_t nonce; 526 clock_t timer; 527 unsigned char *enc_buf; 528 int cipher_index = srtp_cipher_rand_u32_for_tests() % num_cipher; 529 530 /* Over-alloc, for NIST CBC padding */ 531 enc_buf = srtp_crypto_alloc(octets_in_buffer + 17); 532 if (enc_buf == NULL) 533 return 0; /* indicate bad parameters by returning null */ 534 535 /* time repeated trials */ 536 v128_set_to_zero(&nonce); 537 timer = clock(); 538 for (i = 0; i < num_trials; i++, nonce.v32[3] = i) { 539 /* length parameter to srtp_cipher_encrypt is in/out -- out is total, 540 * padded 541 * length -- so reset it each time. */ 542 unsigned octets_to_encrypt = octets_in_buffer; 543 544 /* encrypt buffer with cipher */ 545 srtp_cipher_set_iv(cipher_array[cipher_index], (uint8_t *)&nonce, 546 srtp_direction_encrypt); 547 srtp_cipher_encrypt(cipher_array[cipher_index], enc_buf, 548 &octets_to_encrypt); 549 550 /* choose a cipher at random from the array*/ 551 cipher_index = (*((uint32_t *)enc_buf)) % num_cipher; 552 } 553 timer = clock() - timer; 554 555 srtp_crypto_free(enc_buf); 556 557 if (timer == 0) { 558 /* Too fast! */ 559 return 0; 560 } 561 562 return (uint64_t)CLOCKS_PER_SEC * num_trials * 8 * octets_in_buffer / timer; 563 } 564 565 void cipher_array_test_throughput(srtp_cipher_t *ca[], int num_cipher) 566 { 567 int i; 568 int min_enc_len = 16; 569 int max_enc_len = 2048; /* should be a power of two */ 570 int num_trials = 1000000; 571 572 printf("timing %s throughput with key length %d, array size %d:\n", 573 (ca[0])->type->description, (ca[0])->key_len, num_cipher); 574 fflush(stdout); 575 for (i = min_enc_len; i <= max_enc_len; i = i * 4) 576 printf("msg len: %d\tgigabits per second: %f\n", i, 577 cipher_array_bits_per_second(ca, num_cipher, i, num_trials) / 578 1e9); 579 } 580 581 srtp_err_status_t cipher_driver_test_array_throughput(srtp_cipher_type_t *ct, 582 int klen, 583 int num_cipher) 584 { 585 srtp_cipher_t **ca = NULL; 586 srtp_err_status_t status; 587 588 status = cipher_array_alloc_init(&ca, num_cipher, ct, klen); 589 if (status) { 590 printf("error: cipher_array_alloc_init() failed with error code %d\n", 591 status); 592 return status; 593 } 594 595 cipher_array_test_throughput(ca, num_cipher); 596 597 cipher_array_delete(ca, num_cipher); 598 599 return srtp_err_status_ok; 600 }