libcoap 4.3.5-develop-b61cb76
Loading...
Searching...
No Matches
oscore.c
Go to the documentation of this file.
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
46
48
49#if COAP_OSCORE_SUPPORT
50
51/* oscore_cs_params
52 * returns cbor array [[param_type], [paramtype, param]]
53 */
54uint8_t *
55oscore_cs_params(int8_t param, int8_t param_type, size_t *len) {
56 uint8_t buf[50];
57 size_t rem_size = sizeof(buf);
58 uint8_t *pt = buf;
59
60 *len = 0;
61 *len += oscore_cbor_put_array(&pt, &rem_size, 2);
62 *len += oscore_cbor_put_array(&pt, &rem_size, 1);
63 *len += oscore_cbor_put_number(&pt, &rem_size, param_type);
64 *len += oscore_cbor_put_array(&pt, &rem_size, 2);
65 *len += oscore_cbor_put_number(&pt, &rem_size, param_type);
66 *len += oscore_cbor_put_number(&pt, &rem_size, param);
67 uint8_t *result = coap_malloc_type(COAP_STRING, *len);
68 memcpy(result, buf, *len);
69 return result;
70}
71
72/* oscore_cs_key_params
73 * returns cbor array [paramtype, param]
74 */
75uint8_t *
76oscore_cs_key_params(cose_curve_t param, int8_t param_type, size_t *len) {
77 uint8_t buf[50];
78 size_t rem_size = sizeof(buf);
79 uint8_t *pt = buf;
80
81 *len = 0;
82 *len += oscore_cbor_put_array(&pt, &rem_size, 2);
83 *len += oscore_cbor_put_number(&pt, &rem_size, param_type);
84 *len += oscore_cbor_put_number(&pt, &rem_size, param);
85 uint8_t *result = coap_malloc_type(COAP_STRING, *len);
86 memcpy(result, buf, *len);
87 return result;
88}
89
90/*
91 * Build the CBOR for external_aad
92 *
93 * external_aad = bstr .cbor aad_array
94 *
95 * No group mode
96 * aad_array = [
97 * oscore_version : uint,
98 * algorithms : [ alg_aead : int / tstr ],
99 * request_kid : bstr,
100 * request_piv : bstr,
101 * options : bstr,
102 * ]
103 *
104 * Group mode
105 * aad_array = [
106 * oscore_version : uint,
107 * algorithms : [alg_aead : int / tstr / null,
108 * alg_signature_enc : int / tstr / null,
109 * alg_signature : int / tstr / null,
110 * alg_pairwise_key_agreement : int / tstr / null],
111 * request_kid : bstr,
112 * request_piv : bstr,
113 * options : bstr,
114 * request_kid_context : bstr,
115 * OSCORE_option: bstr,
116 * sender_public_key: bstr, (initiator's key)
117 * gm_public_key: bstr / null
118 * ]
119 */
120size_t
122 cose_encrypt0_t *cose,
123 const uint8_t *oscore_option,
124 size_t oscore_option_len,
125 coap_bin_const_t *sender_public_key,
126 uint8_t *external_aad_ptr,
127 size_t external_aad_size) {
128 size_t external_aad_len = 0;
129 size_t rem_size = external_aad_size;
130 size_t len;
131
132 (void)oscore_option;
133 (void)oscore_option_len;
134 (void)sender_public_key;
135
136 if ((len = oscore_cbor_put_array(&external_aad_ptr, &rem_size, 5)) == 0)
137 goto fail;
138 external_aad_len += len;
139
140 /* oscore_version, always "1" */
141 if ((len = oscore_cbor_put_unsigned(&external_aad_ptr, &rem_size, 1)) == 0)
142 goto fail;
143 external_aad_len += len;
144
145 /* Algorithms array with one item*/
146 if ((len = oscore_cbor_put_array(&external_aad_ptr, &rem_size, 1)) == 0)
147 goto fail;
148 external_aad_len += len;
149
150 /* Encryption Algorithm */
151 if ((len = oscore_cbor_put_number(&external_aad_ptr, &rem_size, ctx->aead_alg)) == 0)
152 goto fail;
153 external_aad_len += len;
154
155 /* request_kid */
156 if ((len = oscore_cbor_put_bytes(&external_aad_ptr,
157 &rem_size,
158 cose->key_id.s,
159 cose->key_id.length)) == 0)
160 goto fail;
161 external_aad_len += len;
162
163 /* request_piv */
164 if ((len = oscore_cbor_put_bytes(&external_aad_ptr,
165 &rem_size,
166 cose->partial_iv.s,
167 cose->partial_iv.length)) == 0)
168 goto fail;
169 external_aad_len += len;
170
171 /* options */
172 /* Put integrity protected options, at present there are none. */
173 if ((len = oscore_cbor_put_bytes(&external_aad_ptr, &rem_size, NULL, 0)) == 0)
174 goto fail;
175 external_aad_len += len;
176
177 return external_aad_len;
178
179fail:
180 coap_log_info("oscore_prepare_e_aad: Buffer size too small: %" PRIuS "\n", external_aad_size);
181 return 0;
182}
183
184/*
185 * oscore_encode_option_value
186 */
187ssize_t
188oscore_encode_option_value(uint8_t *option_buffer,
189 size_t option_buf_len,
190 cose_encrypt0_t *cose,
191 uint8_t group_flag,
192 uint8_t appendix_b_2) {
193 size_t offset;
194 size_t rem_space;
195
196 (void)group_flag;
197 if (cose->partial_iv.length > 5 || option_buf_len == 0) {
198 return -1;
199 }
200 if (option_buf_len > 255)
201 option_buf_len = 255;
202 option_buffer[0] = 0;
203 offset = 1;
204 rem_space = option_buf_len - 1;
205
206 if (cose->partial_iv.length > 0 && cose->partial_iv.length <= 5 &&
207 cose->partial_iv.s != NULL) {
208 if (rem_space < cose->partial_iv.length)
209 return -1;
210 option_buffer[0] |= (0x07 & cose->partial_iv.length);
211 memcpy(&(option_buffer[offset]),
212 cose->partial_iv.s,
213 cose->partial_iv.length);
214 offset += cose->partial_iv.length;
215 rem_space -= cose->partial_iv.length;
216 }
217
218 if (cose->kid_context.length > 0 && cose->kid_context.s != NULL) {
219 if (appendix_b_2) {
220 /* Need to CBOR wrap kid_context - yuk! */
221 uint8_t *ptr = &option_buffer[offset+1];
222
223 if (rem_space < 1)
224 return -1;
225 option_buffer[0] |= 0x10;
226 option_buffer[offset] = (uint8_t)oscore_cbor_put_bytes(&ptr, &rem_space,
227 cose->kid_context.s,
228 cose->kid_context.length);
229 offset += option_buffer[offset] + 1;
230 /* rem_space updated by oscore_cbor_put_bytes() */
231 rem_space -= 1;
232 } else {
233 if (rem_space < 1 + cose->kid_context.length)
234 return -1;
235 option_buffer[0] |= 0x10;
236 option_buffer[offset] = (uint8_t)cose->kid_context.length;
237 offset++;
238 rem_space--;
239 memcpy(&(option_buffer[offset]),
240 cose->kid_context.s,
241 (uint8_t)cose->kid_context.length);
242 offset += cose->kid_context.length;
243 rem_space -= cose->kid_context.length;
244 }
245 }
246
247 if (cose->key_id.s != NULL) {
248 option_buffer[0] |= 0x08;
249 if (rem_space < cose->key_id.length)
250 return -1;
251 if (cose->key_id.length) {
252 memcpy(&(option_buffer[offset]), cose->key_id.s, cose->key_id.length);
253 offset += cose->key_id.length;
254 rem_space -= cose->key_id.length;
255 }
256 }
257
258 if (offset == 1 && option_buffer[0] == 0) {
259 /* If option_value is 0x00 it should be empty. */
260 offset = 0;
261 }
262 cose->oscore_option.s = option_buffer;
263 cose->oscore_option.length = offset;
264 return offset;
265}
266
267/*
268 * oscore_decode_option_value
269 * error: return 0
270 * OK: return 1
271 *
272 * Basic assumption is that all is preset to 0 or NULL on entry
273 */
274int
275oscore_decode_option_value(const uint8_t *opt_value,
276 size_t option_len,
277 cose_encrypt0_t *cose) {
278 uint8_t partial_iv_len = (opt_value[0] & 0x07);
279 size_t offset = 1;
280
281 cose->oscore_option.s = opt_value;
282 cose->oscore_option.length = option_len;
283
284 if (option_len == 0)
285 return 1; /* empty option */
286
287 if (option_len > 255 || partial_iv_len == 6 || partial_iv_len == 7 ||
288 (opt_value[0] & 0xC0) != 0) {
289 return 0;
290 }
291
292 if ((opt_value[0] & 0x20) != 0) {
293 return 0;
294 }
295
296 if (partial_iv_len != 0) {
297 coap_bin_const_t partial_iv;
298 if (offset + partial_iv_len > option_len) {
299 return 0;
300 }
301 partial_iv.s = &(opt_value[offset]);
302 partial_iv.length = partial_iv_len;
303 cose_encrypt0_set_partial_iv(cose, &partial_iv);
304 offset += partial_iv_len;
305 }
306
307 if ((opt_value[0] & 0x10) != 0) {
308 coap_bin_const_t kid_context;
309
310 if (offset >= option_len)
311 return 0;
312 kid_context.length = opt_value[offset];
313 offset++;
314 if (offset + kid_context.length > option_len) {
315 return 0;
316 }
317 kid_context.s = &(opt_value[offset]);
318 cose_encrypt0_set_kid_context(cose, &kid_context);
319 offset = offset + kid_context.length;
320 }
321
322 if ((opt_value[0] & 0x08) != 0) {
323 coap_bin_const_t key_id;
324
325 key_id.length = option_len - offset;
326 if ((int)key_id.length < 0) {
327 return 0;
328 }
329 key_id.s = &(opt_value[offset]);
330 cose_encrypt0_set_key_id(cose, &key_id);
331 }
332 return 1;
333}
334
335/*
336 * oscore_prepare_aad
337 *
338 * Creates and sets External AAD for encryption
339 */
340size_t
341oscore_prepare_aad(const uint8_t *external_aad_buffer,
342 size_t external_aad_len,
343 uint8_t *aad_buffer,
344 size_t aad_size) {
345 size_t ret = 0;
346 size_t len;
347 size_t rem_size = aad_size;
348 char encrypt0[] = "Encrypt0";
349
350 (void)aad_size; /* TODO */
351 /* Creating the AAD */
352 if ((len = oscore_cbor_put_array(&aad_buffer, &rem_size, 3)) == 0)
353 goto fail;
354 ret += len;
355
356 /* 1. "Encrypt0" */
357 if ((len = oscore_cbor_put_text(&aad_buffer, &rem_size, encrypt0, strlen(encrypt0))) == 0)
358 goto fail;
359 ret += len;
360
361 /* 2. Empty h'' entry */
362 if ((len = oscore_cbor_put_bytes(&aad_buffer, &rem_size, NULL, 0)) == 0)
363 goto fail;
364 ret += len;
365
366 /* 3. External AAD */
367 if ((len = oscore_cbor_put_bytes(&aad_buffer,
368 &rem_size,
369 external_aad_buffer,
370 external_aad_len)) == 0)
371 goto fail;
372 ret += len;
373
374 return ret;
375
376fail:
377 coap_log_info("oscore_prepare_aad: Buffer size too small: %" PRIuS "\n", aad_size);
378 return 0;
379}
380
381/*
382 * oscore_generate_nonce
383 *
384 * Creates Nonce
385 * See https://datatracker.ietf.org/doc/html/rfc8613#section-5.2 and Figure 8.
386 */
387void
389 oscore_ctx_t *ctx,
390 uint8_t *buffer,
391 uint8_t size) {
392 size_t tmp_len;
393
394 memset(buffer, 0, size);
395 /* ID_PIV */
396 if (ptr->key_id.length > size - 6UL) {
397 tmp_len = size - 6;
398 } else {
399 tmp_len = ptr->key_id.length;
400 }
401 if (ptr->key_id.s)
402 memcpy(&(buffer[((size - 5) - tmp_len)]),
403 ptr->key_id.s,
404 tmp_len);
405 /* S */
406 buffer[0] = (uint8_t)tmp_len;
407 /* PIV */
408 if (ptr->partial_iv.length > size) {
409 tmp_len = size;
410 } else {
411 tmp_len = ptr->partial_iv.length;
412 }
413 if (tmp_len > 5) {
414 tmp_len = 5;
415 }
416 if (tmp_len) {
417 memcpy(&(buffer[size - tmp_len]),
418 ptr->partial_iv.s,
419 tmp_len);
420 }
421 /* XOR */
422 for (int i = 0; i < size; i++) {
423 buffer[i] = buffer[i] ^ (uint8_t)ctx->common_iv->s[i];
424 }
425}
426
427/*
428 * oscore_validate_sender_seq
429 *
430 * Return 1 if OK, 0 otherwise
431 */
432uint8_t
434 uint64_t incoming_seq =
436
437 if (incoming_seq >= OSCORE_SEQ_MAX) {
438 coap_log_warn("OSCORE Replay protection, SEQ larger than SEQ_MAX.\n");
439 return 0;
440 }
441
442 ctx->rollback_last_seq = ctx->last_seq;
444
445 /* Special case since we do not use unsigned int for seq */
446 if (ctx->initial_state == 1) {
447 ctx->initial_state = 0;
448 /* bitfield. B0 biggest seq seen. B1 seq-1 seen, B2 seq-2 seen etc. */
449 ctx->sliding_window = 1;
450 ctx->last_seq = incoming_seq;
451 } else if (incoming_seq > ctx->last_seq) {
452 /* Update the replay window */
453 uint64_t shift = incoming_seq - ctx->last_seq;
454 /* bitfield. B0 biggest seq seen. B1 seq-1 seen, B2 seq-2 seen etc. */
455 if (shift >= ctx->osc_ctx->replay_window_size || shift >= 64) {
456 ctx->sliding_window = 0;
457 } else {
458 ctx->sliding_window <<= shift;
459 }
460 ctx->sliding_window |= 1;
461 ctx->last_seq = incoming_seq;
462 } else if (incoming_seq == ctx->last_seq) {
463 coap_log_warn("OSCORE: Replay protection, replayed SEQ (%" PRIu64 ")\n",
464 incoming_seq);
465 return 0;
466 } else { /* incoming_seq < last_seq */
467 uint64_t shift = ctx->last_seq - incoming_seq;
468 uint64_t pattern;
469
470 if (shift >= ctx->osc_ctx->replay_window_size || shift >= 64) {
472 "OSCORE: Replay protection, SEQ outside of replay window (%" PRIu64
473 " %" PRIu64 ")\n",
474 ctx->last_seq, incoming_seq);
475 return 0;
476 }
477 /* seq + replay_window_size > last_seq */
478 pattern = 1ULL << shift;
479 if (ctx->sliding_window & pattern) {
480 coap_log_warn("OSCORE: Replay protection, replayed SEQ (%" PRIu64 ")\n",
481 incoming_seq);
482 return 0;
483 }
484 /* bitfield. B0 biggest seq seen. B1 seq-1 seen, B2 seq-2 seen etc. */
485 ctx->sliding_window |= pattern;
486 }
487 coap_log_oscore("OSCORE: window 0x%" PRIx64 " seq-B0 %" PRIu64 " SEQ %"
488 PRIu64 "\n",
489 ctx->sliding_window,
490 ctx->last_seq,
491 incoming_seq);
492 return 1;
493}
494
495/*
496 * oscore_increment_sender_seq
497 *
498 * Return 0 if SEQ MAX, return 1 if OK
499 */
500uint8_t
502 ctx->sender_context->seq++;
503
504 if (ctx->sender_context->seq >= OSCORE_SEQ_MAX) {
505 return 0;
506 } else {
507 return 1;
508 }
509}
510
511/*
512 * oscore_roll_back_seq
513 *
514 * Restore the sequence number and replay-window to the previous state. This
515 * is to be used when decryption fail.
516 */
517void
519
520 if (ctx->rollback_sliding_window != 0) {
523 }
524 if (ctx->rollback_last_seq != 0) {
525 ctx->last_seq = ctx->rollback_last_seq;
526 ctx->rollback_last_seq = 0;
527 }
528}
529
530#else /* ! COAP_OSCORE_SUPPORT */
531
532#ifdef __clang__
533/* Make compilers happy that do not like empty modules. As this function is
534 * never used, we ignore -Wunused-function at the end of compiling this file
535 */
536#pragma GCC diagnostic ignored "-Wunused-function"
537#endif
538static inline void
539dummy(void) {
540}
541
542#endif /* ! COAP_OSCORE_SUPPORT */
#define PRIuS
#define PRIx64
#define PRIu64
Library specific build wrapper for coap_internal.h.
@ COAP_STRING
Definition coap_mem.h:33
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
#define NULL
Definition coap_option.h:30
uint64_t coap_decode_var_bytes8(const uint8_t *buf, size_t len)
Decodes multiple-length byte sequences.
Definition coap_encode.c:71
#define coap_log_oscore(...)
Definition coap_debug.h:132
#define coap_log_info(...)
Definition coap_debug.h:114
#define coap_log_warn(...)
Definition coap_debug.h:108
size_t oscore_cbor_put_text(uint8_t **buffer, size_t *buf_size, const char *text, size_t text_len)
size_t oscore_cbor_put_number(uint8_t **buffer, size_t *buf_size, int64_t value)
size_t oscore_cbor_put_unsigned(uint8_t **buffer, size_t *buf_size, uint64_t value)
size_t oscore_cbor_put_bytes(uint8_t **buffer, size_t *buf_size, const uint8_t *bytes, size_t bytes_len)
size_t oscore_cbor_put_array(uint8_t **buffer, size_t *buf_size, size_t elements)
void cose_encrypt0_set_kid_context(cose_encrypt0_t *ptr, coap_bin_const_t *kid_context)
cose_curve_t
Definition oscore_cose.h:62
void cose_encrypt0_set_partial_iv(cose_encrypt0_t *ptr, coap_bin_const_t *partial_iv)
void cose_encrypt0_set_key_id(cose_encrypt0_t *ptr, coap_bin_const_t *key_id)
size_t oscore_prepare_aad(const uint8_t *external_aad_buffer, size_t external_aad_len, uint8_t *aad_buffer, size_t aad_size)
uint8_t oscore_validate_sender_seq(oscore_recipient_ctx_t *ctx, cose_encrypt0_t *cose)
#define OSCORE_SEQ_MAX
int oscore_decode_option_value(const uint8_t *option_value, size_t option_len, cose_encrypt0_t *cose)
uint8_t oscore_increment_sender_seq(oscore_ctx_t *ctx)
void oscore_roll_back_seq(oscore_recipient_ctx_t *ctx)
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)
uint8_t * oscore_cs_key_params(cose_curve_t param, int8_t param_type, size_t *len)
void oscore_generate_nonce(cose_encrypt0_t *ptr, oscore_ctx_t *ctx, uint8_t *buffer, uint8_t size)
uint8_t * oscore_cs_params(int8_t param, int8_t param_type, size_t *len)
ssize_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)
Encode the OSCORE option.
static void dummy(void)
Definition oscore.c:539
CoAP binary data definition with const data.
Definition coap_str.h:65
size_t length
length of binary data
Definition coap_str.h:66
const uint8_t * s
read-only binary data
Definition coap_str.h:67
coap_bin_const_t partial_iv
coap_bin_const_t kid_context
coap_bin_const_t key_id
coap_bin_const_t oscore_option
uint32_t replay_window_size
coap_bin_const_t * common_iv
Derived from Master Secret, Master Salt, and ID Context.
oscore_sender_ctx_t * sender_context
cose_alg_t aead_alg
Set to one of COSE_ALGORITHM_AES*.
uint64_t seq
Sender Sequence Number.