• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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