1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/srtp/src/crypto/replay/rdbx.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,352 @@ 1.4 +/* 1.5 + * rdbx.c 1.6 + * 1.7 + * a replay database with extended range, using a rollover counter 1.8 + * 1.9 + * David A. McGrew 1.10 + * Cisco Systems, Inc. 1.11 + */ 1.12 + 1.13 +/* 1.14 + * 1.15 + * Copyright (c) 2001-2006, Cisco Systems, Inc. 1.16 + * All rights reserved. 1.17 + * 1.18 + * Redistribution and use in source and binary forms, with or without 1.19 + * modification, are permitted provided that the following conditions 1.20 + * are met: 1.21 + * 1.22 + * Redistributions of source code must retain the above copyright 1.23 + * notice, this list of conditions and the following disclaimer. 1.24 + * 1.25 + * Redistributions in binary form must reproduce the above 1.26 + * copyright notice, this list of conditions and the following 1.27 + * disclaimer in the documentation and/or other materials provided 1.28 + * with the distribution. 1.29 + * 1.30 + * Neither the name of the Cisco Systems, Inc. nor the names of its 1.31 + * contributors may be used to endorse or promote products derived 1.32 + * from this software without specific prior written permission. 1.33 + * 1.34 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.35 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.36 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1.37 + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 1.38 + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 1.39 + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1.40 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 1.41 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1.42 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 1.43 + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.44 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 1.45 + * OF THE POSSIBILITY OF SUCH DAMAGE. 1.46 + * 1.47 + */ 1.48 + 1.49 +#include "rdbx.h" 1.50 + 1.51 + 1.52 +/* 1.53 + * from RFC 3711: 1.54 + * 1.55 + * A receiver reconstructs the index i of a packet with sequence 1.56 + * number SEQ using the estimate 1.57 + * 1.58 + * i = 2^16 * v + SEQ, 1.59 + * 1.60 + * where v is chosen from the set { ROC-1, ROC, ROC+1 } such that i is 1.61 + * closest to the value 2^16 * ROC + s_l. If the value r+1 is used, 1.62 + * then the rollover counter r in the cryptographic context is 1.63 + * incremented by one (if the packet containing s is authentic). 1.64 + */ 1.65 + 1.66 + 1.67 + 1.68 +/* 1.69 + * rdbx implementation notes 1.70 + * 1.71 + * A xtd_seq_num_t is essentially a sequence number for which some of 1.72 + * the data on the wire are implicit. It logically consists of a 1.73 + * rollover counter and a sequence number; the sequence number is the 1.74 + * explicit part, and the rollover counter is the implicit part. 1.75 + * 1.76 + * Upon receiving a sequence_number (e.g. in a newly received SRTP 1.77 + * packet), the complete xtd_seq_num_t can be estimated by using a 1.78 + * local xtd_seq_num_t as a basis. This is done using the function 1.79 + * index_guess(&local, &guess, seq_from_packet). This function 1.80 + * returns the difference of the guess and the local value. The local 1.81 + * xtd_seq_num_t can be moved forward to the guess using the function 1.82 + * index_advance(&guess, delta), where delta is the difference. 1.83 + * 1.84 + * 1.85 + * A rdbx_t consists of a xtd_seq_num_t and a bitmask. The index is highest 1.86 + * sequence number that has been received, and the bitmask indicates 1.87 + * which of the recent indicies have been received as well. The 1.88 + * highest bit in the bitmask corresponds to the index in the bitmask. 1.89 + */ 1.90 + 1.91 + 1.92 +void 1.93 +index_init(xtd_seq_num_t *pi) { 1.94 +#ifdef NO_64BIT_MATH 1.95 + *pi = make64(0,0); 1.96 +#else 1.97 + *pi = 0; 1.98 +#endif 1.99 +} 1.100 + 1.101 +void 1.102 +index_advance(xtd_seq_num_t *pi, sequence_number_t s) { 1.103 +#ifdef NO_64BIT_MATH 1.104 + /* a > ~b means a+b will generate a carry */ 1.105 + /* s is uint16 here */ 1.106 + *pi = make64(high32(*pi) + (s > ~low32(*pi) ? 1 : 0),low32(*pi) + s); 1.107 +#else 1.108 + *pi += s; 1.109 +#endif 1.110 +} 1.111 + 1.112 + 1.113 +/* 1.114 + * index_guess(local, guess, s) 1.115 + * 1.116 + * given a xtd_seq_num_t local (which represents the last 1.117 + * known-to-be-good received xtd_seq_num_t) and a sequence number s 1.118 + * (from a newly arrived packet), sets the contents of *guess to 1.119 + * contain the best guess of the packet index to which s corresponds, 1.120 + * and returns the difference between *guess and *local 1.121 + * 1.122 + * nota bene - the output is a signed integer, DON'T cast it to a 1.123 + * unsigned integer! 1.124 + */ 1.125 + 1.126 +int 1.127 +index_guess(const xtd_seq_num_t *local, 1.128 + xtd_seq_num_t *guess, 1.129 + sequence_number_t s) { 1.130 +#ifdef NO_64BIT_MATH 1.131 + uint32_t local_roc = ((high32(*local) << 16) | 1.132 + (low32(*local) >> 16)); 1.133 + uint16_t local_seq = (uint16_t) (low32(*local)); 1.134 +#else 1.135 + uint32_t local_roc = (uint32_t)(*local >> 16); 1.136 + uint16_t local_seq = (uint16_t) *local; 1.137 +#endif 1.138 +#ifdef NO_64BIT_MATH 1.139 + uint32_t guess_roc = ((high32(*guess) << 16) | 1.140 + (low32(*guess) >> 16)); 1.141 + uint16_t guess_seq = (uint16_t) (low32(*guess)); 1.142 +#else 1.143 + uint32_t guess_roc = (uint32_t)(*guess >> 16); 1.144 + uint16_t guess_seq = (uint16_t) *guess; 1.145 +#endif 1.146 + int difference; 1.147 + 1.148 + if (local_seq < seq_num_median) { 1.149 + if (s - local_seq > seq_num_median) { 1.150 + guess_roc = local_roc - 1; 1.151 + difference = seq_num_max - s + local_seq; 1.152 + } else { 1.153 + guess_roc = local_roc; 1.154 + difference = s - local_seq; 1.155 + } 1.156 + } else { 1.157 + if (local_seq - seq_num_median > s) { 1.158 + guess_roc = local_roc+1; 1.159 + difference = seq_num_max - local_seq + s; 1.160 + } else { 1.161 + difference = s - local_seq; 1.162 + guess_roc = local_roc; 1.163 + } 1.164 + } 1.165 + guess_seq = s; 1.166 + 1.167 + /* Note: guess_roc is 32 bits, so this generates a 48-bit result! */ 1.168 +#ifdef NO_64BIT_MATH 1.169 + *guess = make64(guess_roc >> 16, 1.170 + (guess_roc << 16) | guess_seq); 1.171 +#else 1.172 + *guess = (((uint64_t) guess_roc) << 16) | guess_seq; 1.173 +#endif 1.174 + 1.175 + return difference; 1.176 +} 1.177 + 1.178 +/* 1.179 + * rdbx 1.180 + * 1.181 + */ 1.182 + 1.183 + 1.184 +/* 1.185 + * rdbx_init(&r, ws) initializes the rdbx_t pointed to by r with window size ws 1.186 + */ 1.187 + 1.188 +err_status_t 1.189 +rdbx_init(rdbx_t *rdbx, unsigned long ws) { 1.190 + if (ws == 0) 1.191 + return err_status_bad_param; 1.192 + 1.193 + if (bitvector_alloc(&rdbx->bitmask, ws) != 0) 1.194 + return err_status_alloc_fail; 1.195 + 1.196 + index_init(&rdbx->index); 1.197 + 1.198 + return err_status_ok; 1.199 +} 1.200 + 1.201 +/* 1.202 + * rdbx_dealloc(&r) frees memory for the rdbx_t pointed to by r 1.203 + */ 1.204 + 1.205 +err_status_t 1.206 +rdbx_dealloc(rdbx_t *rdbx) { 1.207 + bitvector_dealloc(&rdbx->bitmask); 1.208 + 1.209 + return err_status_ok; 1.210 +} 1.211 + 1.212 +/* 1.213 + * rdbx_set_roc(rdbx, roc) initalizes the rdbx_t at the location rdbx 1.214 + * to have the rollover counter value roc. If that value is less than 1.215 + * the current rollover counter value, then the function returns 1.216 + * err_status_replay_old; otherwise, err_status_ok is returned. 1.217 + * 1.218 + */ 1.219 + 1.220 +err_status_t 1.221 +rdbx_set_roc(rdbx_t *rdbx, uint32_t roc) { 1.222 + bitvector_set_to_zero(&rdbx->bitmask); 1.223 + 1.224 +#ifdef NO_64BIT_MATH 1.225 + #error not yet implemented 1.226 +#else 1.227 + 1.228 + /* make sure that we're not moving backwards */ 1.229 + if (roc < (rdbx->index >> 16)) 1.230 + return err_status_replay_old; 1.231 + 1.232 + rdbx->index &= 0xffff; /* retain lowest 16 bits */ 1.233 + rdbx->index |= ((uint64_t)roc) << 16; /* set ROC */ 1.234 +#endif 1.235 + 1.236 + return err_status_ok; 1.237 +} 1.238 + 1.239 +/* 1.240 + * rdbx_get_packet_index(rdbx) returns the value of the packet index 1.241 + * for the rdbx_t pointed to by rdbx 1.242 + * 1.243 + */ 1.244 + 1.245 +xtd_seq_num_t 1.246 +rdbx_get_packet_index(const rdbx_t *rdbx) { 1.247 + return rdbx->index; 1.248 +} 1.249 + 1.250 +/* 1.251 + * rdbx_get_window_size(rdbx) returns the value of the window size 1.252 + * for the rdbx_t pointed to by rdbx 1.253 + * 1.254 + */ 1.255 + 1.256 +unsigned long 1.257 +rdbx_get_window_size(const rdbx_t *rdbx) { 1.258 + return bitvector_get_length(&rdbx->bitmask); 1.259 +} 1.260 + 1.261 +/* 1.262 + * rdbx_check(&r, delta) checks to see if the xtd_seq_num_t 1.263 + * which is at rdbx->index + delta is in the rdb 1.264 + */ 1.265 + 1.266 +err_status_t 1.267 +rdbx_check(const rdbx_t *rdbx, int delta) { 1.268 + 1.269 + if (delta > 0) { /* if delta is positive, it's good */ 1.270 + return err_status_ok; 1.271 + } else if ((int)(bitvector_get_length(&rdbx->bitmask) - 1) + delta < 0) { 1.272 + /* if delta is lower than the bitmask, it's bad */ 1.273 + return err_status_replay_old; 1.274 + } else if (bitvector_get_bit(&rdbx->bitmask, 1.275 + (int)(bitvector_get_length(&rdbx->bitmask) - 1) + delta) == 1) { 1.276 + /* delta is within the window, so check the bitmask */ 1.277 + return err_status_replay_fail; 1.278 + } 1.279 + /* otherwise, the index is okay */ 1.280 + 1.281 + return err_status_ok; 1.282 +} 1.283 + 1.284 +/* 1.285 + * rdbx_add_index adds the xtd_seq_num_t at rdbx->window_start + d to 1.286 + * replay_db (and does *not* check if that xtd_seq_num_t appears in db) 1.287 + * 1.288 + * this function should be called only after replay_check has 1.289 + * indicated that the index does not appear in the rdbx, e.g., a mutex 1.290 + * should protect the rdbx between these calls if need be 1.291 + */ 1.292 + 1.293 +err_status_t 1.294 +rdbx_add_index(rdbx_t *rdbx, int delta) { 1.295 + 1.296 + if (delta > 0) { 1.297 + /* shift forward by delta */ 1.298 + index_advance(&rdbx->index, delta); 1.299 + bitvector_left_shift(&rdbx->bitmask, delta); 1.300 + bitvector_set_bit(&rdbx->bitmask, bitvector_get_length(&rdbx->bitmask) - 1); 1.301 + } else { 1.302 + /* delta is in window */ 1.303 + bitvector_set_bit(&rdbx->bitmask, bitvector_get_length(&rdbx->bitmask) -1 + delta); 1.304 + } 1.305 + 1.306 + /* note that we need not consider the case that delta == 0 */ 1.307 + 1.308 + return err_status_ok; 1.309 +} 1.310 + 1.311 + 1.312 + 1.313 +/* 1.314 + * rdbx_estimate_index(rdbx, guess, s) 1.315 + * 1.316 + * given an rdbx and a sequence number s (from a newly arrived packet), 1.317 + * sets the contents of *guess to contain the best guess of the packet 1.318 + * index to which s corresponds, and returns the difference between 1.319 + * *guess and the locally stored synch info 1.320 + */ 1.321 + 1.322 +int 1.323 +rdbx_estimate_index(const rdbx_t *rdbx, 1.324 + xtd_seq_num_t *guess, 1.325 + sequence_number_t s) { 1.326 + 1.327 + /* 1.328 + * if the sequence number and rollover counter in the rdbx are 1.329 + * non-zero, then use the index_guess(...) function, otherwise, just 1.330 + * set the rollover counter to zero (since the index_guess(...) 1.331 + * function might incorrectly guess that the rollover counter is 1.332 + * 0xffffffff) 1.333 + */ 1.334 + 1.335 +#ifdef NO_64BIT_MATH 1.336 + /* seq_num_median = 0x8000 */ 1.337 + if (high32(rdbx->index) > 0 || 1.338 + low32(rdbx->index) > seq_num_median) 1.339 +#else 1.340 + if (rdbx->index > seq_num_median) 1.341 +#endif 1.342 + return index_guess(&rdbx->index, guess, s); 1.343 + 1.344 +#ifdef NO_64BIT_MATH 1.345 + *guess = make64(0,(uint32_t) s); 1.346 +#else 1.347 + *guess = s; 1.348 +#endif 1.349 + 1.350 +#ifdef NO_64BIT_MATH 1.351 + return s - (uint16_t) low32(rdbx->index); 1.352 +#else 1.353 + return s - (uint16_t) rdbx->index; 1.354 +#endif 1.355 +}