1 /*
2 * rdbx_driver.c
3 *
4 * driver for the rdbx implementation (replay database with extended range)
5 *
6 * David A. McGrew
7 * Cisco Systems, Inc.
8 */
9 /*
10 *
11 * Copyright (c) 2001-2017, Cisco Systems, Inc.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 *
18 * Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials provided
24 * with the distribution.
25 *
26 * Neither the name of the Cisco Systems, Inc. nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
33 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
34 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
35 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
37 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41 * OF THE POSSIBILITY OF SUCH DAMAGE.
42 *
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48
49 #include <stdio.h> /* for printf() */
50 #include "getopt_s.h" /* for local getopt() */
51
52 #include "rdbx.h"
53 #include "cipher_priv.h"
54
55 #ifdef ROC_TEST
56 #error "srtp_rdbx_t won't work with ROC_TEST - bitmask same size as seq_median"
57 #endif
58
59 #include "ut_sim.h"
60
61 srtp_err_status_t test_replay_dbx(int num_trials, unsigned long ws);
62
63 double rdbx_check_adds_per_second(int num_trials, unsigned long ws);
64
usage(char * prog_name)65 void usage(char *prog_name)
66 {
67 printf("usage: %s [ -t | -v ]\n", prog_name);
68 exit(255);
69 }
70
main(int argc,char * argv[])71 int main(int argc, char *argv[])
72 {
73 double rate;
74 srtp_err_status_t status;
75 int q;
76 unsigned do_timing_test = 0;
77 unsigned do_validation = 0;
78
79 /* process input arguments */
80 while (1) {
81 q = getopt_s(argc, argv, "tv");
82 if (q == -1)
83 break;
84 switch (q) {
85 case 't':
86 do_timing_test = 1;
87 break;
88 case 'v':
89 do_validation = 1;
90 break;
91 default:
92 usage(argv[0]);
93 }
94 }
95
96 printf("rdbx (replay database w/ extended range) test driver\n"
97 "David A. McGrew\n"
98 "Cisco Systems, Inc.\n");
99
100 if (!do_validation && !do_timing_test)
101 usage(argv[0]);
102
103 if (do_validation) {
104 printf("testing srtp_rdbx_t (ws=128)...\n");
105
106 status = test_replay_dbx(1 << 12, 128);
107 if (status) {
108 printf("failed\n");
109 exit(1);
110 }
111 printf("passed\n");
112
113 printf("testing srtp_rdbx_t (ws=1024)...\n");
114
115 status = test_replay_dbx(1 << 12, 1024);
116 if (status) {
117 printf("failed\n");
118 exit(1);
119 }
120 printf("passed\n");
121 }
122
123 if (do_timing_test) {
124 rate = rdbx_check_adds_per_second(1 << 18, 128);
125 printf("rdbx_check/replay_adds per second (ws=128): %e\n", rate);
126 rate = rdbx_check_adds_per_second(1 << 18, 1024);
127 printf("rdbx_check/replay_adds per second (ws=1024): %e\n", rate);
128 }
129
130 return 0;
131 }
132
print_rdbx(srtp_rdbx_t * rdbx)133 void print_rdbx(srtp_rdbx_t *rdbx)
134 {
135 char buf[2048];
136 printf("rdbx: {%llu, %s}\n", (unsigned long long)(rdbx->index),
137 bitvector_bit_string(&rdbx->bitmask, buf, sizeof(buf)));
138 }
139
140 /*
141 * rdbx_check_add(rdbx, idx) checks a known-to-be-good idx against
142 * rdbx, then adds it. if a failure is detected (i.e., the check
143 * indicates that the value is already in rdbx) then
144 * srtp_err_status_algo_fail is returned.
145 *
146 */
147
rdbx_check_add(srtp_rdbx_t * rdbx,uint32_t idx)148 srtp_err_status_t rdbx_check_add(srtp_rdbx_t *rdbx, uint32_t idx)
149 {
150 int delta;
151 srtp_xtd_seq_num_t est;
152
153 delta = srtp_index_guess(&rdbx->index, &est, idx);
154
155 if (srtp_rdbx_check(rdbx, delta) != srtp_err_status_ok) {
156 printf("replay_check failed at index %u\n", idx);
157 return srtp_err_status_algo_fail;
158 }
159
160 /*
161 * in practice, we'd authenticate the packet containing idx, using
162 * the estimated value est, at this point
163 */
164
165 if (srtp_rdbx_add_index(rdbx, delta) != srtp_err_status_ok) {
166 printf("rdbx_add_index failed at index %u\n", idx);
167 return srtp_err_status_algo_fail;
168 }
169
170 return srtp_err_status_ok;
171 }
172
173 /*
174 * rdbx_check_expect_failure(srtp_rdbx_t *rdbx, uint32_t idx)
175 *
176 * checks that a sequence number idx is in the replay database
177 * and thus will be rejected
178 */
179
rdbx_check_expect_failure(srtp_rdbx_t * rdbx,uint32_t idx)180 srtp_err_status_t rdbx_check_expect_failure(srtp_rdbx_t *rdbx, uint32_t idx)
181 {
182 int delta;
183 srtp_xtd_seq_num_t est;
184 srtp_err_status_t status;
185
186 delta = srtp_index_guess(&rdbx->index, &est, idx);
187
188 status = srtp_rdbx_check(rdbx, delta);
189 if (status == srtp_err_status_ok) {
190 printf("delta: %d ", delta);
191 printf("replay_check failed at index %u (false positive)\n", idx);
192 return srtp_err_status_algo_fail;
193 }
194
195 return srtp_err_status_ok;
196 }
197
rdbx_check_add_unordered(srtp_rdbx_t * rdbx,uint32_t idx)198 srtp_err_status_t rdbx_check_add_unordered(srtp_rdbx_t *rdbx, uint32_t idx)
199 {
200 int delta;
201 srtp_xtd_seq_num_t est;
202 srtp_err_status_t rstat;
203
204 delta = srtp_index_guess(&rdbx->index, &est, idx);
205
206 rstat = srtp_rdbx_check(rdbx, delta);
207 if ((rstat != srtp_err_status_ok) &&
208 (rstat != srtp_err_status_replay_old)) {
209 printf("replay_check_add_unordered failed at index %u\n", idx);
210 return srtp_err_status_algo_fail;
211 }
212 if (rstat == srtp_err_status_replay_old) {
213 return srtp_err_status_ok;
214 }
215 if (srtp_rdbx_add_index(rdbx, delta) != srtp_err_status_ok) {
216 printf("rdbx_add_index failed at index %u\n", idx);
217 return srtp_err_status_algo_fail;
218 }
219
220 return srtp_err_status_ok;
221 }
222
test_replay_dbx(int num_trials,unsigned long ws)223 srtp_err_status_t test_replay_dbx(int num_trials, unsigned long ws)
224 {
225 srtp_rdbx_t rdbx;
226 uint32_t idx, ircvd;
227 ut_connection utc;
228 srtp_err_status_t status;
229 int num_fp_trials;
230
231 status = srtp_rdbx_init(&rdbx, ws);
232 if (status) {
233 printf("replay_init failed with error code %d\n", status);
234 exit(1);
235 }
236
237 /*
238 * test sequential insertion
239 */
240 printf("\ttesting sequential insertion...");
241 for (idx = 0; (int)idx < num_trials; idx++) {
242 status = rdbx_check_add(&rdbx, idx);
243 if (status)
244 return status;
245 }
246 printf("passed\n");
247
248 /*
249 * test for false positives by checking all of the index
250 * values which we've just added
251 *
252 * note that we limit the number of trials here, since allowing the
253 * rollover counter to roll over would defeat this test
254 */
255 num_fp_trials = num_trials % 0x10000;
256 if (num_fp_trials == 0) {
257 printf("warning: no false positive tests performed\n");
258 }
259 printf("\ttesting for false positives...");
260 for (idx = 0; (int)idx < num_fp_trials; idx++) {
261 status = rdbx_check_expect_failure(&rdbx, idx);
262 if (status)
263 return status;
264 }
265 printf("passed\n");
266
267 /* re-initialize */
268 srtp_rdbx_dealloc(&rdbx);
269
270 if (srtp_rdbx_init(&rdbx, ws) != srtp_err_status_ok) {
271 printf("replay_init failed\n");
272 return srtp_err_status_init_fail;
273 }
274
275 /*
276 * test non-sequential insertion
277 *
278 * this test covers only fase negatives, since the values returned
279 * by ut_next_index(...) are distinct
280 */
281 ut_init(&utc);
282
283 printf("\ttesting non-sequential insertion...");
284 for (idx = 0; (int)idx < num_trials; idx++) {
285 ircvd = ut_next_index(&utc);
286 status = rdbx_check_add_unordered(&rdbx, ircvd);
287 if (status)
288 return status;
289 status = rdbx_check_expect_failure(&rdbx, ircvd);
290 if (status)
291 return status;
292 }
293 printf("passed\n");
294
295 /* re-initialize */
296 srtp_rdbx_dealloc(&rdbx);
297
298 if (srtp_rdbx_init(&rdbx, ws) != srtp_err_status_ok) {
299 printf("replay_init failed\n");
300 return srtp_err_status_init_fail;
301 }
302
303 /*
304 * test insertion with large gaps.
305 * check for false positives for each insertion.
306 */
307 printf("\ttesting insertion with large gaps...");
308 for (idx = 0, ircvd = 0; (int)idx < num_trials;
309 idx++, ircvd += (1 << (srtp_cipher_rand_u32_for_tests() % 12))) {
310 status = rdbx_check_add(&rdbx, ircvd);
311 if (status)
312 return status;
313 status = rdbx_check_expect_failure(&rdbx, ircvd);
314 if (status)
315 return status;
316 }
317 printf("passed\n");
318
319 srtp_rdbx_dealloc(&rdbx);
320
321 return srtp_err_status_ok;
322 }
323
324 #include <time.h> /* for clock() */
325
rdbx_check_adds_per_second(int num_trials,unsigned long ws)326 double rdbx_check_adds_per_second(int num_trials, unsigned long ws)
327 {
328 uint32_t i;
329 int delta;
330 srtp_rdbx_t rdbx;
331 srtp_xtd_seq_num_t est;
332 clock_t timer;
333 int failures; /* count number of failures */
334
335 if (srtp_rdbx_init(&rdbx, ws) != srtp_err_status_ok) {
336 printf("replay_init failed\n");
337 exit(1);
338 }
339
340 failures = 0;
341 timer = clock();
342 for (i = 0; (int)i < num_trials; i++) {
343 delta = srtp_index_guess(&rdbx.index, &est, i);
344
345 if (srtp_rdbx_check(&rdbx, delta) != srtp_err_status_ok)
346 ++failures;
347 else if (srtp_rdbx_add_index(&rdbx, delta) != srtp_err_status_ok)
348 ++failures;
349 }
350 timer = clock() - timer;
351 if (timer < 1) {
352 timer = 1;
353 }
354
355 printf("number of failures: %d \n", failures);
356
357 srtp_rdbx_dealloc(&rdbx);
358
359 return (double)CLOCKS_PER_SEC * num_trials / timer;
360 }
361