1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3 /*
4 * coap_oscore.c -- Object Security for Constrained RESTful Environments
5 * (OSCORE) support for libcoap
6 *
7 * Copyright (C) 2019-2021 Olaf Bergmann <bergmann@tzi.org>
8 * Copyright (C) 2021 Jon Shallow <supjps-libcoap@jpshallow.com>
9 *
10 * SPDX-License-Identifier: BSD-2-Clause
11 *
12 * This file is part of the CoAP library libcoap. Please see README for terms
13 * of use.
14 */
15
16 /**
17 * @file coap_oscore.c
18 * @brief CoAP OSCORE handling
19 */
20
21 #include "coap3/coap_internal.h"
22
23 #if COAP_OSCORE_SUPPORT
24 #include <ctype.h>
25
26 #define AAD_BUF_LEN 200 /* length of aad_buffer */
27
28 static oscore_ctx_t *coap_oscore_init(coap_context_t *c_context,
29 coap_oscore_conf_t *oscore_conf);
30
31 #if COAP_CLIENT_SUPPORT
32
33 int
coap_oscore_initiate(coap_session_t * session,coap_oscore_conf_t * oscore_conf)34 coap_oscore_initiate(coap_session_t *session, coap_oscore_conf_t *oscore_conf) {
35 if (oscore_conf) {
36 oscore_ctx_t *osc_ctx;
37
38 if (oscore_conf->recipient_id_count == 0) {
39 coap_log_warn("OSCORE: Recipient ID must be defined for a client\n");
40 return 0;
41 }
42 if (oscore_conf->rfc8613_b_2) {
43 /* Need to replace id_context with random value */
44 coap_binary_t *id_context = coap_new_binary(8);
45
46 if (id_context == NULL)
47 return 0;
48 coap_delete_bin_const(oscore_conf->id_context);
49 coap_prng(id_context->s, id_context->length);
50 oscore_conf->id_context = (coap_bin_const_t *)id_context;
51 session->b_2_step = COAP_OSCORE_B_2_STEP_1;
52 coap_log_oscore("Appendix B.2 client step 1 (Generated ID1)\n");
53 }
54
55 osc_ctx = coap_oscore_init(session->context, oscore_conf);
56 if (osc_ctx == NULL) {
57 return 0;
58 }
59 session->recipient_ctx = osc_ctx->recipient_chain;
60 session->oscore_encryption = 1;
61 }
62 return 1;
63 }
64
65 coap_session_t *
coap_new_client_session_oscore(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_oscore_conf_t * oscore_conf)66 coap_new_client_session_oscore(coap_context_t *ctx,
67 const coap_address_t *local_if,
68 const coap_address_t *server,
69 coap_proto_t proto,
70 coap_oscore_conf_t *oscore_conf) {
71 coap_session_t *session =
72 coap_new_client_session(ctx, local_if, server, proto);
73
74 if (!session)
75 return NULL;
76
77 if (coap_oscore_initiate(session, oscore_conf) == 0) {
78 coap_session_release(session);
79 return NULL;
80 }
81 return session;
82 }
83
84 coap_session_t *
coap_new_client_session_oscore_psk(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_dtls_cpsk_t * psk_data,coap_oscore_conf_t * oscore_conf)85 coap_new_client_session_oscore_psk(coap_context_t *ctx,
86 const coap_address_t *local_if,
87 const coap_address_t *server,
88 coap_proto_t proto,
89 coap_dtls_cpsk_t *psk_data,
90 coap_oscore_conf_t *oscore_conf) {
91 coap_session_t *session =
92 coap_new_client_session_psk2(ctx, local_if, server, proto, psk_data);
93
94 if (!session)
95 return NULL;
96
97 if (coap_oscore_initiate(session, oscore_conf) == 0) {
98 coap_session_release(session);
99 return NULL;
100 }
101 return session;
102 }
103
104 coap_session_t *
coap_new_client_session_oscore_pki(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_dtls_pki_t * pki_data,coap_oscore_conf_t * oscore_conf)105 coap_new_client_session_oscore_pki(coap_context_t *ctx,
106 const coap_address_t *local_if,
107 const coap_address_t *server,
108 coap_proto_t proto,
109 coap_dtls_pki_t *pki_data,
110 coap_oscore_conf_t *oscore_conf) {
111 coap_session_t *session =
112 coap_new_client_session_pki(ctx, local_if, server, proto, pki_data);
113
114 if (!session)
115 return NULL;
116
117 if (coap_oscore_initiate(session, oscore_conf) == 0) {
118 coap_session_release(session);
119 return NULL;
120 }
121 return session;
122 }
123 #endif /* COAP_CLIENT_SUPPORT */
124 #if COAP_SERVER_SUPPORT
125
126 int
coap_context_oscore_server(coap_context_t * context,coap_oscore_conf_t * oscore_conf)127 coap_context_oscore_server(coap_context_t *context,
128 coap_oscore_conf_t *oscore_conf) {
129 oscore_ctx_t *osc_ctx = coap_oscore_init(context, oscore_conf);
130
131 /* osc_ctx already added to context->osc_ctx */
132 if (osc_ctx)
133 return 1;
134 return 0;
135 }
136
137 #endif /* COAP_SERVER_SUPPORT */
138
139 int
coap_rebuild_pdu_for_proxy(coap_pdu_t * pdu)140 coap_rebuild_pdu_for_proxy(coap_pdu_t *pdu) {
141 coap_uri_t uri;
142 coap_opt_iterator_t opt_iter;
143 coap_opt_t *option;
144 uint8_t option_value_buffer[15];
145 uint8_t *keep_proxy_uri = NULL;
146
147 if ((option =
148 coap_check_option(pdu, COAP_OPTION_PROXY_URI, &opt_iter)) == NULL)
149 return 1;
150
151 /* Need to break down into the component parts, but keep data safe */
152 memset(&uri, 0, sizeof(uri));
153 keep_proxy_uri = coap_malloc_type(COAP_STRING, coap_opt_length(option));
154 if (keep_proxy_uri == NULL)
155 goto error;
156 memcpy(keep_proxy_uri, coap_opt_value(option), coap_opt_length(option));
157
158 if (coap_split_proxy_uri(keep_proxy_uri,
159 coap_opt_length(option),
160 &uri) < 0 || uri.scheme >= COAP_URI_SCHEME_LAST) {
161 coap_log_warn("Proxy URI '%.*s' not decodable\n",
162 coap_opt_length(option),
163 (const char *)coap_opt_value(option));
164 goto error;
165 }
166 if (!coap_remove_option(pdu, COAP_OPTION_PROXY_URI))
167 goto error;
168
169 if (!coap_insert_option(pdu,
170 COAP_OPTION_URI_HOST,
171 uri.host.length,
172 uri.host.s))
173 goto error;
174 if (uri.port != (coap_uri_scheme_is_secure(&uri) ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT) &&
175 !coap_insert_option(pdu,
176 COAP_OPTION_URI_PORT,
177 coap_encode_var_safe(option_value_buffer,
178 sizeof(option_value_buffer),
179 uri.port & 0xffff),
180 option_value_buffer))
181 goto error;
182 if (uri.path.length) {
183 uint8_t *buf;
184 uint8_t *kbuf;
185 size_t buflen = uri.path.length + 1;
186 int res;
187
188 kbuf = buf = coap_malloc_type(COAP_STRING, uri.path.length + 1);
189 if (buf) {
190 res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
191 while (res--) {
192 if (!coap_insert_option(pdu,
193 COAP_OPTION_URI_PATH,
194 coap_opt_length(buf),
195 coap_opt_value(buf))) {
196 coap_free_type(COAP_STRING, buf);
197 goto error;
198 }
199 buf += coap_opt_size(buf);
200 }
201 }
202 coap_free_type(COAP_STRING, kbuf);
203 }
204 if (uri.query.length) {
205 uint8_t *buf;
206 size_t buflen = uri.query.length + 1;
207 int res;
208
209 buf = coap_malloc_type(COAP_STRING, uri.query.length + 1);
210 if (buf) {
211 res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
212 while (res--) {
213 if (!coap_insert_option(pdu,
214 COAP_OPTION_URI_QUERY,
215 coap_opt_length(buf),
216 coap_opt_value(buf))) {
217 coap_free_type(COAP_STRING, buf);
218 goto error;
219 }
220
221 buf += coap_opt_size(buf);
222 }
223 coap_free_type(COAP_STRING, buf);
224 }
225 }
226 if (!coap_insert_option(pdu,
227 COAP_OPTION_PROXY_SCHEME,
228 strlen(coap_uri_scheme[uri.scheme].name),
229 (const uint8_t *)coap_uri_scheme[uri.scheme].name))
230 goto error;
231 coap_free_type(COAP_STRING, keep_proxy_uri);
232 return 1;
233
234 error:
235 coap_free_type(COAP_STRING, keep_proxy_uri);
236 return 0;
237 }
238
239 static void
dump_cose(cose_encrypt0_t * cose,const char * message)240 dump_cose(cose_encrypt0_t *cose, const char *message) {
241 #if COAP_MAX_LOGGING_LEVEL < _COAP_LOG_OSCORE
242 (void)cose;
243 (void)message;
244 #else /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_OSCORE */
245 if (coap_get_log_level() >= COAP_LOG_OSCORE) {
246 char buffer[30];
247
248 coap_log_oscore("%s Cose information\n", message);
249 oscore_log_char_value(COAP_LOG_OSCORE, "alg",
250 cose_get_alg_name(cose->alg, buffer, sizeof(buffer)));
251 oscore_log_hex_value(COAP_LOG_OSCORE, "key", &cose->key);
252 oscore_log_hex_value(COAP_LOG_OSCORE, "partial_iv", &cose->partial_iv);
253 oscore_log_hex_value(COAP_LOG_OSCORE, "key_id", &cose->key_id);
254 oscore_log_hex_value(COAP_LOG_OSCORE, "kid_context", &cose->kid_context);
255 oscore_log_hex_value(COAP_LOG_OSCORE,
256 "oscore_option",
257 &cose->oscore_option);
258 oscore_log_hex_value(COAP_LOG_OSCORE, "nonce", &cose->nonce);
259 oscore_log_hex_value(COAP_LOG_OSCORE, "external_aad", &cose->external_aad);
260 oscore_log_hex_value(COAP_LOG_OSCORE, "aad", &cose->aad);
261 }
262 #endif /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_OSCORE */
263 }
264
265 /*
266 * Take current PDU, create a new one approriately separated as per RFC8613
267 * and then encrypt / integrity check the OSCORE data
268 */
269 coap_pdu_t *
coap_oscore_new_pdu_encrypted(coap_session_t * session,coap_pdu_t * pdu,coap_bin_const_t * kid_context,oscore_partial_iv_t send_partial_iv)270 coap_oscore_new_pdu_encrypted(coap_session_t *session,
271 coap_pdu_t *pdu,
272 coap_bin_const_t *kid_context,
273 oscore_partial_iv_t send_partial_iv) {
274 uint8_t coap_request = COAP_PDU_IS_REQUEST(pdu);
275 coap_pdu_code_t code =
276 coap_request ? COAP_REQUEST_CODE_POST : COAP_RESPONSE_CODE(204);
277 coap_pdu_t *osc_pdu;
278 coap_pdu_t *plain_pdu = NULL;
279 coap_bin_const_t pdu_token;
280 coap_opt_iterator_t opt_iter;
281 coap_opt_t *option;
282 uint8_t pdu_code = pdu->code;
283 size_t length;
284 const uint8_t *data;
285 uint8_t *ciphertext_buffer = NULL;
286 size_t ciphertext_len = 0;
287 uint8_t aad_buffer[AAD_BUF_LEN];
288 uint8_t nonce_buffer[13];
289 coap_bin_const_t aad;
290 coap_bin_const_t nonce;
291 oscore_recipient_ctx_t *rcp_ctx = session->recipient_ctx;
292 oscore_ctx_t *osc_ctx = rcp_ctx ? rcp_ctx->osc_ctx : NULL;
293 cose_encrypt0_t cose[1];
294 uint8_t group_flag = 0;
295 int show_pdu = 0;
296 int doing_observe = 0;
297 uint32_t observe_value = 0;
298 oscore_association_t *association = NULL;
299 oscore_sender_ctx_t *snd_ctx = osc_ctx ? osc_ctx->sender_context : NULL;
300 uint8_t external_aad_buffer[200];
301 coap_bin_const_t external_aad;
302 uint8_t oscore_option[48];
303 size_t oscore_option_len;
304
305 /* Check that OSCORE has not already been done */
306 if (coap_check_option(pdu, COAP_OPTION_OSCORE, &opt_iter))
307 return NULL;
308
309 if (coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter))
310 doing_observe = 1;
311
312 coap_log_debug("PDU to encrypt\n");
313 coap_show_pdu(COAP_LOG_DEBUG, pdu);
314 osc_pdu = coap_pdu_init(pdu->type == COAP_MESSAGE_NON &&
315 session->b_2_step != COAP_OSCORE_B_2_NONE ?
316 COAP_MESSAGE_CON : pdu->type,
317 code,
318 pdu->mid,
319 pdu->used_size + coap_oscore_overhead(session, pdu));
320 if (osc_pdu == NULL)
321 return NULL;
322
323 cose_encrypt0_init(cose); /* clears cose memory */
324 pdu_token = coap_pdu_get_token(pdu);
325 if (coap_request) {
326 /*
327 * RFC8613 8.1 Step 1. Protecting the client's request
328 * Get the Sender Context
329 */
330 rcp_ctx = session->recipient_ctx;
331 if (rcp_ctx == NULL)
332 goto error;
333 osc_ctx = rcp_ctx->osc_ctx;
334 snd_ctx = osc_ctx->sender_context;
335 } else {
336 /*
337 * RFC8613 8.3 Step 1. Protecting the server's response
338 * Get the Sender Context
339 */
340 association = oscore_find_association(session, &pdu_token);
341 if (association == NULL)
342 goto error;
343
344 rcp_ctx = association->recipient_ctx;
345 osc_ctx = rcp_ctx->osc_ctx;
346 snd_ctx = osc_ctx->sender_context;
347 cose_encrypt0_set_partial_iv(cose, association->partial_iv);
348 cose_encrypt0_set_aad(cose, association->aad);
349 }
350
351 cose_encrypt0_set_alg(cose, osc_ctx->aead_alg);
352
353 if (coap_request || doing_observe ||
354 send_partial_iv == OSCORE_SEND_PARTIAL_IV) {
355 uint8_t partial_iv_buffer[8];
356 size_t partial_iv_len;
357 coap_bin_const_t partial_iv;
358 partial_iv_len = coap_encode_var_safe8(partial_iv_buffer,
359 sizeof(partial_iv_buffer),
360 snd_ctx->seq);
361 if (snd_ctx->seq == 0) {
362 /* Need to special case */
363 partial_iv_buffer[0] = '\000';
364 partial_iv_len = 1;
365 }
366 partial_iv.s = partial_iv_buffer;
367 partial_iv.length = partial_iv_len;
368 cose_encrypt0_set_partial_iv(cose, &partial_iv);
369 }
370
371 if (coap_request)
372 cose_encrypt0_set_kid_context(cose, osc_ctx->id_context);
373
374 cose_encrypt0_set_key_id(cose, snd_ctx->sender_id);
375
376 /* nonce (needs to have sender information correctly set up) */
377
378 if (coap_request || doing_observe ||
379 send_partial_iv == OSCORE_SEND_PARTIAL_IV) {
380 /*
381 * 8.1 Step 3 or RFC8613 8.3.1 Step A
382 * Compose the AEAD nonce
383 *
384 * Requires in COSE object as appropriate
385 * key_id (kid) (sender)
386 * partial_iv (sender)
387 * common_iv (already in osc_ctx)
388 */
389 nonce.s = nonce_buffer;
390 nonce.length = 13;
391 oscore_generate_nonce(cose, osc_ctx, nonce_buffer, 13);
392 cose_encrypt0_set_nonce(cose, &nonce);
393 if (!oscore_increment_sender_seq(osc_ctx))
394 goto error;
395 if (osc_ctx->save_seq_num_func) {
396 if (osc_ctx->sender_context->seq > osc_ctx->sender_context->next_seq) {
397 /* Only update at ssn_freq rate */
398 osc_ctx->sender_context->next_seq += osc_ctx->ssn_freq;
399 osc_ctx->save_seq_num_func(osc_ctx->sender_context->next_seq,
400 osc_ctx->save_seq_num_func_param);
401 }
402 }
403 } else {
404 /*
405 * 8.3 Step 3.
406 * Use nonce from request
407 */
408 cose_encrypt0_set_nonce(cose, association->nonce);
409 }
410
411 /* OSCORE_option (needs to be before AAD as included in AAD if group) */
412
413 /* cose is modified for encode option in response message */
414 if (!coap_request) {
415 /* no kid on response */
416 cose_encrypt0_set_key_id(cose, NULL);
417 if (!doing_observe && send_partial_iv == OSCORE_SEND_NO_IV)
418 cose_encrypt0_set_partial_iv(cose, NULL);
419 }
420 if (kid_context) {
421 cose_encrypt0_set_kid_context(cose, kid_context);
422 }
423 oscore_option_len =
424 oscore_encode_option_value(oscore_option, sizeof(oscore_option), cose,
425 group_flag,
426 session->b_2_step != COAP_OSCORE_B_2_NONE);
427 if (!coap_request) {
428 /* Reset what was just unset as appropriate for AAD */
429 cose_encrypt0_set_key_id(cose, rcp_ctx->recipient_id);
430 cose_encrypt0_set_partial_iv(cose, association->partial_iv);
431 }
432 if (kid_context)
433 cose_encrypt0_set_kid_context(cose, osc_ctx->id_context);
434
435 /*
436 * RFC8613 8.1/8.3 Step 2(a) (5.4).
437 * Compose the External AAD and then AAD
438 *
439 * OSCORE_option requires
440 * partial_iv (cose partial_iv)
441 * kid_context (cose kid_context)
442 * key_id (cose key_id)
443 * group_flag
444 *
445 * Non Group (based on osc_tx->mode) requires the following
446 * aead_alg (osc_ctx)
447 * request_kid (request key_id using cose)
448 * request_piv (request partial_iv using cose)
449 * options (none at present)
450 * Group (based on osc_tx->mode) requires the following
451 * aead_alg (osc_ctx) (pairwise mode)
452 * sign_enc_alg (osc_ctx) (group mode)
453 * sign_alg (osc_ctx) (group mode)
454 * alg_pairwise_key_agreement (osc_ctx) (pairwise mode)
455 * request_kid (request key_id using cose)
456 * request_piv (request partial_iv using cose)
457 * options (none at present)
458 * request_kid_context (osc_ctx id_context)
459 * OSCORE_option (parameter)
460 * test_gs_public_key (osc_ctx sender_context public_key)
461 * gm_public_key (osc_ctx gm_public_key)
462 *
463 * Note: No I options at present
464 */
465 if (coap_request || osc_ctx->mode != OSCORE_MODE_SINGLE ||
466 send_partial_iv == OSCORE_SEND_PARTIAL_IV) {
467 /* External AAD */
468 external_aad.s = external_aad_buffer;
469 external_aad.length = oscore_prepare_e_aad(osc_ctx,
470 cose,
471 NULL,
472 0,
473 NULL,
474 external_aad_buffer,
475 sizeof(external_aad_buffer));
476 cose_encrypt0_set_external_aad(cose, &external_aad);
477
478 /* AAD */
479 aad.s = aad_buffer;
480 aad.length = oscore_prepare_aad(external_aad_buffer,
481 external_aad.length,
482 aad_buffer,
483 sizeof(aad_buffer));
484 assert(aad.length < AAD_BUF_LEN);
485 cose_encrypt0_set_aad(cose, &aad);
486 }
487
488 /*
489 * RFC8613 8.1/8.3 Step 2(b) (5.3).
490 *
491 * Set up temp plaintext pdu, the data including token, options and
492 * optional payload will get encrypted as COSE ciphertext.
493 */
494 plain_pdu = coap_pdu_init(pdu->type,
495 pdu->code,
496 pdu->mid,
497 pdu->used_size);
498 if (plain_pdu == NULL)
499 goto error;
500
501 coap_add_token(osc_pdu, pdu_token.length, pdu_token.s);
502
503 /* First byte of plain is real CoAP code. Pretend it is token */
504 coap_add_token(plain_pdu, 1, &pdu_code);
505
506 /* Copy across the Outer/Inner Options to respective PDUs */
507 coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
508 while ((option = coap_option_next(&opt_iter))) {
509 switch (opt_iter.number) {
510 case COAP_OPTION_URI_HOST:
511 case COAP_OPTION_URI_PORT:
512 case COAP_OPTION_PROXY_SCHEME:
513 case COAP_OPTION_HOP_LIMIT:
514 /* Outer only */
515 if (!coap_insert_option(osc_pdu,
516 opt_iter.number,
517 coap_opt_length(option),
518 coap_opt_value(option)))
519 goto error;
520 break;
521 case COAP_OPTION_OBSERVE:
522 /* Make as Outer option as-is */
523 if (!coap_insert_option(osc_pdu,
524 opt_iter.number,
525 coap_opt_length(option),
526 coap_opt_value(option)))
527 goto error;
528 if (coap_request) {
529 /* Make as Inner option (unchanged) */
530 if (!coap_insert_option(plain_pdu,
531 opt_iter.number,
532 coap_opt_length(option),
533 coap_opt_value(option)))
534 goto error;
535 osc_pdu->code = COAP_REQUEST_CODE_FETCH;
536 } else {
537 /* Make as Inner option but empty */
538 if (!coap_insert_option(plain_pdu, opt_iter.number, 0, NULL))
539 goto error;
540 osc_pdu->code = COAP_RESPONSE_CODE(205);
541 }
542 show_pdu = 1;
543 doing_observe = 1;
544 observe_value = coap_decode_var_bytes(coap_opt_value(option),
545 coap_opt_length(option));
546 break;
547 case COAP_OPTION_PROXY_URI:
548 /*
549 * Should have already been caught by doing
550 * coap_rebuild_pdu_for_proxy() before calling
551 * coap_oscore_new_pdu_encrypted()
552 */
553 assert(0);
554 break;
555 default:
556 /* Make as Inner option */
557 if (!coap_insert_option(plain_pdu,
558 opt_iter.number,
559 coap_opt_length(option),
560 coap_opt_value(option)))
561 goto error;
562 break;
563 }
564 }
565 /* Add in data to plain */
566 if (coap_get_data(pdu, &length, &data)) {
567 if (!coap_add_data(plain_pdu, length, data))
568 goto error;
569 }
570 if (show_pdu) {
571 coap_log_oscore("OSCORE payload\n");
572 coap_show_pdu(COAP_LOG_OSCORE, plain_pdu);
573 }
574
575 /*
576 * 8.1/8.3 Step 4.
577 * Encrypt the COSE object.
578 *
579 * Requires in COSE object as appropriate
580 * alg (already set)
581 * key (sender key)
582 * nonce (already set)
583 * aad (already set)
584 * plaintext
585 */
586 cose_encrypt0_set_key(cose, snd_ctx->sender_key);
587 cose_encrypt0_set_plaintext(cose, plain_pdu->token, plain_pdu->used_size);
588 dump_cose(cose, "Pre encrypt");
589 ciphertext_buffer =
590 coap_malloc_type(COAP_OSCORE_BUF, OSCORE_CRYPTO_BUFFER_SIZE);
591 if (ciphertext_buffer == NULL)
592 goto error;
593 ciphertext_len = cose_encrypt0_encrypt(cose,
594 ciphertext_buffer,
595 plain_pdu->used_size + AES_CCM_TAG);
596 if ((int)ciphertext_len <= 0) {
597 coap_log_warn("OSCORE: Encryption Failure, result code: %d \n",
598 (int)ciphertext_len);
599 goto error;
600 }
601 assert(ciphertext_len < OSCORE_CRYPTO_BUFFER_SIZE);
602
603 /* Add in OSCORE option (previously computed) */
604 if (!coap_insert_option(osc_pdu,
605 COAP_OPTION_OSCORE,
606 oscore_option_len,
607 oscore_option))
608 goto error;
609
610 /* Add now encrypted payload */
611 if (!coap_add_data(osc_pdu, ciphertext_len, ciphertext_buffer))
612 goto error;
613
614 coap_free_type(COAP_OSCORE_BUF, ciphertext_buffer);
615 ciphertext_buffer = NULL;
616
617 coap_delete_pdu(plain_pdu);
618 plain_pdu = NULL;
619
620 if (association && association->is_observe == 0)
621 oscore_delete_association(session, association);
622 association = NULL;
623
624 /*
625 * If this is a response ACK with data, make it a separate response
626 * by sending an Empty ACK and changing osc_pdu's MID and type. This
627 * then allows lost response ACK with data to be recovered.
628 */
629 if (coap_request == 0 && osc_pdu->type == COAP_MESSAGE_ACK &&
630 COAP_PROTO_NOT_RELIABLE(session->proto)) {
631 coap_pdu_t *empty = coap_pdu_init(COAP_MESSAGE_ACK,
632 0,
633 osc_pdu->mid,
634 0);
635 if (empty) {
636 if (coap_send_internal(session, empty) != COAP_INVALID_MID) {
637 osc_pdu->mid = coap_new_message_id(session);
638 osc_pdu->type = COAP_MESSAGE_CON;
639 }
640 }
641 }
642
643 if (!coap_pdu_encode_header(osc_pdu, session->proto)) {
644 goto error;
645 }
646
647 /*
648 * Set up an association for handling a response if this is a request
649 */
650 if (coap_request) {
651 association = oscore_find_association(session, &pdu_token);
652 if (association) {
653 if (doing_observe && observe_value == 1) {
654 association->is_observe = 0;
655 }
656 /* Refresh the association */
657 coap_delete_bin_const(association->nonce);
658 association->nonce =
659 coap_new_bin_const(cose->nonce.s, cose->nonce.length);
660 if (association->nonce == NULL)
661 goto error;
662 coap_delete_bin_const(association->aad);
663 association->aad = coap_new_bin_const(cose->aad.s, cose->aad.length);
664 if (association->aad == NULL)
665 goto error;
666 coap_delete_bin_const(association->partial_iv);
667 association->partial_iv =
668 coap_new_bin_const(cose->partial_iv.s, cose->partial_iv.length);
669 if (association->partial_iv == NULL)
670 goto error;
671 association->recipient_ctx = rcp_ctx;
672 coap_delete_pdu(association->sent_pdu);
673 if (session->b_2_step != COAP_OSCORE_B_2_NONE) {
674 size_t size;
675
676 association->sent_pdu = coap_pdu_duplicate(pdu, session,
677 pdu_token.length,
678 pdu_token.s, NULL);
679 if (association->sent_pdu == NULL)
680 goto error;
681 if (coap_get_data(pdu, &size, &data)) {
682 coap_add_data(association->sent_pdu, size, data);
683 }
684 } else {
685 association->sent_pdu = NULL;
686 }
687 } else if (!oscore_new_association(session,
688 session->b_2_step != COAP_OSCORE_B_2_NONE ? pdu : NULL,
689 &pdu_token,
690 rcp_ctx,
691 &cose->aad,
692 &cose->nonce,
693 &cose->partial_iv,
694 doing_observe)) {
695 goto error;
696 }
697 }
698 return osc_pdu;
699
700 error:
701 if (ciphertext_buffer)
702 coap_free_type(COAP_OSCORE_BUF, ciphertext_buffer);
703 coap_delete_pdu(osc_pdu);
704 coap_delete_pdu(plain_pdu);
705 return NULL;
706 }
707
708 static void
build_and_send_error_pdu(coap_session_t * session,coap_pdu_t * rcvd,coap_pdu_code_t code,const char * diagnostic,uint8_t * echo_data,coap_bin_const_t * kid_context,int encrypt_oscore)709 build_and_send_error_pdu(coap_session_t *session,
710 coap_pdu_t *rcvd,
711 coap_pdu_code_t code,
712 const char *diagnostic,
713 uint8_t *echo_data,
714 coap_bin_const_t *kid_context,
715 int encrypt_oscore) {
716 coap_pdu_t *err_pdu = NULL;
717 coap_bin_const_t token;
718 int oscore_encryption = session->oscore_encryption;
719 unsigned char buf[4];
720
721 token = coap_pdu_get_token(rcvd);
722 err_pdu = coap_pdu_init(rcvd->type == COAP_MESSAGE_NON ? COAP_MESSAGE_NON :
723 COAP_MESSAGE_ACK,
724 code,
725 rcvd->mid,
726 token.length + 2 + 8 +
727 (diagnostic ? strlen(diagnostic) : 0));
728 if (!err_pdu)
729 return;
730 coap_add_token(err_pdu, token.length, token.s);
731 if (echo_data) {
732 coap_add_option_internal(err_pdu, COAP_OPTION_ECHO, 8, echo_data);
733 } else if (kid_context == NULL) {
734 coap_add_option_internal(err_pdu,
735 COAP_OPTION_MAXAGE,
736 coap_encode_var_safe(buf, sizeof(buf), 0),
737 buf);
738 }
739 if (diagnostic)
740 coap_add_data(err_pdu, strlen(diagnostic), (const uint8_t *)diagnostic);
741 session->oscore_encryption = encrypt_oscore;
742
743 if ((echo_data || kid_context) && encrypt_oscore) {
744 coap_pdu_t *osc_pdu;
745
746 osc_pdu =
747 coap_oscore_new_pdu_encrypted(session, err_pdu, kid_context,
748 echo_data ? 1 : 0);
749 if (!osc_pdu)
750 goto fail_resp;
751 session->oscore_encryption = 0;
752 coap_send_internal(session, osc_pdu);
753 coap_delete_pdu(err_pdu);
754 err_pdu = NULL;
755 } else {
756 coap_send_internal(session, err_pdu);
757 err_pdu = NULL;
758 }
759 fail_resp:
760 session->oscore_encryption = oscore_encryption;
761 coap_delete_pdu(err_pdu);
762 return;
763 }
764
765 /* pdu contains incoming message with encrypted COSE ciphertext payload
766 * function returns decrypted message
767 * and verifies signature, if present
768 * returns NULL when decryption,verification fails
769 */
770 coap_pdu_t *
coap_oscore_decrypt_pdu(coap_session_t * session,coap_pdu_t * pdu)771 coap_oscore_decrypt_pdu(coap_session_t *session,
772 coap_pdu_t *pdu) {
773 coap_pdu_t *decrypt_pdu = NULL;
774 coap_pdu_t *plain_pdu = NULL;
775 const uint8_t *osc_value; /* value of OSCORE option */
776 uint8_t osc_size; /* size of OSCORE OPTION */
777 coap_opt_iterator_t opt_iter;
778 coap_opt_t *opt = NULL;
779 cose_encrypt0_t cose[1];
780 oscore_ctx_t *osc_ctx = NULL;
781 uint8_t aad_buffer[AAD_BUF_LEN];
782 uint8_t nonce_buffer[13];
783 coap_bin_const_t aad;
784 coap_bin_const_t nonce;
785 int pltxt_size = 0;
786 uint8_t coap_request = COAP_PDU_IS_REQUEST(pdu);
787 coap_bin_const_t pdu_token;
788 uint8_t *st_encrypt;
789 size_t encrypt_len;
790 size_t tag_len;
791 oscore_recipient_ctx_t *rcp_ctx = NULL;
792 oscore_association_t *association = NULL;
793 uint8_t external_aad_buffer[100];
794 coap_bin_const_t external_aad;
795 oscore_sender_ctx_t *snd_ctx = NULL;
796 #if COAP_CLIENT_SUPPORT
797 coap_pdu_t *sent_pdu = NULL;
798 #endif /* COAP_CLIENT_SUPPORT */
799
800 opt = coap_check_option(pdu, COAP_OPTION_OSCORE, &opt_iter);
801 assert(opt);
802 if (opt == NULL)
803 return NULL;
804
805 if (session->context->p_osc_ctx == NULL) {
806 coap_log_warn("OSCORE: Not enabled\n");
807 if (!coap_request)
808 coap_handle_event(session->context,
809 COAP_EVENT_OSCORE_NOT_ENABLED,
810 session);
811 return NULL;
812 }
813
814 if (pdu->data == NULL) {
815 coap_log_warn("OSCORE: No protected payload\n");
816 if (!coap_request)
817 coap_handle_event(session->context,
818 COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD,
819 session);
820 return NULL;
821 }
822
823 osc_size = coap_opt_length(opt);
824 osc_value = coap_opt_value(opt);
825
826 cose_encrypt0_init(cose); /* clear cose memory */
827
828 /* PDU code will be filled in after decryption */
829 decrypt_pdu =
830 coap_pdu_init(pdu->type, 0, pdu->mid, pdu->used_size);
831 if (decrypt_pdu == NULL) {
832 if (!coap_request)
833 coap_handle_event(session->context,
834 COAP_EVENT_OSCORE_INTERNAL_ERROR,
835 session);
836 goto error;
837 }
838
839 /* Copy across the Token */
840 pdu_token = coap_pdu_get_token(pdu);
841 coap_add_token(decrypt_pdu, pdu_token.length, pdu_token.s);
842
843 /*
844 * 8.2/8.4 Step 1.
845 * Copy outer options across, except E and OSCORE options
846 */
847 coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
848 while ((opt = coap_option_next(&opt_iter))) {
849 switch (opt_iter.number) {
850 /* 'E' options skipped */
851 case COAP_OPTION_IF_MATCH:
852 case COAP_OPTION_ETAG:
853 case COAP_OPTION_IF_NONE_MATCH:
854 case COAP_OPTION_OBSERVE:
855 case COAP_OPTION_LOCATION_PATH:
856 case COAP_OPTION_URI_PATH:
857 case COAP_OPTION_CONTENT_FORMAT:
858 case COAP_OPTION_MAXAGE:
859 case COAP_OPTION_URI_QUERY:
860 case COAP_OPTION_ACCEPT:
861 case COAP_OPTION_LOCATION_QUERY:
862 case COAP_OPTION_BLOCK2:
863 case COAP_OPTION_BLOCK1:
864 case COAP_OPTION_SIZE2:
865 case COAP_OPTION_SIZE1:
866 case COAP_OPTION_NORESPONSE:
867 case COAP_OPTION_ECHO:
868 case COAP_OPTION_RTAG:
869 /* OSCORE does not get copied across */
870 case COAP_OPTION_OSCORE:
871 break;
872 default:
873 if (!coap_add_option_internal(decrypt_pdu,
874 opt_iter.number,
875 coap_opt_length(opt),
876 coap_opt_value(opt))) {
877 if (!coap_request)
878 coap_handle_event(session->context,
879 COAP_EVENT_OSCORE_INTERNAL_ERROR,
880 session);
881 goto error;
882 }
883 break;
884 }
885 }
886
887 if (coap_request) {
888 uint64_t incoming_seq;
889 /*
890 * 8.2 Step 2
891 * Decompress COSE object
892 * Get Recipient Context based on kid and optional kid_context
893 */
894 if (oscore_decode_option_value(osc_value, osc_size, cose) == 0) {
895 coap_log_warn("OSCORE: OSCORE Option cannot be decoded.\n");
896 build_and_send_error_pdu(session,
897 pdu,
898 COAP_RESPONSE_CODE(402),
899 "Failed to decode COSE",
900 NULL,
901 NULL,
902 0);
903 goto error_no_ack;
904 }
905 osc_ctx = oscore_find_context(session->context,
906 cose->key_id,
907 &cose->kid_context,
908 NULL,
909 &rcp_ctx);
910 if (!osc_ctx) {
911 if (cose->kid_context.length > 0) {
912 const uint8_t *ptr;
913 size_t length;
914 /* Appendix B.2 protocol check - Is the recipient key_id known */
915 osc_ctx = oscore_find_context(session->context,
916 cose->key_id,
917 NULL,
918 session->oscore_r2 != 0 ? (uint8_t *)&session->oscore_r2 : NULL,
919 &rcp_ctx);
920 ptr = cose->kid_context.s;
921 length = cose->kid_context.length;
922 if (ptr && osc_ctx && osc_ctx->rfc8613_b_2 &&
923 osc_ctx->mode == OSCORE_MODE_SINGLE) {
924 /* Processing Appendix B.2 protocol */
925 /* Need to CBOR unwrap kid_context */
926 coap_bin_const_t kid_context;
927
928 kid_context.length = oscore_cbor_get_element_size(&ptr, &length);
929 kid_context.s = ptr;
930 cose_encrypt0_set_kid_context(cose, (coap_bin_const_t *)&kid_context);
931
932 if (session->oscore_r2 != 0) {
933 /* B.2 step 4 */
934 coap_bin_const_t *kc = coap_new_bin_const(cose->kid_context.s,
935 cose->kid_context.length);
936
937 if (kc == NULL)
938 goto error;
939
940 session->b_2_step = COAP_OSCORE_B_2_STEP_4;
941 coap_log_oscore("Appendix B.2 server step 4 (R2 || R3)\n");
942 oscore_update_ctx(osc_ctx, kc);
943 } else {
944 session->b_2_step = COAP_OSCORE_B_2_STEP_2;
945 coap_log_oscore("Appendix B.2 server step 2 (ID1)\n");
946 osc_ctx = oscore_duplicate_ctx(session->context,
947 osc_ctx,
948 osc_ctx->sender_context->sender_id,
949 &cose->key_id,
950 &cose->kid_context);
951 if (osc_ctx == NULL)
952 goto error;
953 /*
954 * Complete the Verify (B.2 step 2)
955 * before sending back the response
956 */
957 rcp_ctx = osc_ctx->recipient_chain;
958 }
959 } else {
960 osc_ctx = NULL;
961 }
962 }
963 } else if (session->b_2_step != COAP_OSCORE_B_2_NONE) {
964 session->b_2_step = COAP_OSCORE_B_2_NONE;
965 coap_log_oscore("Appendix B.2 server finished\n");
966 }
967 if (!osc_ctx) {
968 coap_log_crit("OSCORE: Security Context not found\n");
969 oscore_log_hex_value(COAP_LOG_OSCORE, "key_id", &cose->key_id);
970 oscore_log_hex_value(COAP_LOG_OSCORE, "kid_context", &cose->kid_context);
971 build_and_send_error_pdu(session,
972 pdu,
973 COAP_RESPONSE_CODE(401),
974 "Security context not found",
975 NULL,
976 NULL,
977 0);
978 goto error_no_ack;
979 }
980 /* to be used for encryption of returned response later */
981 session->recipient_ctx = rcp_ctx;
982 snd_ctx = osc_ctx->sender_context;
983
984 /*
985 * 8.2 Step 3.
986 * Verify Partial IV is not duplicated.
987 *
988 * Requires in COSE object as appropriate
989 * partial_iv (as received)
990 */
991 if (rcp_ctx->initial_state == 0 &&
992 !oscore_validate_sender_seq(rcp_ctx, cose)) {
993 coap_log_warn("OSCORE: Replayed or old message\n");
994 build_and_send_error_pdu(session,
995 pdu,
996 COAP_RESPONSE_CODE(401),
997 "Replay detected",
998 NULL,
999 NULL,
1000 0);
1001 goto error_no_ack;
1002 }
1003
1004 incoming_seq =
1005 coap_decode_var_bytes8(cose->partial_iv.s, cose->partial_iv.length);
1006 rcp_ctx->last_seq = incoming_seq;
1007 } else { /* !coap_request */
1008 /*
1009 * 8.4 Step 2
1010 * Decompress COSE object
1011 * Get Recipient Context based on token
1012 */
1013 if (oscore_decode_option_value(osc_value, osc_size, cose) == 0) {
1014 coap_log_warn("OSCORE: OSCORE Option cannot be decoded.\n");
1015 coap_handle_event(session->context,
1016 COAP_EVENT_OSCORE_DECODE_ERROR,
1017 session);
1018 goto error;
1019 }
1020 association = oscore_find_association(session, &pdu_token);
1021 if (association) {
1022 rcp_ctx = association->recipient_ctx;
1023 osc_ctx = rcp_ctx->osc_ctx;
1024 snd_ctx = osc_ctx->sender_context;
1025 #if COAP_CLIENT_SUPPORT
1026 sent_pdu = association->sent_pdu;
1027 if (session->b_2_step != COAP_OSCORE_B_2_NONE) {
1028 const uint8_t *ptr = cose->kid_context.s;
1029 size_t length = cose->kid_context.length;
1030
1031 if (ptr) {
1032 /* Need to CBOR unwrap kid_context */
1033 coap_bin_const_t kid_context;
1034
1035 kid_context.length = oscore_cbor_get_element_size(&ptr, &length);
1036 kid_context.s = ptr;
1037 cose_encrypt0_set_kid_context(cose, &kid_context);
1038 }
1039 if (ptr && !coap_binary_equal(osc_ctx->id_context, &cose->kid_context)) {
1040 /* If Appendix B.2 step 3 is in operation */
1041 /* Need to update Security Context with new (R2 || ID1) ID Context */
1042 coap_binary_t *kc = coap_new_binary(cose->kid_context.length +
1043 osc_ctx->id_context->length);
1044
1045 if (kc == NULL) {
1046 coap_handle_event(session->context,
1047 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1048 session);
1049 goto error;
1050 }
1051
1052 memcpy(kc->s, cose->kid_context.s, cose->kid_context.length);
1053 memcpy(&kc->s[cose->kid_context.length],
1054 osc_ctx->id_context->s,
1055 osc_ctx->id_context->length);
1056
1057 session->b_2_step = COAP_OSCORE_B_2_STEP_3;
1058 coap_log_oscore("Appendix B.2 client step 3 (R2 || ID1)\n");
1059 oscore_update_ctx(osc_ctx, (coap_bin_const_t *)kc);
1060 } else {
1061 session->b_2_step = COAP_OSCORE_B_2_STEP_5;
1062 coap_log_oscore("Appendix B.2 client step 5 (R2 || R3)\n");
1063 }
1064 }
1065 #endif /* COAP_CLIENT_SUPPORT */
1066 } else {
1067 coap_log_crit("OSCORE: Security Context association not found\n");
1068 coap_handle_event(session->context,
1069 COAP_EVENT_OSCORE_NO_SECURITY,
1070 session);
1071 goto error;
1072 }
1073 }
1074
1075 cose_encrypt0_set_alg(cose, osc_ctx->aead_alg);
1076
1077 if (coap_request) {
1078 /*
1079 * RFC8613 8.2 Step 4.
1080 * Compose the External AAD and then AAD
1081 *
1082 * Non Group (based on osc_tx->mode) requires the following
1083 * aead_alg (osc_ctx)
1084 * request_kid (request key_id using cose)
1085 * request_piv (request partial_iv using cose)
1086 * options (none at present)
1087 * Group (based on osc_tx->mode) requires the following
1088 * aead_alg (osc_ctx) (pairwise mode)
1089 * sign_enc_alg (osc_ctx) (group mode)
1090 * sign_alg (osc_ctx) (group mode)
1091 * alg_pairwise_key_agreement (osc_ctx) (pairwise mode)
1092 * request_kid (request key_id using cose)
1093 * request_piv (request partial_iv using cose)
1094 * options (none at present)
1095 * request_kid_context (osc_ctx id_context)
1096 * OSCORE_option (as received in request)
1097 * test_gs_public_key (recipient public key)
1098 * gm_public_key (osc_ctx gm_public_key)
1099 *
1100 * Note: No I options at present
1101 */
1102
1103 /* External AAD */
1104 external_aad.s = external_aad_buffer;
1105 external_aad.length = oscore_prepare_e_aad(osc_ctx,
1106 cose,
1107 osc_value,
1108 osc_size,
1109 NULL,
1110 external_aad_buffer,
1111 sizeof(external_aad_buffer));
1112 cose_encrypt0_set_external_aad(cose, &external_aad);
1113
1114 /* AAD */
1115 aad.s = aad_buffer;
1116 aad.length = oscore_prepare_aad(external_aad_buffer,
1117 external_aad.length,
1118 aad_buffer,
1119 sizeof(aad_buffer));
1120 assert(aad.length < AAD_BUF_LEN);
1121 cose_encrypt0_set_aad(cose, &aad);
1122
1123 /*
1124 * RFC8613 8.2 Step 5.
1125 * Compute the AEAD nonce.
1126 *
1127 * Requires in COSE object as appropriate
1128 * key_id (kid) (Recipient ID)
1129 * partial_iv (as received in request)
1130 * common_iv (already in osc_ctx)
1131 */
1132 nonce.s = nonce_buffer;
1133 nonce.length = 13;
1134 oscore_generate_nonce(cose, osc_ctx, nonce_buffer, 13);
1135 cose_encrypt0_set_nonce(cose, &nonce);
1136 /*
1137 * Set up an association for use in the response
1138 */
1139 association = oscore_find_association(session, &pdu_token);
1140 if (association) {
1141 /* Refresh the association */
1142 coap_delete_bin_const(association->nonce);
1143 association->nonce =
1144 coap_new_bin_const(cose->nonce.s, cose->nonce.length);
1145 if (association->nonce == NULL)
1146 goto error;
1147 coap_delete_bin_const(association->partial_iv);
1148 association->partial_iv =
1149 coap_new_bin_const(cose->partial_iv.s, cose->partial_iv.length);
1150 if (association->partial_iv == NULL)
1151 goto error;
1152 coap_delete_bin_const(association->aad);
1153 association->aad = coap_new_bin_const(cose->aad.s, cose->aad.length);
1154 if (association->aad == NULL)
1155 goto error;
1156 association->recipient_ctx = rcp_ctx;
1157 } else if (!oscore_new_association(session,
1158 NULL,
1159 &pdu_token,
1160 rcp_ctx,
1161 &cose->aad,
1162 &cose->nonce,
1163 &cose->partial_iv,
1164 0)) {
1165 goto error;
1166 }
1167 /* So association is not released when handling decrypt */
1168 association = NULL;
1169 } else { /* ! coap_request */
1170 /* Need to do nonce before AAD because of different partial_iv */
1171 /*
1172 * 8.4 Step 4.
1173 * Compose the AEAD nonce.
1174 */
1175 cose_encrypt0_set_key_id(cose, rcp_ctx->recipient_id);
1176 if (cose->partial_iv.length == 0) {
1177 cose_encrypt0_set_partial_iv(cose, association->partial_iv);
1178 cose_encrypt0_set_nonce(cose, association->nonce);
1179 } else {
1180 uint64_t last_seq;
1181
1182 if (rcp_ctx->initial_state == 0 &&
1183 !oscore_validate_sender_seq(rcp_ctx, cose)) {
1184 coap_log_warn("OSCORE: Replayed or old message\n");
1185 goto error;
1186 }
1187 last_seq =
1188 coap_decode_var_bytes8(cose->partial_iv.s, cose->partial_iv.length);
1189 if (rcp_ctx->last_seq>= OSCORE_SEQ_MAX) {
1190 coap_log_warn("OSCORE Replay protection, SEQ larger than SEQ_MAX.\n");
1191 goto error;
1192 }
1193 if (last_seq > rcp_ctx->last_seq)
1194 rcp_ctx->last_seq = last_seq;
1195 /*
1196 * Requires in COSE object as appropriate
1197 * kid (set above)
1198 * partial_iv (as received)
1199 * common_iv (already in osc_ctx)
1200 */
1201 oscore_generate_nonce(cose, osc_ctx, nonce_buffer, 13);
1202 nonce.s = nonce_buffer;
1203 nonce.length = 13;
1204 cose_encrypt0_set_nonce(cose, &nonce);
1205 }
1206 #ifdef OSCORE_EXTRA_DEBUG
1207 dump_cose(cose, "!req post set nonce");
1208 #endif /* OSCORE_EXTRA_DEBUG */
1209 /*
1210 * 8.4 Step 3.
1211 * Compose the External AAD and then AAD (same as request non-group (5.4)
1212 *
1213 * Non Group (based on osc_tx->mode) requires the following
1214 * aead_alg (osc_ctx)
1215 * request_kid (request key_id using cose)
1216 * request_piv (request partial_iv using cose)
1217 * options (none at present)
1218 * Group (based on osc_tx->mode) requires the following
1219 * aead_alg (osc_ctx) (pairwise mode)
1220 * sign_enc_alg (osc_ctx) (group mode)
1221 * sign_alg (osc_ctx) (group mode)
1222 * alg_pairwise_key_agreement (osc_ctx) (pairwise mode)
1223 * request_kid (request key_id using cose)
1224 * request_piv (request partial_iv using cose)
1225 * options (none at present)
1226 * request_kid_context (osc_ctx id_context)
1227 * OSCORE_option (as received in request)
1228 * test_gs_public_key (recipient public key)
1229 * gm_public_key (osc_ctx gm_public_key)
1230 *
1231 * Note: No I options at present
1232 */
1233
1234 /* External AAD */
1235 cose_encrypt0_set_key_id(cose, snd_ctx->sender_id);
1236 cose_encrypt0_set_partial_iv(cose, association->partial_iv);
1237 #ifdef OSCORE_EXTRA_DEBUG
1238 dump_cose(cose, "!req pre aad");
1239 #endif /* OSCORE_EXTRA_DEBUG */
1240 external_aad.s = external_aad_buffer;
1241 external_aad.length = oscore_prepare_e_aad(osc_ctx,
1242 cose,
1243 NULL,
1244 0,
1245 NULL,
1246 external_aad_buffer,
1247 sizeof(external_aad_buffer));
1248 cose_encrypt0_set_external_aad(cose, &external_aad);
1249
1250 /* AAD */
1251 aad.s = aad_buffer;
1252 aad.length = oscore_prepare_aad(external_aad_buffer,
1253 external_aad.length,
1254 aad_buffer,
1255 sizeof(aad_buffer));
1256 assert(aad.length < AAD_BUF_LEN);
1257 cose_encrypt0_set_aad(cose, &aad);
1258 #ifdef OSCORE_EXTRA_DEBUG
1259 dump_cose(cose, "!req post set aad");
1260 #endif /* OSCORE_EXTRA_DEBUG */
1261 }
1262
1263 /*
1264 * 8.2 Step 6 / 8.4 Step 5.
1265 * Decrypt the COSE object.
1266 *
1267 * Requires in COSE object as appropriate
1268 * alg (already set)
1269 * key
1270 * nonce (already set)
1271 * aad (already set)
1272 * ciphertext
1273 */
1274 st_encrypt = pdu->data;
1275 encrypt_len = pdu->used_size - (pdu->data - pdu->token);
1276 if (encrypt_len <= 0) {
1277 coap_log_warn("OSCORE: No protected payload\n");
1278 if (!coap_request)
1279 coap_handle_event(session->context,
1280 COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD,
1281 session);
1282 goto error;
1283 }
1284 cose_encrypt0_set_key(cose, rcp_ctx->recipient_key);
1285 cose_encrypt0_set_ciphertext(cose, st_encrypt, encrypt_len);
1286
1287 tag_len = cose_tag_len(cose->alg);
1288 /* Decrypt into plain_pdu, so code (token), options and data are in place */
1289 plain_pdu = coap_pdu_init(0, 0, 0, encrypt_len /* - tag_len */);
1290 if (plain_pdu == NULL) {
1291 if (!coap_request)
1292 coap_handle_event(session->context,
1293 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1294 session);
1295 goto error;
1296 }
1297
1298 /* need the tag_len on the end for TinyDTLS to do its work - yuk */
1299 if (!coap_pdu_resize(plain_pdu, encrypt_len /* - tag_len */)) {
1300 if (!coap_request)
1301 coap_handle_event(session->context,
1302 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1303 session);
1304 goto error;
1305 }
1306
1307 /* Account for 1 byte 'code' used as token */
1308 plain_pdu->e_token_length = 1;
1309 plain_pdu->actual_token.length = 1;
1310 /* Account for the decrypted data */
1311 plain_pdu->used_size = encrypt_len - tag_len;
1312
1313 dump_cose(cose, "Pre decrypt");
1314 pltxt_size =
1315 cose_encrypt0_decrypt(cose, plain_pdu->token, encrypt_len - tag_len);
1316 if (pltxt_size <= 0) {
1317 coap_log_warn("OSCORE: Decryption Failure, result code: %d \n",
1318 (int)pltxt_size);
1319 if (coap_request) {
1320 build_and_send_error_pdu(session,
1321 pdu,
1322 COAP_RESPONSE_CODE(400),
1323 "Decryption failed",
1324 NULL,
1325 NULL,
1326 0);
1327 oscore_roll_back_seq(rcp_ctx);
1328 goto error_no_ack;
1329 } else {
1330 coap_handle_event(session->context,
1331 COAP_EVENT_OSCORE_DECRYPTION_FAILURE,
1332 session);
1333 }
1334 goto error;
1335 }
1336
1337 assert((size_t)pltxt_size < pdu->alloc_size + pdu->max_hdr_size);
1338
1339 /* Appendix B.2 Trap */
1340 if (session->b_2_step == COAP_OSCORE_B_2_STEP_2) {
1341 /* Need to update Security Context with new (R2 || ID1) ID Context */
1342 coap_binary_t *kc =
1343 coap_new_binary(sizeof(session->oscore_r2) + cose->kid_context.length);
1344 coap_bin_const_t oscore_r2;
1345
1346 if (kc == NULL) {
1347 if (!coap_request)
1348 coap_handle_event(session->context,
1349 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1350 session);
1351 goto error;
1352 }
1353
1354 coap_prng(&session->oscore_r2, sizeof(session->oscore_r2));
1355 memcpy(kc->s, &session->oscore_r2, sizeof(session->oscore_r2));
1356 memcpy(&kc->s[sizeof(session->oscore_r2)],
1357 cose->kid_context.s,
1358 cose->kid_context.length);
1359
1360 coap_log_oscore("Appendix B.2 server step 2 (R2 || ID1)\n");
1361 oscore_update_ctx(osc_ctx, (coap_bin_const_t *)kc);
1362
1363 oscore_r2.length = sizeof(session->oscore_r2);
1364 oscore_r2.s = (const uint8_t *)&session->oscore_r2;
1365 coap_log_oscore("Appendix B.2 server step 2 plain response\n");
1366 build_and_send_error_pdu(session,
1367 pdu,
1368 COAP_RESPONSE_CODE(401),
1369 NULL,
1370 NULL,
1371 &oscore_r2,
1372 1);
1373 goto error_no_ack;
1374 }
1375 #if COAP_CLIENT_SUPPORT
1376 if (session->b_2_step == COAP_OSCORE_B_2_STEP_3) {
1377 coap_log_oscore("Appendix B.2 client step 3 (R2 || R3)\n");
1378 coap_pdu_encode_header(plain_pdu, session->proto);
1379 plain_pdu->actual_token.s = plain_pdu->token;
1380 plain_pdu->code = plain_pdu->token[0];
1381 if (plain_pdu->code != COAP_RESPONSE_CODE(401)) {
1382 coap_log_warn("OSCORE Appendix B.2: Expected 4.01 response\n");
1383 }
1384 /* Skip the options */
1385 coap_option_iterator_init(plain_pdu, &opt_iter, COAP_OPT_ALL);
1386 while ((opt = coap_option_next(&opt_iter))) {
1387 }
1388 if (opt_iter.length > 0 && opt_iter.next_option &&
1389 opt_iter.next_option[0] == COAP_PAYLOAD_START) {
1390 plain_pdu->data = &opt_iter.next_option[1];
1391 }
1392 coap_log_oscore("Inner Response PDU (plaintext)\n");
1393 coap_show_pdu(COAP_LOG_OSCORE, plain_pdu);
1394 /*
1395 * Need to update Security Context with new (R2 || R3) ID Context
1396 * and retransmit the request
1397 */
1398 coap_binary_t *kc = coap_new_binary(cose->kid_context.length + 8);
1399
1400 if (kc == NULL) {
1401 if (!coap_request)
1402 coap_handle_event(session->context,
1403 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1404 session);
1405 goto error;
1406 }
1407 memcpy(kc->s, cose->kid_context.s, cose->kid_context.length);
1408 coap_prng(&kc->s[cose->kid_context.length], 8);
1409
1410 oscore_update_ctx(osc_ctx, (coap_bin_const_t *)kc);
1411
1412 coap_cancel_all_messages(session->context,
1413 session,
1414 &pdu->actual_token);
1415 if (session->con_active)
1416 session->con_active--;
1417 coap_send_ack(session, pdu);
1418 if (sent_pdu) {
1419 coap_log_oscore("Appendix B.2 retransmit pdu\n");
1420 if (coap_retransmit_oscore_pdu(session, sent_pdu, NULL) ==
1421 COAP_INVALID_MID)
1422 goto error_no_ack;
1423 }
1424 goto error_no_ack;
1425 }
1426 #endif /* COAP_CLIENT_SUPPORT */
1427
1428 #if COAP_SERVER_SUPPORT
1429 /* Appendix B.1.2 request Trap */
1430 if (coap_request && osc_ctx->rfc8613_b_1_2) {
1431 if (rcp_ctx->initial_state == 1) {
1432 opt = coap_check_option(plain_pdu, COAP_OPTION_ECHO, &opt_iter);
1433 if (opt) {
1434 /* Verify Client is genuine */
1435 if (coap_opt_length(opt) == 8 &&
1436 memcmp(coap_opt_value(opt), rcp_ctx->echo_value, 8) == 0) {
1437 if (!oscore_validate_sender_seq(rcp_ctx, cose)) {
1438 coap_log_warn("OSCORE: Replayed or old message\n");
1439 build_and_send_error_pdu(session,
1440 pdu,
1441 COAP_RESPONSE_CODE(401),
1442 "Replay detected",
1443 NULL,
1444 NULL,
1445 0);
1446 goto error_no_ack;
1447 }
1448 } else
1449 goto error;
1450 } else {
1451 /* RFC 8163 Appendix B.1.2 */
1452 if (session->b_2_step == COAP_OSCORE_B_2_STEP_4) {
1453 session->b_2_step = COAP_OSCORE_B_2_NONE;
1454 coap_log_oscore("Appendix B.2 server finished\n");
1455 }
1456 coap_prng(rcp_ctx->echo_value, sizeof(rcp_ctx->echo_value));
1457 coap_log_oscore("Appendix B.1.2 server plain response\n");
1458 build_and_send_error_pdu(session,
1459 pdu,
1460 COAP_RESPONSE_CODE(401),
1461 NULL,
1462 rcp_ctx->echo_value,
1463 NULL,
1464 1);
1465 goto error_no_ack;
1466 }
1467 }
1468 }
1469 #endif /* COAP_SERVER_SUPPORT */
1470
1471 /*
1472 * 8.2 Step 7 / 8.4 Step 6.
1473 * Add decrypted Code, options and payload
1474 * [OSCORE option not copied across previously]
1475 */
1476
1477 /* PDU code is pseudo plain_pdu token */
1478 decrypt_pdu->code = plain_pdu->token[0];
1479
1480 /* Copy inner decrypted options across */
1481 coap_option_iterator_init(plain_pdu, &opt_iter, COAP_OPT_ALL);
1482 while ((opt = coap_option_next(&opt_iter))) {
1483 size_t len;
1484 size_t bias;
1485
1486 switch (opt_iter.number) {
1487 case COAP_OPTION_OSCORE:
1488 break;
1489 case COAP_OPTION_OBSERVE:
1490 if (!coap_request) {
1491 bias = cose->partial_iv.length > 3 ? cose->partial_iv.length - 3 : 0;
1492 len = cose->partial_iv.length > 3 ? 3 : cose->partial_iv.length;
1493 /* Make Observe option reflect last 3 bytes of partial_iv */
1494 if (!coap_add_option_internal(
1495 decrypt_pdu,
1496 opt_iter.number,
1497 len,
1498 cose->partial_iv.s ? &cose->partial_iv.s[bias] : NULL)) {
1499 coap_handle_event(session->context,
1500 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1501 session);
1502 goto error;
1503 }
1504 break;
1505 }
1506 association = oscore_find_association(session, &pdu_token);
1507 if (association) {
1508 association->is_observe = 1;
1509 association = NULL;
1510 }
1511 /* Fall Through */
1512 default:
1513 if (!coap_insert_option(decrypt_pdu,
1514 opt_iter.number,
1515 coap_opt_length(opt),
1516 coap_opt_value(opt))) {
1517 if (!coap_request)
1518 coap_handle_event(session->context,
1519 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1520 session);
1521 goto error;
1522 }
1523 break;
1524 }
1525 }
1526 /* Need to copy across any data */
1527 if (opt_iter.length > 0 && opt_iter.next_option &&
1528 opt_iter.next_option[0] == COAP_PAYLOAD_START) {
1529 plain_pdu->data = &opt_iter.next_option[1];
1530 if (!coap_add_data(decrypt_pdu,
1531 plain_pdu->used_size -
1532 (plain_pdu->data - plain_pdu->token),
1533 plain_pdu->data)) {
1534 if (!coap_request)
1535 coap_handle_event(session->context,
1536 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1537 session);
1538 goto error;
1539 }
1540 }
1541 coap_delete_pdu(plain_pdu);
1542 plain_pdu = NULL;
1543
1544 /* Make sure headers are correctly set up */
1545 if (!coap_pdu_encode_header(decrypt_pdu, session->proto)) {
1546 if (!coap_request)
1547 coap_handle_event(session->context,
1548 COAP_EVENT_OSCORE_INTERNAL_ERROR,
1549 session);
1550 goto error;
1551 }
1552
1553 if (session->b_2_step != COAP_OSCORE_B_2_NONE) {
1554 session->b_2_step = COAP_OSCORE_B_2_NONE;
1555 coap_log_oscore("Appendix B.2 client finished\n");
1556 }
1557 #if COAP_CLIENT_SUPPORT
1558 if (decrypt_pdu->code == COAP_RESPONSE_CODE(401) &&
1559 (opt = coap_check_option(decrypt_pdu, COAP_OPTION_ECHO, &opt_iter))) {
1560 /* Server is requesting Echo refresh check */
1561 coap_cancel_all_messages(session->context,
1562 session,
1563 &pdu->actual_token);
1564 if (session->con_active)
1565 session->con_active--;
1566 if (sent_pdu) {
1567 coap_send_ack(session, pdu);
1568 coap_log_debug("PDU requesting re-transmit\n");
1569 coap_show_pdu(COAP_LOG_DEBUG, decrypt_pdu);
1570 coap_log_oscore("RFC9175 retransmit pdu\n");
1571 /* Do not care if this fails */
1572 coap_retransmit_oscore_pdu(session, sent_pdu, opt);
1573 goto error_no_ack;
1574 }
1575 }
1576 #endif /* COAP_CLIENT_SUPPORT */
1577 if (association && association->is_observe == 0)
1578 oscore_delete_association(session, association);
1579 return decrypt_pdu;
1580
1581 error:
1582 coap_send_ack(session, pdu);
1583 error_no_ack:
1584 if (association && association->is_observe == 0)
1585 oscore_delete_association(session, association);
1586 coap_delete_pdu(decrypt_pdu);
1587 coap_delete_pdu(plain_pdu);
1588 return NULL;
1589 }
1590
1591 typedef enum {
1592 COAP_ENC_ASCII = 0x01,
1593 COAP_ENC_HEX = 0x02,
1594 COAP_ENC_INTEGER = 0x08,
1595 COAP_ENC_TEXT = 0x10,
1596 COAP_ENC_BOOL = 0x20,
1597 COAP_ENC_LAST
1598 } coap_oscore_coding_t;
1599
1600 #undef TEXT_MAPPING
1601 #define TEXT_MAPPING(t, v) \
1602 { { sizeof(#t)-1, (const uint8_t *)#t }, v }
1603
1604 static struct coap_oscore_encoding_t {
1605 coap_str_const_t name;
1606 coap_oscore_coding_t encoding;
1607 } oscore_encoding[] = {
1608 TEXT_MAPPING(ascii, COAP_ENC_ASCII),
1609 TEXT_MAPPING(hex, COAP_ENC_HEX),
1610 TEXT_MAPPING(integer, COAP_ENC_INTEGER),
1611 TEXT_MAPPING(text, COAP_ENC_TEXT),
1612 TEXT_MAPPING(bool, COAP_ENC_BOOL),
1613 {{0, NULL}, COAP_ENC_LAST}
1614 };
1615
1616 typedef struct {
1617 coap_oscore_coding_t encoding;
1618 const char *encoding_name;
1619 union {
1620 int value_int;
1621 coap_bin_const_t *value_bin;
1622 coap_str_const_t value_str;
1623 } u;
1624 } oscore_value_t;
1625
1626 static uint8_t
hex2char(char c)1627 hex2char(char c) {
1628 assert(isxdigit(c));
1629 if ('a' <= c && c <= 'f')
1630 return c - 'a' + 10;
1631 else if ('A' <= c && c <= 'F')
1632 return c - 'A' + 10;
1633 else
1634 return c - '0';
1635 }
1636
1637 /* Parse the hex into binary */
1638 static coap_bin_const_t *
parse_hex_bin(const char * begin,const char * end)1639 parse_hex_bin(const char *begin, const char *end) {
1640 coap_binary_t *binary = NULL;
1641 size_t i;
1642
1643 if ((end - begin) % 2 != 0)
1644 goto bad_entry;
1645 binary = coap_new_binary((end - begin) / 2);
1646 if (binary == NULL)
1647 goto bad_entry;
1648 for (i = 0; (i < (size_t)(end - begin)) && isxdigit((u_char)begin[i]) &&
1649 isxdigit((u_char)begin[i + 1]);
1650 i += 2) {
1651 binary->s[i / 2] = (hex2char(begin[i]) << 4) + hex2char(begin[i + 1]);
1652 }
1653 if (i != (size_t)(end - begin))
1654 goto bad_entry;
1655 return (coap_bin_const_t *)binary;
1656
1657 bad_entry:
1658 coap_delete_binary(binary);
1659 return NULL;
1660 }
1661
1662 /*
1663 * Break up each OSCORE Configuration line entry into the 3 parts which
1664 * are comma separated
1665 *
1666 * keyword,encoding,value
1667 */
1668 static int
get_split_entry(const char ** start,size_t size,coap_str_const_t * keyword,oscore_value_t * value)1669 get_split_entry(const char **start,
1670 size_t size,
1671 coap_str_const_t *keyword,
1672 oscore_value_t *value) {
1673 const char *begin = *start;
1674 const char *end;
1675 const char *kend;
1676 const char *split;
1677 size_t i;
1678
1679 retry:
1680 kend = end = memchr(begin, '\n', size);
1681 if (end == NULL)
1682 return 0;
1683
1684 /* Track beginning of next line */
1685 *start = end + 1;
1686 if (end > begin && end[-1] == '\r')
1687 end--;
1688
1689 if (begin[0] == '#' || (end - begin) == 0) {
1690 /* Skip comment / blank line */
1691 size -= kend - begin + 1;
1692 begin = *start;
1693 goto retry;
1694 }
1695
1696 /* Get in the keyword */
1697 split = memchr(begin, ',', end - begin);
1698 if (split == NULL)
1699 goto bad_entry;
1700
1701 keyword->s = (const uint8_t *)begin;
1702 keyword->length = split - begin;
1703
1704 begin = split + 1;
1705 if ((end - begin) == 0)
1706 goto bad_entry;
1707 /* Get in the encoding */
1708 split = memchr(begin, ',', end - begin);
1709 if (split == NULL)
1710 goto bad_entry;
1711
1712 for (i = 0; oscore_encoding[i].name.s; i++) {
1713 coap_str_const_t temp = { split - begin, (const uint8_t *)begin };
1714
1715 if (coap_string_equal(&temp, &oscore_encoding[i].name)) {
1716 value->encoding = oscore_encoding[i].encoding;
1717 value->encoding_name = (const char *)oscore_encoding[i].name.s;
1718 break;
1719 }
1720 }
1721 if (oscore_encoding[i].name.s == NULL)
1722 goto bad_entry;
1723
1724 begin = split + 1;
1725 if ((end - begin) == 0)
1726 goto bad_entry;
1727 /* Get in the keyword's value */
1728 if (begin[0] == '"') {
1729 split = memchr(&begin[1], '"', end - split - 1);
1730 if (split == NULL)
1731 goto bad_entry;
1732 end = split;
1733 begin++;
1734 }
1735 switch (value->encoding) {
1736 case COAP_ENC_ASCII:
1737 value->u.value_bin =
1738 coap_new_bin_const((const uint8_t *)begin, end - begin);
1739 break;
1740 case COAP_ENC_HEX:
1741 /* Parse the hex into binary */
1742 value->u.value_bin = parse_hex_bin(begin, end);
1743 if (value->u.value_bin == NULL)
1744 goto bad_entry;
1745 break;
1746 case COAP_ENC_INTEGER:
1747 value->u.value_int = atoi(begin);
1748 break;
1749 case COAP_ENC_TEXT:
1750 value->u.value_str.s = (const uint8_t *)begin;
1751 value->u.value_str.length = end - begin;
1752 break;
1753 case COAP_ENC_BOOL:
1754 if (memcmp("true", begin, end - begin) == 0)
1755 value->u.value_int = 1;
1756 else if (memcmp("false", begin, end - begin) == 0)
1757 value->u.value_int = 0;
1758 else
1759 goto bad_entry;
1760 break;
1761 case COAP_ENC_LAST:
1762 default:
1763 goto bad_entry;
1764 }
1765 return 1;
1766
1767 bad_entry:
1768 coap_log_warn("oscore_conf: Unrecognized configuration entry '%.*s'\n",
1769 (int)(end - begin),
1770 begin);
1771 return 0;
1772 }
1773
1774 #undef CONFIG_ENTRY
1775 #define CONFIG_ENTRY(n, e, t) \
1776 { { sizeof(#n)-1, (const uint8_t *)#n }, e, \
1777 offsetof(coap_oscore_conf_t, n), t }
1778
1779 typedef struct oscore_text_mapping_t {
1780 coap_str_const_t text;
1781 int value;
1782 } oscore_text_mapping_t;
1783
1784 /* Naming as per https://www.iana.org/assignments/cose/cose.xhtml#algorithms */
1785 static oscore_text_mapping_t text_aead_alg[] = {
1786 TEXT_MAPPING(AES-CCM-16-64-128, COSE_ALGORITHM_AES_CCM_16_64_128),
1787 TEXT_MAPPING(AES-CCM-16-64-256, COSE_ALGORITHM_AES_CCM_16_64_256),
1788 {{0, NULL}, 0}
1789 };
1790
1791 static oscore_text_mapping_t text_hkdf_alg[] = {
1792 TEXT_MAPPING(direct+HKDF-SHA-256, COSE_HKDF_ALG_HKDF_SHA_256),
1793 {{0, NULL}, 0}
1794 };
1795
1796 static struct oscore_config_t {
1797 coap_str_const_t str_keyword;
1798 coap_oscore_coding_t encoding;
1799 size_t offset;
1800 oscore_text_mapping_t *text_mapping;
1801 } oscore_config[] = {
1802 CONFIG_ENTRY(master_secret, COAP_ENC_HEX | COAP_ENC_ASCII, NULL),
1803 CONFIG_ENTRY(master_salt, COAP_ENC_HEX | COAP_ENC_ASCII, NULL),
1804 CONFIG_ENTRY(sender_id, COAP_ENC_HEX | COAP_ENC_ASCII, NULL),
1805 CONFIG_ENTRY(id_context, COAP_ENC_HEX | COAP_ENC_ASCII, NULL),
1806 CONFIG_ENTRY(recipient_id, COAP_ENC_HEX | COAP_ENC_ASCII, NULL),
1807 CONFIG_ENTRY(replay_window, COAP_ENC_INTEGER, NULL),
1808 CONFIG_ENTRY(ssn_freq, COAP_ENC_INTEGER, NULL),
1809 CONFIG_ENTRY(aead_alg, COAP_ENC_INTEGER | COAP_ENC_TEXT, text_aead_alg),
1810 CONFIG_ENTRY(hkdf_alg, COAP_ENC_INTEGER | COAP_ENC_TEXT, text_hkdf_alg),
1811 CONFIG_ENTRY(rfc8613_b_1_2, COAP_ENC_BOOL, NULL),
1812 CONFIG_ENTRY(rfc8613_b_2, COAP_ENC_BOOL, NULL),
1813 CONFIG_ENTRY(break_sender_key, COAP_ENC_BOOL, NULL),
1814 CONFIG_ENTRY(break_recipient_key, COAP_ENC_BOOL, NULL),
1815 };
1816
1817 int
coap_delete_oscore_conf(coap_oscore_conf_t * oscore_conf)1818 coap_delete_oscore_conf(coap_oscore_conf_t *oscore_conf) {
1819 uint32_t i;
1820
1821 if (oscore_conf == NULL)
1822 return 0;
1823
1824 coap_delete_bin_const(oscore_conf->master_secret);
1825 coap_delete_bin_const(oscore_conf->master_salt);
1826 coap_delete_bin_const(oscore_conf->id_context);
1827 coap_delete_bin_const(oscore_conf->sender_id);
1828 for (i = 0; i < oscore_conf->recipient_id_count; i++) {
1829 coap_delete_bin_const(oscore_conf->recipient_id[i]);
1830 }
1831 coap_free_type(COAP_STRING, oscore_conf->recipient_id);
1832 coap_free_type(COAP_STRING, oscore_conf);
1833 return 1;
1834 }
1835
1836 static coap_oscore_conf_t *
coap_parse_oscore_conf_mem(coap_str_const_t conf_mem)1837 coap_parse_oscore_conf_mem(coap_str_const_t conf_mem) {
1838 const char *start = (const char *)conf_mem.s;
1839 const char *end = start + conf_mem.length;
1840 coap_str_const_t keyword;
1841 oscore_value_t value;
1842 coap_oscore_conf_t *oscore_conf;
1843
1844 oscore_conf = coap_malloc_type(COAP_STRING, sizeof(coap_oscore_conf_t));
1845 if (oscore_conf == NULL)
1846 return NULL;
1847 memset(oscore_conf, 0, sizeof(coap_oscore_conf_t));
1848
1849 memset(&value, 0, sizeof(value));
1850 /* Preset with defaults */
1851 oscore_conf->replay_window = COAP_OSCORE_DEFAULT_REPLAY_WINDOW;
1852 oscore_conf->ssn_freq = 1;
1853 oscore_conf->aead_alg = COSE_ALGORITHM_AES_CCM_16_64_128;
1854 oscore_conf->hkdf_alg = COSE_HKDF_ALG_HKDF_SHA_256;
1855 oscore_conf->rfc8613_b_1_2 = 1;
1856 oscore_conf->rfc8613_b_2 = 0;
1857 oscore_conf->break_sender_key = 0;
1858 oscore_conf->break_recipient_key = 0;
1859
1860 while (end > start &&
1861 get_split_entry(&start, end - start, &keyword, &value)) {
1862 size_t i;
1863 size_t j;
1864
1865 for (i = 0; i < sizeof(oscore_config) / sizeof(oscore_config[0]); i++) {
1866 if (coap_string_equal(&oscore_config[i].str_keyword, &keyword) != 0 &&
1867 value.encoding & oscore_config[i].encoding) {
1868 if (coap_string_equal(coap_make_str_const("recipient_id"), &keyword)) {
1869 if (value.u.value_bin->length > 7) {
1870 coap_log_warn("oscore_conf: Maximum size of recipient_id is 7 bytes\n");
1871 goto error_free_value_bin;
1872 }
1873 /* Special case as there are potentially multiple entries */
1874 oscore_conf->recipient_id =
1875 coap_realloc_type(COAP_STRING,
1876 oscore_conf->recipient_id,
1877 sizeof(oscore_conf->recipient_id[0]) *
1878 (oscore_conf->recipient_id_count + 1));
1879 if (oscore_conf->recipient_id == NULL) {
1880 goto error_free_value_bin;
1881 }
1882 oscore_conf->recipient_id[oscore_conf->recipient_id_count++] =
1883 value.u.value_bin;
1884 } else {
1885 coap_bin_const_t *unused_check;
1886
1887 switch (value.encoding) {
1888 case COAP_ENC_HEX:
1889 case COAP_ENC_ASCII:
1890 memcpy(&unused_check,
1891 &(((char *)oscore_conf)[oscore_config[i].offset]),
1892 sizeof(unused_check));
1893 if (unused_check != NULL) {
1894 coap_log_warn("oscore_conf: Keyword '%.*s' duplicated\n",
1895 (int)keyword.length,
1896 (const char *)keyword.s);
1897 goto error;
1898 }
1899 memcpy(&(((char *)oscore_conf)[oscore_config[i].offset]),
1900 &value.u.value_bin,
1901 sizeof(value.u.value_bin));
1902 break;
1903 case COAP_ENC_INTEGER:
1904 case COAP_ENC_BOOL:
1905 memcpy(&(((char *)oscore_conf)[oscore_config[i].offset]),
1906 &value.u.value_int,
1907 sizeof(value.u.value_int));
1908 break;
1909 case COAP_ENC_TEXT:
1910 for (j = 0; oscore_config[i].text_mapping[j].text.s != NULL; j++) {
1911 if (coap_string_equal(&value.u.value_str,
1912 &oscore_config[i].text_mapping[j].text)) {
1913 memcpy(&(((char *)oscore_conf)[oscore_config[i].offset]),
1914 &oscore_config[i].text_mapping[j].value,
1915 sizeof(oscore_config[i].text_mapping[j].value));
1916 break;
1917 }
1918 }
1919 if (oscore_config[i].text_mapping[j].text.s == NULL) {
1920 coap_log_warn("oscore_conf: Keyword '%.*s': value '%.*s' unknown\n",
1921 (int)keyword.length,
1922 (const char *)keyword.s,
1923 (int)value.u.value_str.length,
1924 (const char *)value.u.value_str.s);
1925 goto error;
1926 }
1927 break;
1928 case COAP_ENC_LAST:
1929 default:
1930 assert(0);
1931 break;
1932 }
1933 }
1934 break;
1935 }
1936 }
1937 if (i == sizeof(oscore_config) / sizeof(oscore_config[0])) {
1938 coap_log_warn("oscore_conf: Keyword '%.*s', type '%s' unknown\n",
1939 (int)keyword.length,
1940 (const char *)keyword.s,
1941 value.encoding_name);
1942 if (value.encoding == COAP_ENC_HEX || value.encoding == COAP_ENC_ASCII)
1943 coap_delete_bin_const(value.u.value_bin);
1944 goto error;
1945 }
1946 }
1947 if (!oscore_conf->master_secret) {
1948 coap_log_warn("oscore_conf: master_secret not defined\n");
1949 goto error;
1950 }
1951 if (!oscore_conf->sender_id) {
1952 coap_log_warn("oscore_conf: sender_id not defined\n");
1953 goto error;
1954 }
1955 if (oscore_conf->sender_id->length > 7) {
1956 coap_log_warn("oscore_conf: Maximum size of sender_id is 7 bytes\n");
1957 goto error;
1958 }
1959 if (oscore_conf->recipient_id && oscore_conf->recipient_id[0]->length > 7) {
1960 coap_log_warn("oscore_conf: Maximum size of recipient_id is 7 bytes\n");
1961 goto error;
1962 }
1963 return oscore_conf;
1964
1965 error_free_value_bin:
1966 coap_delete_bin_const(value.u.value_bin);
1967 error:
1968 coap_delete_oscore_conf(oscore_conf);
1969 return NULL;
1970 }
1971
1972 static oscore_ctx_t *
coap_oscore_init(coap_context_t * c_context,coap_oscore_conf_t * oscore_conf)1973 coap_oscore_init(coap_context_t *c_context, coap_oscore_conf_t *oscore_conf) {
1974 oscore_ctx_t *osc_ctx = NULL;
1975
1976 if (!coap_crypto_check_cipher_alg(oscore_conf->aead_alg)) {
1977 coap_log_warn("COSE: Cipher Algorithm %d not supported\n",
1978 oscore_conf->aead_alg);
1979 goto error;
1980 }
1981 if (!coap_crypto_check_hkdf_alg(oscore_conf->hkdf_alg)) {
1982 coap_log_warn("COSE: HKDF Algorithm %d not supported\n",
1983 oscore_conf->hkdf_alg);
1984 goto error;
1985 }
1986
1987 osc_ctx = oscore_derive_ctx(c_context, oscore_conf);
1988 if (!osc_ctx) {
1989 coap_log_crit("OSCORE: Could not create Security Context!\n");
1990 goto error;
1991 }
1992
1993 /* Free off the recipient_id array */
1994 coap_free_type(COAP_STRING, oscore_conf->recipient_id);
1995 oscore_conf->recipient_id = NULL;
1996
1997 /* As all is stored in osc_ctx, oscore_conf is no longer needed */
1998 coap_free_type(COAP_STRING, oscore_conf);
1999
2000 /* return default first context */
2001 return osc_ctx;
2002
2003 error:
2004 /* Remove from linked chain */
2005 oscore_remove_context(c_context, osc_ctx);
2006
2007 coap_delete_oscore_conf(oscore_conf);
2008 return NULL;
2009 }
2010
2011 void
coap_delete_all_oscore(coap_context_t * c_context)2012 coap_delete_all_oscore(coap_context_t *c_context) {
2013 oscore_free_contexts(c_context);
2014 }
2015
2016 void
coap_delete_oscore_associations(coap_session_t * session)2017 coap_delete_oscore_associations(coap_session_t *session) {
2018 oscore_delete_server_associations(session);
2019 }
2020
2021 coap_oscore_conf_t *
coap_new_oscore_conf(coap_str_const_t conf_mem,coap_oscore_save_seq_num_t save_seq_num_func,void * save_seq_num_func_param,uint64_t start_seq_num)2022 coap_new_oscore_conf(coap_str_const_t conf_mem,
2023 coap_oscore_save_seq_num_t save_seq_num_func,
2024 void *save_seq_num_func_param,
2025 uint64_t start_seq_num) {
2026 coap_oscore_conf_t *oscore_conf = coap_parse_oscore_conf_mem(conf_mem);
2027
2028 if (oscore_conf == NULL)
2029 return NULL;
2030
2031 oscore_conf->save_seq_num_func = save_seq_num_func;
2032 oscore_conf->save_seq_num_func_param = save_seq_num_func_param;
2033 oscore_conf->start_seq_num = start_seq_num;
2034 coap_log_oscore("Start Seq no %" PRIu64 "\n", start_seq_num);
2035 return oscore_conf;
2036 }
2037
2038 /*
2039 * Compute the size of the potential OSCORE overhead
2040 */
2041 size_t
coap_oscore_overhead(coap_session_t * session,coap_pdu_t * pdu)2042 coap_oscore_overhead(coap_session_t *session, coap_pdu_t *pdu) {
2043 size_t overhead = 0;
2044 oscore_recipient_ctx_t *rcp_ctx = session->recipient_ctx;
2045 oscore_ctx_t *osc_ctx = rcp_ctx ? rcp_ctx->osc_ctx : NULL;
2046 coap_opt_iterator_t opt_iter;
2047 coap_opt_t *option;
2048
2049 if (osc_ctx == NULL)
2050 return 0;
2051
2052 /* Protected code held in inner PDU as token */
2053 overhead += 1;
2054
2055 /* Observe option (creates inner and outer */
2056 option = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter);
2057 if (option) {
2058 /* Assume delta is small */
2059 overhead += 2 + coap_opt_length(option);
2060 }
2061
2062 /* Proxy URI option Split - covered by coap_rebuild_pdu_for_proxy () */
2063
2064 /* OSCORE option */
2065 /* Option header */
2066 overhead += 1 +
2067 /* Partial IV (64 bits max)*/
2068 8 +
2069 /* kid context */
2070 (osc_ctx->id_context ? osc_ctx->id_context->length : 0) +
2071 /* kid */
2072 osc_ctx->sender_context->sender_id->length;
2073
2074 /* AAD overhead */
2075 overhead += AES_CCM_TAG;
2076
2077 /* End of options marker */
2078 overhead += 1;
2079
2080 return overhead;
2081 }
2082
2083 int
coap_new_oscore_recipient(coap_context_t * context,coap_bin_const_t * recipient_id)2084 coap_new_oscore_recipient(coap_context_t *context,
2085 coap_bin_const_t *recipient_id) {
2086 if (context->p_osc_ctx == NULL)
2087 return 0;
2088 if (oscore_add_recipient(context->p_osc_ctx, recipient_id, 0) == NULL)
2089 return 0;
2090 return 1;
2091 }
2092
2093 int
coap_delete_oscore_recipient(coap_context_t * context,coap_bin_const_t * recipient_id)2094 coap_delete_oscore_recipient(coap_context_t *context,
2095 coap_bin_const_t *recipient_id) {
2096 if (context->p_osc_ctx == NULL)
2097 return 0;
2098 return oscore_delete_recipient(context->p_osc_ctx, recipient_id);
2099 }
2100
2101 /** @} */
2102
2103 #else /* !COAP_OSCORE_SUPPORT */
2104 int
coap_oscore_is_supported(void)2105 coap_oscore_is_supported(void) {
2106 return 0;
2107 }
2108
2109 coap_session_t *
coap_new_client_session_oscore(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_oscore_conf_t * oscore_conf)2110 coap_new_client_session_oscore(coap_context_t *ctx,
2111 const coap_address_t *local_if,
2112 const coap_address_t *server,
2113 coap_proto_t proto,
2114 coap_oscore_conf_t *oscore_conf) {
2115 (void)ctx;
2116 (void)local_if;
2117 (void)server;
2118 (void)proto;
2119 (void)oscore_conf;
2120 return NULL;
2121 }
2122
2123 coap_session_t *
coap_new_client_session_oscore_psk(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_dtls_cpsk_t * psk_data,coap_oscore_conf_t * oscore_conf)2124 coap_new_client_session_oscore_psk(coap_context_t *ctx,
2125 const coap_address_t *local_if,
2126 const coap_address_t *server,
2127 coap_proto_t proto,
2128 coap_dtls_cpsk_t *psk_data,
2129 coap_oscore_conf_t *oscore_conf) {
2130 (void)ctx;
2131 (void)local_if;
2132 (void)server;
2133 (void)proto;
2134 (void)psk_data;
2135 (void)oscore_conf;
2136 return NULL;
2137 }
2138
2139 coap_session_t *
coap_new_client_session_oscore_pki(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_dtls_pki_t * pki_data,coap_oscore_conf_t * oscore_conf)2140 coap_new_client_session_oscore_pki(coap_context_t *ctx,
2141 const coap_address_t *local_if,
2142 const coap_address_t *server,
2143 coap_proto_t proto,
2144 coap_dtls_pki_t *pki_data,
2145 coap_oscore_conf_t *oscore_conf) {
2146 (void)ctx;
2147 (void)local_if;
2148 (void)server;
2149 (void)proto;
2150 (void)pki_data;
2151 (void)oscore_conf;
2152 return NULL;
2153 }
2154
2155 int
coap_context_oscore_server(coap_context_t * context,coap_oscore_conf_t * oscore_conf)2156 coap_context_oscore_server(coap_context_t *context,
2157 coap_oscore_conf_t *oscore_conf) {
2158 (void)context;
2159 (void)oscore_conf;
2160 return 0;
2161 }
2162
2163 coap_oscore_conf_t *
coap_new_oscore_conf(coap_str_const_t conf_mem,coap_oscore_save_seq_num_t save_seq_num_func,void * save_seq_num_func_param,uint64_t start_seq_num)2164 coap_new_oscore_conf(coap_str_const_t conf_mem,
2165 coap_oscore_save_seq_num_t save_seq_num_func,
2166 void *save_seq_num_func_param,
2167 uint64_t start_seq_num) {
2168 (void)conf_mem;
2169 (void)save_seq_num_func;
2170 (void)save_seq_num_func_param;
2171 (void)start_seq_num;
2172 return NULL;
2173 }
2174
2175 int
coap_delete_oscore_conf(coap_oscore_conf_t * oscore_conf)2176 coap_delete_oscore_conf(coap_oscore_conf_t *oscore_conf) {
2177 (void)oscore_conf;
2178 return 0;
2179 }
2180
2181 int
coap_new_oscore_recipient(coap_context_t * context,coap_bin_const_t * recipient_id)2182 coap_new_oscore_recipient(coap_context_t *context,
2183 coap_bin_const_t *recipient_id) {
2184 (void)context;
2185 (void)recipient_id;
2186 return 0;
2187 }
2188
2189 int
coap_delete_oscore_recipient(coap_context_t * context,coap_bin_const_t * recipient_id)2190 coap_delete_oscore_recipient(coap_context_t *context,
2191 coap_bin_const_t *recipient_id) {
2192 (void)context;
2193 (void)recipient_id;
2194 return 0;
2195 }
2196
2197 #endif /* !COAP_OSCORE_SUPPORT */
2198