netwerk/srtp/src/crypto/test/cipher_driver.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /*
     2  * cipher_driver.c
     3  *
     4  * A driver for the generic cipher type
     5  *
     6  * David A. McGrew
     7  * Cisco Systems, Inc.
     8  */
    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  */
    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"
    54 #define PRINT_DEBUG 0
    56 void
    57 cipher_driver_test_throughput(cipher_t *c);
    59 err_status_t
    60 cipher_driver_self_test(cipher_type_t *ct);
    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  */
    69 err_status_t
    70 cipher_driver_test_buffering(cipher_t *c);
    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);
    80 void
    81 cipher_array_test_throughput(cipher_t *ca[], int num_cipher);
    83 uint64_t
    84 cipher_array_bits_per_second(cipher_t *cipher_array[], int num_cipher, 
    85 			     unsigned octets_in_buffer, int num_trials);
    87 err_status_t
    88 cipher_array_delete(cipher_t *cipher_array[], int num_cipher);
    90 err_status_t
    91 cipher_array_alloc_init(cipher_t ***cipher_array, int num_ciphers,
    92 			cipher_type_t *ctype, int klen);
    94 void
    95 usage(char *prog_name) {
    96   printf("usage: %s [ -t | -v | -a ]\n", prog_name);
    97   exit(255);
    98 }
   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 }
   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  */
   115 extern cipher_type_t null_cipher;
   116 extern cipher_type_t aes_icm;
   117 extern cipher_type_t aes_cbc;
   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;
   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   }
   156   printf("cipher test driver\n"
   157 	 "David A. McGrew\n"
   158 	 "Cisco Systems, Inc.\n");
   160   if (!do_validation && !do_timing_test && !do_array_timing_test)
   161     usage(argv[0]);
   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;
   168     for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8)
   169       cipher_driver_test_array_throughput(&null_cipher, 0, num_cipher); 
   171     for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8)
   172       cipher_driver_test_array_throughput(&aes_icm, 30, num_cipher); 
   174     for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8)
   175       cipher_driver_test_array_throughput(&aes_icm, 46, num_cipher); 
   177     for (num_cipher=1; num_cipher < max_num_cipher; num_cipher *=8)
   178       cipher_driver_test_array_throughput(&aes_cbc, 16, num_cipher); 
   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   }
   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   }
   190   /* do timing and/or buffer_test on null_cipher */
   191   status = cipher_type_alloc(&null_cipher, &c, 0); 
   192   check_status(status);
   194   status = cipher_init(c, NULL, direction_encrypt);
   195   check_status(status);
   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);
   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     }
   214     status = cipher_init(c, test_key, direction_encrypt);
   215     check_status(status);
   217     if (do_timing_test)
   218       cipher_driver_test_throughput(c);
   220     if (do_validation) {
   221       status = cipher_driver_test_buffering(c);
   222       check_status(status);
   223     }
   225     status = cipher_dealloc(c);
   226     check_status(status);
   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     }
   235     status = cipher_init(c, test_key, direction_encrypt);
   236     check_status(status);
   238     if (do_timing_test)
   239       cipher_driver_test_throughput(c);
   241     if (do_validation) {
   242       status = cipher_driver_test_buffering(c);
   243       check_status(status);
   244     }
   246     status = cipher_dealloc(c);
   247     check_status(status);
   249   return 0;
   250 }
   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;  
   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);
   265 }
   267 err_status_t
   268 cipher_driver_self_test(cipher_type_t *ct) {
   269   err_status_t status;
   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");
   279   return err_status_ok;
   280 }
   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  */
   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;
   299   printf("testing output buffering for cipher %s...",
   300 	 c->type->description);
   302   for (i=0; i < num_trials; i++) {
   304    /* set buffers to zero */
   305     for (j=0; j < buflen; j++) 
   306       buffer0[j] = buffer1[j] = 0;
   308     /* initialize cipher  */
   309     status = cipher_set_iv(c, idx);
   310     if (status)
   311       return status;
   313     /* generate 'reference' value by encrypting all at once */
   314     status = cipher_encrypt(c, buffer0, &buflen);
   315     if (status)
   316       return status;
   318     /* re-initialize cipher */
   319     status = cipher_set_iv(c, idx);
   320     if (status)
   321       return status;
   323     /* now loop over short lengths until buffer1 is encrypted */
   324     current = buffer1;
   325     end = buffer1 + buflen;
   326     while (current < end) {
   328       /* choose a short length */
   329       len = rand() & 0x01f;
   331       /* make sure that len doesn't cause us to overreach the buffer */
   332       if (current + len > end)
   333 	len = end - current;
   335       status = cipher_encrypt(c, current, &len);
   336       if (status) 
   337 	return status;
   339       /* advance pointer into buffer1 to reflect encryption */
   340       current += len;
   342       /* if buffer1 is all encrypted, break out of loop */
   343       if (current == end)
   344 	break;
   345     }
   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   }
   359   printf("passed\n");
   361   return err_status_ok;
   362 }
   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  */
   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;
   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;
   389   /* set ca to location of cipher_array */
   390   *ca = cipher_array;
   392   /* allocate key */
   393   key = crypto_alloc(klen_pad);
   394   if (key == NULL) {
   395     free(cipher_array);
   396     return err_status_alloc_fail;
   397   }
   399   /* allocate and initialize an array of ciphers */
   400   for (i=0; i < num_ciphers; i++) {
   402     /* allocate cipher */
   403     status = cipher_type_alloc(ctype, cipher_array, klen);
   404     if (status)
   405       return status;
   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;
   416 /*     printf("%dth cipher is at %p\n", i, *cipher_array); */
   417 /*     printf("%dth cipher description: %s\n", i,  */
   418 /* 	   (*cipher_array)->type->description); */
   420     /* advance cipher array pointer */
   421     cipher_array++;
   422   }
   424   crypto_free(key);
   426   return err_status_ok;
   427 }
   429 err_status_t
   430 cipher_array_delete(cipher_t *cipher_array[], int num_cipher) {
   431   int i;
   433   for (i=0; i < num_cipher; i++) {
   434     cipher_dealloc(cipher_array[i]);
   435   }
   437   free(cipher_array);
   439   return err_status_ok;
   440 }
   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  */
   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;
   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);
   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;
   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);
   482     /* choose a cipher at random from the array*/
   483     cipher_index = (*((uint32_t *)enc_buf)) % num_cipher;
   484   }
   485   timer = clock() - timer;
   487   free(enc_buf);
   489   if (timer == 0) {
   490     /* Too fast! */
   491     return 0;
   492   }
   494   return (uint64_t)CLOCKS_PER_SEC * num_trials * 8 * octets_in_buffer / timer;
   495 }
   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;
   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);
   511 }
   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;
   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   }
   526   cipher_array_test_throughput(ca, num_cipher);
   528   cipher_array_delete(ca, num_cipher);    
   530   return err_status_ok;
   531 }

mercurial