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