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 */
print_interaction_field(const char * name,uint8_t * buffer,uint32_t len)33 static void print_interaction_field(const char *name, uint8_t *buffer,
34 uint32_t len)
35 {
36 PRINTF(" %s: ", name);
37 if (NULL != buffer) {
38 for (uint32_t index = 0; index < len; index++) {
39 PRINTF("%02x ", buffer[index]);
40 }
41 }
42 PRINT_MSG("\n");
43 }
44
45 /**
46 * @brief Print interactions array.
47 *
48 * @param interactions Input interactions array.
49 */
print_interactions(struct oscore_interaction_t * interactions)50 static void print_interactions(struct oscore_interaction_t *interactions)
51 {
52 for (uint8_t index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
53 struct oscore_interaction_t *record = &interactions[index];
54 PRINTF("record %02u:\n", index);
55 PRINTF(" type : %d\n", record->request_type);
56 print_interaction_field("uri paths", record->uri_paths,
57 record->uri_paths_len);
58 print_interaction_field("token ", record->token,
59 record->token_len);
60 print_interaction_field("req_piv ", record->request_piv,
61 record->request_piv_len);
62 print_interaction_field("req_kid ", record->request_kid,
63 record->request_kid_len);
64 PRINTF(" occupied : %s\n",
65 record->is_occupied ? "true" : "false");
66 }
67 }
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 */
compare_memory(uint8_t * actual,uint32_t actual_size,uint8_t * expected,uint32_t expected_size)85 static bool compare_memory(uint8_t *actual, uint32_t actual_size,
86 uint8_t *expected, uint32_t expected_size)
87 {
88 if (actual_size != expected_size) {
89 return false;
90 }
91
92 if ((NULL != actual) && (NULL != expected)) {
93 return (0 == memcmp(actual, expected, expected_size));
94 } else if ((NULL == actual) && (0 == expected_size)) {
95 return true;
96 }
97
98 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 */
find_unoccupied_index(struct oscore_interaction_t * interactions)106 static uint32_t find_unoccupied_index(struct oscore_interaction_t *interactions)
107 {
108 uint32_t index;
109 for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
110 if (false == interactions[index].is_occupied) {
111 break;
112 }
113 }
114 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
find_record_index_by_resource(struct oscore_interaction_t * interactions,uint8_t * uri_paths,uint8_t uri_paths_len,enum o_coap_msg request_type)126 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 for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
132 bool is_occupied = interactions[index].is_occupied;
133 bool request_type_ok =
134 (interactions[index].request_type == request_type);
135 bool uri_path_ok =
136 compare_memory(uri_paths, uri_paths_len,
137 interactions[index].uri_paths,
138 interactions[index].uri_paths_len);
139 if (is_occupied && request_type_ok && uri_path_ok) {
140 break;
141 }
142 }
143 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
find_record_index_by_token(struct oscore_interaction_t * interactions,uint8_t * token,uint8_t token_len)154 find_record_index_by_token(struct oscore_interaction_t *interactions,
155 uint8_t *token, uint8_t token_len)
156 {
157 uint32_t index;
158 for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
159 bool is_occupied = interactions[index].is_occupied;
160 bool token_ok = compare_memory(token, token_len,
161 interactions[index].token,
162 interactions[index].token_len);
163 if (is_occupied && token_ok) {
164 break;
165 }
166 }
167 return index;
168 }
169
oscore_interactions_init(struct oscore_interaction_t * interactions)170 enum err oscore_interactions_init(struct oscore_interaction_t *interactions)
171 {
172 if (NULL == interactions) {
173 return wrong_parameter;
174 }
175
176 memset(interactions, 0,
177 sizeof(struct oscore_interaction_t) * OSCORE_INTERACTIONS_COUNT);
178 return ok;
179 }
180
181 enum err
oscore_interactions_set_record(struct oscore_interaction_t * interactions,struct oscore_interaction_t * record)182 oscore_interactions_set_record(struct oscore_interaction_t *interactions,
183 struct oscore_interaction_t *record)
184 {
185 if ((NULL == interactions) || (NULL == record) ||
186 (record->token_len > MAX_TOKEN_LEN) ||
187 (record->uri_paths_len > OSCORE_MAX_URI_PATH_LEN) ||
188 (record->request_piv_len > MAX_PIV_LEN) ||
189 (record->request_kid_len > MAX_KID_LEN)) {
190 return wrong_parameter;
191 }
192
193 // Find the entry at which the record will be stored.
194 uint32_t index_by_uri =
195 find_record_index_by_resource(interactions, record->uri_paths,
196 record->uri_paths_len,
197 record->request_type);
198 if (index_by_uri >= OSCORE_INTERACTIONS_COUNT) {
199 index_by_uri = find_unoccupied_index(interactions);
200 if (index_by_uri >= OSCORE_INTERACTIONS_COUNT) {
201 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 uint32_t index_by_token = find_record_index_by_token(
207 interactions, record->token, record->token_len);
208 if ((index_by_token < OSCORE_INTERACTIONS_COUNT) &&
209 (index_by_token != index_by_uri)) {
210 PRINTF(msg_token_already_used, index_by_token);
211 return oscore_interaction_duplicated_token;
212 }
213
214 record->is_occupied = true;
215
216 // Memmove is used to avoid overlapping issues when get_record output is used as the record.
217 memmove(&interactions[index_by_uri], record, sizeof(*record));
218 PRINT_MSG("set record:\n");
219 PRINT_INTERACTIONS(interactions);
220 return ok;
221 }
222
223 enum err
oscore_interactions_get_record(struct oscore_interaction_t * interactions,uint8_t * token,uint8_t token_len,struct oscore_interaction_t ** record)224 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 if ((NULL == interactions) || (NULL == record) ||
229 (token_len > MAX_TOKEN_LEN)) {
230 return wrong_parameter;
231 }
232 *record = NULL;
233
234 PRINT_MSG("get record:\n");
235 PRINT_INTERACTIONS(interactions);
236
237 uint32_t index =
238 find_record_index_by_token(interactions, token, token_len);
239 if (index >= OSCORE_INTERACTIONS_COUNT) {
240 PRINT_MSG(msg_interaction_not_found);
241 PRINT_ARRAY("token", token, token_len);
242 return oscore_interaction_not_found;
243 }
244
245 *record = &interactions[index];
246 return ok;
247 }
248
249 enum err
oscore_interactions_remove_record(struct oscore_interaction_t * interactions,uint8_t * token,uint8_t token_len)250 oscore_interactions_remove_record(struct oscore_interaction_t *interactions,
251 uint8_t *token, uint8_t token_len)
252 {
253 if ((NULL == interactions) || (token_len > MAX_TOKEN_LEN)) {
254 return wrong_parameter;
255 }
256
257 PRINT_MSG("remove record (before):\n");
258 PRINT_INTERACTIONS(interactions);
259
260 uint32_t index =
261 find_record_index_by_token(interactions, token, token_len);
262 if (index >= OSCORE_INTERACTIONS_COUNT) {
263 PRINT_MSG(msg_interaction_not_found);
264 PRINT_ARRAY("token", token, token_len);
265 return oscore_interaction_not_found;
266 }
267
268 memset(&interactions[index], 0, sizeof(struct oscore_interaction_t));
269 PRINT_MSG("remove record (after):\n");
270 PRINT_INTERACTIONS(interactions);
271 return ok;
272 }
273
oscore_interactions_read_wrapper(enum o_coap_msg msg_type,struct byte_array * token,struct oscore_interaction_t * interactions,struct byte_array * request_piv,struct byte_array * request_kid)274 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 if ((NULL == token) || (NULL == interactions) ||
280 (NULL == request_piv) || (NULL == request_kid)) {
281 return wrong_parameter;
282 }
283
284 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 TRY(oscore_interactions_get_record(interactions, token->ptr,
289 (uint8_t)token->len,
290 &record));
291 request_piv->ptr = record->request_piv;
292 request_piv->len = record->request_piv_len;
293 request_kid->ptr = record->request_kid;
294 request_kid->len = record->request_kid_len;
295 }
296
297 return ok;
298 }
299
oscore_interactions_update_wrapper(enum o_coap_msg msg_type,struct byte_array * token,struct byte_array * uri_paths,struct oscore_interaction_t * interactions,struct byte_array * request_piv,struct byte_array * request_kid)300 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 if ((NULL == token) || (NULL == uri_paths) || (NULL == interactions) ||
306 (NULL == request_piv) || (NULL == request_kid)) {
307 return wrong_parameter;
308 }
309
310 // cancellation must be interpreted as a registration, to properly match the corresponding record from the interactions table.
311 if (COAP_MSG_CANCELLATION == msg_type) {
312 msg_type = COAP_MSG_REGISTRATION;
313 }
314
315 if ((COAP_MSG_REQUEST == msg_type) ||
316 (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 struct oscore_interaction_t record = {
320 .request_piv_len = (uint8_t)request_piv->len,
321 .request_kid_len = (uint8_t)request_kid->len,
322 .token_len = (uint8_t)token->len,
323 .uri_paths_len = (uint8_t)uri_paths->len,
324 .request_type = msg_type
325 };
326 TRY(_memcpy_s(record.request_piv, MAX_PIV_LEN, request_piv->ptr,
327 request_piv->len));
328 TRY(_memcpy_s(record.request_kid, MAX_KID_LEN, request_kid->ptr,
329 request_kid->len));
330 TRY(_memcpy_s(record.token, MAX_TOKEN_LEN, token->ptr,
331 token->len));
332 TRY(_memcpy_s(record.uri_paths, OSCORE_MAX_URI_PATH_LEN,
333 uri_paths->ptr, uri_paths->len));
334 TRY(oscore_interactions_set_record(interactions, &record));
335 } 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 TRY(oscore_interactions_remove_record(interactions, token->ptr,
339 (uint8_t)token->len));
340 }
341
342 return ok;
343 }
344