Home | History | Annotate | Download | only in test
      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