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