• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
52 uint8_t *
oscore_cs_params(int8_t param,int8_t param_type,size_t * len)53 oscore_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  */
73 uint8_t *
oscore_cs_key_params(cose_curve_t param,int8_t param_type,size_t * len)74 oscore_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  */
118 size_t
oscore_prepare_e_aad(oscore_ctx_t * ctx,cose_encrypt0_t * cose,const uint8_t * oscore_option,size_t oscore_option_len,coap_bin_const_t * sender_public_key,uint8_t * external_aad_ptr,size_t external_aad_size)119 oscore_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  */
169 size_t
oscore_encode_option_value(uint8_t * option_buffer,size_t option_buf_len,cose_encrypt0_t * cose,uint8_t group_flag,uint8_t appendix_b_2)170 oscore_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  */
245 int
oscore_decode_option_value(const uint8_t * opt_value,size_t option_len,cose_encrypt0_t * cose)246 oscore_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  */
311 size_t
oscore_prepare_aad(const uint8_t * external_aad_buffer,size_t external_aad_len,uint8_t * aad_buffer,size_t aad_size)312 oscore_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  */
342 void
oscore_generate_nonce(cose_encrypt0_t * ptr,oscore_ctx_t * ctx,uint8_t * buffer,uint8_t size)343 oscore_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  */
365 uint8_t
oscore_validate_sender_seq(oscore_recipient_ctx_t * ctx,cose_encrypt0_t * cose)366 oscore_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  */
429 uint8_t
oscore_increment_sender_seq(oscore_ctx_t * ctx)430 oscore_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  */
446 void
oscore_roll_back_seq(oscore_recipient_ctx_t * ctx)447 oscore_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