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