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 */
inner_outer_option_split(struct o_coap_packet * in_o_coap,struct o_coap_option * e_options,uint8_t * e_options_cnt,uint16_t * e_options_len,struct o_coap_option * U_options,uint8_t * U_options_cnt)43 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 enum err r = ok;
51
52 /* Initialize to 0 */
53 *e_options_len = 0;
54
55 uint8_t temp_option_nr = 0;
56 uint16_t temp_len = 0;
57 uint8_t temp_E_option_delta_sum = 0;
58 uint8_t temp_U_option_delta_sum = 0;
59
60 if (MAX_OPTION_COUNT < in_o_coap->options_cnt) {
61 return too_many_options;
62 }
63
64 for (uint8_t i = 0; i < in_o_coap->options_cnt; i++) {
65 uint8_t extra_bytes =
66 opt_extra_bytes(in_o_coap->options[i].delta) +
67 opt_extra_bytes(in_o_coap->options[i].len);
68
69 temp_option_nr =
70 (uint8_t)(temp_option_nr + in_o_coap->options[i].delta);
71 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 switch (temp_option_nr) {
78 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 e_options[*e_options_cnt].delta =
87 (uint16_t)(temp_option_nr -
88 temp_E_option_delta_sum);
89 if (is_request(in_o_coap)) {
90 /*registrations/cancellations are requests */
91 e_options[*e_options_cnt].len = temp_len;
92 e_options[*e_options_cnt].value =
93 in_o_coap->options[i].value;
94
95 /* Add option header length and value length */
96 (*e_options_len) =
97 (uint16_t)((*e_options_len) + 1 +
98 extra_bytes + temp_len);
99 } else {
100 /*notifications are responses*/
101 e_options[*e_options_cnt].len = 0;
102 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 (*e_options_len)++;
106 }
107
108 e_options[*e_options_cnt].option_number =
109 temp_option_nr;
110
111 /* Update delta sum of E-options */
112 temp_E_option_delta_sum =
113 (uint8_t)(temp_E_option_delta_sum +
114 e_options[*e_options_cnt].delta);
115
116 /* Increment E-options count */
117 (*e_options_cnt)++;
118
119 /*
120 *outer option (value as in the original coap packet
121 */
122 U_options[*U_options_cnt].delta =
123 (uint16_t)(temp_option_nr -
124 temp_U_option_delta_sum);
125 U_options[*U_options_cnt].len = temp_len;
126 U_options[*U_options_cnt].value =
127 in_o_coap->options[i].value;
128 U_options[*U_options_cnt].option_number =
129 temp_option_nr;
130
131 /* Update delta sum of E-options */
132 temp_U_option_delta_sum =
133 (uint8_t)(temp_U_option_delta_sum +
134 U_options[*U_options_cnt].delta);
135
136 /* Increment E-options count */
137 (*U_options_cnt)++;
138
139 break;
140
141 default:
142 /* check delta, whether current option U or E */
143 if (is_class_e(temp_option_nr) == 1) {
144 /* E-options, which will be copied in plaintext to be encrypted*/
145 e_options[*e_options_cnt].delta =
146 (uint16_t)(temp_option_nr -
147 temp_E_option_delta_sum);
148 e_options[*e_options_cnt].len = temp_len;
149 e_options[*e_options_cnt].value =
150 in_o_coap->options[i].value;
151 e_options[*e_options_cnt].option_number =
152 temp_option_nr;
153
154 /* Update delta sum of E-options */
155 temp_E_option_delta_sum =
156 (uint8_t)(temp_E_option_delta_sum +
157 e_options[*e_options_cnt]
158 .delta);
159
160 /* Increment E-options count */
161 (*e_options_cnt)++;
162 /* Add option header length and value length */
163 (*e_options_len) =
164 (uint16_t)((*e_options_len) + 1 +
165 extra_bytes + temp_len);
166 } else {
167 /* U-options */
168 U_options[*U_options_cnt].delta =
169 (uint16_t)(temp_option_nr -
170 temp_U_option_delta_sum);
171 U_options[*U_options_cnt].len = temp_len;
172 U_options[*U_options_cnt].value =
173 in_o_coap->options[i].value;
174 U_options[*U_options_cnt].option_number =
175 temp_option_nr;
176
177 /* Update delta sum of E-options */
178 temp_U_option_delta_sum =
179 (uint8_t)(temp_U_option_delta_sum +
180 U_options[*U_options_cnt]
181 .delta);
182
183 /* Increment E-options count */
184 (*U_options_cnt)++;
185 }
186 break;
187 }
188 }
189 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 */
plaintext_setup(struct o_coap_packet * in_o_coap,struct o_coap_option * E_options,uint8_t E_options_cnt,struct byte_array * plaintext)201 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 uint8_t *temp_plaintext_ptr = plaintext->ptr;
207
208 /* Add code to plaintext */
209 *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 uint16_t e_opt_serial_len = 0;
214 for (uint8_t i = 0; i < E_options_cnt; i++) {
215 e_opt_serial_len = (uint16_t)(e_opt_serial_len + 1 + 2 + 2 +
216 E_options[i].len);
217 }
218 /* Setup buffer */
219 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 TRY(options_serialize(E_options, E_options_cnt, &e_opt_serial));
225
226 uint32_t dest_size = (plaintext->len - (uint32_t)(temp_plaintext_ptr +
227 1 - plaintext->ptr));
228 TRY(_memcpy_s(++temp_plaintext_ptr, dest_size, e_opt_serial.ptr,
229 e_opt_serial.len));
230 temp_plaintext_ptr += e_opt_serial.len;
231
232 /* Add payload to plaintext*/
233 if (in_o_coap->payload.len != 0) {
234 /* An extra byte 0xFF before payload*/
235 *temp_plaintext_ptr = 0xff;
236
237 dest_size = (plaintext->len - (uint32_t)(temp_plaintext_ptr +
238 1 - plaintext->ptr));
239 TRY(_memcpy_s(++temp_plaintext_ptr, dest_size,
240 in_o_coap->payload.ptr, in_o_coap->payload.len));
241 }
242 PRINT_ARRAY("Plain text", plaintext->ptr, plaintext->len);
243 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 */
get_oscore_opt_val_len(uint32_t piv_len,uint32_t kid_len,uint32_t kid_context_len)253 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 uint32_t length = piv_len + kid_len + kid_context_len;
258 if (length) {
259 /*if any of piv, kid_context or kid is present 1 byte for the flags is reserved */
260 length++;
261 }
262 if (kid_context_len) {
263 /*if kid_context is present one byte is reserved for the s field*/
264 length++;
265 }
266 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 */
oscore_option_generate(struct byte_array * piv,struct byte_array * kid,struct byte_array * kid_context,struct oscore_option * oscore_option)279 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 uint32_t piv_len = (NULL == piv) ? 0 : piv->len;
285 uint32_t kid_len = (NULL == kid) ? 0 : kid->len;
286 uint32_t kid_context_len = (NULL == kid_context) ? 0 : kid_context->len;
287
288 oscore_option->option_number = OSCORE;
289 oscore_option->len = (uint8_t)get_oscore_opt_val_len(piv_len, kid_len,
290 kid_context_len);
291 TRY(check_buffer_size(OSCORE_OPT_VALUE_LEN, oscore_option->len));
292 oscore_option->value = oscore_option->buf;
293
294 uint32_t dest_size;
295
296 if (oscore_option->len == 0) {
297 oscore_option->value = NULL;
298 } else {
299 memset(oscore_option->value, 0, oscore_option->len);
300
301 uint8_t *temp_ptr = oscore_option->value;
302
303 if (piv_len != 0) {
304 /* Set header bits of PIV */
305 oscore_option->value[0] =
306 (uint8_t)(oscore_option->value[0] | piv->len);
307 /* copy PIV (sender sequence) */
308
309 dest_size = (uint32_t)(oscore_option->len -
310 (temp_ptr + 1 -
311 oscore_option->value));
312 TRY(_memcpy_s(++temp_ptr, dest_size, piv->ptr,
313 piv->len));
314
315 temp_ptr += piv->len;
316 } else {
317 temp_ptr++;
318 }
319
320 if (kid_context_len != 0) {
321 /* Set header flag bit of KID context */
322 oscore_option->value[0] |= COMP_OSCORE_OPT_KIDC_H_MASK;
323 /* Copy length and context value */
324 *temp_ptr = (uint8_t)(kid_context->len);
325
326 dest_size = (uint32_t)(oscore_option->len -
327 (temp_ptr + 1 -
328 oscore_option->value));
329 TRY(_memcpy_s(++temp_ptr, dest_size, kid_context->ptr,
330 kid_context->len));
331
332 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 oscore_option->value[0] |= COMP_OSCORE_OPT_KID_K_MASK;
339 if (kid_len != 0) {
340 /* Copy KID */
341 dest_size =
342 (uint32_t)(oscore_option->len -
343 (temp_ptr - oscore_option->value));
344 TRY(_memcpy_s(temp_ptr, dest_size, kid->ptr, kid->len));
345 }
346 }
347
348 PRINT_ARRAY("OSCORE option value", oscore_option->value,
349 oscore_option->len);
350 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 */
oscore_pkg_generate(struct o_coap_packet * in_o_coap,struct o_coap_packet * out_oscore,struct o_coap_option * u_options,uint8_t u_options_cnt,struct byte_array * in_ciphertext,struct oscore_option * oscore_option)364 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 out_oscore->header.ver = in_o_coap->header.ver;
373 out_oscore->header.type = in_o_coap->header.type;
374 out_oscore->header.TKL = in_o_coap->header.TKL;
375 out_oscore->header.MID = in_o_coap->header.MID;
376 if (out_oscore->header.TKL == 0) {
377 out_oscore->token = NULL;
378 } else {
379 out_oscore->token = in_o_coap->token;
380 }
381
382 bool observe = is_observe(u_options, u_options_cnt);
383 if (is_request(in_o_coap)) {
384 if (observe) {
385 out_oscore->header.code = CODE_REQ_FETCH;
386 } else {
387 out_oscore->header.code = CODE_REQ_POST;
388 }
389 } else {
390 if (observe) {
391 out_oscore->header.code = CODE_RESP_CONTENT;
392 } else {
393 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 uint8_t oscore_opt_pos = u_options_cnt;
400 for (uint8_t i = 0; i < u_options_cnt; i++) {
401 /* Once found, finish the for-loop */
402 if (u_options[i].option_number > OSCORE) {
403 oscore_opt_pos = i;
404 break;
405 }
406 }
407
408 /* Update options count number to output*/
409 out_oscore->options_cnt = (uint8_t)(1 + u_options_cnt);
410
411 uint8_t temp_opt_number_sum = 0;
412 /* Show the position of U-options */
413 uint8_t u_opt_pos = 0;
414 for (uint8_t i = 0; i < u_options_cnt + 1; i++) {
415 if (i == oscore_opt_pos) {
416 /* OSCORE_option */
417 out_oscore->options[i].delta =
418 (uint16_t)(oscore_option->option_number -
419 temp_opt_number_sum);
420 out_oscore->options[i].len = oscore_option->len;
421 out_oscore->options[i].option_number =
422 oscore_option->option_number;
423 out_oscore->options[i].value = oscore_option->value;
424 } else {
425 /* U-options */
426 out_oscore->options[i].delta =
427 (uint16_t)(u_options[u_opt_pos].option_number -
428 temp_opt_number_sum);
429 out_oscore->options[i].len = u_options[u_opt_pos].len;
430 out_oscore->options[i].option_number =
431 u_options[u_opt_pos].option_number;
432 out_oscore->options[i].value =
433 u_options[u_opt_pos].value;
434
435 u_opt_pos++;
436 }
437 temp_opt_number_sum = (uint8_t)(temp_opt_number_sum +
438 out_oscore->options[i].delta);
439 }
440
441 /* Protected Payload */
442 out_oscore->payload.len = in_ciphertext->len;
443 out_oscore->payload.ptr = in_ciphertext->ptr;
444 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 */
generate_new_ssn(struct context * c)453 static enum err generate_new_ssn(struct context *c)
454 {
455 if (NULL == c) {
456 return wrong_parameter;
457 }
458
459 c->sc.ssn++;
460 if (!c->cc.fresh_master_secret_salt) {
461 #ifdef OSCORE_NVM_SUPPORT
462 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 bool echo_sync_in_progress =
466 (ECHO_SYNCHRONIZED != c->rrc.echo_state_machine);
467 return ssn_store_in_nvm(&nvm_key, c->sc.ssn,
468 echo_sync_in_progress);
469 #else
470 return ok;
471 #endif
472 }
473 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 */
needs_new_piv(enum o_coap_msg msg_type,enum echo_state echo_state)482 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 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 */
encrypt_wrapper(struct byte_array * plaintext,struct byte_array * ciphertext,struct context * c,struct o_coap_packet * input_coap,struct oscore_option * oscore_option)506 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 BYTE_ARRAY_NEW(new_piv, MAX_PIV_LEN, MAX_PIV_LEN);
513 BYTE_ARRAY_NEW(new_nonce, NONCE_LEN, NONCE_LEN);
514 struct byte_array piv = BYTE_ARRAY_INIT(NULL, 0);
515 struct byte_array kid = BYTE_ARRAY_INIT(NULL, 0);
516 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 TRY(coap_get_message_type(input_coap, &msg_type));
522 struct byte_array token =
523 BYTE_ARRAY_INIT(input_coap->token, input_coap->header.TKL);
524
525 /* Generate new PIV/nonce if needed. */
526 bool use_new_piv = needs_new_piv(msg_type, c->rrc.echo_state_machine);
527 if (use_new_piv) {
528 TRY(ssn2piv(c->sc.ssn, &new_piv));
529 TRY(generate_new_ssn(c));
530 TRY(create_nonce(&c->sc.sender_id, &new_piv, &c->cc.common_iv,
531 &new_nonce));
532
533 nonce = new_nonce;
534 piv = new_piv;
535 kid = c->sc.sender_id;
536 kid_context = c->cc.id_context;
537 } else {
538 nonce = c->rrc.nonce;
539 }
540
541 /* Generate OSCORE option based on selected values. */
542 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 BYTE_ARRAY_NEW(aad, MAX_AAD_LEN, MAX_AAD_LEN);
548 struct byte_array request_piv = piv;
549 struct byte_array request_kid = kid;
550 TRY(oscore_interactions_read_wrapper(msg_type, &token,
551 c->rrc.interactions, &request_piv,
552 &request_kid));
553 TRY(create_aad(NULL, 0, c->cc.aead_alg, &request_kid, &request_piv,
554 &aad));
555
556 /* Encrypt the plaintext */
557 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 if (use_new_piv) {
562 TRY(byte_array_cpy(&c->rrc.nonce, &nonce, NONCE_LEN));
563 }
564
565 /* Handle OSCORE interactions after successful encryption. */
566 BYTE_ARRAY_NEW(uri_paths, OSCORE_MAX_URI_PATH_LEN,
567 OSCORE_MAX_URI_PATH_LEN);
568 TRY(uri_path_create(input_coap->options, input_coap->options_cnt,
569 uri_paths.ptr, &(uri_paths.len)));
570 TRY(oscore_interactions_update_wrapper(msg_type, &token, &uri_paths,
571 c->rrc.interactions,
572 &request_piv, &request_kid));
573
574 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 */
coap2oscore(uint8_t * buf_o_coap,uint32_t buf_o_coap_len,uint8_t * buf_oscore,uint32_t * buf_oscore_len,struct context * c)590 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 uint32_t plaintext_len = 0;
597
598 PRINT_MSG("\n\n\ncoap2oscore***************************************\n");
599 PRINT_ARRAY("Input CoAP packet", buf_o_coap, buf_o_coap_len);
600
601 buf.len = buf_o_coap_len;
602 buf.ptr = buf_o_coap;
603
604 /* Make sure that given context is fresh enough to process the message. */
605 TRY(check_context_freshness(c));
606
607 /* Parse the coap buf into a CoAP struct */
608 memset(&o_coap_pkt, 0, sizeof(o_coap_pkt));
609 TRY(coap_deserialize(&buf, &o_coap_pkt));
610
611 /* Dismiss OSCORE encryption if messaging layer detected (simple ACK, code=0.00) */
612 if ((TYPE_ACK == o_coap_pkt.header.type) &&
613 (CODE_EMPTY == o_coap_pkt.header.code)) {
614 PRINT_MSG(
615 "Messaging Layer CoAP packet detected, encryption dismissed\n");
616 *buf_oscore_len = buf_o_coap_len;
617 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 uint8_t e_options_cnt = 0;
624 uint16_t e_options_len = 0;
625 struct o_coap_option u_options[MAX_OPTION_COUNT];
626 uint8_t u_options_cnt = 0;
627
628 /* Analyze CoAP options, extract E-options and U-options */
629 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 plaintext_len = (uint32_t)(1 + e_options_len);
636
637 if (o_coap_pkt.payload.len) {
638 plaintext_len = plaintext_len + 1 + o_coap_pkt.payload.len;
639 }
640
641 /* Setup buffer for plaintext */
642 BYTE_ARRAY_NEW(plaintext, MAX_PLAINTEXT_LEN, plaintext_len);
643
644 /* Combine code, E-options and payload of CoAP to plaintext */
645 TRY(plaintext_setup(&o_coap_pkt, e_options, e_options_cnt, &plaintext));
646
647 /* Generate ciphertext array */
648 BYTE_ARRAY_NEW(ciphertext, MAX_CIPHERTEXT_LEN,
649 plaintext.len + AUTH_TAG_LEN);
650
651 if (ECHO_VERIFY == c->rrc.echo_state_machine) {
652 /* A server prepares a response with ECHO challenge after the reboot. */
653 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 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 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 return coap_serialize(&oscore_pkt, buf_oscore, buf_oscore_len);
669 }
670