Branch data Line data Source code
1 : : /*
2 : : Copyright (c) 2022 Assa Abloy. See the COPYRIGHT
3 : : file at the top-level directory of this distribution.
4 : :
5 : : Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 : : http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 : : <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 : : option. This file may not be copied, modified, or distributed
9 : : except according to those terms.
10 : : */
11 : : #include <stdio.h>
12 : : #include <string.h>
13 : : #include <zephyr/ztest.h>
14 : :
15 : : #include "oscore/replay_protection.h"
16 : :
17 : : #define WINDOW_SIZE OSCORE_SERVER_REPLAY_WINDOW_SIZE
18 : : #define DUMMY_BYTE 10
19 : : #define WINDOW_SIZE_BYTES (WINDOW_SIZE * sizeof(uint64_t))
20 : :
21 : : static struct server_replay_window_t replay_window;
22 : :
23 : 3 : static void _copy_window(struct server_replay_window_t *dest,
24 : : const struct server_replay_window_t *src)
25 : : {
26 : 3 : memcpy(dest, src, sizeof(struct server_replay_window_t));
27 : 3 : }
28 : :
29 : 14 : static void _compare_windows(struct server_replay_window_t *current,
30 : : const struct server_replay_window_t *expected)
31 : : {
32 : 14 : zassert_mem_equal(current->window, expected->window, WINDOW_SIZE_BYTES,
33 : : "");
34 : 14 : zassert_equal(current->seq_num_zero_received,
35 : : expected->seq_num_zero_received, "");
36 : 14 : }
37 : :
38 : : static void
39 : 198 : _update_window_and_check_result(uint64_t seq_num,
40 : : struct server_replay_window_t *replay_window,
41 : : bool expected_result)
42 : : {
43 : 198 : bool result = server_replay_window_update(seq_num, replay_window);
44 : 198 : zassert_equal(expected_result, result, "");
45 : 198 : }
46 : :
47 : : static void
48 : 65 : _validate_window_and_check_result(uint64_t seq_num,
49 : : struct server_replay_window_t *replay_window,
50 : : bool expected_result)
51 : : {
52 : 65 : bool result = server_is_sequence_number_valid(seq_num, replay_window);
53 : 65 : zassert_equal(expected_result, result, "");
54 : 65 : }
55 : :
56 : : /**
57 : : * @brief Test replay window initialization.
58 : : */
59 : 1 : void t600_server_replay_init_test(void)
60 : : {
61 : 1 : static struct server_replay_window_t compare_window = { 0 };
62 : :
63 : : /* set random data to all fields */
64 : 1 : memset(replay_window.window, DUMMY_BYTE, WINDOW_SIZE_BYTES);
65 : 1 : replay_window.seq_num_zero_received = true;
66 : :
67 : 1 : enum err result;
68 : 1 : result = server_replay_window_init(NULL);
69 : 1 : zassert_equal(wrong_parameter, result, "");
70 : :
71 : 1 : result = server_replay_window_init(&replay_window);
72 : 1 : zassert_equal(ok, result, "");
73 : 1 : _compare_windows(&replay_window, &compare_window);
74 : :
75 : : /* extra check of helper function */
76 : 1 : zassert_equal(false, server_is_sequence_number_valid(0, NULL), "");
77 : 1 : }
78 : :
79 : : /**
80 : : * @brief Test replay window re-initialization.
81 : : */
82 : 1 : void t601_server_replay_reinit_test(void)
83 : : {
84 : 1 : static const struct server_replay_window_t compare_window_1 = {
85 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6 },
87 : : true
88 : : };
89 : :
90 : 1 : static const struct server_replay_window_t compare_window_2 = {
91 : : { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
92 : : 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
93 : : 122, 123, 124, 125, 126, 127, 128, 129, 130, 131 },
94 : : true
95 : : };
96 : :
97 : 1 : enum err result;
98 : 1 : result = server_replay_window_reinit(6, NULL);
99 : 1 : zassert_equal(wrong_parameter, result, "");
100 : :
101 : 1 : result = server_replay_window_reinit(6, &replay_window);
102 : 1 : zassert_equal(ok, result, "");
103 : 1 : _compare_windows(&replay_window, &compare_window_1);
104 : :
105 : 1 : result = server_replay_window_reinit(131, &replay_window);
106 : 1 : zassert_equal(ok, result, "");
107 : 1 : _compare_windows(&replay_window, &compare_window_2);
108 : 1 : }
109 : :
110 : : /**
111 : : * @brief Test replay window check for various sequence numbers - this case represents beginning of the communication.
112 : : */
113 : 1 : void t602_server_replay_check_at_start_test(void)
114 : : {
115 : : // missing Sequence Numbers in starting_point: 9, 5, 3, 2, 1, 0
116 : : // SN 11 is ahead of current window = OK
117 : : // SN 12 might be received before SN 11 = also OK
118 : : // SN 10 is received in the last message = NOT OK
119 : : // SN 9 is delayed and not received yet = OK
120 : : // SN 8 is already received = NOT OK
121 : : // SN 5 is delayed = OK
122 : : // SN 0 is delayed and still in the window range = OK
123 : :
124 : 1 : static const struct server_replay_window_t starting_point = {
125 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 7, 8, 10 },
127 : : false
128 : : };
129 : :
130 : 1 : static const uint64_t numbers_to_check[] = { 11, 12, 10, 9, 8, 5, 0 };
131 : 1 : static const bool numbers_results[] = { true, true, false, true,
132 : : false, true, true };
133 : 1 : const uint16_t check_count =
134 : : sizeof(numbers_to_check) / sizeof(numbers_to_check[0]);
135 : :
136 : 1 : _copy_window(&replay_window, &starting_point);
137 : :
138 [ + + ]: 8 : for (uint16_t index = 0; index < check_count; index++) {
139 : 7 : bool result_valid = numbers_results[index];
140 : 7 : uint64_t seq_num = numbers_to_check[index];
141 : 7 : _validate_window_and_check_result(seq_num, &replay_window,
142 : : result_valid);
143 : : }
144 : 1 : }
145 : :
146 : : /**
147 : : * @brief Test replay window check for various sequence numbers - this case represents communication in progress.
148 : : */
149 : 1 : void t603_server_replay_check_in_progress_test(void)
150 : : {
151 : : // missing Sequence Numbers in starting_point: 126, 127, 133
152 : : // SN 99 and below are behind the window = NOT OK
153 : :
154 : 1 : static const struct server_replay_window_t starting_point = {
155 : : { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
156 : : 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
157 : : 122, 123, 124, 125, 128, 129, 130, 131, 132, 134 },
158 : : true
159 : : };
160 : :
161 : 1 : static const uint64_t numbers_to_check[] = { 135, 134, 133, 132,
162 : : 127, 126, 99, 80 };
163 : 1 : static const bool numbers_results[] = { true, false, true, false,
164 : : true, true, false, false };
165 : 1 : const uint16_t check_count =
166 : : sizeof(numbers_to_check) / sizeof(numbers_to_check[0]);
167 : :
168 : 1 : _copy_window(&replay_window, &starting_point);
169 : :
170 [ + + ]: 9 : for (uint16_t index = 0; index < check_count; index++) {
171 : 8 : bool result_valid = numbers_results[index];
172 : 8 : uint64_t seq_num = numbers_to_check[index];
173 : 8 : _validate_window_and_check_result(seq_num, &replay_window,
174 : : result_valid);
175 : : }
176 : 1 : }
177 : :
178 : : /**
179 : : * @brief Test inserting zero into replay window at different moments of the session.
180 : : */
181 : 1 : void t604_server_replay_insert_zero_test(void)
182 : : {
183 : 1 : static const struct server_replay_window_t compare_window_1 = { { 0 }, true };
184 : :
185 : 1 : static const struct server_replay_window_t compare_window_2 = {
186 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
187 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
188 : : true
189 : : };
190 : :
191 : 1 : server_replay_window_init(&replay_window);
192 : :
193 : : /* First, check if sequence number 0 at the beginning of the session doesn't break anything. */
194 : : /* After inserting 0, window should still be empty, but zero received flag should be true. */
195 : 1 : _update_window_and_check_result(0, &replay_window, true);
196 : 1 : _compare_windows(&replay_window, &compare_window_1);
197 : :
198 : : /* Inserting 0 for the second time should result in error. */
199 : 1 : _update_window_and_check_result(0, &replay_window, false);
200 : :
201 : : /* Inserting valid number should be ok. */
202 : 1 : _update_window_and_check_result(1, &replay_window, true);
203 : 1 : _compare_windows(&replay_window, &compare_window_2);
204 : :
205 : : /* Reset replay window and insert SeqNum=1. Later, inserting delayed SeqNum=0 should still be ok. */
206 : 1 : server_replay_window_init(&replay_window);
207 : 1 : _update_window_and_check_result(1, &replay_window, true);
208 : 1 : _update_window_and_check_result(0, &replay_window, true);
209 : 1 : _compare_windows(&replay_window, &compare_window_2);
210 : :
211 : : /* Reset replay window and test immunity to simple replay attack using SeqNum=0. */
212 : 1 : server_replay_window_init(&replay_window);
213 : 1 : _update_window_and_check_result(0, &replay_window, true);
214 : 1 : _update_window_and_check_result(1, &replay_window, true);
215 : 1 : _update_window_and_check_result(0, &replay_window, false);
216 : 1 : _compare_windows(&replay_window, &compare_window_2);
217 : :
218 : : /* Reset replay window and insert multiple values that will roll the window. Later, inserting delayed SeqNum=0 should fail. */
219 : 1 : server_replay_window_init(&replay_window);
220 [ + + ]: 51 : for (uint64_t seq_num = 1; seq_num <= 50; seq_num++) {
221 : 50 : _update_window_and_check_result(seq_num, &replay_window, true);
222 : : }
223 : 1 : _update_window_and_check_result(0, &replay_window, false);
224 : 1 : }
225 : :
226 : : /**
227 : : * @brief Test inserting values into replay window at different moments of the session.
228 : : */
229 : 1 : void t605_server_replay_insert_test(void)
230 : : {
231 : 1 : static const struct server_replay_window_t starting_point = {
232 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
233 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 7, 8, 10 },
234 : : false
235 : : };
236 : 1 : static const struct server_replay_window_t compare_window_1 = {
237 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
238 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 6, 7, 8, 10 },
239 : : false
240 : : };
241 : 1 : static const struct server_replay_window_t compare_window_2 = {
242 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
243 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 5, 6, 7, 8, 10 },
244 : : false
245 : : };
246 : 1 : static const struct server_replay_window_t compare_window_3 = {
247 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248 : : 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 5, 6, 7, 8, 9, 10 },
249 : : false
250 : : };
251 : 1 : static const struct server_replay_window_t compare_window_4 = {
252 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
253 : : 0, 0, 0, 0, 0, 0, 0, 1, 4, 5, 6, 7, 8, 9, 10, 12 },
254 : : false
255 : : };
256 : 1 : static const struct server_replay_window_t compare_window_5 = {
257 : : { 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
258 : : 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
259 : : 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 },
260 : : false
261 : : };
262 : :
263 : 1 : _copy_window(&replay_window, &starting_point);
264 : :
265 : 1 : _update_window_and_check_result(1, &replay_window, true);
266 : 1 : _compare_windows(&replay_window, &compare_window_1);
267 : :
268 : 1 : _update_window_and_check_result(5, &replay_window, true);
269 : 1 : _compare_windows(&replay_window, &compare_window_2);
270 : :
271 : 1 : _update_window_and_check_result(9, &replay_window, true);
272 : 1 : _compare_windows(&replay_window, &compare_window_3);
273 : :
274 : 1 : _update_window_and_check_result(12, &replay_window, true);
275 : 1 : _compare_windows(&replay_window, &compare_window_4);
276 : :
277 [ + + ]: 89 : for (uint64_t seq_num = 13; seq_num <= 100; seq_num++) {
278 : 88 : _update_window_and_check_result(seq_num, &replay_window, true);
279 : : }
280 : 1 : _compare_windows(&replay_window, &compare_window_5);
281 : 1 : }
282 : :
283 : : /**
284 : : * @brief Standard scenario test - checks and updates
285 : : */
286 : 1 : void t606_server_replay_standard_scenario_test(void)
287 : : {
288 : 1 : static const struct server_replay_window_t compare_window_1 = {
289 : : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5 },
291 : : true
292 : : };
293 : :
294 : 1 : static const struct server_replay_window_t compare_window_2 = {
295 : : { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
296 : : 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
297 : : 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 },
298 : : true
299 : : };
300 : :
301 : 1 : static const uint64_t incoming_numbers[] = { 1, 0, 4, 4, 2, 3, 5, 1, 0 };
302 : 1 : static const enum err check_results[] = { true, true, true,
303 : : false, true, true,
304 : : true, false, false };
305 : 1 : uint16_t const check_count =
306 : : sizeof(incoming_numbers) / sizeof(incoming_numbers[0]);
307 : :
308 : 1 : server_replay_window_init(&replay_window);
309 : :
310 : : //several messages are out of order or repeated
311 [ + + ]: 10 : for (uint16_t index = 0; index < check_count; index++) {
312 : 9 : bool result_valid = check_results[index];
313 : 9 : uint64_t seq_num = incoming_numbers[index];
314 : 9 : _validate_window_and_check_result(seq_num, &replay_window,
315 : : result_valid);
316 : :
317 [ + + ]: 9 : if (result_valid) {
318 : : //replay check OK - after MAC verification, window can be updated
319 : 6 : _update_window_and_check_result(seq_num, &replay_window,
320 : : true);
321 : : }
322 : : }
323 : 1 : _compare_windows(&replay_window, &compare_window_1);
324 : :
325 : : //proper reception of multiple messages, to make sure that in real scenario window can do its job
326 [ + + ]: 42 : for (uint64_t seq_num = 10; seq_num <= 50; seq_num++) {
327 : 41 : _validate_window_and_check_result(seq_num, &replay_window,
328 : : true);
329 : 41 : _update_window_and_check_result(seq_num, &replay_window, true);
330 : : }
331 : 1 : _compare_windows(&replay_window, &compare_window_2);
332 : 1 : }
|