Branch data Line data Source code
1 : : /*
2 : : Copyright (c) 2023 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 : :
12 : : #include <stdbool.h>
13 : : #include <stdint.h>
14 : : #include <string.h>
15 : :
16 : : #include "oscore/oscore_interactions.h"
17 : : #include "common/byte_array.h"
18 : : #include "common/print_util.h"
19 : :
20 : : #ifdef DEBUG_PRINT
21 : : static const char msg_interaction_not_found[] =
22 : : "Couldn't find the interaction with given key.\n";
23 : : static const char msg_token_already_used[] =
24 : : "Given token is already used by other interaction (index=%u).\n";
25 : :
26 : : /**
27 : : * @brief Print single interaction field.
28 : : *
29 : : * @param msg Field name, null char included.
30 : : * @param buffer Field buffer.
31 : : * @param len Buffer size in bytes.
32 : : */
33 : 576 : static void print_interaction_field(const char *name, uint8_t *buffer,
34 : : uint32_t len)
35 : : {
36 : 576 : PRINTF(" %s: ", name);
37 [ + - ]: 576 : if (NULL != buffer) {
38 [ + + ]: 971 : for (uint32_t index = 0; index < len; index++) {
39 : 395 : PRINTF("%02x ", buffer[index]);
40 : : }
41 : : }
42 : 576 : PRINT_MSG("\n");
43 : 576 : }
44 : :
45 : : /**
46 : : * @brief Print interactions array.
47 : : *
48 : : * @param interactions Input interactions array.
49 : : */
50 : 48 : static void print_interactions(struct oscore_interaction_t *interactions)
51 : : {
52 [ + + ]: 192 : for (uint8_t index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
53 : 144 : struct oscore_interaction_t *record = &interactions[index];
54 : 144 : PRINTF("record %02u:\n", index);
55 : 144 : PRINTF(" type : %d\n", record->request_type);
56 : 144 : print_interaction_field("uri paths", record->uri_paths,
57 : 144 : record->uri_paths_len);
58 : 144 : print_interaction_field("token ", record->token,
59 : 144 : record->token_len);
60 : 144 : print_interaction_field("req_piv ", record->request_piv,
61 : 144 : record->request_piv_len);
62 : 144 : print_interaction_field("req_kid ", record->request_kid,
63 : 144 : record->request_kid_len);
64 [ + + ]: 144 : PRINTF(" occupied : %s\n",
65 : : record->is_occupied ? "true" : "false");
66 : : }
67 : 48 : }
68 : :
69 : : #define PRINT_INTERACTIONS(table) print_interactions(table)
70 : :
71 : : #else
72 : : #define PRINT_INTERACTIONS(table) \
73 : : { \
74 : : }
75 : : #endif
76 : :
77 : : /**
78 : : * @brief Securely compares two memory buffers.
79 : : *
80 : : * @param actual Actual value.
81 : : * @param expected Expected value.
82 : : * @param expected_size Number of bytes to be compared.
83 : : * @return True if memory buffers are identical, false otherwise.
84 : : */
85 : 118 : static bool compare_memory(uint8_t *actual, uint32_t actual_size,
86 : : uint8_t *expected, uint32_t expected_size)
87 : : {
88 [ + + ]: 118 : if (actual_size != expected_size) {
89 : 96 : return false;
90 : : }
91 : :
92 [ + - + - ]: 22 : if ((NULL != actual) && (NULL != expected)) {
93 : 22 : return (0 == memcmp(actual, expected, expected_size));
94 [ # # # # ]: 0 : } else if ((NULL == actual) && (0 == expected_size)) {
95 : 0 : return true;
96 : : }
97 : :
98 : 0 : return false;
99 : : }
100 : :
101 : : /**
102 : : * @brief Searches given interactions array for a first free slot.
103 : : * @param interactions Interactions array, MUST have exactly OSCORE_INTERACTIONS_COUNT elements.
104 : : * @return Index of the first unoccupied slot (or OSCORE_INTERACTIONS_COUNT if the array is full).
105 : : */
106 : 16 : static uint32_t find_unoccupied_index(struct oscore_interaction_t *interactions)
107 : : {
108 : : uint32_t index;
109 [ + - ]: 16 : for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
110 [ + - ]: 16 : if (false == interactions[index].is_occupied) {
111 : 16 : break;
112 : : }
113 : : }
114 : 16 : return index;
115 : : }
116 : :
117 : : /**
118 : : * @brief Searches given interactions array for a record that matches given resource and request type.
119 : : * @param interactions Interactions array, MUST have exactly OSCORE_INTERACTIONS_COUNT elements.
120 : : * @param uri_paths Resource path buffer to match.
121 : : * @param uri_paths_len Resource path buffer size.
122 : : * @param request_type Request type to match.
123 : : * @return Index of the record (of OSCORE_INTERACTIONS_COUNT if not found).
124 : : */
125 : : static uint32_t
126 : 16 : find_record_index_by_resource(struct oscore_interaction_t *interactions,
127 : : uint8_t *uri_paths, uint8_t uri_paths_len,
128 : : enum o_coap_msg request_type)
129 : : {
130 : : uint32_t index;
131 [ + + ]: 64 : for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
132 : 48 : bool is_occupied = interactions[index].is_occupied;
133 : 48 : bool request_type_ok =
134 : 48 : (interactions[index].request_type == request_type);
135 : : bool uri_path_ok =
136 : 48 : compare_memory(uri_paths, uri_paths_len,
137 : 48 : interactions[index].uri_paths,
138 : 48 : interactions[index].uri_paths_len);
139 [ - + - - : 48 : if (is_occupied && request_type_ok && uri_path_ok) {
- - ]
140 : 0 : break;
141 : : }
142 : : }
143 : 16 : return index;
144 : : }
145 : :
146 : : /**
147 : : * @brief Searches given interactions array for a record that matches given token.
148 : : * @param interactions Interactions array, MUST have exactly OSCORE_INTERACTIONS_COUNT elements.
149 : : * @param token Token buffer to match.
150 : : * @param token_len Token buffer size.
151 : : * @return Index of the record (if found), or OSCORE_INTERACTIONS_COUNT (if not found).
152 : : */
153 : : static uint32_t
154 : 38 : find_record_index_by_token(struct oscore_interaction_t *interactions,
155 : : uint8_t *token, uint8_t token_len)
156 : : {
157 : : uint32_t index;
158 [ + + ]: 86 : for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
159 : 70 : bool is_occupied = interactions[index].is_occupied;
160 : 70 : bool token_ok = compare_memory(token, token_len,
161 : 70 : interactions[index].token,
162 : 70 : interactions[index].token_len);
163 [ + + + - ]: 70 : if (is_occupied && token_ok) {
164 : 22 : break;
165 : : }
166 : : }
167 : 38 : return index;
168 : : }
169 : :
170 : 11 : enum err oscore_interactions_init(struct oscore_interaction_t *interactions)
171 : : {
172 [ - + ]: 11 : if (NULL == interactions) {
173 : 0 : return wrong_parameter;
174 : : }
175 : :
176 : 11 : memset(interactions, 0,
177 : : sizeof(struct oscore_interaction_t) * OSCORE_INTERACTIONS_COUNT);
178 : 11 : return ok;
179 : : }
180 : :
181 : : enum err
182 : 16 : oscore_interactions_set_record(struct oscore_interaction_t *interactions,
183 : : struct oscore_interaction_t *record)
184 : : {
185 [ + - + - ]: 16 : if ((NULL == interactions) || (NULL == record) ||
186 [ + - ]: 16 : (record->token_len > MAX_TOKEN_LEN) ||
187 [ + - ]: 16 : (record->uri_paths_len > OSCORE_MAX_URI_PATH_LEN) ||
188 [ + - ]: 16 : (record->request_piv_len > MAX_PIV_LEN) ||
189 [ - + ]: 16 : (record->request_kid_len > MAX_KID_LEN)) {
190 : 0 : return wrong_parameter;
191 : : }
192 : :
193 : : // Find the entry at which the record will be stored.
194 : : uint32_t index_by_uri =
195 : 16 : find_record_index_by_resource(interactions, record->uri_paths,
196 : 16 : record->uri_paths_len,
197 : : record->request_type);
198 [ + - ]: 16 : if (index_by_uri >= OSCORE_INTERACTIONS_COUNT) {
199 : 16 : index_by_uri = find_unoccupied_index(interactions);
200 [ - + ]: 16 : if (index_by_uri >= OSCORE_INTERACTIONS_COUNT) {
201 : 0 : return oscore_max_interactions;
202 : : }
203 : : }
204 : :
205 : : // Prevent from using the same token twice, as it would be impossible to find the proper record with get_record.
206 : 16 : uint32_t index_by_token = find_record_index_by_token(
207 : 16 : interactions, record->token, record->token_len);
208 [ - + - - ]: 16 : if ((index_by_token < OSCORE_INTERACTIONS_COUNT) &&
209 : : (index_by_token != index_by_uri)) {
210 : 0 : PRINTF(msg_token_already_used, index_by_token);
211 : 0 : return oscore_interaction_duplicated_token;
212 : : }
213 : :
214 : 16 : record->is_occupied = true;
215 : :
216 : : // Memmove is used to avoid overlapping issues when get_record output is used as the record.
217 : 16 : memmove(&interactions[index_by_uri], record, sizeof(*record));
218 : 16 : PRINT_MSG("set record:\n");
219 : 16 : PRINT_INTERACTIONS(interactions);
220 : 16 : return ok;
221 : : }
222 : :
223 : : enum err
224 : 12 : oscore_interactions_get_record(struct oscore_interaction_t *interactions,
225 : : uint8_t *token, uint8_t token_len,
226 : : struct oscore_interaction_t **record)
227 : : {
228 [ + - + - : 12 : if ((NULL == interactions) || (NULL == record) ||
- + ]
229 : : (token_len > MAX_TOKEN_LEN)) {
230 : 0 : return wrong_parameter;
231 : : }
232 : 12 : *record = NULL;
233 : :
234 : 12 : PRINT_MSG("get record:\n");
235 : 12 : PRINT_INTERACTIONS(interactions);
236 : :
237 : : uint32_t index =
238 : 12 : find_record_index_by_token(interactions, token, token_len);
239 [ - + ]: 12 : if (index >= OSCORE_INTERACTIONS_COUNT) {
240 : 0 : PRINT_MSG(msg_interaction_not_found);
241 : 0 : PRINT_ARRAY("token", token, token_len);
242 : 0 : return oscore_interaction_not_found;
243 : : }
244 : :
245 : 12 : *record = &interactions[index];
246 : 12 : return ok;
247 : : }
248 : :
249 : : enum err
250 : 10 : oscore_interactions_remove_record(struct oscore_interaction_t *interactions,
251 : : uint8_t *token, uint8_t token_len)
252 : : {
253 [ + - - + ]: 10 : if ((NULL == interactions) || (token_len > MAX_TOKEN_LEN)) {
254 : 0 : return wrong_parameter;
255 : : }
256 : :
257 : 10 : PRINT_MSG("remove record (before):\n");
258 : 10 : PRINT_INTERACTIONS(interactions);
259 : :
260 : : uint32_t index =
261 : 10 : find_record_index_by_token(interactions, token, token_len);
262 [ - + ]: 10 : if (index >= OSCORE_INTERACTIONS_COUNT) {
263 : 0 : PRINT_MSG(msg_interaction_not_found);
264 : 0 : PRINT_ARRAY("token", token, token_len);
265 : 0 : return oscore_interaction_not_found;
266 : : }
267 : :
268 : 10 : memset(&interactions[index], 0, sizeof(struct oscore_interaction_t));
269 : 10 : PRINT_MSG("remove record (after):\n");
270 : 10 : PRINT_INTERACTIONS(interactions);
271 : 10 : return ok;
272 : : }
273 : :
274 : 28 : enum err oscore_interactions_read_wrapper(
275 : : enum o_coap_msg msg_type, struct byte_array *token,
276 : : struct oscore_interaction_t *interactions,
277 : : struct byte_array *request_piv, struct byte_array *request_kid)
278 : : {
279 [ + - + - : 28 : if ((NULL == token) || (NULL == interactions) ||
+ - ]
280 [ - + ]: 28 : (NULL == request_piv) || (NULL == request_kid)) {
281 : 0 : return wrong_parameter;
282 : : }
283 : :
284 [ + + + + ]: 28 : if ((COAP_MSG_RESPONSE == msg_type) ||
285 : : (COAP_MSG_NOTIFICATION == msg_type)) {
286 : : /* Server sends / Client receives any response (notification included) - read the record from interactions array and update request_piv and request_kid. */
287 : : struct oscore_interaction_t *record;
288 [ - + ]: 12 : TRY(oscore_interactions_get_record(interactions, token->ptr,
289 : : (uint8_t)token->len,
290 : : &record));
291 : 12 : request_piv->ptr = record->request_piv;
292 : 12 : request_piv->len = record->request_piv_len;
293 : 12 : request_kid->ptr = record->request_kid;
294 : 12 : request_kid->len = record->request_kid_len;
295 : : }
296 : :
297 : 28 : return ok;
298 : : }
299 : :
300 : 28 : enum err oscore_interactions_update_wrapper(
301 : : enum o_coap_msg msg_type, struct byte_array *token,
302 : : struct byte_array *uri_paths, struct oscore_interaction_t *interactions,
303 : : struct byte_array *request_piv, struct byte_array *request_kid)
304 : : {
305 [ + - + - : 28 : if ((NULL == token) || (NULL == uri_paths) || (NULL == interactions) ||
+ - + - ]
306 [ - + ]: 28 : (NULL == request_piv) || (NULL == request_kid)) {
307 : 0 : return wrong_parameter;
308 : : }
309 : :
310 : : // cancellation must be interpreted as a registration, to properly match the corresponding record from the interactions table.
311 [ - + ]: 28 : if (COAP_MSG_CANCELLATION == msg_type) {
312 : 0 : msg_type = COAP_MSG_REGISTRATION;
313 : : }
314 : :
315 [ + + + + ]: 28 : if ((COAP_MSG_REQUEST == msg_type) ||
316 : 16 : (COAP_MSG_REGISTRATION == msg_type)) {
317 : : /* Server receives / client sends any request (including registration and cancellation) - add the record to the interactions array.
318 : : Request_piv and request_kid not updated - current values of PIV and KID (Sender ID) are used. */
319 : 16 : struct oscore_interaction_t record = {
320 : 16 : .request_piv_len = (uint8_t)request_piv->len,
321 : 16 : .request_kid_len = (uint8_t)request_kid->len,
322 : 16 : .token_len = (uint8_t)token->len,
323 : 16 : .uri_paths_len = (uint8_t)uri_paths->len,
324 : : .request_type = msg_type
325 : : };
326 [ - + ]: 16 : TRY(_memcpy_s(record.request_piv, MAX_PIV_LEN, request_piv->ptr,
327 : : request_piv->len));
328 [ - + ]: 16 : TRY(_memcpy_s(record.request_kid, MAX_KID_LEN, request_kid->ptr,
329 : : request_kid->len));
330 [ - + ]: 16 : TRY(_memcpy_s(record.token, MAX_TOKEN_LEN, token->ptr,
331 : : token->len));
332 [ - + ]: 16 : TRY(_memcpy_s(record.uri_paths, OSCORE_MAX_URI_PATH_LEN,
333 : : uri_paths->ptr, uri_paths->len));
334 [ - + ]: 16 : TRY(oscore_interactions_set_record(interactions, &record));
335 [ + + ]: 12 : } else if (COAP_MSG_RESPONSE == msg_type) {
336 : : /* Server sends / client receives a regular response - remove the record. */
337 : : //TODO removing records must be taken into account when No-Response support will be added.
338 [ - + ]: 10 : TRY(oscore_interactions_remove_record(interactions, token->ptr,
339 : : (uint8_t)token->len));
340 : : }
341 : :
342 : 28 : return ok;
343 : : }
|