xref: /third_party/libcoap/src/oscore/oscore.c (revision c87c5fba)
1/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3/*
4 * Copyright (c) 2018, SICS, RISE AB
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the Institute nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33/**
34 * @file oscore.c
35 * @brief An implementation of the Object Security for Constrained RESTful
36 * Enviornments (RFC 8613).
37 *
38 * \author Martin Gunnarsson  <martin.gunnarsson@ri.se>
39 * adapted to libcoap and major rewrite
40 *     Peter van der Stok <consultancy@vanderstok.org>
41 *     on request of Fairhair alliance
42 * adapted for libcoap integration
43 *      Jon Shallow <supjps-libcoap@jpshallow.com>
44 *
45 */
46
47#include "coap3/coap_internal.h"
48
49/* oscore_cs_params
50 * returns cbor array [[param_type], [paramtype, param]]
51 */
52uint8_t *
53oscore_cs_params(int8_t param, int8_t param_type, size_t *len) {
54  uint8_t buf[50];
55  size_t rem_size = sizeof(buf);
56  uint8_t *pt = buf;
57
58  *len = 0;
59  *len += oscore_cbor_put_array(&pt, &rem_size, 2);
60  *len += oscore_cbor_put_array(&pt, &rem_size, 1);
61  *len += oscore_cbor_put_number(&pt, &rem_size, param_type);
62  *len += oscore_cbor_put_array(&pt, &rem_size, 2);
63  *len += oscore_cbor_put_number(&pt, &rem_size, param_type);
64  *len += oscore_cbor_put_number(&pt, &rem_size, param);
65  uint8_t *result = coap_malloc_type(COAP_STRING, *len);
66  memcpy(result, buf, *len);
67  return result;
68}
69
70/* oscore_cs_key_params
71 * returns cbor array [paramtype, param]
72 */
73uint8_t *
74oscore_cs_key_params(cose_curve_t param, int8_t param_type, size_t *len) {
75  uint8_t buf[50];
76  size_t rem_size = sizeof(buf);
77  uint8_t *pt = buf;
78
79  *len = 0;
80  *len += oscore_cbor_put_array(&pt, &rem_size, 2);
81  *len += oscore_cbor_put_number(&pt, &rem_size, param_type);
82  *len += oscore_cbor_put_number(&pt, &rem_size, param);
83  uint8_t *result = coap_malloc_type(COAP_STRING, *len);
84  memcpy(result, buf, *len);
85  return result;
86}
87
88/*
89 * Build the CBOR for external_aad
90 *
91 * external_aad = bstr .cbor aad_array
92 *
93 * No group mode
94 * aad_array = [
95 *   oscore_version : uint,
96 *   algorithms : [ alg_aead : int / tstr ],
97 *   request_kid : bstr,
98 *   request_piv : bstr,
99 *   options : bstr,
100 * ]
101 *
102 * Group mode
103 * aad_array = [
104 *   oscore_version : uint,
105 *   algorithms : [alg_aead : int / tstr / null,
106 *                 alg_signature_enc : int / tstr / null,
107 *                 alg_signature : int / tstr / null,
108 *                 alg_pairwise_key_agreement : int / tstr / null],
109 *   request_kid : bstr,
110 *   request_piv : bstr,
111 *   options : bstr,
112 *   request_kid_context : bstr,
113 *   OSCORE_option: bstr,
114 *   sender_public_key: bstr,        (initiator's key)
115 *   gm_public_key: bstr / null
116 * ]
117 */
118size_t
119oscore_prepare_e_aad(oscore_ctx_t *ctx,
120                     cose_encrypt0_t *cose,
121                     const uint8_t *oscore_option,
122                     size_t oscore_option_len,
123                     coap_bin_const_t *sender_public_key,
124                     uint8_t *external_aad_ptr,
125                     size_t external_aad_size) {
126  size_t external_aad_len = 0;
127  size_t rem_size = external_aad_size;
128
129  (void)oscore_option;
130  (void)oscore_option_len;
131  (void)sender_public_key;
132
133  if (ctx->mode != OSCORE_MODE_SINGLE)
134    external_aad_len += oscore_cbor_put_array(&external_aad_ptr, &rem_size, 9);
135  else
136    external_aad_len += oscore_cbor_put_array(&external_aad_ptr, &rem_size, 5);
137
138  /* oscore_version, always "1" */
139  external_aad_len += oscore_cbor_put_unsigned(&external_aad_ptr, &rem_size, 1);
140
141  if (ctx->mode == OSCORE_MODE_SINGLE) {
142    /* Algoritms array with one item*/
143    external_aad_len += oscore_cbor_put_array(&external_aad_ptr, &rem_size, 1);
144    /* Encryption Algorithm   */
145    external_aad_len +=
146        oscore_cbor_put_number(&external_aad_ptr, &rem_size, ctx->aead_alg);
147  }
148  /* request_kid */
149  external_aad_len += oscore_cbor_put_bytes(&external_aad_ptr,
150                                            &rem_size,
151                                            cose->key_id.s,
152                                            cose->key_id.length);
153  /* request_piv */
154  external_aad_len += oscore_cbor_put_bytes(&external_aad_ptr,
155                                            &rem_size,
156                                            cose->partial_iv.s,
157                                            cose->partial_iv.length);
158  /* options */
159  /* Put integrity protected options, at present there are none. */
160  external_aad_len +=
161      oscore_cbor_put_bytes(&external_aad_ptr, &rem_size, NULL, 0);
162
163  return external_aad_len;
164}
165
166/*
167 * oscore_encode_option_value
168 */
169size_t
170oscore_encode_option_value(uint8_t *option_buffer,
171                           size_t option_buf_len,
172                           cose_encrypt0_t *cose,
173                           uint8_t group_flag,
174                           uint8_t appendix_b_2) {
175  size_t offset = 1;
176  size_t rem_space = option_buf_len;
177
178  (void)group_flag;
179  if (cose->partial_iv.length > 5) {
180    return 0;
181  }
182  option_buffer[0] = 0;
183
184  if (cose->partial_iv.length > 0 && cose->partial_iv.length <= 5 &&
185      cose->partial_iv.s != NULL) {
186    option_buffer[0] |= (0x07 & cose->partial_iv.length);
187    memcpy(&(option_buffer[offset]),
188           cose->partial_iv.s,
189           cose->partial_iv.length);
190    offset += cose->partial_iv.length;
191    assert(rem_space > cose->partial_iv.length);
192    rem_space -= cose->partial_iv.length;
193  }
194
195  if (cose->kid_context.length > 0 && cose->kid_context.s != NULL) {
196    if (appendix_b_2) {
197      /* Need to CBOR wrap kid_context - yuk! */
198      uint8_t *ptr = &option_buffer[offset+1];
199
200      option_buffer[0] |= 0x10;
201      option_buffer[offset] = (uint8_t)oscore_cbor_put_bytes(&ptr, &rem_space,
202                                                             cose->kid_context.s,
203                                                             cose->kid_context.length);
204      offset += option_buffer[offset] + 1;
205    } else {
206      option_buffer[0] |= 0x10;
207      option_buffer[offset] = (uint8_t)cose->kid_context.length;
208      offset++;
209      memcpy(&(option_buffer[offset]),
210             cose->kid_context.s,
211             (uint8_t)cose->kid_context.length);
212      offset += cose->kid_context.length;
213      assert(rem_space > cose->kid_context.length);
214      rem_space -= cose->kid_context.length;
215    }
216  }
217
218  if (cose->key_id.s != NULL) {
219    option_buffer[0] |= 0x08;
220    if (cose->key_id.length) {
221      memcpy(&(option_buffer[offset]), cose->key_id.s, cose->key_id.length);
222      offset += cose->key_id.length;
223      assert(rem_space > cose->key_id.length);
224      rem_space -= cose->key_id.length;
225    }
226  }
227
228  if (offset == 1 && option_buffer[0] == 0) {
229    /* If option_value is 0x00 it should be empty. */
230    offset = 0;
231  }
232  assert(offset <= option_buf_len);
233  cose->oscore_option.s = option_buffer;
234  cose->oscore_option.length = offset;
235  return offset;
236}
237
238/*
239 * oscore_decode_option_value
240 * error: return 0
241 * OK: return 1
242 *
243 * Basic assupmption is that all is preset to 0 or NULL on entry
244 */
245int
246oscore_decode_option_value(const uint8_t *opt_value,
247                           size_t option_len,
248                           cose_encrypt0_t *cose) {
249  uint8_t partial_iv_len = (opt_value[0] & 0x07);
250  size_t offset = 1;
251
252  cose->oscore_option.s = opt_value;
253  cose->oscore_option.length = option_len;
254
255  if (option_len == 0)
256    return 1; /* empty option */
257
258  if (option_len > 255 || partial_iv_len == 6 || partial_iv_len == 7 ||
259      (opt_value[0] & 0xC0) != 0) {
260    return 0;
261  }
262
263  if ((opt_value[0] & 0x20) != 0) {
264    return 0;
265  }
266
267  if (partial_iv_len != 0) {
268    coap_bin_const_t partial_iv;
269    if (offset + partial_iv_len > option_len) {
270      return 0;
271    }
272    partial_iv.s = &(opt_value[offset]);
273    partial_iv.length = partial_iv_len;
274    cose_encrypt0_set_partial_iv(cose, &partial_iv);
275    offset += partial_iv_len;
276  }
277
278  if ((opt_value[0] & 0x10) != 0) {
279    coap_bin_const_t kid_context;
280
281    if (offset >= option_len)
282      return 0;
283    kid_context.length = opt_value[offset];
284    offset++;
285    if (offset + kid_context.length > option_len) {
286      return 0;
287    }
288    kid_context.s = &(opt_value[offset]);
289    cose_encrypt0_set_kid_context(cose, &kid_context);
290    offset = offset + kid_context.length;
291  }
292
293  if ((opt_value[0] & 0x08) != 0) {
294    coap_bin_const_t key_id;
295
296    key_id.length = option_len - offset;
297    if ((int)key_id.length < 0) {
298      return 0;
299    }
300    key_id.s = &(opt_value[offset]);
301    cose_encrypt0_set_key_id(cose, &key_id);
302  }
303  return 1;
304}
305
306/*
307 * oscore_prepare_aad
308 *
309 * Creates and sets External AAD for encryption
310 */
311size_t
312oscore_prepare_aad(const uint8_t *external_aad_buffer,
313                   size_t external_aad_len,
314                   uint8_t *aad_buffer,
315                   size_t aad_size) {
316  size_t ret = 0;
317  size_t rem_size = aad_size;
318  char encrypt0[] = "Encrypt0";
319
320  (void)aad_size; /* TODO */
321  /* Creating the AAD */
322  ret += oscore_cbor_put_array(&aad_buffer, &rem_size, 3);
323  /* 1. "Encrypt0" */
324  ret +=
325      oscore_cbor_put_text(&aad_buffer, &rem_size, encrypt0, strlen(encrypt0));
326  /* 2. Empty h'' entry */
327  ret += oscore_cbor_put_bytes(&aad_buffer, &rem_size, NULL, 0);
328  /* 3. External AAD */
329  ret += oscore_cbor_put_bytes(&aad_buffer,
330                               &rem_size,
331                               external_aad_buffer,
332                               external_aad_len);
333
334  return ret;
335}
336
337/*
338 * oscore_generate_nonce
339 *
340 * Creates Nonce
341 */
342void
343oscore_generate_nonce(cose_encrypt0_t *ptr,
344                      oscore_ctx_t *ctx,
345                      uint8_t *buffer,
346                      uint8_t size) {
347  memset(buffer, 0, size);
348  buffer[0] = (uint8_t)(ptr->key_id.length);
349  memcpy(&(buffer[((size - 5) - ptr->key_id.length)]),
350         ptr->key_id.s,
351         ptr->key_id.length);
352  memcpy(&(buffer[size - ptr->partial_iv.length]),
353         ptr->partial_iv.s,
354         ptr->partial_iv.length);
355  for (int i = 0; i < size; i++) {
356    buffer[i] = buffer[i] ^ (uint8_t)ctx->common_iv->s[i];
357  }
358}
359
360/*
361 * oscore_validate_sender_seq
362 *
363 * Return 1 if OK, 0 otherwise
364 */
365uint8_t
366oscore_validate_sender_seq(oscore_recipient_ctx_t *ctx, cose_encrypt0_t *cose) {
367  uint64_t incoming_seq =
368      coap_decode_var_bytes8(cose->partial_iv.s, cose->partial_iv.length);
369
370  if (incoming_seq >= OSCORE_SEQ_MAX) {
371    coap_log_warn("OSCORE Replay protection, SEQ larger than SEQ_MAX.\n");
372    return 0;
373  }
374
375  ctx->rollback_last_seq = ctx->last_seq;
376  ctx->rollback_sliding_window = ctx->sliding_window;
377
378  /* Special case since we do not use unsigned int for seq */
379  if (ctx->initial_state == 1) {
380    ctx->initial_state = 0;
381    /* bitfield. B0 biggest seq seen.  B1 seq-1 seen, B2 seq-2 seen etc. */
382    ctx->sliding_window = 1;
383    ctx->last_seq = incoming_seq;
384  } else if (incoming_seq > ctx->last_seq) {
385    /* Update the replay window */
386    uint64_t shift = incoming_seq - ctx->last_seq;
387    ctx->sliding_window = ctx->sliding_window << shift;
388    /* bitfield. B0 biggest seq seen.  B1 seq-1 seen, B2 seq-2 seen etc. */
389    ctx->sliding_window |= 1;
390    ctx->last_seq = incoming_seq;
391  } else if (incoming_seq == ctx->last_seq) {
392    coap_log_warn("OSCORE: Replay protection, replayed SEQ (%" PRIu64 ")\n",
393                  incoming_seq);
394    return 0;
395  } else { /* incoming_seq < last_seq */
396    uint64_t shift = ctx->last_seq - incoming_seq - 1;
397    uint64_t pattern;
398
399    if (shift > ctx->osc_ctx->replay_window_size || shift > 63) {
400      coap_log_warn("OSCORE: Replay protection, SEQ outside of replay window (%"
401                    PRIu64 " %" PRIu64 ")\n",
402                    ctx->last_seq,
403                    incoming_seq);
404      return 0;
405    }
406    /* seq + replay_window_size > last_seq */
407    pattern = 1ULL << shift;
408    if (ctx->sliding_window & pattern) {
409      coap_log_warn("OSCORE: Replay protection, replayed SEQ (%" PRIu64 ")\n",
410                    incoming_seq);
411      return 0;
412    }
413    /* bitfield. B0 biggest seq seen.  B1 seq-1 seen, B2 seq-2 seen etc. */
414    ctx->sliding_window |= pattern;
415  }
416  coap_log_oscore("OSCORE: window 0x%" PRIx64 " seq-B0 %" PRIu64 " SEQ %"
417                  PRIu64 "\n",
418                  ctx->sliding_window,
419                  ctx->last_seq,
420                  incoming_seq);
421  return 1;
422}
423
424/*
425 * oscore_increment_sender_seq
426 *
427 * Return 0 if SEQ MAX, return 1 if OK
428 */
429uint8_t
430oscore_increment_sender_seq(oscore_ctx_t *ctx) {
431  ctx->sender_context->seq++;
432
433  if (ctx->sender_context->seq >= OSCORE_SEQ_MAX) {
434    return 0;
435  } else {
436    return 1;
437  }
438}
439
440/*
441 * oscore_roll_back_seq
442 *
443 * Restore the sequence number and replay-window to the previous state. This
444 * is to be used when decryption fail.
445 */
446void
447oscore_roll_back_seq(oscore_recipient_ctx_t *ctx) {
448
449  if (ctx->rollback_sliding_window != 0) {
450    ctx->sliding_window = ctx->rollback_sliding_window;
451    ctx->rollback_sliding_window = 0;
452  }
453  if (ctx->rollback_last_seq != 0) {
454    ctx->last_seq = ctx->rollback_last_seq;
455    ctx->rollback_last_seq = 0;
456  }
457}
458