• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
usage(char * prog_name)95 usage(char *prog_name) {
96   printf("usage: %s [ -t | -v | -a ]\n", prog_name);
97   exit(255);
98 }
99 
100 void
check_status(err_status_t s)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
main(int argc,char * argv[])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
cipher_driver_test_throughput(cipher_t * c)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
cipher_driver_self_test(cipher_type_t * ct)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
cipher_driver_test_buffering(cipher_t * c)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
cipher_array_alloc_init(cipher_t *** ca,int num_ciphers,cipher_type_t * ctype,int klen)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
cipher_array_delete(cipher_t * cipher_array[],int num_cipher)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
cipher_array_bits_per_second(cipher_t * cipher_array[],int num_cipher,unsigned octets_in_buffer,int num_trials)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
cipher_array_test_throughput(cipher_t * ca[],int num_cipher)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
cipher_driver_test_array_throughput(cipher_type_t * ct,int klen,int num_cipher)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