/* * rdbx_driver.c * * driver for the rdbx implementation (replay database with extended range) * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2017, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include #endif #include /* for printf() */ #include "getopt_s.h" /* for local getopt() */ #include "rdbx.h" #include "cipher_priv.h" #ifdef ROC_TEST #error "srtp_rdbx_t won't work with ROC_TEST - bitmask same size as seq_median" #endif #include "ut_sim.h" srtp_err_status_t test_replay_dbx(int num_trials, unsigned long ws); double rdbx_check_adds_per_second(int num_trials, unsigned long ws); void usage(char *prog_name) { printf("usage: %s [ -t | -v ]\n", prog_name); exit(255); } int main(int argc, char *argv[]) { double rate; srtp_err_status_t status; int q; unsigned do_timing_test = 0; unsigned do_validation = 0; /* process input arguments */ while (1) { q = getopt_s(argc, argv, "tv"); if (q == -1) break; switch (q) { case 't': do_timing_test = 1; break; case 'v': do_validation = 1; break; default: usage(argv[0]); } } printf("rdbx (replay database w/ extended range) test driver\n" "David A. McGrew\n" "Cisco Systems, Inc.\n"); if (!do_validation && !do_timing_test) usage(argv[0]); if (do_validation) { printf("testing srtp_rdbx_t (ws=128)...\n"); status = test_replay_dbx(1 << 12, 128); if (status) { printf("failed\n"); exit(1); } printf("passed\n"); printf("testing srtp_rdbx_t (ws=1024)...\n"); status = test_replay_dbx(1 << 12, 1024); if (status) { printf("failed\n"); exit(1); } printf("passed\n"); } if (do_timing_test) { rate = rdbx_check_adds_per_second(1 << 18, 128); printf("rdbx_check/replay_adds per second (ws=128): %e\n", rate); rate = rdbx_check_adds_per_second(1 << 18, 1024); printf("rdbx_check/replay_adds per second (ws=1024): %e\n", rate); } return 0; } void print_rdbx(srtp_rdbx_t *rdbx) { char buf[2048]; printf("rdbx: {%llu, %s}\n", (unsigned long long)(rdbx->index), bitvector_bit_string(&rdbx->bitmask, buf, sizeof(buf))); } /* * rdbx_check_add(rdbx, idx) checks a known-to-be-good idx against * rdbx, then adds it. if a failure is detected (i.e., the check * indicates that the value is already in rdbx) then * srtp_err_status_algo_fail is returned. * */ srtp_err_status_t rdbx_check_add(srtp_rdbx_t *rdbx, uint32_t idx) { int delta; srtp_xtd_seq_num_t est; delta = srtp_index_guess(&rdbx->index, &est, idx); if (srtp_rdbx_check(rdbx, delta) != srtp_err_status_ok) { printf("replay_check failed at index %u\n", idx); return srtp_err_status_algo_fail; } /* * in practice, we'd authenticate the packet containing idx, using * the estimated value est, at this point */ if (srtp_rdbx_add_index(rdbx, delta) != srtp_err_status_ok) { printf("rdbx_add_index failed at index %u\n", idx); return srtp_err_status_algo_fail; } return srtp_err_status_ok; } /* * rdbx_check_expect_failure(srtp_rdbx_t *rdbx, uint32_t idx) * * checks that a sequence number idx is in the replay database * and thus will be rejected */ srtp_err_status_t rdbx_check_expect_failure(srtp_rdbx_t *rdbx, uint32_t idx) { int delta; srtp_xtd_seq_num_t est; srtp_err_status_t status; delta = srtp_index_guess(&rdbx->index, &est, idx); status = srtp_rdbx_check(rdbx, delta); if (status == srtp_err_status_ok) { printf("delta: %d ", delta); printf("replay_check failed at index %u (false positive)\n", idx); return srtp_err_status_algo_fail; } return srtp_err_status_ok; } srtp_err_status_t rdbx_check_add_unordered(srtp_rdbx_t *rdbx, uint32_t idx) { int delta; srtp_xtd_seq_num_t est; srtp_err_status_t rstat; delta = srtp_index_guess(&rdbx->index, &est, idx); rstat = srtp_rdbx_check(rdbx, delta); if ((rstat != srtp_err_status_ok) && (rstat != srtp_err_status_replay_old)) { printf("replay_check_add_unordered failed at index %u\n", idx); return srtp_err_status_algo_fail; } if (rstat == srtp_err_status_replay_old) { return srtp_err_status_ok; } if (srtp_rdbx_add_index(rdbx, delta) != srtp_err_status_ok) { printf("rdbx_add_index failed at index %u\n", idx); return srtp_err_status_algo_fail; } return srtp_err_status_ok; } srtp_err_status_t test_replay_dbx(int num_trials, unsigned long ws) { srtp_rdbx_t rdbx; uint32_t idx, ircvd; ut_connection utc; srtp_err_status_t status; int num_fp_trials; status = srtp_rdbx_init(&rdbx, ws); if (status) { printf("replay_init failed with error code %d\n", status); exit(1); } /* * test sequential insertion */ printf("\ttesting sequential insertion..."); for (idx = 0; (int)idx < num_trials; idx++) { status = rdbx_check_add(&rdbx, idx); if (status) return status; } printf("passed\n"); /* * test for false positives by checking all of the index * values which we've just added * * note that we limit the number of trials here, since allowing the * rollover counter to roll over would defeat this test */ num_fp_trials = num_trials % 0x10000; if (num_fp_trials == 0) { printf("warning: no false positive tests performed\n"); } printf("\ttesting for false positives..."); for (idx = 0; (int)idx < num_fp_trials; idx++) { status = rdbx_check_expect_failure(&rdbx, idx); if (status) return status; } printf("passed\n"); /* re-initialize */ srtp_rdbx_dealloc(&rdbx); if (srtp_rdbx_init(&rdbx, ws) != srtp_err_status_ok) { printf("replay_init failed\n"); return srtp_err_status_init_fail; } /* * test non-sequential insertion * * this test covers only fase negatives, since the values returned * by ut_next_index(...) are distinct */ ut_init(&utc); printf("\ttesting non-sequential insertion..."); for (idx = 0; (int)idx < num_trials; idx++) { ircvd = ut_next_index(&utc); status = rdbx_check_add_unordered(&rdbx, ircvd); if (status) return status; status = rdbx_check_expect_failure(&rdbx, ircvd); if (status) return status; } printf("passed\n"); /* re-initialize */ srtp_rdbx_dealloc(&rdbx); if (srtp_rdbx_init(&rdbx, ws) != srtp_err_status_ok) { printf("replay_init failed\n"); return srtp_err_status_init_fail; } /* * test insertion with large gaps. * check for false positives for each insertion. */ printf("\ttesting insertion with large gaps..."); for (idx = 0, ircvd = 0; (int)idx < num_trials; idx++, ircvd += (1 << (srtp_cipher_rand_u32_for_tests() % 12))) { status = rdbx_check_add(&rdbx, ircvd); if (status) return status; status = rdbx_check_expect_failure(&rdbx, ircvd); if (status) return status; } printf("passed\n"); srtp_rdbx_dealloc(&rdbx); return srtp_err_status_ok; } #include /* for clock() */ double rdbx_check_adds_per_second(int num_trials, unsigned long ws) { uint32_t i; int delta; srtp_rdbx_t rdbx; srtp_xtd_seq_num_t est; clock_t timer; int failures; /* count number of failures */ if (srtp_rdbx_init(&rdbx, ws) != srtp_err_status_ok) { printf("replay_init failed\n"); exit(1); } failures = 0; timer = clock(); for (i = 0; (int)i < num_trials; i++) { delta = srtp_index_guess(&rdbx.index, &est, i); if (srtp_rdbx_check(&rdbx, delta) != srtp_err_status_ok) ++failures; else if (srtp_rdbx_add_index(&rdbx, delta) != srtp_err_status_ok) ++failures; } timer = clock() - timer; if (timer < 1) { timer = 1; } printf("number of failures: %d \n", failures); srtp_rdbx_dealloc(&rdbx); return (double)CLOCKS_PER_SEC * num_trials / timer; }