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