1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/srtp/src/crypto/test/cipher_driver.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,531 @@ 1.4 +/* 1.5 + * cipher_driver.c 1.6 + * 1.7 + * A driver for the generic cipher type 1.8 + * 1.9 + * David A. McGrew 1.10 + * Cisco Systems, Inc. 1.11 + */ 1.12 + 1.13 +/* 1.14 + * 1.15 + * Copyright (c) 2001-2006, Cisco Systems, Inc. 1.16 + * All rights reserved. 1.17 + * 1.18 + * Redistribution and use in source and binary forms, with or without 1.19 + * modification, are permitted provided that the following conditions 1.20 + * are met: 1.21 + * 1.22 + * Redistributions of source code must retain the above copyright 1.23 + * notice, this list of conditions and the following disclaimer. 1.24 + * 1.25 + * Redistributions in binary form must reproduce the above 1.26 + * copyright notice, this list of conditions and the following 1.27 + * disclaimer in the documentation and/or other materials provided 1.28 + * with the distribution. 1.29 + * 1.30 + * Neither the name of the Cisco Systems, Inc. nor the names of its 1.31 + * contributors may be used to endorse or promote products derived 1.32 + * from this software without specific prior written permission. 1.33 + * 1.34 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.35 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.36 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1.37 + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 1.38 + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 1.39 + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1.40 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 1.41 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1.42 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 1.43 + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.44 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 1.45 + * OF THE POSSIBILITY OF SUCH DAMAGE. 1.46 + * 1.47 + */ 1.48 + 1.49 +#include <stdio.h> /* for printf() */ 1.50 +#include <stdlib.h> /* for rand() */ 1.51 +#include <string.h> /* for memset() */ 1.52 +#include <unistd.h> /* for getopt() */ 1.53 +#include "cipher.h" 1.54 +#include "aes_icm.h" 1.55 +#include "null_cipher.h" 1.56 + 1.57 +#define PRINT_DEBUG 0 1.58 + 1.59 +void 1.60 +cipher_driver_test_throughput(cipher_t *c); 1.61 + 1.62 +err_status_t 1.63 +cipher_driver_self_test(cipher_type_t *ct); 1.64 + 1.65 + 1.66 +/* 1.67 + * cipher_driver_test_buffering(ct) tests the cipher's output 1.68 + * buffering for correctness by checking the consistency of succesive 1.69 + * calls 1.70 + */ 1.71 + 1.72 +err_status_t 1.73 +cipher_driver_test_buffering(cipher_t *c); 1.74 + 1.75 + 1.76 +/* 1.77 + * functions for testing cipher cache thrash 1.78 + */ 1.79 +err_status_t 1.80 +cipher_driver_test_array_throughput(cipher_type_t *ct, 1.81 + int klen, int num_cipher); 1.82 + 1.83 +void 1.84 +cipher_array_test_throughput(cipher_t *ca[], int num_cipher); 1.85 + 1.86 +uint64_t 1.87 +cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 1.88 + unsigned octets_in_buffer, int num_trials); 1.89 + 1.90 +err_status_t 1.91 +cipher_array_delete(cipher_t *cipher_array[], int num_cipher); 1.92 + 1.93 +err_status_t 1.94 +cipher_array_alloc_init(cipher_t ***cipher_array, int num_ciphers, 1.95 + cipher_type_t *ctype, int klen); 1.96 + 1.97 +void 1.98 +usage(char *prog_name) { 1.99 + printf("usage: %s [ -t | -v | -a ]\n", prog_name); 1.100 + exit(255); 1.101 +} 1.102 + 1.103 +void 1.104 +check_status(err_status_t s) { 1.105 + if (s) { 1.106 + printf("error (code %d)\n", s); 1.107 + exit(s); 1.108 + } 1.109 + return; 1.110 +} 1.111 + 1.112 +/* 1.113 + * null_cipher, aes_icm, and aes_cbc are the cipher meta-objects 1.114 + * defined in the files in crypto/cipher subdirectory. these are 1.115 + * declared external so that we can use these cipher types here 1.116 + */ 1.117 + 1.118 +extern cipher_type_t null_cipher; 1.119 +extern cipher_type_t aes_icm; 1.120 +extern cipher_type_t aes_cbc; 1.121 + 1.122 +int 1.123 +main(int argc, char *argv[]) { 1.124 + cipher_t *c = NULL; 1.125 + err_status_t status; 1.126 + unsigned char test_key[48] = { 1.127 + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 1.128 + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 1.129 + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 1.130 + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 1.131 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 1.132 + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 1.133 + }; 1.134 + int q; 1.135 + unsigned do_timing_test = 0; 1.136 + unsigned do_validation = 0; 1.137 + unsigned do_array_timing_test = 0; 1.138 + 1.139 + /* process input arguments */ 1.140 + while (1) { 1.141 + q = getopt(argc, argv, "tva"); 1.142 + if (q == -1) 1.143 + break; 1.144 + switch (q) { 1.145 + case 't': 1.146 + do_timing_test = 1; 1.147 + break; 1.148 + case 'v': 1.149 + do_validation = 1; 1.150 + break; 1.151 + case 'a': 1.152 + do_array_timing_test = 1; 1.153 + break; 1.154 + default: 1.155 + usage(argv[0]); 1.156 + } 1.157 + } 1.158 + 1.159 + printf("cipher test driver\n" 1.160 + "David A. McGrew\n" 1.161 + "Cisco Systems, Inc.\n"); 1.162 + 1.163 + if (!do_validation && !do_timing_test && !do_array_timing_test) 1.164 + usage(argv[0]); 1.165 + 1.166 + /* arry timing (cache thrash) test */ 1.167 + if (do_array_timing_test) { 1.168 + int max_num_cipher = 1 << 16; /* number of ciphers in cipher_array */ 1.169 + int num_cipher; 1.170 + 1.171 + for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 1.172 + cipher_driver_test_array_throughput(&null_cipher, 0, num_cipher); 1.173 + 1.174 + for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 1.175 + cipher_driver_test_array_throughput(&aes_icm, 30, num_cipher); 1.176 + 1.177 + for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 1.178 + cipher_driver_test_array_throughput(&aes_icm, 46, num_cipher); 1.179 + 1.180 + for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 1.181 + cipher_driver_test_array_throughput(&aes_cbc, 16, num_cipher); 1.182 + 1.183 + for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8) 1.184 + cipher_driver_test_array_throughput(&aes_cbc, 32, num_cipher); 1.185 + } 1.186 + 1.187 + if (do_validation) { 1.188 + cipher_driver_self_test(&null_cipher); 1.189 + cipher_driver_self_test(&aes_icm); 1.190 + cipher_driver_self_test(&aes_cbc); 1.191 + } 1.192 + 1.193 + /* do timing and/or buffer_test on null_cipher */ 1.194 + status = cipher_type_alloc(&null_cipher, &c, 0); 1.195 + check_status(status); 1.196 + 1.197 + status = cipher_init(c, NULL, direction_encrypt); 1.198 + check_status(status); 1.199 + 1.200 + if (do_timing_test) 1.201 + cipher_driver_test_throughput(c); 1.202 + if (do_validation) { 1.203 + status = cipher_driver_test_buffering(c); 1.204 + check_status(status); 1.205 + } 1.206 + status = cipher_dealloc(c); 1.207 + check_status(status); 1.208 + 1.209 + 1.210 + /* run the throughput test on the aes_icm cipher (128-bit key) */ 1.211 + status = cipher_type_alloc(&aes_icm, &c, 30); 1.212 + if (status) { 1.213 + fprintf(stderr, "error: can't allocate cipher\n"); 1.214 + exit(status); 1.215 + } 1.216 + 1.217 + status = cipher_init(c, test_key, direction_encrypt); 1.218 + check_status(status); 1.219 + 1.220 + if (do_timing_test) 1.221 + cipher_driver_test_throughput(c); 1.222 + 1.223 + if (do_validation) { 1.224 + status = cipher_driver_test_buffering(c); 1.225 + check_status(status); 1.226 + } 1.227 + 1.228 + status = cipher_dealloc(c); 1.229 + check_status(status); 1.230 + 1.231 + /* repeat the tests with 256-bit keys */ 1.232 + status = cipher_type_alloc(&aes_icm, &c, 46); 1.233 + if (status) { 1.234 + fprintf(stderr, "error: can't allocate cipher\n"); 1.235 + exit(status); 1.236 + } 1.237 + 1.238 + status = cipher_init(c, test_key, direction_encrypt); 1.239 + check_status(status); 1.240 + 1.241 + if (do_timing_test) 1.242 + cipher_driver_test_throughput(c); 1.243 + 1.244 + if (do_validation) { 1.245 + status = cipher_driver_test_buffering(c); 1.246 + check_status(status); 1.247 + } 1.248 + 1.249 + status = cipher_dealloc(c); 1.250 + check_status(status); 1.251 + 1.252 + return 0; 1.253 +} 1.254 + 1.255 +void 1.256 +cipher_driver_test_throughput(cipher_t *c) { 1.257 + int i; 1.258 + int min_enc_len = 32; 1.259 + int max_enc_len = 2048; /* should be a power of two */ 1.260 + int num_trials = 1000000; 1.261 + 1.262 + printf("timing %s throughput, key length %d:\n", c->type->description, c->key_len); 1.263 + fflush(stdout); 1.264 + for (i=min_enc_len; i <= max_enc_len; i = i * 2) 1.265 + printf("msg len: %d\tgigabits per second: %f\n", 1.266 + i, cipher_bits_per_second(c, i, num_trials) / 1e9); 1.267 + 1.268 +} 1.269 + 1.270 +err_status_t 1.271 +cipher_driver_self_test(cipher_type_t *ct) { 1.272 + err_status_t status; 1.273 + 1.274 + printf("running cipher self-test for %s...", ct->description); 1.275 + status = cipher_type_self_test(ct); 1.276 + if (status) { 1.277 + printf("failed with error code %d\n", status); 1.278 + exit(status); 1.279 + } 1.280 + printf("passed\n"); 1.281 + 1.282 + return err_status_ok; 1.283 +} 1.284 + 1.285 +/* 1.286 + * cipher_driver_test_buffering(ct) tests the cipher's output 1.287 + * buffering for correctness by checking the consistency of succesive 1.288 + * calls 1.289 + */ 1.290 + 1.291 +err_status_t 1.292 +cipher_driver_test_buffering(cipher_t *c) { 1.293 + int i, j, num_trials = 1000; 1.294 + unsigned len, buflen = 1024; 1.295 + uint8_t buffer0[buflen], buffer1[buflen], *current, *end; 1.296 + uint8_t idx[16] = { 1.297 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1.298 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34 1.299 + }; 1.300 + err_status_t status; 1.301 + 1.302 + printf("testing output buffering for cipher %s...", 1.303 + c->type->description); 1.304 + 1.305 + for (i=0; i < num_trials; i++) { 1.306 + 1.307 + /* set buffers to zero */ 1.308 + for (j=0; j < buflen; j++) 1.309 + buffer0[j] = buffer1[j] = 0; 1.310 + 1.311 + /* initialize cipher */ 1.312 + status = cipher_set_iv(c, idx); 1.313 + if (status) 1.314 + return status; 1.315 + 1.316 + /* generate 'reference' value by encrypting all at once */ 1.317 + status = cipher_encrypt(c, buffer0, &buflen); 1.318 + if (status) 1.319 + return status; 1.320 + 1.321 + /* re-initialize cipher */ 1.322 + status = cipher_set_iv(c, idx); 1.323 + if (status) 1.324 + return status; 1.325 + 1.326 + /* now loop over short lengths until buffer1 is encrypted */ 1.327 + current = buffer1; 1.328 + end = buffer1 + buflen; 1.329 + while (current < end) { 1.330 + 1.331 + /* choose a short length */ 1.332 + len = rand() & 0x01f; 1.333 + 1.334 + /* make sure that len doesn't cause us to overreach the buffer */ 1.335 + if (current + len > end) 1.336 + len = end - current; 1.337 + 1.338 + status = cipher_encrypt(c, current, &len); 1.339 + if (status) 1.340 + return status; 1.341 + 1.342 + /* advance pointer into buffer1 to reflect encryption */ 1.343 + current += len; 1.344 + 1.345 + /* if buffer1 is all encrypted, break out of loop */ 1.346 + if (current == end) 1.347 + break; 1.348 + } 1.349 + 1.350 + /* compare buffers */ 1.351 + for (j=0; j < buflen; j++) 1.352 + if (buffer0[j] != buffer1[j]) { 1.353 +#if PRINT_DEBUG 1.354 + printf("test case %d failed at byte %d\n", i, j); 1.355 + printf("computed: %s\n", octet_string_hex_string(buffer1, buflen)); 1.356 + printf("expected: %s\n", octet_string_hex_string(buffer0, buflen)); 1.357 +#endif 1.358 + return err_status_algo_fail; 1.359 + } 1.360 + } 1.361 + 1.362 + printf("passed\n"); 1.363 + 1.364 + return err_status_ok; 1.365 +} 1.366 + 1.367 + 1.368 +/* 1.369 + * The function cipher_test_throughput_array() tests the effect of CPU 1.370 + * cache thrash on cipher throughput. 1.371 + * 1.372 + * cipher_array_alloc_init(ctype, array, num_ciphers) creates an array 1.373 + * of cipher_t of type ctype 1.374 + */ 1.375 + 1.376 +err_status_t 1.377 +cipher_array_alloc_init(cipher_t ***ca, int num_ciphers, 1.378 + cipher_type_t *ctype, int klen) { 1.379 + int i, j; 1.380 + err_status_t status; 1.381 + uint8_t *key; 1.382 + cipher_t **cipher_array; 1.383 + /* pad klen allocation, to handle aes_icm reading 16 bytes for the 1.384 + 14-byte salt */ 1.385 + int klen_pad = ((klen + 15) >> 4) << 4; 1.386 + 1.387 + /* allocate array of pointers to ciphers */ 1.388 + cipher_array = (cipher_t **) malloc(sizeof(cipher_t *) * num_ciphers); 1.389 + if (cipher_array == NULL) 1.390 + return err_status_alloc_fail; 1.391 + 1.392 + /* set ca to location of cipher_array */ 1.393 + *ca = cipher_array; 1.394 + 1.395 + /* allocate key */ 1.396 + key = crypto_alloc(klen_pad); 1.397 + if (key == NULL) { 1.398 + free(cipher_array); 1.399 + return err_status_alloc_fail; 1.400 + } 1.401 + 1.402 + /* allocate and initialize an array of ciphers */ 1.403 + for (i=0; i < num_ciphers; i++) { 1.404 + 1.405 + /* allocate cipher */ 1.406 + status = cipher_type_alloc(ctype, cipher_array, klen); 1.407 + if (status) 1.408 + return status; 1.409 + 1.410 + /* generate random key and initialize cipher */ 1.411 + for (j=0; j < klen; j++) 1.412 + key[j] = (uint8_t) rand(); 1.413 + for (; j < klen_pad; j++) 1.414 + key[j] = 0; 1.415 + status = cipher_init(*cipher_array, key, direction_encrypt); 1.416 + if (status) 1.417 + return status; 1.418 + 1.419 +/* printf("%dth cipher is at %p\n", i, *cipher_array); */ 1.420 +/* printf("%dth cipher description: %s\n", i, */ 1.421 +/* (*cipher_array)->type->description); */ 1.422 + 1.423 + /* advance cipher array pointer */ 1.424 + cipher_array++; 1.425 + } 1.426 + 1.427 + crypto_free(key); 1.428 + 1.429 + return err_status_ok; 1.430 +} 1.431 + 1.432 +err_status_t 1.433 +cipher_array_delete(cipher_t *cipher_array[], int num_cipher) { 1.434 + int i; 1.435 + 1.436 + for (i=0; i < num_cipher; i++) { 1.437 + cipher_dealloc(cipher_array[i]); 1.438 + } 1.439 + 1.440 + free(cipher_array); 1.441 + 1.442 + return err_status_ok; 1.443 +} 1.444 + 1.445 + 1.446 +/* 1.447 + * cipher_array_bits_per_second(c, l, t) computes (an estimate of) the 1.448 + * number of bits that a cipher implementation can encrypt in a second 1.449 + * when distinct keys are used to encrypt distinct messages 1.450 + * 1.451 + * c is a cipher (which MUST be allocated an initialized already), l 1.452 + * is the length in octets of the test data to be encrypted, and t is 1.453 + * the number of trials 1.454 + * 1.455 + * if an error is encountered, the value 0 is returned 1.456 + */ 1.457 + 1.458 +uint64_t 1.459 +cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 1.460 + unsigned octets_in_buffer, int num_trials) { 1.461 + int i; 1.462 + v128_t nonce; 1.463 + clock_t timer; 1.464 + unsigned char *enc_buf; 1.465 + int cipher_index = rand() % num_cipher; 1.466 + 1.467 + /* Over-alloc, for NIST CBC padding */ 1.468 + enc_buf = crypto_alloc(octets_in_buffer+17); 1.469 + if (enc_buf == NULL) 1.470 + return 0; /* indicate bad parameters by returning null */ 1.471 + memset(enc_buf, 0, octets_in_buffer); 1.472 + 1.473 + /* time repeated trials */ 1.474 + v128_set_to_zero(&nonce); 1.475 + timer = clock(); 1.476 + for(i=0; i < num_trials; i++, nonce.v32[3] = i) { 1.477 + /* length parameter to cipher_encrypt is in/out -- out is total, padded 1.478 + * length -- so reset it each time. */ 1.479 + unsigned octets_to_encrypt = octets_in_buffer; 1.480 + 1.481 + /* encrypt buffer with cipher */ 1.482 + cipher_set_iv(cipher_array[cipher_index], &nonce); 1.483 + cipher_encrypt(cipher_array[cipher_index], enc_buf, &octets_to_encrypt); 1.484 + 1.485 + /* choose a cipher at random from the array*/ 1.486 + cipher_index = (*((uint32_t *)enc_buf)) % num_cipher; 1.487 + } 1.488 + timer = clock() - timer; 1.489 + 1.490 + free(enc_buf); 1.491 + 1.492 + if (timer == 0) { 1.493 + /* Too fast! */ 1.494 + return 0; 1.495 + } 1.496 + 1.497 + return (uint64_t)CLOCKS_PER_SEC * num_trials * 8 * octets_in_buffer / timer; 1.498 +} 1.499 + 1.500 +void 1.501 +cipher_array_test_throughput(cipher_t *ca[], int num_cipher) { 1.502 + int i; 1.503 + int min_enc_len = 16; 1.504 + int max_enc_len = 2048; /* should be a power of two */ 1.505 + int num_trials = 1000000; 1.506 + 1.507 + printf("timing %s throughput with key length %d, array size %d:\n", 1.508 + (ca[0])->type->description, (ca[0])->key_len, num_cipher); 1.509 + fflush(stdout); 1.510 + for (i=min_enc_len; i <= max_enc_len; i = i * 4) 1.511 + printf("msg len: %d\tgigabits per second: %f\n", i, 1.512 + cipher_array_bits_per_second(ca, num_cipher, i, num_trials) / 1e9); 1.513 + 1.514 +} 1.515 + 1.516 +err_status_t 1.517 +cipher_driver_test_array_throughput(cipher_type_t *ct, 1.518 + int klen, int num_cipher) { 1.519 + cipher_t **ca = NULL; 1.520 + err_status_t status; 1.521 + 1.522 + status = cipher_array_alloc_init(&ca, num_cipher, ct, klen); 1.523 + if (status) { 1.524 + printf("error: cipher_array_alloc_init() failed with error code %d\n", 1.525 + status); 1.526 + return status; 1.527 + } 1.528 + 1.529 + cipher_array_test_throughput(ca, num_cipher); 1.530 + 1.531 + cipher_array_delete(ca, num_cipher); 1.532 + 1.533 + return err_status_ok; 1.534 +}