/src/libsrtp/crypto/replay/rdbx.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * rdbx.c |
3 | | * |
4 | | * a replay database with extended range, using a rollover counter |
5 | | * |
6 | | * David A. McGrew |
7 | | * Cisco Systems, Inc. |
8 | | */ |
9 | | |
10 | | /* |
11 | | * |
12 | | * Copyright (c) 2001-2017, 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 | | */ |
45 | | |
46 | | #ifdef HAVE_CONFIG_H |
47 | | #include <config.h> |
48 | | #endif |
49 | | |
50 | | #include "rdbx.h" |
51 | | |
52 | | /* |
53 | | * from RFC 3711: |
54 | | * |
55 | | * A receiver reconstructs the index i of a packet with sequence |
56 | | * number SEQ using the estimate |
57 | | * |
58 | | * i = 2^16 * v + SEQ, |
59 | | * |
60 | | * where v is chosen from the set { ROC-1, ROC, ROC+1 } such that i is |
61 | | * closest to the value 2^16 * ROC + s_l. If the value r+1 is used, |
62 | | * then the rollover counter r in the cryptographic context is |
63 | | * incremented by one (if the packet containing s is authentic). |
64 | | */ |
65 | | |
66 | | /* |
67 | | * rdbx implementation notes |
68 | | * |
69 | | * A srtp_xtd_seq_num_t is essentially a sequence number for which some of |
70 | | * the data on the wire are implicit. It logically consists of a |
71 | | * rollover counter and a sequence number; the sequence number is the |
72 | | * explicit part, and the rollover counter is the implicit part. |
73 | | * |
74 | | * Upon receiving a sequence_number (e.g. in a newly received SRTP |
75 | | * packet), the complete srtp_xtd_seq_num_t can be estimated by using a |
76 | | * local srtp_xtd_seq_num_t as a basis. This is done using the function |
77 | | * srtp_index_guess(&local, &guess, seq_from_packet). This function |
78 | | * returns the difference of the guess and the local value. The local |
79 | | * srtp_xtd_seq_num_t can be moved forward to the guess using the function |
80 | | * srtp_index_advance(&guess, delta), where delta is the difference. |
81 | | * |
82 | | * |
83 | | * A srtp_rdbx_t consists of a srtp_xtd_seq_num_t and a bitmask. The index is |
84 | | * highest sequence number that has been received, and the bitmask indicates |
85 | | * which of the recent indicies have been received as well. The |
86 | | * highest bit in the bitmask corresponds to the index in the bitmask. |
87 | | */ |
88 | | |
89 | | void srtp_index_init(srtp_xtd_seq_num_t *pi) |
90 | 379k | { |
91 | 379k | *pi = 0; |
92 | 379k | } |
93 | | |
94 | | void srtp_index_advance(srtp_xtd_seq_num_t *pi, srtp_sequence_number_t s) |
95 | 314k | { |
96 | 314k | *pi += s; |
97 | 314k | } |
98 | | |
99 | | /* |
100 | | * srtp_index_guess(local, guess, s) |
101 | | * |
102 | | * given a srtp_xtd_seq_num_t local (which represents the last |
103 | | * known-to-be-good received srtp_xtd_seq_num_t) and a sequence number s |
104 | | * (from a newly arrived packet), sets the contents of *guess to |
105 | | * contain the best guess of the packet index to which s corresponds, |
106 | | * and returns the difference between *guess and *local |
107 | | * |
108 | | * nota bene - the output is a signed integer, DON'T cast it to a |
109 | | * unsigned integer! |
110 | | */ |
111 | | |
112 | | ssize_t srtp_index_guess(const srtp_xtd_seq_num_t *local, |
113 | | srtp_xtd_seq_num_t *guess, |
114 | | srtp_sequence_number_t s) |
115 | 74.2k | { |
116 | 74.2k | uint32_t local_roc = (uint32_t)(*local >> 16); |
117 | 74.2k | uint16_t local_seq = (uint16_t)*local; |
118 | 74.2k | uint32_t guess_roc; |
119 | 74.2k | uint16_t guess_seq; |
120 | 74.2k | ssize_t difference; |
121 | | |
122 | 74.2k | if (local_seq < seq_num_median) { |
123 | 42.2k | if (s - local_seq > seq_num_median) { |
124 | 18.7k | guess_roc = local_roc - 1; |
125 | 18.7k | difference = s - local_seq - seq_num_max; |
126 | 23.5k | } else { |
127 | 23.5k | guess_roc = local_roc; |
128 | 23.5k | difference = s - local_seq; |
129 | 23.5k | } |
130 | 42.2k | } else { |
131 | 31.9k | if (local_seq - seq_num_median > s) { |
132 | 4.73k | guess_roc = local_roc + 1; |
133 | 4.73k | difference = s - local_seq + seq_num_max; |
134 | 27.2k | } else { |
135 | 27.2k | guess_roc = local_roc; |
136 | 27.2k | difference = s - local_seq; |
137 | 27.2k | } |
138 | 31.9k | } |
139 | 74.2k | guess_seq = s; |
140 | | |
141 | | /* Note: guess_roc is 32 bits, so this generates a 48-bit result! */ |
142 | 74.2k | *guess = (((uint64_t)guess_roc) << 16) | guess_seq; |
143 | | |
144 | 74.2k | return difference; |
145 | 74.2k | } |
146 | | |
147 | | /* |
148 | | * rdbx |
149 | | * |
150 | | */ |
151 | | |
152 | | /* |
153 | | * srtp_rdbx_init(&r, ws) initializes the srtp_rdbx_t pointed to by r with |
154 | | * window size ws |
155 | | */ |
156 | | srtp_err_status_t srtp_rdbx_init(srtp_rdbx_t *rdbx, size_t ws) |
157 | 379k | { |
158 | 379k | if (ws == 0) { |
159 | 0 | return srtp_err_status_bad_param; |
160 | 0 | } |
161 | | |
162 | 379k | if (!bitvector_alloc(&rdbx->bitmask, ws)) { |
163 | 0 | return srtp_err_status_alloc_fail; |
164 | 0 | } |
165 | | |
166 | 379k | srtp_index_init(&rdbx->index); |
167 | | |
168 | 379k | return srtp_err_status_ok; |
169 | 379k | } |
170 | | |
171 | | /* |
172 | | * srtp_rdbx_dealloc(&r) frees memory for the srtp_rdbx_t pointed to by r |
173 | | */ |
174 | | srtp_err_status_t srtp_rdbx_dealloc(srtp_rdbx_t *rdbx) |
175 | 380k | { |
176 | 380k | bitvector_dealloc(&rdbx->bitmask); |
177 | | |
178 | 380k | return srtp_err_status_ok; |
179 | 380k | } |
180 | | |
181 | | /* |
182 | | * srtp_rdbx_set_roc(rdbx, roc) initializes the srtp_rdbx_t at the location rdbx |
183 | | * to have the rollover counter value roc. If that value is less than |
184 | | * the current rollover counter value, then the function returns |
185 | | * srtp_err_status_replay_old; otherwise, srtp_err_status_ok is returned. |
186 | | * |
187 | | */ |
188 | | srtp_err_status_t srtp_rdbx_set_roc(srtp_rdbx_t *rdbx, uint32_t roc) |
189 | 0 | { |
190 | 0 | bitvector_set_to_zero(&rdbx->bitmask); |
191 | | |
192 | | /* make sure that we're not moving backwards */ |
193 | 0 | if (roc < (rdbx->index >> 16)) { |
194 | 0 | return srtp_err_status_replay_old; |
195 | 0 | } |
196 | | |
197 | 0 | rdbx->index &= 0xffff; /* retain lowest 16 bits */ |
198 | 0 | rdbx->index |= ((uint64_t)roc) << 16; /* set ROC */ |
199 | |
|
200 | 0 | return srtp_err_status_ok; |
201 | 0 | } |
202 | | |
203 | | /* |
204 | | * srtp_rdbx_get_packet_index(rdbx) returns the value of the packet index |
205 | | * for the srtp_rdbx_t pointed to by rdbx |
206 | | * |
207 | | */ |
208 | | srtp_xtd_seq_num_t srtp_rdbx_get_packet_index(const srtp_rdbx_t *rdbx) |
209 | 0 | { |
210 | 0 | return rdbx->index; |
211 | 0 | } |
212 | | |
213 | | /* |
214 | | * srtp_rdbx_get_window_size(rdbx) returns the value of the window size |
215 | | * for the srtp_rdbx_t pointed to by rdbx |
216 | | * |
217 | | */ |
218 | | size_t srtp_rdbx_get_window_size(const srtp_rdbx_t *rdbx) |
219 | 304k | { |
220 | 304k | return bitvector_get_length(&rdbx->bitmask); |
221 | 304k | } |
222 | | |
223 | | /* |
224 | | * srtp_rdbx_check(&r, delta) checks to see if the srtp_xtd_seq_num_t |
225 | | * which is at rdbx->index + delta is in the rdb |
226 | | */ |
227 | | srtp_err_status_t srtp_rdbx_check(const srtp_rdbx_t *rdbx, ssize_t delta) |
228 | 85.0k | { |
229 | 85.0k | if (delta > 0) { /* if delta is positive, it's good */ |
230 | 24.4k | return srtp_err_status_ok; |
231 | 60.6k | } else if ((int)(bitvector_get_length(&rdbx->bitmask) - 1) + delta < 0) { |
232 | | /* if delta is lower than the bitmask, it's bad */ |
233 | 91 | return srtp_err_status_replay_old; |
234 | 60.5k | } else if (bitvector_get_bit(&rdbx->bitmask, |
235 | 60.5k | (bitvector_get_length(&rdbx->bitmask) - 1) + |
236 | 60.5k | delta) == 1) { |
237 | | /* delta is within the window, so check the bitmask */ |
238 | 53.5k | return srtp_err_status_replay_fail; |
239 | 53.5k | } |
240 | | /* otherwise, the index is okay */ |
241 | | |
242 | 7.04k | return srtp_err_status_ok; |
243 | 85.0k | } |
244 | | |
245 | | /* |
246 | | * srtp_rdbx_add_index adds the srtp_xtd_seq_num_t at rdbx->window_start + d to |
247 | | * replay_db (and does *not* check if that srtp_xtd_seq_num_t appears in db) |
248 | | * |
249 | | * this function should be called only after replay_check has |
250 | | * indicated that the index does not appear in the rdbx, e.g., a mutex |
251 | | * should protect the rdbx between these calls if need be |
252 | | */ |
253 | | srtp_err_status_t srtp_rdbx_add_index(srtp_rdbx_t *rdbx, ssize_t delta) |
254 | 375k | { |
255 | 375k | if (delta > 0) { |
256 | | /* shift forward by delta */ |
257 | 314k | srtp_index_advance(&rdbx->index, (srtp_sequence_number_t)delta); |
258 | 314k | bitvector_left_shift(&rdbx->bitmask, delta); |
259 | 314k | bitvector_set_bit(&rdbx->bitmask, |
260 | 314k | bitvector_get_length(&rdbx->bitmask) - 1); |
261 | 314k | } else { |
262 | | /* delta is in window */ |
263 | 60.9k | bitvector_set_bit(&rdbx->bitmask, |
264 | 60.9k | bitvector_get_length(&rdbx->bitmask) - 1 + delta); |
265 | 60.9k | } |
266 | | |
267 | | /* note that we need not consider the case that delta == 0 */ |
268 | | |
269 | 375k | return srtp_err_status_ok; |
270 | 375k | } |
271 | | |
272 | | /* |
273 | | * srtp_rdbx_estimate_index(rdbx, guess, s) |
274 | | * |
275 | | * given an rdbx and a sequence number s (from a newly arrived packet), |
276 | | * sets the contents of *guess to contain the best guess of the packet |
277 | | * index to which s corresponds, and returns the difference between |
278 | | * *guess and the locally stored synch info |
279 | | */ |
280 | | ssize_t srtp_rdbx_estimate_index(const srtp_rdbx_t *rdbx, |
281 | | srtp_xtd_seq_num_t *guess, |
282 | | srtp_sequence_number_t s) |
283 | 84.4k | { |
284 | | /* |
285 | | * if the sequence number and rollover counter in the rdbx are |
286 | | * non-zero, then use the srtp_index_guess(...) function, otherwise, just |
287 | | * set the rollover counter to zero (since the srtp_index_guess(...) |
288 | | * function might incorrectly guess that the rollover counter is |
289 | | * 0xffffffff) |
290 | | */ |
291 | | |
292 | 84.4k | if (rdbx->index > seq_num_median) { |
293 | 74.2k | return srtp_index_guess(&rdbx->index, guess, s); |
294 | 74.2k | } |
295 | | |
296 | 10.1k | *guess = s; |
297 | | |
298 | 10.1k | return s - rdbx->index; |
299 | 84.4k | } |
300 | | |
301 | | /* |
302 | | * srtp_rdbx_get_roc(rdbx) |
303 | | * |
304 | | * Get the current rollover counter |
305 | | * |
306 | | */ |
307 | | uint32_t srtp_rdbx_get_roc(const srtp_rdbx_t *rdbx) |
308 | 2.01k | { |
309 | 2.01k | uint32_t roc; |
310 | | |
311 | 2.01k | roc = (uint32_t)(rdbx->index >> 16); |
312 | | |
313 | 2.01k | return roc; |
314 | 2.01k | } |
315 | | |
316 | | /* |
317 | | * srtp_rdbx_set_roc_seq(rdbx, roc, seq) initializes the srtp_rdbx_t at the |
318 | | * location rdbx to have the rollover counter value roc and packet sequence |
319 | | * number seq. If the new rollover counter value is less than the current |
320 | | * rollover counter value, then the function returns |
321 | | * srtp_err_status_replay_old, otherwise, srtp_err_status_ok is returned. |
322 | | */ |
323 | | srtp_err_status_t srtp_rdbx_set_roc_seq(srtp_rdbx_t *rdbx, |
324 | | uint32_t roc, |
325 | | uint16_t seq) |
326 | 319 | { |
327 | | /* make sure that we're not moving backwards */ |
328 | 319 | if (roc < (rdbx->index >> 16)) { |
329 | 0 | return srtp_err_status_replay_old; |
330 | 0 | } |
331 | | |
332 | 319 | rdbx->index = seq; |
333 | 319 | rdbx->index |= ((uint64_t)roc) << 16; /* set ROC */ |
334 | | |
335 | 319 | bitvector_set_to_zero(&rdbx->bitmask); |
336 | | |
337 | 319 | return srtp_err_status_ok; |
338 | 319 | } |