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 <stdbool.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <string.h>
16
17 #include "oscore.h"
18
19 #include "oscore/aad.h"
20 #include "oscore/oscore_coap.h"
21 #include "oscore/nonce.h"
22 #include "oscore/option.h"
23 #include "oscore/oscore_cose.h"
24 #include "oscore/security_context.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
31 /**
32 * @brief Parse all received options to find the OSCORE_option. If it doesn't * have OSCORE option, then this packet is a normal CoAP. If it does have, it's * an OSCORE packet, and then parse the compressed OSCORE_option value to get * value of PIV, KID and KID context of the client.
33 * @param in: input OSCORE packet
34 * @param out: pointer output compressed OSCORE_option
35 * @return error types or is or not OSCORE packet
36 */
37 static inline enum err
oscore_option_parser(struct o_coap_packet * in,struct compressed_oscore_option * out,bool * oscore_pkt)38 oscore_option_parser(struct o_coap_packet *in,
39 struct compressed_oscore_option *out, bool *oscore_pkt)
40 {
41 uint8_t temp_option_count = in->options_cnt;
42 struct o_coap_option *temp_options = in->options;
43 uint8_t temp_option_num = 0;
44 uint8_t *temp_current_option_value_ptr;
45 uint8_t temp_kid_len = 0;
46
47 *oscore_pkt = false;
48
49 for (uint8_t i = 0; i < temp_option_count; i++) {
50 temp_option_num = temp_options[i].option_number;
51 temp_kid_len = temp_options[i].len;
52
53 /* Check current option is OSCORE_option or not */
54 if (temp_option_num == COAP_OPTION_OSCORE) {
55 if (temp_options[i].len == 0) {
56 /* No OSCORE option value*/
57 out->h = 0;
58 out->k = 0;
59 out->n = 0;
60 // out->KID_len = 0;
61 // out->KIDC_len = 0;
62 // out->PIV = NULL;
63 // out->KID = NULL;
64 // out->KID_context = NULL;
65 } else {
66 /* Get address of current option value*/
67 temp_current_option_value_ptr =
68 temp_options[i].value;
69 /* Parse first byte of OSCORE value*/
70 out->h = ((*temp_current_option_value_ptr) &
71 COMP_OSCORE_OPT_KIDC_H_MASK) >>
72 COMP_OSCORE_OPT_KIDC_H_OFFSET;
73 out->k = ((*temp_current_option_value_ptr) &
74 COMP_OSCORE_OPT_KID_K_MASK) >>
75 COMP_OSCORE_OPT_KID_K_OFFSET;
76 out->n = ((*temp_current_option_value_ptr) &
77 COMP_OSCORE_OPT_PIV_N_MASK) >>
78 COMP_OSCORE_OPT_PIV_N_OFFSET;
79 temp_current_option_value_ptr++;
80 temp_kid_len--;
81
82 /* Get PIV */
83 switch (out->n) {
84 case 0:
85 /* NO PIV in COSE object*/
86 out->piv.ptr = NULL;
87 break;
88 case 6:
89 case 7:
90 /* ERROR: Byte length of PIV not right, max. 5 bytes */
91 return oscore_inpkt_invalid_piv;
92 break;
93 default:
94 out->piv.ptr =
95 temp_current_option_value_ptr;
96 out->piv.len = out->n;
97 temp_current_option_value_ptr += out->n;
98 temp_kid_len = (uint8_t)(temp_kid_len -
99 out->n);
100 break;
101 }
102
103 /* Get KID context */
104 if (out->h == 0) {
105 out->kid_context.len = 0;
106 out->kid_context.ptr = NULL;
107 } else {
108 out->kid_context.len =
109 *temp_current_option_value_ptr;
110 out->kid_context.ptr =
111 ++temp_current_option_value_ptr;
112 temp_current_option_value_ptr +=
113 out->kid_context.len;
114 temp_kid_len =
115 (uint8_t)(temp_kid_len -
116 (out->kid_context.len + 1));
117 }
118
119 /* Get KID */
120 if (out->k == 0) {
121 out->kid.len = 0;
122 out->kid.ptr = NULL;
123 } else {
124 out->kid.len = temp_kid_len;
125 out->kid.ptr =
126 temp_current_option_value_ptr;
127 }
128 }
129
130 *oscore_pkt = true;
131 }
132 }
133
134 return ok;
135 }
136
137 /**
138 * @brief Decrypt the OSCORE payload (ciphertext)
139 * @param out_plaintext: output plaintext
140 * @param received_piv_kid_context: received PIV, KID and KID context, will be used to calculate AEAD nonce and AAD
141 * @param oscore_packet: complete OSCORE packet which contains the ciphertext to be decrypted
142 * @return void
143 */
payload_decrypt(struct context * c,struct byte_array * out_plaintext,struct o_coap_packet * oscore_packet)144 static inline enum err payload_decrypt(struct context *c,
145 struct byte_array *out_plaintext,
146 struct o_coap_packet *oscore_packet)
147 {
148 struct byte_array oscore_ciphertext = {
149 .len = oscore_packet->payload_len,
150 .ptr = oscore_packet->payload,
151 };
152 return oscore_cose_decrypt(&oscore_ciphertext, out_plaintext, &c->rrc.nonce,
153 &c->rrc.aad, &c->rc.recipient_key);
154 }
155
156 /**
157 * @brief Reorder E-options and other U-options, and update their delta, and combine them all to normal CoAP packet
158 * @param in_oscore_packet: input OSCORE, which contains U-options
159 * @param E_options: input pointer to E-options array
160 * @param E_options_cnt: count number of input E-options
161 * @param out: output pointer to CoAP packet, which will have all reordered options
162 * @return ok or error code
163 */
164 static inline enum err
options_from_oscore_reorder(struct o_coap_packet * in_oscore_packet,struct o_coap_option * E_options,uint8_t E_options_cnt,struct o_coap_packet * out)165 options_from_oscore_reorder(struct o_coap_packet *in_oscore_packet,
166 struct o_coap_option *E_options,
167 uint8_t E_options_cnt, struct o_coap_packet *out)
168 {
169 uint16_t temp_delta_sum = 0;
170 uint8_t temp_opt_cnt =
171 (uint8_t)(in_oscore_packet->options_cnt + E_options_cnt);
172 uint8_t o_coap_opt_cnt = (uint8_t)(temp_opt_cnt - 1);
173
174 /* Get all option numbers */
175 if (o_coap_opt_cnt > 0) {
176 TRY(check_buffer_size(MAX_OPTION_COUNT, o_coap_opt_cnt));
177 uint8_t temp_opt_number[MAX_OPTION_COUNT];
178 memset(temp_opt_number, 0, o_coap_opt_cnt);
179
180 /* Get all option numbers but discard OSCORE option */
181 uint8_t j = 0;
182 for (uint8_t i = 0; i < o_coap_opt_cnt + 1; i++) {
183 if (i < in_oscore_packet->options_cnt) {
184 if (in_oscore_packet->options[i].option_number !=
185 COAP_OPTION_OSCORE)
186 temp_opt_number[j++] =
187 in_oscore_packet->options[i]
188 .option_number;
189 } else {
190 temp_opt_number[j++] =
191 E_options[i -
192 in_oscore_packet->options_cnt]
193 .option_number;
194 }
195 }
196
197 /* Reorder the option numbers from minimum to maximum */
198 // uint8_t min_opt_number;
199 for (uint8_t i = 0; i < o_coap_opt_cnt; i++) {
200 uint8_t ipp = (uint8_t)(i + 1);
201 for (uint8_t k = ipp; k < o_coap_opt_cnt; k++) {
202 if (temp_opt_number[i] > temp_opt_number[k]) {
203 uint8_t temp;
204 temp = temp_opt_number[i];
205 temp_opt_number[i] = temp_opt_number[k];
206 temp_opt_number[k] = temp;
207 }
208 }
209 }
210
211 /* Reset output CoAP options count */
212 out->options_cnt = 0;
213
214 /* Copy all U-options and E-options in increasing number sequence*/
215 uint8_t U_opt_idx = 0;
216 uint8_t E_opt_idx = 0;
217 for (uint8_t i = 0; i < o_coap_opt_cnt; i++) {
218 if (temp_opt_number[i] ==
219 in_oscore_packet->options[U_opt_idx].option_number) {
220 out->options[out->options_cnt].delta =
221 (uint16_t)(in_oscore_packet
222 ->options[U_opt_idx]
223 .option_number -
224 temp_delta_sum);
225 out->options[out->options_cnt].len =
226 in_oscore_packet->options[U_opt_idx].len;
227 out->options[out->options_cnt].option_number =
228 in_oscore_packet->options[U_opt_idx]
229 .option_number;
230 out->options[out->options_cnt].value =
231 in_oscore_packet->options[U_opt_idx]
232 .value;
233
234 temp_delta_sum =
235 (uint16_t)(temp_delta_sum +
236 out->options[out->options_cnt]
237 .delta);
238 out->options_cnt++;
239 U_opt_idx++;
240 continue;
241 } else if (temp_opt_number[i] ==
242 E_options[E_opt_idx].option_number) {
243 out->options[out->options_cnt].delta =
244 (uint16_t)(E_options[E_opt_idx]
245 .option_number -
246 temp_delta_sum);
247 out->options[out->options_cnt].len =
248 E_options[E_opt_idx].len;
249 out->options[out->options_cnt].option_number =
250 E_options[E_opt_idx].option_number;
251 out->options[out->options_cnt].value =
252 E_options[E_opt_idx].value;
253
254 temp_delta_sum =
255 (uint16_t)(temp_delta_sum +
256 out->options[out->options_cnt]
257 .delta);
258 out->options_cnt++;
259 E_opt_idx++;
260 continue;
261 }
262 }
263 } else {
264 /* No any options! */
265 out->options_cnt = 0;
266 }
267 return ok;
268 }
269
270 /**
271 * @brief Parse incoming options byte string into options structure
272 * @param in_data: pointer to input data in byte string format
273 * @param in_data_len: length of input byte string
274 * @param out_options: pointer to output options structure array
275 * @param out_options_count: count number of output options
276 * @return err
277 */
278 static inline enum err
oscore_packet_options_parser(uint8_t * in_data,uint16_t in_data_len,struct o_coap_option * out_options,uint8_t * out_options_count)279 oscore_packet_options_parser(uint8_t *in_data, uint16_t in_data_len,
280 struct o_coap_option *out_options,
281 uint8_t *out_options_count)
282 {
283 uint8_t *temp_options_ptr = in_data;
284 uint8_t temp_options_count = 0;
285 uint8_t temp_option_header_len = 0;
286 uint8_t temp_option_delta = 0;
287 uint8_t temp_option_len = 0;
288 uint8_t temp_option_number = 0;
289
290 // Go through the in_data to find out how many options are there
291 uint16_t i = 0;
292 while (i < in_data_len) {
293 temp_option_header_len = 1;
294 // Parser first byte,lower 4 bits for option value length and higher 4 bits for option delta
295 temp_option_delta = ((*temp_options_ptr) & 0xF0) >> 4;
296 temp_option_len = (*temp_options_ptr) & 0x0F;
297
298 temp_options_ptr++;
299
300 // Special cases for extended option delta: 13 - 1 extra delta byte, 14 - 2 extra delta bytes, 15 - reserved
301 switch (temp_option_delta) {
302 case 13:
303 temp_option_header_len =
304 (uint8_t)(temp_option_header_len + 1);
305 temp_option_delta = (uint8_t)(*temp_options_ptr - 13);
306 temp_options_ptr += 1;
307 break;
308 case 14:
309 temp_option_header_len =
310 (uint8_t)(temp_option_header_len + 2);
311 temp_option_delta =
312 (uint8_t)(((*temp_options_ptr) << 8 |
313 *(temp_options_ptr + 1)) -
314 269);
315 temp_options_ptr += 2;
316 break;
317 case 15:
318 // ERROR
319 return oscore_inpkt_invalid_option_delta;
320 break;
321 default:
322 break;
323 }
324
325 // Special cases for extended option value length: 13 - 1 extra length byte, 14 - 2 extra length bytes, 15 - reserved
326 switch (temp_option_len) {
327 case 13:
328 temp_option_header_len =
329 (uint8_t)(temp_option_header_len + 1);
330 temp_option_len = (uint8_t)(*temp_options_ptr - 13);
331 temp_options_ptr += 1;
332 break;
333 case 14:
334 temp_option_header_len =
335 (uint8_t)(temp_option_header_len + 2);
336 temp_option_len = (uint8_t)(((*temp_options_ptr) << 8 |
337 *(temp_options_ptr + 1)) -
338 269);
339 temp_options_ptr += 2;
340 break;
341 case 15:
342 // ERROR
343 return oscore_inpkt_invalid_optionlen;
344 break;
345 default:
346 break;
347 }
348
349 temp_option_number =
350 (uint8_t)(temp_option_number + temp_option_delta);
351 // Update in output options
352 out_options[temp_options_count].delta = temp_option_delta;
353 out_options[temp_options_count].len = temp_option_len;
354 out_options[temp_options_count].option_number =
355 temp_option_number;
356 if (temp_option_len == 0)
357 out_options[temp_options_count].value = NULL;
358 else
359 out_options[temp_options_count].value =
360 temp_options_ptr;
361
362 // Update parameters
363 i = (uint16_t)(i + temp_option_header_len + temp_option_len);
364 temp_options_ptr += temp_option_len;
365 temp_options_count++;
366 }
367
368 // Assign options count number
369 *out_options_count = temp_options_count;
370
371 return ok;
372 }
373
374 /**
375 * @brief Parse the decrypted OSCORE payload into code, E-options and original unprotected CoAP payload
376 * @param in_payload: input decrypted payload
377 * @param out_code: pointer to code number of the request
378 * @param out_E_options: output pointer to an array of E-options
379 * @param E_options_cnt: count number of E-options
380 * @param out_o_coap_payload: output pointer original unprotected CoAP payload
381 * @return err
382 */
oscore_decrypted_payload_parser(struct byte_array * in_payload,uint8_t * out_code,struct o_coap_option * out_E_options,uint8_t * E_options_cnt,struct byte_array * out_o_coap_payload)383 static inline enum err oscore_decrypted_payload_parser(
384 struct byte_array *in_payload, uint8_t *out_code,
385 struct o_coap_option *out_E_options, uint8_t *E_options_cnt,
386 struct byte_array *out_o_coap_payload)
387 {
388 uint8_t *temp_payload_ptr = in_payload->ptr;
389 uint32_t temp_payload_len = in_payload->len;
390
391 /* Code */
392 *out_code = *(temp_payload_ptr++);
393 temp_payload_len--;
394
395 /* Check whether E-options exists */
396 if (*temp_payload_ptr == 0xFF || temp_payload_len == 0) {
397 *E_options_cnt = 0;
398 } else {
399 uint8_t *temp_option_ptr = temp_payload_ptr;
400 /* Length of E-options byte string*/
401 uint16_t options_len = 0;
402
403 /* Move the temp_ptr to the original unprotected payload */
404 while (*temp_payload_ptr != 0xFF && temp_payload_len != 0) {
405 temp_payload_len--;
406 temp_payload_ptr++;
407 options_len++;
408 }
409
410 /* Parser all options */
411 if (options_len > 0) {
412 TRY(oscore_packet_options_parser(
413 temp_option_ptr, options_len, out_E_options,
414 E_options_cnt));
415
416 } else
417 *E_options_cnt = 0;
418 }
419
420 /* Unprotected CoAP payload */
421 ++temp_payload_ptr;
422 if (temp_payload_len == 0) {
423 out_o_coap_payload->len = 0;
424 out_o_coap_payload->ptr = NULL;
425 } else {
426 /* Minus byte of 0xFF */
427 out_o_coap_payload->len = (uint32_t)(temp_payload_len - 1);
428 out_o_coap_payload->ptr = temp_payload_ptr;
429 }
430
431 return ok;
432 }
433
434 /**
435 * @brief Generate CoAP packet from OSCORE packet
436 * @param decrypted_payload: decrypted OSCORE payload, which contains code, E-options and original unprotected CoAP payload
437 * @param in_oscore_packet: input OSCORE packet
438 * @param out: pointer to output CoAP packet
439 * @return
440 */
441 static inline enum err
o_coap_pkg_generate(struct byte_array * decrypted_payload,struct o_coap_packet * in_oscore_packet,struct o_coap_packet * out)442 o_coap_pkg_generate(struct byte_array *decrypted_payload,
443 struct o_coap_packet *in_oscore_packet,
444 struct o_coap_packet *out)
445 {
446 uint8_t code = 0;
447 struct byte_array unprotected_o_coap_payload = {
448 .len = 0,
449 .ptr = NULL,
450 };
451 struct o_coap_option E_options[10];
452 uint8_t E_options_cnt = 0;
453
454 /* Parse decrypted payload: code + options + unprotected CoAP payload*/
455 TRY(oscore_decrypted_payload_parser(decrypted_payload, &code, E_options,
456 &E_options_cnt,
457 &unprotected_o_coap_payload));
458
459 /* Copy each items from OSCORE packet to CoAP packet */
460 /* Header */
461 out->header.ver = in_oscore_packet->header.ver;
462 out->header.type = in_oscore_packet->header.type;
463 out->header.TKL = in_oscore_packet->header.TKL;
464 out->header.code = code;
465 out->header.MID = in_oscore_packet->header.MID;
466
467 /* Token */
468 if (in_oscore_packet->header.TKL == 0)
469 out->token = NULL;
470 else
471 out->token = in_oscore_packet->token;
472
473 /* Payload */
474 out->payload_len = unprotected_o_coap_payload.len;
475 if (unprotected_o_coap_payload.len == 0)
476 out->payload = NULL;
477 else
478 out->payload = unprotected_o_coap_payload.ptr;
479
480 /* reorder all options, and copy it to output coap packet */
481 TRY(options_from_oscore_reorder(in_oscore_packet, E_options,
482 E_options_cnt, out));
483 return ok;
484 }
485
is_request(struct o_coap_packet * packet)486 static bool is_request(struct o_coap_packet *packet)
487 {
488 if ((CODE_CLASS_MASK & packet->header.code) == REQUEST_CLASS) {
489 return true;
490 } else {
491 return false;
492 }
493 }
494
replay_check(uint64_t sender_sequence_number,uint64_t * replay_window,uint8_t replay_window_len)495 static inline enum err replay_check(uint64_t sender_sequence_number,
496 uint64_t *replay_window,
497 uint8_t replay_window_len)
498 {
499 bool first_run = true;
500
501 if (first_run) {
502 first_run = false;
503 return ok;
504 } else {
505 /*if the sender sequence number is bigger than the
506 right most element -> all good */
507 if (sender_sequence_number >
508 replay_window[replay_window_len - 1]) {
509 return ok;
510 }
511
512 /*if the sender sequence number is smaller than the
513 left most element -> a replay is detected*/
514 if (sender_sequence_number < replay_window[0]) {
515 return replayed_packed_received;
516 }
517
518 /*if the sender sequence number is in the replay window
519 -> a replay is detected*/
520 for (uint8_t i = 0; i < replay_window_len; i++) {
521 if (sender_sequence_number == replay_window[i]) {
522 return replayed_packed_received;
523 }
524 }
525 }
526
527 return ok;
528 }
529
insert_sender_seq_number(uint64_t sender_seq_number,uint64_t * replay_window,uint8_t position)530 static void insert_sender_seq_number(uint64_t sender_seq_number,
531 uint64_t *replay_window, uint8_t position)
532 {
533 /*shift all old values to the left*/
534 for (uint8_t j = 0; j < position; j++) {
535 replay_window[j] = replay_window[j + 1];
536 }
537 /*insert the new sender sequence number at a given position*/
538 replay_window[position] = sender_seq_number;
539 }
540
update_replay_window(uint64_t sender_seq_number,uint64_t * replay_window,uint8_t replay_window_len)541 static void update_replay_window(uint64_t sender_seq_number,
542 uint64_t *replay_window,
543 uint8_t replay_window_len)
544 {
545 for (uint8_t i = 0; i < replay_window_len - 1; i++) {
546 if ((replay_window[i] < sender_seq_number) &&
547 (sender_seq_number < replay_window[i + 1])) {
548 insert_sender_seq_number(sender_seq_number,
549 replay_window, i);
550 PRINT_ARRAY("Replay window:", (uint8_t *)replay_window,
551 (uint32_t)(replay_window_len *
552 sizeof(replay_window[0])));
553 return;
554 }
555 }
556 insert_sender_seq_number(sender_seq_number, replay_window,
557 (uint8_t)(replay_window_len - 1));
558 PRINT_ARRAY("Replay window:", (uint8_t *)replay_window,
559 (uint32_t)(replay_window_len * sizeof(replay_window[0])));
560 }
561
oscore2coap(uint8_t * buf_in,uint32_t buf_in_len,uint8_t * buf_out,uint32_t * buf_out_len,bool * oscore_pkg_flag,struct context * c)562 enum err oscore2coap(uint8_t *buf_in, uint32_t buf_in_len, uint8_t *buf_out,
563 uint32_t *buf_out_len, bool *oscore_pkg_flag,
564 struct context *c)
565 {
566 enum err r = ok;
567 struct o_coap_packet oscore_packet;
568 struct compressed_oscore_option oscore_option;
569 struct byte_array buf;
570
571 PRINT_MSG("\n\n\noscore2coap***************************************\n");
572 PRINT_ARRAY("Input OSCORE packet", buf_in, buf_in_len);
573
574 buf.ptr = buf_in;
575 buf.len = buf_in_len;
576
577 /*Parse the incoming message (buf_in) into a CoAP struct*/
578 TRY(buf2coap(&buf, &oscore_packet));
579
580 /* Check if the packet is OSCORE packet and if so parse the OSCORE option */
581 TRY(oscore_option_parser(&oscore_packet, &oscore_option,
582 oscore_pkg_flag));
583
584 /* If the incoming packet is OSCORE packet -- analyze and and decrypt it. */
585 if (*oscore_pkg_flag) {
586 /*In requests the OSCORE packet contains at least a KID = sender ID
587 and eventually sender sequence number*/
588 if (is_request(&oscore_packet)) {
589 /*Check that the recipient context c->rc has a Recipient ID that
590 matches the received with the oscore option KID (Sender ID).
591 If this is not true return an error which indicates the caller
592 application to tray another context. This is useful when the caller
593 app doesn't know in advance to which context an incoming packet
594 belongs.*/
595 if (!array_equals(&c->rc.recipient_id,
596 &oscore_option.kid)) {
597 return oscore_kid_recipent_id_mismatch;
598 }
599
600 /*check is the packet is replayed*/
601 TRY(replay_check(*oscore_option.piv.ptr,
602 c->rc.replay_window,
603 c->rc.replay_window_len));
604
605 /*If this is a request message we need to calculate the nonce, aad
606 and eventually update the Common IV, Sender and Recipient Keys*/
607 TRY(context_update(
608 SERVER,
609 (struct o_coap_option *)&oscore_packet.options,
610 oscore_packet.options_cnt, &oscore_option.piv,
611 &oscore_option.kid_context, c));
612 }
613
614 /* Setup buffer for the plaintext. The plaintext is shorter than the ciphertext because of the authentication tag*/
615 uint32_t plaintext_bytes_len =
616 oscore_packet.payload_len - AUTH_TAG_LEN;
617 TRY(check_buffer_size(MAX_PLAINTEXT_LEN, plaintext_bytes_len));
618 uint8_t plaintext_bytes[MAX_PLAINTEXT_LEN];
619 struct byte_array plaintext = {
620 .len = plaintext_bytes_len,
621 .ptr = plaintext_bytes,
622 };
623
624 /* Decrypt payload */
625 r = payload_decrypt(c, &plaintext, &oscore_packet);
626 if (r == ok) {
627 /*update the replay window after the decryption*/
628 if (is_request(&oscore_packet)) {
629 update_replay_window(*oscore_option.piv.ptr,
630 c->rc.replay_window,
631 c->rc.replay_window_len);
632 }
633 } else {
634 return r;
635 }
636
637 /* Generate corresponding CoAP packet */
638 struct o_coap_packet o_coap_packet;
639 TRY(o_coap_pkg_generate(&plaintext, &oscore_packet,
640 &o_coap_packet));
641
642 /*Convert to byte string*/
643 r = coap2buf(&o_coap_packet, buf_out, buf_out_len);
644 }
645 return r;
646 }
647