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/aad.h"
19 : : #include "oscore/oscore_coap.h"
20 : : #include "oscore/nonce.h"
21 : : #include "oscore/option.h"
22 : : #include "oscore/oscore_cose.h"
23 : : #include "oscore/security_context.h"
24 : : #include "oscore/nvm.h"
25 : :
26 : : #include "common/byte_array.h"
27 : : #include "common/oscore_edhoc_error.h"
28 : : #include "common/memcpy_s.h"
29 : : #include "common/print_util.h"
30 : : #include "common/unit_test.h"
31 : :
32 : : /**
33 : : * @brief Extract input CoAP options into E(encrypted) and U(unprotected)
34 : : * @param in_o_coap: input CoAP packet
35 : : * @param e_options: output pointer to E-options
36 : : * @param e_options_cnt: count number of output E-options
37 : : * @param e_options_len: Byte string length of all E-options, which will be used when forming E-options into plaintext
38 : : * @param U_options: output pointer to U-options
39 : : * @param U_options_cnt: count number of output U-options
40 : : * @return err
41 : : *
42 : : */
43 : 20 : STATIC enum err inner_outer_option_split(struct o_coap_packet *in_o_coap,
44 : : struct o_coap_option *e_options,
45 : : uint8_t *e_options_cnt,
46 : : uint16_t *e_options_len,
47 : : struct o_coap_option *U_options,
48 : : uint8_t *U_options_cnt)
49 : : {
50 : 20 : enum err r = ok;
51 : :
52 : : /* Initialize to 0 */
53 : 20 : *e_options_len = 0;
54 : :
55 : 20 : uint8_t temp_option_nr = 0;
56 : 20 : uint16_t temp_len = 0;
57 : 20 : uint8_t temp_E_option_delta_sum = 0;
58 : 20 : uint8_t temp_U_option_delta_sum = 0;
59 : :
60 [ + + ]: 20 : if (MAX_OPTION_COUNT < in_o_coap->options_cnt) {
61 : 1 : return too_many_options;
62 : : }
63 : :
64 [ + + ]: 51 : for (uint8_t i = 0; i < in_o_coap->options_cnt; i++) {
65 : 32 : uint8_t extra_bytes =
66 : 32 : opt_extra_bytes(in_o_coap->options[i].delta) +
67 : 32 : opt_extra_bytes(in_o_coap->options[i].len);
68 : :
69 : 32 : temp_option_nr =
70 : 32 : (uint8_t)(temp_option_nr + in_o_coap->options[i].delta);
71 : 32 : temp_len = in_o_coap->options[i].len;
72 : :
73 : : /* process special options, see 4.1.3 in RFC8613*/
74 : : /* if the option does not need special processing just put it in the
75 : : E or U array*/
76 : :
77 [ + + ]: 32 : switch (temp_option_nr) {
78 : 4 : case OBSERVE:
79 : : /*An observe option in an a CoAP packet is transformed to an inner
80 : : and outer option in a OSCORE packet.*/
81 : :
82 : : /*
83 : : * Inner option has value NULL if notification or the original value
84 : : * in the coap packet if registration/cancellation.
85 : : */
86 : 4 : e_options[*e_options_cnt].delta =
87 : 4 : (uint16_t)(temp_option_nr -
88 : : temp_E_option_delta_sum);
89 [ + + ]: 4 : if (is_request(in_o_coap)) {
90 : : /*registrations/cancellations are requests */
91 : 2 : e_options[*e_options_cnt].len = temp_len;
92 : 2 : e_options[*e_options_cnt].value =
93 : 2 : in_o_coap->options[i].value;
94 : :
95 : : /* Add option header length and value length */
96 : 2 : (*e_options_len) =
97 : 2 : (uint16_t)((*e_options_len) + 1 +
98 : 2 : extra_bytes + temp_len);
99 : : } else {
100 : : /*notifications are responses*/
101 : 2 : e_options[*e_options_cnt].len = 0;
102 : 2 : e_options[*e_options_cnt].value = NULL;
103 : :
104 : : /* since the option value has length 0, we add 1 for the option header which is always there */
105 : 2 : (*e_options_len)++;
106 : : }
107 : :
108 : 4 : e_options[*e_options_cnt].option_number =
109 : : temp_option_nr;
110 : :
111 : : /* Update delta sum of E-options */
112 : 4 : temp_E_option_delta_sum =
113 : 4 : (uint8_t)(temp_E_option_delta_sum +
114 : 4 : e_options[*e_options_cnt].delta);
115 : :
116 : : /* Increment E-options count */
117 : 4 : (*e_options_cnt)++;
118 : :
119 : : /*
120 : : *outer option (value as in the original coap packet
121 : : */
122 : 4 : U_options[*U_options_cnt].delta =
123 : 4 : (uint16_t)(temp_option_nr -
124 : : temp_U_option_delta_sum);
125 : 4 : U_options[*U_options_cnt].len = temp_len;
126 : 4 : U_options[*U_options_cnt].value =
127 : 4 : in_o_coap->options[i].value;
128 : 4 : U_options[*U_options_cnt].option_number =
129 : : temp_option_nr;
130 : :
131 : : /* Update delta sum of E-options */
132 : 4 : temp_U_option_delta_sum =
133 : 4 : (uint8_t)(temp_U_option_delta_sum +
134 : 4 : U_options[*U_options_cnt].delta);
135 : :
136 : : /* Increment E-options count */
137 : 4 : (*U_options_cnt)++;
138 : :
139 : 4 : break;
140 : :
141 : 28 : default:
142 : : /* check delta, whether current option U or E */
143 [ + + ]: 28 : if (is_class_e(temp_option_nr) == 1) {
144 : : /* E-options, which will be copied in plaintext to be encrypted*/
145 : 22 : e_options[*e_options_cnt].delta =
146 : 22 : (uint16_t)(temp_option_nr -
147 : : temp_E_option_delta_sum);
148 : 22 : e_options[*e_options_cnt].len = temp_len;
149 : 22 : e_options[*e_options_cnt].value =
150 : 22 : in_o_coap->options[i].value;
151 : 22 : e_options[*e_options_cnt].option_number =
152 : : temp_option_nr;
153 : :
154 : : /* Update delta sum of E-options */
155 : 22 : temp_E_option_delta_sum =
156 : 22 : (uint8_t)(temp_E_option_delta_sum +
157 : 22 : e_options[*e_options_cnt]
158 : 22 : .delta);
159 : :
160 : : /* Increment E-options count */
161 : 22 : (*e_options_cnt)++;
162 : : /* Add option header length and value length */
163 : 22 : (*e_options_len) =
164 : 22 : (uint16_t)((*e_options_len) + 1 +
165 : 22 : extra_bytes + temp_len);
166 : : } else {
167 : : /* U-options */
168 : 6 : U_options[*U_options_cnt].delta =
169 : 6 : (uint16_t)(temp_option_nr -
170 : : temp_U_option_delta_sum);
171 : 6 : U_options[*U_options_cnt].len = temp_len;
172 : 6 : U_options[*U_options_cnt].value =
173 : 6 : in_o_coap->options[i].value;
174 : 6 : U_options[*U_options_cnt].option_number =
175 : : temp_option_nr;
176 : :
177 : : /* Update delta sum of E-options */
178 : 6 : temp_U_option_delta_sum =
179 : 6 : (uint8_t)(temp_U_option_delta_sum +
180 : 6 : U_options[*U_options_cnt]
181 : 6 : .delta);
182 : :
183 : : /* Increment E-options count */
184 : 6 : (*U_options_cnt)++;
185 : : }
186 : 28 : break;
187 : : }
188 : : }
189 : 19 : return r;
190 : : }
191 : :
192 : : /**
193 : : * @brief Build up plaintext which should be encrypted and protected
194 : : * @param in_o_coap: input CoAP packet that will be analyzed
195 : : * @param E_options: E-options, which should be protected
196 : : * @param E_options_cnt: count number of E-options
197 : : * @param plaintext: output plaintext, which will be encrypted
198 : : * @return err
199 : : *
200 : : */
201 : 16 : static inline enum err plaintext_setup(struct o_coap_packet *in_o_coap,
202 : : struct o_coap_option *E_options,
203 : : uint8_t E_options_cnt,
204 : : struct byte_array *plaintext)
205 : : {
206 : 16 : uint8_t *temp_plaintext_ptr = plaintext->ptr;
207 : :
208 : : /* Add code to plaintext */
209 : 16 : *temp_plaintext_ptr = in_o_coap->header.code;
210 : :
211 : : /* Calculate the maximal length of all options, i.e. all options
212 : : have two bytes extra delta and length */
213 : 16 : uint16_t e_opt_serial_len = 0;
214 [ + + ]: 31 : for (uint8_t i = 0; i < E_options_cnt; i++) {
215 : 15 : e_opt_serial_len = (uint16_t)(e_opt_serial_len + 1 + 2 + 2 +
216 : 15 : E_options[i].len);
217 : : }
218 : : /* Setup buffer */
219 [ - + ]: 16 : BYTE_ARRAY_NEW(e_opt_serial, E_OPTIONS_BUFF_MAX_LEN,
220 : : E_OPTIONS_BUFF_MAX_LEN);
221 : :
222 : : /* Convert all E-options structure to byte string, and copy it to
223 : : output*/
224 [ - + ]: 16 : TRY(options_serialize(E_options, E_options_cnt, &e_opt_serial));
225 : :
226 : 16 : uint32_t dest_size = (plaintext->len - (uint32_t)(temp_plaintext_ptr +
227 : 16 : 1 - plaintext->ptr));
228 [ - + ]: 16 : TRY(_memcpy_s(++temp_plaintext_ptr, dest_size, e_opt_serial.ptr,
229 : : e_opt_serial.len));
230 : 16 : temp_plaintext_ptr += e_opt_serial.len;
231 : :
232 : : /* Add payload to plaintext*/
233 [ + + ]: 16 : if (in_o_coap->payload.len != 0) {
234 : : /* An extra byte 0xFF before payload*/
235 : 2 : *temp_plaintext_ptr = 0xff;
236 : :
237 : 2 : dest_size = (plaintext->len - (uint32_t)(temp_plaintext_ptr +
238 : 2 : 1 - plaintext->ptr));
239 [ - + ]: 2 : TRY(_memcpy_s(++temp_plaintext_ptr, dest_size,
240 : : in_o_coap->payload.ptr, in_o_coap->payload.len));
241 : : }
242 : 16 : PRINT_ARRAY("Plain text", plaintext->ptr, plaintext->len);
243 : 16 : return ok;
244 : : }
245 : :
246 : : /**
247 : : * @brief OSCORE option value length
248 : : * @param piv_len length of the PIV array
249 : : * @param kid_len length of the KID array
250 : : * @param kid_context_len length of the KID context array
251 : : * @return length of the OSCORE option value
252 : : */
253 : 16 : static inline uint32_t get_oscore_opt_val_len(uint32_t piv_len,
254 : : uint32_t kid_len,
255 : : uint32_t kid_context_len)
256 : : {
257 : 16 : uint32_t length = piv_len + kid_len + kid_context_len;
258 [ + + ]: 16 : if (length) {
259 : : /*if any of piv, kid_context or kid is present 1 byte for the flags is reserved */
260 : 14 : length++;
261 : : }
262 [ + + ]: 16 : if (kid_context_len) {
263 : : /*if kid_context is present one byte is reserved for the s field*/
264 : 2 : length++;
265 : : }
266 : 16 : return length;
267 : : }
268 : :
269 : : /**
270 : : * @brief Generate an OSCORE option.
271 : : * @param piv set to the trimmed sender sequence number in requests or NULL
272 : : * in responses
273 : : * @param kid set to Sender ID in requests or NULL in responses
274 : : * @param kid_context set to ID context in request when present. If not
275 : : * present or a response set to NULL
276 : : * @param oscore_option: output pointer OSCORE option structure
277 : : * @return err
278 : : */
279 : 16 : STATIC enum err oscore_option_generate(struct byte_array *piv,
280 : : struct byte_array *kid,
281 : : struct byte_array *kid_context,
282 : : struct oscore_option *oscore_option)
283 : : {
284 [ + - ]: 16 : uint32_t piv_len = (NULL == piv) ? 0 : piv->len;
285 [ + - ]: 16 : uint32_t kid_len = (NULL == kid) ? 0 : kid->len;
286 [ + - ]: 16 : uint32_t kid_context_len = (NULL == kid_context) ? 0 : kid_context->len;
287 : :
288 : 16 : oscore_option->option_number = OSCORE;
289 : 16 : oscore_option->len = (uint8_t)get_oscore_opt_val_len(piv_len, kid_len,
290 : : kid_context_len);
291 [ - + ]: 16 : TRY(check_buffer_size(OSCORE_OPT_VALUE_LEN, oscore_option->len));
292 : 16 : oscore_option->value = oscore_option->buf;
293 : :
294 : : uint32_t dest_size;
295 : :
296 [ + + ]: 16 : if (oscore_option->len == 0) {
297 : 2 : oscore_option->value = NULL;
298 : : } else {
299 : 14 : memset(oscore_option->value, 0, oscore_option->len);
300 : :
301 : 14 : uint8_t *temp_ptr = oscore_option->value;
302 : :
303 [ + + ]: 14 : if (piv_len != 0) {
304 : : /* Set header bits of PIV */
305 : 13 : oscore_option->value[0] =
306 : 13 : (uint8_t)(oscore_option->value[0] | piv->len);
307 : : /* copy PIV (sender sequence) */
308 : :
309 : 13 : dest_size = (uint32_t)(oscore_option->len -
310 : 13 : (temp_ptr + 1 -
311 : 13 : oscore_option->value));
312 [ - + ]: 13 : TRY(_memcpy_s(++temp_ptr, dest_size, piv->ptr,
313 : : piv->len));
314 : :
315 : 13 : temp_ptr += piv->len;
316 : : } else {
317 : 1 : temp_ptr++;
318 : : }
319 : :
320 [ + + ]: 14 : if (kid_context_len != 0) {
321 : : /* Set header flag bit of KID context */
322 : 2 : oscore_option->value[0] |= COMP_OSCORE_OPT_KIDC_H_MASK;
323 : : /* Copy length and context value */
324 : 2 : *temp_ptr = (uint8_t)(kid_context->len);
325 : :
326 : 2 : dest_size = (uint32_t)(oscore_option->len -
327 : 2 : (temp_ptr + 1 -
328 : 2 : oscore_option->value));
329 [ - + ]: 2 : TRY(_memcpy_s(++temp_ptr, dest_size, kid_context->ptr,
330 : : kid_context->len));
331 : :
332 : 2 : temp_ptr += kid_context->len;
333 : : }
334 : :
335 : : /* Set header flag bit of KID */
336 : : /* The KID header flag is set always in requests */
337 : : /* This function is not called in responses */
338 : 14 : oscore_option->value[0] |= COMP_OSCORE_OPT_KID_K_MASK;
339 [ + + ]: 14 : if (kid_len != 0) {
340 : : /* Copy KID */
341 : 6 : dest_size =
342 : 6 : (uint32_t)(oscore_option->len -
343 : 6 : (temp_ptr - oscore_option->value));
344 [ - + ]: 6 : TRY(_memcpy_s(temp_ptr, dest_size, kid->ptr, kid->len));
345 : : }
346 : : }
347 : :
348 : 16 : PRINT_ARRAY("OSCORE option value", oscore_option->value,
349 : : oscore_option->len);
350 : 16 : return ok;
351 : : }
352 : :
353 : : /**
354 : : * @brief Generate an OSCORE packet with all needed data
355 : : * @param in_o_coap: input CoAP packet
356 : : * @param out_oscore: output pointer to OSCORE packet
357 : : * @param U_options: pointer to array of all unprotected options, including OSCORE_option
358 : : * @param U_options_cnt: count number of U-options
359 : : * @param in_ciphertext: input ciphertext, will be set into payload in OSCORE packet
360 : : * @param oscore_option: The OSCORE option
361 : : * @return err
362 : : *
363 : : */
364 : 17 : STATIC enum err oscore_pkg_generate(struct o_coap_packet *in_o_coap,
365 : : struct o_coap_packet *out_oscore,
366 : : struct o_coap_option *u_options,
367 : : uint8_t u_options_cnt,
368 : : struct byte_array *in_ciphertext,
369 : : struct oscore_option *oscore_option)
370 : : {
371 : : /* Set OSCORE header and Token*/
372 : 17 : out_oscore->header.ver = in_o_coap->header.ver;
373 : 17 : out_oscore->header.type = in_o_coap->header.type;
374 : 17 : out_oscore->header.TKL = in_o_coap->header.TKL;
375 : 17 : out_oscore->header.MID = in_o_coap->header.MID;
376 [ + + ]: 17 : if (out_oscore->header.TKL == 0) {
377 : 2 : out_oscore->token = NULL;
378 : : } else {
379 : 15 : out_oscore->token = in_o_coap->token;
380 : : }
381 : :
382 : 17 : bool observe = is_observe(u_options, u_options_cnt);
383 [ + + ]: 17 : if (is_request(in_o_coap)) {
384 [ + + ]: 10 : if (observe) {
385 : 2 : out_oscore->header.code = CODE_REQ_FETCH;
386 : : } else {
387 : 8 : out_oscore->header.code = CODE_REQ_POST;
388 : : }
389 : : } else {
390 [ + + ]: 7 : if (observe) {
391 : 2 : out_oscore->header.code = CODE_RESP_CONTENT;
392 : : } else {
393 : 5 : out_oscore->header.code = CODE_RESP_CHANGED;
394 : : }
395 : : }
396 : :
397 : : /* U-options + OSCORE option (compare oscore option number with others)
398 : : Find out the appropriate position of OSCORE option */
399 : 17 : uint8_t oscore_opt_pos = u_options_cnt;
400 [ + + ]: 24 : for (uint8_t i = 0; i < u_options_cnt; i++) {
401 : : /* Once found, finish the for-loop */
402 [ + + ]: 8 : if (u_options[i].option_number > OSCORE) {
403 : 1 : oscore_opt_pos = i;
404 : 1 : break;
405 : : }
406 : : }
407 : :
408 : : /* Update options count number to output*/
409 : 17 : out_oscore->options_cnt = (uint8_t)(1 + u_options_cnt);
410 : :
411 : 17 : uint8_t temp_opt_number_sum = 0;
412 : : /* Show the position of U-options */
413 : 17 : uint8_t u_opt_pos = 0;
414 [ + + ]: 42 : for (uint8_t i = 0; i < u_options_cnt + 1; i++) {
415 [ + + ]: 25 : if (i == oscore_opt_pos) {
416 : : /* OSCORE_option */
417 : 17 : out_oscore->options[i].delta =
418 : 17 : (uint16_t)(oscore_option->option_number -
419 : : temp_opt_number_sum);
420 : 17 : out_oscore->options[i].len = oscore_option->len;
421 : 17 : out_oscore->options[i].option_number =
422 : 17 : oscore_option->option_number;
423 : 17 : out_oscore->options[i].value = oscore_option->value;
424 : : } else {
425 : : /* U-options */
426 : 8 : out_oscore->options[i].delta =
427 : 8 : (uint16_t)(u_options[u_opt_pos].option_number -
428 : : temp_opt_number_sum);
429 : 8 : out_oscore->options[i].len = u_options[u_opt_pos].len;
430 : 8 : out_oscore->options[i].option_number =
431 : 8 : u_options[u_opt_pos].option_number;
432 : 8 : out_oscore->options[i].value =
433 : 8 : u_options[u_opt_pos].value;
434 : :
435 : 8 : u_opt_pos++;
436 : : }
437 : 25 : temp_opt_number_sum = (uint8_t)(temp_opt_number_sum +
438 : 25 : out_oscore->options[i].delta);
439 : : }
440 : :
441 : : /* Protected Payload */
442 : 17 : out_oscore->payload.len = in_ciphertext->len;
443 : 17 : out_oscore->payload.ptr = in_ciphertext->ptr;
444 : 17 : return ok;
445 : : }
446 : :
447 : : /**
448 : : * @brief Increment Sender Sequence Number and call the function to periodically write it to NVM.
449 : : *
450 : : * @param c Security context.
451 : : * @return enum err
452 : : */
453 : 13 : static enum err generate_new_ssn(struct context *c)
454 : : {
455 [ - + ]: 13 : if (NULL == c) {
456 : 0 : return wrong_parameter;
457 : : }
458 : :
459 : 13 : c->sc.ssn++;
460 [ + + ]: 13 : if (!c->cc.fresh_master_secret_salt) {
461 : : #ifdef OSCORE_NVM_SUPPORT
462 : 9 : struct nvm_key_t nvm_key = { .sender_id = c->sc.sender_id,
463 : : .recipient_id = c->rc.recipient_id,
464 : : .id_context = c->cc.id_context };
465 : 9 : bool echo_sync_in_progress =
466 : 9 : (ECHO_SYNCHRONIZED != c->rrc.echo_state_machine);
467 : 9 : return ssn_store_in_nvm(&nvm_key, c->sc.ssn,
468 : : echo_sync_in_progress);
469 : : #else
470 : : return ok;
471 : : #endif
472 : : }
473 : 4 : return ok;
474 : : }
475 : :
476 : : /**
477 : : * @brief Checks if given message needs a fresh PIV/nonce, based on its type and current state of ECHO challenge.
478 : : * @param msg_type Message type.
479 : : * @param echo_state ECHO challenge state.
480 : : * @return If true, new PIV/nonce must be generated.
481 : : */
482 : 15 : static bool needs_new_piv(enum o_coap_msg msg_type, enum echo_state echo_state)
483 : : {
484 : : /* Encrypt data using new PIV/nonce in the following cases:
485 : : - Client prepares any kind of request.
486 : : - Server prepares a response with ECHO challenge after the reboot (see RFC 8613 Appendix B.1.2).
487 : : - Server prepares a notification (response) to an observe registration.
488 : : Encrypt data using corresponding request nonce:
489 : : - Server prepares a response to a request after the ECHO challenge.
490 : : For more details, see RFC 8613 p. 8.3 and the following hyperlinks.*/
491 [ + + + + ]: 15 : return ((COAP_MSG_RESPONSE != msg_type) || (ECHO_VERIFY == echo_state));
492 : : }
493 : :
494 : : /**
495 : : * @brief Wrapper function with common operations for encrypting the payload.
496 : : * These operations are shared in all possible scenarios.
497 : : * For more info, see RFC8616 8.1 and 8.3.
498 : : *
499 : : * @param plaintext Input plaintext to be encrypted.
500 : : * @param ciphertext Output encrypted payload for the OSCORE packet.
501 : : * @param c Security context.
502 : : * @param input_coap Input coap packet.
503 : : * @param oscore_option Output OSCORE option.
504 : : * @return enum err
505 : : */
506 : 15 : static enum err encrypt_wrapper(struct byte_array *plaintext,
507 : : struct byte_array *ciphertext,
508 : : struct context *c,
509 : : struct o_coap_packet *input_coap,
510 : : struct oscore_option *oscore_option)
511 : : {
512 [ - + ]: 15 : BYTE_ARRAY_NEW(new_piv, MAX_PIV_LEN, MAX_PIV_LEN);
513 [ - + ]: 15 : BYTE_ARRAY_NEW(new_nonce, NONCE_LEN, NONCE_LEN);
514 : 15 : struct byte_array piv = BYTE_ARRAY_INIT(NULL, 0);
515 : 15 : struct byte_array kid = BYTE_ARRAY_INIT(NULL, 0);
516 : 15 : struct byte_array kid_context = BYTE_ARRAY_INIT(NULL, 0);
517 : : struct byte_array nonce;
518 : :
519 : : /* Read necessary fields from the input packet. */
520 : : enum o_coap_msg msg_type;
521 [ - + ]: 15 : TRY(coap_get_message_type(input_coap, &msg_type));
522 : 15 : struct byte_array token =
523 : 15 : BYTE_ARRAY_INIT(input_coap->token, input_coap->header.TKL);
524 : :
525 : : /* Generate new PIV/nonce if needed. */
526 : 15 : bool use_new_piv = needs_new_piv(msg_type, c->rrc.echo_state_machine);
527 [ + + ]: 15 : if (use_new_piv) {
528 [ - + ]: 13 : TRY(ssn2piv(c->sc.ssn, &new_piv));
529 [ - + ]: 13 : TRY(generate_new_ssn(c));
530 [ - + ]: 13 : TRY(create_nonce(&c->sc.sender_id, &new_piv, &c->cc.common_iv,
531 : : &new_nonce));
532 : :
533 : 13 : nonce = new_nonce;
534 : 13 : piv = new_piv;
535 : 13 : kid = c->sc.sender_id;
536 : 13 : kid_context = c->cc.id_context;
537 : : } else {
538 : 2 : nonce = c->rrc.nonce;
539 : : }
540 : :
541 : : /* Generate OSCORE option based on selected values. */
542 [ - + ]: 15 : TRY(oscore_option_generate(&piv, &kid, &kid_context, oscore_option));
543 : :
544 : : /* AAD shares the same format for both requests and responses,
545 : : yet request_kid and request_piv fields are only used by responses.
546 : : For more details, see 5.4. */
547 [ - + ]: 15 : BYTE_ARRAY_NEW(aad, MAX_AAD_LEN, MAX_AAD_LEN);
548 : 15 : struct byte_array request_piv = piv;
549 : 15 : struct byte_array request_kid = kid;
550 [ - + ]: 15 : TRY(oscore_interactions_read_wrapper(msg_type, &token,
551 : : c->rrc.interactions, &request_piv,
552 : : &request_kid));
553 [ - + ]: 15 : TRY(create_aad(NULL, 0, c->cc.aead_alg, &request_kid, &request_piv,
554 : : &aad));
555 : :
556 : : /* Encrypt the plaintext */
557 [ - + ]: 15 : TRY(oscore_cose_encrypt(plaintext, ciphertext, &nonce, &aad,
558 : : &c->sc.sender_key));
559 : :
560 : : /* Update nonce only after successful encryption (for handling future responses). */
561 [ + + ]: 15 : if (use_new_piv) {
562 [ - + ]: 13 : TRY(byte_array_cpy(&c->rrc.nonce, &nonce, NONCE_LEN));
563 : : }
564 : :
565 : : /* Handle OSCORE interactions after successful encryption. */
566 [ - + ]: 15 : BYTE_ARRAY_NEW(uri_paths, OSCORE_MAX_URI_PATH_LEN,
567 : : OSCORE_MAX_URI_PATH_LEN);
568 [ - + ]: 15 : TRY(uri_path_create(input_coap->options, input_coap->options_cnt,
569 : : uri_paths.ptr, &(uri_paths.len)));
570 [ - + ]: 15 : TRY(oscore_interactions_update_wrapper(msg_type, &token, &uri_paths,
571 : : c->rrc.interactions,
572 : : &request_piv, &request_kid));
573 : :
574 : 15 : return ok;
575 : : }
576 : :
577 : : /**
578 : : *@brief Converts a CoAP packet to OSCORE packet
579 : : *@note For messaging layer packets (simple ACK with no payload, code 0.00),
580 : : * encryption is dismissed and raw input buffer is copied,
581 : : * as specified at section 4.2 in RFC8613.
582 : : *@param buf_o_coap a buffer containing a CoAP packet
583 : : *@param buf_o_coap_len length of the CoAP buffer
584 : : *@param buf_oscore a buffer where the OSCORE packet will be written
585 : : *@param buf_oscore_len length of the OSCORE packet
586 : : *@param c a struct containing the OSCORE context
587 : : *
588 : : *@return err
589 : : */
590 : 17 : enum err coap2oscore(uint8_t *buf_o_coap, uint32_t buf_o_coap_len,
591 : : uint8_t *buf_oscore, uint32_t *buf_oscore_len,
592 : : struct context *c)
593 : : {
594 : : struct o_coap_packet o_coap_pkt;
595 : : struct byte_array buf;
596 : 17 : uint32_t plaintext_len = 0;
597 : :
598 : 17 : PRINT_MSG("\n\n\ncoap2oscore***************************************\n");
599 : 17 : PRINT_ARRAY("Input CoAP packet", buf_o_coap, buf_o_coap_len);
600 : :
601 : 17 : buf.len = buf_o_coap_len;
602 : 17 : buf.ptr = buf_o_coap;
603 : :
604 : : /* Make sure that given context is fresh enough to process the message. */
605 [ - + ]: 17 : TRY(check_context_freshness(c));
606 : :
607 : : /* Parse the coap buf into a CoAP struct */
608 : 17 : memset(&o_coap_pkt, 0, sizeof(o_coap_pkt));
609 [ - + ]: 17 : TRY(coap_deserialize(&buf, &o_coap_pkt));
610 : :
611 : : /* Dismiss OSCORE encryption if messaging layer detected (simple ACK, code=0.00) */
612 [ + + ]: 17 : if ((TYPE_ACK == o_coap_pkt.header.type) &&
613 [ + + ]: 8 : (CODE_EMPTY == o_coap_pkt.header.code)) {
614 : 1 : PRINT_MSG(
615 : : "Messaging Layer CoAP packet detected, encryption dismissed\n");
616 : 1 : *buf_oscore_len = buf_o_coap_len;
617 : 1 : return _memcpy_s(buf_oscore, buf_o_coap_len, buf_o_coap,
618 : : buf_o_coap_len);
619 : : }
620 : :
621 : : /* 1. Divide CoAP options into E-option and U-option */
622 : : struct o_coap_option e_options[MAX_OPTION_COUNT];
623 : 16 : uint8_t e_options_cnt = 0;
624 : 16 : uint16_t e_options_len = 0;
625 : : struct o_coap_option u_options[MAX_OPTION_COUNT];
626 : 16 : uint8_t u_options_cnt = 0;
627 : :
628 : : /* Analyze CoAP options, extract E-options and U-options */
629 [ - + ]: 16 : TRY(inner_outer_option_split(&o_coap_pkt, e_options, &e_options_cnt,
630 : : &e_options_len, u_options,
631 : : &u_options_cnt));
632 : :
633 : : /* 2. Create plaintext (code + E-options + o_coap_payload) */
634 : : /* Calculate complete plaintext length: 1 byte code + E-options + 1 byte 0xFF + payload */
635 : 16 : plaintext_len = (uint32_t)(1 + e_options_len);
636 : :
637 [ + + ]: 16 : if (o_coap_pkt.payload.len) {
638 : 2 : plaintext_len = plaintext_len + 1 + o_coap_pkt.payload.len;
639 : : }
640 : :
641 : : /* Setup buffer for plaintext */
642 [ - + - + ]: 16 : BYTE_ARRAY_NEW(plaintext, MAX_PLAINTEXT_LEN, plaintext_len);
643 : :
644 : : /* Combine code, E-options and payload of CoAP to plaintext */
645 [ - + ]: 16 : TRY(plaintext_setup(&o_coap_pkt, e_options, e_options_cnt, &plaintext));
646 : :
647 : : /* Generate ciphertext array */
648 [ - + - + ]: 16 : BYTE_ARRAY_NEW(ciphertext, MAX_CIPHERTEXT_LEN,
649 : : plaintext.len + AUTH_TAG_LEN);
650 : :
651 [ + + ]: 16 : if (ECHO_VERIFY == c->rrc.echo_state_machine) {
652 : : /* A server prepares a response with ECHO challenge after the reboot. */
653 [ + + ]: 4 : TRY(cache_echo_val(&c->rrc.echo_opt_val, e_options,
654 : : e_options_cnt));
655 : : }
656 : :
657 : : /* Encrypt data using either a freshly generated nonce (if needed), or the one cached from the corresponding request. */
658 : : struct oscore_option oscore_option;
659 [ - + ]: 15 : TRY(encrypt_wrapper(&plaintext, &ciphertext, c, &o_coap_pkt,
660 : : &oscore_option));
661 : :
662 : : /*create an OSCORE packet*/
663 : : struct o_coap_packet oscore_pkt;
664 [ - + ]: 15 : TRY(oscore_pkg_generate(&o_coap_pkt, &oscore_pkt, u_options,
665 : : u_options_cnt, &ciphertext, &oscore_option));
666 : :
667 : : /*convert the oscore pkg to byte string*/
668 : 15 : return coap_serialize(&oscore_pkt, buf_oscore, buf_oscore_len);
669 : : }
|