Wed, 31 Dec 2014 06:09:35 +0100
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 }