1 /*
2 Copyright (c) 2021 Fraunhofer AISEC. 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 <stdint.h>
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "oscore.h"
17
18 #include "oscore/oscore_coap.h"
19 #include "oscore/option.h"
20
21 #include "common/oscore_edhoc_error.h"
22 #include "common/memcpy_s.h"
23 #include "common/print_util.h"
24 #include "common/unit_test.h"
25
26 #define OSCORE_OBSERVE_REGISTRATION_VALUE 0
27 #define OSCORE_OBSERVE_CANCELLATION_VALUE 1
28
opt_extra_bytes(uint16_t delta_or_len)29 uint8_t opt_extra_bytes(uint16_t delta_or_len)
30 {
31 if (delta_or_len < 13) {
32 return 0;
33 }
34
35 if (delta_or_len < 269) {
36 return 1;
37 }
38
39 return 2;
40 }
41
options_serialize(struct o_coap_option * options,uint8_t options_cnt,struct byte_array * out)42 enum err options_serialize(struct o_coap_option *options, uint8_t options_cnt,
43 struct byte_array *out)
44 {
45 uint8_t delta_extra_byte = 0;
46 uint8_t len_extra_byte = 0;
47 uint8_t *temp_ptr = out->ptr;
48 uint8_t *header_byte;
49
50 /* Reset length */
51 uint32_t out_capacity = out->len;
52 out->len = 0;
53
54 for (uint8_t i = 0; i < options_cnt; i++) {
55 delta_extra_byte = opt_extra_bytes(options[i].delta);
56 len_extra_byte = opt_extra_bytes(options[i].len);
57
58 header_byte = temp_ptr;
59 *header_byte = 0;
60
61 switch (delta_extra_byte) {
62 case 0:
63 *(header_byte) = (uint8_t)(options[i].delta << 4);
64 break;
65 case 1:
66 *(header_byte) = (uint8_t)(13 << 4);
67 *(temp_ptr + 1) = (uint8_t)(options[i].delta - 13);
68 break;
69 case 2:
70 *(header_byte) = (uint8_t)(14 << 4);
71 uint16_t temp_delta =
72 (uint16_t)(options[i].delta - 269);
73 *(temp_ptr + 1) = (uint8_t)((temp_delta & 0xFF00) >> 8);
74 *(temp_ptr + 2) = (uint8_t)((temp_delta & 0x00FF) >> 0);
75 break;
76 }
77
78 switch (len_extra_byte) {
79 case 0:
80 *(header_byte) |= (uint8_t)(options[i].len);
81 break;
82 case 1:
83 *(header_byte) |= 13;
84 *(temp_ptr + delta_extra_byte + 1) =
85 (uint8_t)(options[i].len - 13);
86 break;
87 case 2:
88 *(header_byte) |= 14;
89 uint16_t temp_len = (uint16_t)(options[i].len - 269);
90 *(temp_ptr + delta_extra_byte + 1) =
91 (uint8_t)((temp_len & 0xFF00) >> 8);
92 *(temp_ptr + delta_extra_byte + 2) =
93 (uint8_t)((temp_len & 0x00FF) >> 0);
94 break;
95 }
96
97 /* Move to the position, where option value begins */
98 temp_ptr += 1 + delta_extra_byte + len_extra_byte;
99 /* Add length of current option*/
100 out->len = (uint32_t)(out->len + 1 + delta_extra_byte +
101 len_extra_byte + options[i].len);
102 /* Copy the byte string of current option into output*/
103 if (0 != options[i].len) {
104 uint32_t dest_size =
105 out_capacity - (uint32_t)(temp_ptr - out->ptr);
106 TRY(_memcpy_s(temp_ptr, dest_size, options[i].value,
107 options[i].len));
108
109 temp_ptr += options[i].len;
110 }
111 }
112 return ok;
113 }
114
options_deserialize(struct byte_array * in_data,struct o_coap_option * opt,uint8_t * opt_cnt,struct byte_array * payload)115 enum err options_deserialize(struct byte_array *in_data,
116 struct o_coap_option *opt, uint8_t *opt_cnt,
117 struct byte_array *payload)
118 {
119 uint8_t *temp_options_ptr = in_data->ptr;
120 uint8_t temp_options_count = 0;
121 uint8_t temp_option_header_len = 0;
122 uint16_t temp_option_delta = 0;
123 uint16_t temp_option_len = 0;
124 uint16_t temp_option_number = 0;
125
126 if (0 == in_data->len) {
127 payload->len = 0;
128 payload->ptr = NULL;
129 *opt_cnt = 0;
130 return ok;
131 }
132
133 /* Go through the in_data to find out how many options are there */
134 uint16_t i = 0;
135 while (i < in_data->len) {
136 if (OPTION_PAYLOAD_MARKER == in_data->ptr[i]) {
137 if ((in_data->len - i) < 2) {
138 return not_valid_input_packet;
139 }
140 i++;
141 payload->len = (uint32_t)in_data->len - i;
142 payload->ptr = &in_data->ptr[i];
143 return ok;
144 }
145
146 temp_option_header_len = 1;
147 /* Parser first byte,lower 4 bits for option value length and higher 4 bits for option delta*/
148 temp_option_delta = ((*temp_options_ptr) & 0xF0) >> 4;
149 temp_option_len = (*temp_options_ptr) & 0x0F;
150
151 temp_options_ptr++;
152
153 /* Special cases for extended option delta: 13 - 1 extra delta byte, 14 - 2 extra delta bytes, 15 - reserved */
154 switch (temp_option_delta) {
155 case 13:
156 temp_option_header_len =
157 (uint8_t)(temp_option_header_len + 1);
158 temp_option_delta = (uint8_t)(*temp_options_ptr + 13);
159 temp_options_ptr += 1;
160 break;
161 case 14:
162 temp_option_header_len =
163 (uint8_t)(temp_option_header_len + 2);
164 temp_option_delta =
165 (uint16_t)(((*temp_options_ptr) << 8) |
166 *(temp_options_ptr + 1)) +
167 269;
168 temp_options_ptr += 2;
169 break;
170 case 15:
171 // ERROR
172 return oscore_inpkt_invalid_option_delta;
173 break;
174 default:
175 break;
176 }
177
178 /* Special cases for extended option value length: 13 - 1 extra length byte, 14 - 2 extra length bytes, 15 - reserved */
179 switch (temp_option_len) {
180 case 13:
181 temp_option_header_len =
182 (uint8_t)(temp_option_header_len + 1);
183 temp_option_len = (uint8_t)(*temp_options_ptr + 13);
184 temp_options_ptr += 1;
185 break;
186 case 14:
187 temp_option_header_len =
188 (uint8_t)(temp_option_header_len + 2);
189 temp_option_len =
190 (uint16_t)(((*temp_options_ptr) << 8) |
191 (*(temp_options_ptr + 1) + 269));
192 temp_options_ptr += 2;
193 break;
194 case 15:
195 /* ERROR */
196 return oscore_inpkt_invalid_optionlen;
197 break;
198 default:
199 break;
200 }
201
202 temp_option_number = temp_option_number + temp_option_delta;
203 /* Update in output options */
204 opt[temp_options_count].delta = temp_option_delta;
205 opt[temp_options_count].len = temp_option_len;
206 opt[temp_options_count].option_number = temp_option_number;
207 if (temp_option_len == 0)
208 opt[temp_options_count].value = NULL;
209 else
210 opt[temp_options_count].value = temp_options_ptr;
211
212 /* Update parameters*/
213 i = (uint16_t)(i + temp_option_header_len + temp_option_len);
214 temp_options_ptr += temp_option_len;
215 if (MAX_OPTION_COUNT > temp_options_count) {
216 temp_options_count++;
217 } else {
218 return too_many_options;
219 }
220 *opt_cnt = temp_options_count;
221 }
222 return ok;
223 }
224
coap_deserialize(struct byte_array * in,struct o_coap_packet * out)225 enum err coap_deserialize(struct byte_array *in, struct o_coap_packet *out)
226 {
227 uint8_t *tmp_p = in->ptr;
228 uint32_t payload_len = in->len;
229
230 /* Read CoAP/OSCORE header (4 bytes)*/
231 if (payload_len < HEADER_LEN) {
232 return not_valid_input_packet;
233 }
234 out->options_cnt = 0;
235 out->header.ver =
236 ((*tmp_p) & HEADER_VERSION_MASK) >> HEADER_VERSION_OFFSET;
237 out->header.type = ((*tmp_p) & HEADER_TYPE_MASK) >> HEADER_TYPE_OFFSET;
238 out->header.TKL = ((*tmp_p) & HEADER_TKL_MASK) >> HEADER_TKL_OFFSET;
239 out->header.code = *(tmp_p + 1);
240 uint16_t mid_l = *(tmp_p + 3);
241 uint16_t mid_h = *(tmp_p + 2);
242 out->header.MID = (uint16_t)(mid_h << 8 | mid_l);
243
244 /* Update pointer and length*/
245 tmp_p += 4;
246 payload_len -= 4;
247
248 /*Read the token, if it exists*/
249 if (out->header.TKL == 0) {
250 out->token = NULL;
251 } else if (out->header.TKL <= 8) {
252 if (out->header.TKL <= payload_len) {
253 out->token = tmp_p;
254 } else {
255 return oscore_inpkt_invalid_tkl;
256 }
257 } else {
258 /* ERROR: CoAP token length maximal 8 bytes */
259 return oscore_inpkt_invalid_tkl;
260 }
261 /* Update pointer and length */
262 tmp_p += out->header.TKL;
263 payload_len -= out->header.TKL;
264
265 struct byte_array remaining_bytes = BYTE_ARRAY_INIT(tmp_p, payload_len);
266 TRY(options_deserialize(&remaining_bytes,
267 (struct o_coap_option *)&out->options,
268 &out->options_cnt, &out->payload));
269
270 return ok;
271 }
272
coap_serialize(struct o_coap_packet * in,uint8_t * out_byte_string,uint32_t * out_byte_string_len)273 enum err coap_serialize(struct o_coap_packet *in, uint8_t *out_byte_string,
274 uint32_t *out_byte_string_len)
275 {
276 uint8_t *temp_out_ptr = out_byte_string;
277
278 /* First byte in header (version + type + token length) */
279 *temp_out_ptr = (uint8_t)((in->header.ver << HEADER_VERSION_OFFSET) |
280 (in->header.type << HEADER_TYPE_OFFSET) |
281 (in->header.TKL));
282 /* Following 3 bytes in header (1 byte code + 2 bytes message ID)*/
283 *(temp_out_ptr + 1) = in->header.code;
284 uint16_t temp_MID = in->header.MID;
285 *(temp_out_ptr + 2) = (uint8_t)((temp_MID & 0xFF00) >> 8);
286 *(temp_out_ptr + 3) = (uint8_t)(temp_MID & 0x00FF);
287
288 temp_out_ptr += 4;
289 /* Copy token */
290 if (in->header.TKL > 0) {
291 uint32_t dest_size = *out_byte_string_len -
292 (uint32_t)(temp_out_ptr - out_byte_string);
293 TRY(_memcpy_s(temp_out_ptr, dest_size, in->token,
294 in->header.TKL));
295
296 temp_out_ptr += in->header.TKL;
297 }
298
299 /* Calculate the maximal length of all options, i.e. all options have two bytes extra delta and length*/
300 uint32_t opt_bytes_len = 0;
301 for (uint8_t i = 0; i < in->options_cnt; i++) {
302 opt_bytes_len += OPT_SERIAL_OVERHEAD + in->options[i].len;
303 }
304
305 BYTE_ARRAY_NEW(option_byte_string, MAX_COAP_OPTIONS_LEN, opt_bytes_len);
306
307 /* Convert all OSCORE U-options structure into byte string*/
308 TRY(options_serialize(in->options, in->options_cnt,
309 &option_byte_string));
310
311 /* Copy options byte string into output*/
312
313 uint32_t dest_size = *out_byte_string_len -
314 (uint32_t)(temp_out_ptr - out_byte_string);
315 TRY(_memcpy_s(temp_out_ptr, dest_size, option_byte_string.ptr,
316 option_byte_string.len));
317
318 temp_out_ptr += option_byte_string.len;
319
320 /* Payload */
321 if (in->payload.len != 0) {
322 *temp_out_ptr = OPTION_PAYLOAD_MARKER;
323
324 dest_size = *out_byte_string_len -
325 (uint32_t)(temp_out_ptr + 1 - out_byte_string);
326 TRY(_memcpy_s(++temp_out_ptr, dest_size, in->payload.ptr,
327 in->payload.len));
328 }
329 *out_byte_string_len =
330 (uint32_t)4 + in->header.TKL + option_byte_string.len;
331 if (in->payload.len) {
332 *out_byte_string_len += 1 + in->payload.len;
333 }
334
335 PRINT_ARRAY("Byte string of the converted packet", out_byte_string,
336 *out_byte_string_len);
337 return ok;
338 }
339
is_request(struct o_coap_packet * packet)340 bool is_request(struct o_coap_packet *packet)
341 {
342 if ((CODE_CLASS_MASK & packet->header.code) == REQUEST_CLASS) {
343 return true;
344 } else {
345 return false;
346 }
347 }
348
coap_get_message_type(struct o_coap_packet * coap_packet,enum o_coap_msg * msg_type)349 enum err coap_get_message_type(struct o_coap_packet *coap_packet,
350 enum o_coap_msg *msg_type)
351 {
352 if ((NULL == coap_packet) || (NULL == msg_type)) {
353 return wrong_parameter;
354 }
355
356 enum o_coap_msg result;
357 struct byte_array observe;
358 bool observe_valid = get_observe_value(
359 coap_packet->options, coap_packet->options_cnt, &observe);
360 bool request = is_request(coap_packet);
361 if (request) {
362 // packet can be a request, a registration or a cancellation
363 result = COAP_MSG_REQUEST;
364 if (observe_valid) {
365 if ((0 == observe.len) ||
366 ((1 == observe.len) &&
367 (OSCORE_OBSERVE_REGISTRATION_VALUE ==
368 observe.ptr[0]))) {
369 /* Empty uint option is interpreted as a value 0.
370 For more info, see RFC 7252 section 3.2. */
371 result = COAP_MSG_REGISTRATION;
372 } else if ((1 == observe.len) &&
373 (OSCORE_OBSERVE_CANCELLATION_VALUE ==
374 observe.ptr[0])) {
375 result = COAP_MSG_CANCELLATION;
376 }
377 }
378 } else {
379 // packet can be a regular response or a notification
380 result = (observe_valid ? COAP_MSG_NOTIFICATION :
381 COAP_MSG_RESPONSE);
382 }
383
384 *msg_type = result;
385 return ok;
386 }
387