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