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