jcs's openbsd hax
openbsd
at jcs 1024 lines 24 kB view raw
1/* 2 * Copyright (c) 2018-2021 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 */ 6 7#include <openssl/sha.h> 8 9#include "fido.h" 10#include "fido/es256.h" 11#include "fido/rs256.h" 12#include "fido/eddsa.h" 13 14static int 15adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) 16{ 17 fido_assert_t *assert = arg; 18 uint64_t n; 19 20 /* numberOfCredentials; see section 6.2 */ 21 if (cbor_isa_uint(key) == false || 22 cbor_int_get_width(key) != CBOR_INT_8 || 23 cbor_get_uint8(key) != 5) { 24 fido_log_debug("%s: cbor_type", __func__); 25 return (0); /* ignore */ 26 } 27 28 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { 29 fido_log_debug("%s: cbor_decode_uint64", __func__); 30 return (-1); 31 } 32 33 if (assert->stmt_len != 0 || assert->stmt_cnt != 1 || 34 (size_t)n < assert->stmt_cnt) { 35 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu", 36 __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n); 37 return (-1); 38 } 39 40 if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) { 41 fido_log_debug("%s: fido_assert_set_count", __func__); 42 return (-1); 43 } 44 45 assert->stmt_len = 0; /* XXX */ 46 47 return (0); 48} 49 50static int 51parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) 52{ 53 fido_assert_stmt *stmt = arg; 54 55 if (cbor_isa_uint(key) == false || 56 cbor_int_get_width(key) != CBOR_INT_8) { 57 fido_log_debug("%s: cbor type", __func__); 58 return (0); /* ignore */ 59 } 60 61 switch (cbor_get_uint8(key)) { 62 case 1: /* credential id */ 63 return (cbor_decode_cred_id(val, &stmt->id)); 64 case 2: /* authdata */ 65 return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor, 66 &stmt->authdata, &stmt->authdata_ext)); 67 case 3: /* signature */ 68 return (fido_blob_decode(val, &stmt->sig)); 69 case 4: /* user attributes */ 70 return (cbor_decode_user(val, &stmt->user)); 71 case 7: /* large blob key */ 72 return (fido_blob_decode(val, &stmt->largeblob_key)); 73 default: /* ignore */ 74 fido_log_debug("%s: cbor type", __func__); 75 return (0); 76 } 77} 78 79static int 80fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert, 81 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms) 82{ 83 fido_blob_t f; 84 fido_opt_t uv = assert->uv; 85 cbor_item_t *argv[7]; 86 const uint8_t cmd = CTAP_CBOR_ASSERT; 87 int r; 88 89 memset(argv, 0, sizeof(argv)); 90 memset(&f, 0, sizeof(f)); 91 92 /* do we have everything we need? */ 93 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { 94 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, 95 (void *)assert->rp_id, (void *)assert->cdh.ptr); 96 r = FIDO_ERR_INVALID_ARGUMENT; 97 goto fail; 98 } 99 100 if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL || 101 (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) { 102 fido_log_debug("%s: cbor encode", __func__); 103 r = FIDO_ERR_INTERNAL; 104 goto fail; 105 } 106 107 /* allowed credentials */ 108 if (assert->allow_list.len) { 109 const fido_blob_array_t *cl = &assert->allow_list; 110 if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) { 111 fido_log_debug("%s: cbor_encode_pubkey_list", __func__); 112 r = FIDO_ERR_INTERNAL; 113 goto fail; 114 } 115 } 116 117 if (assert->ext.mask) 118 if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh, 119 pk)) == NULL) { 120 fido_log_debug("%s: cbor_encode_assert_ext", __func__); 121 r = FIDO_ERR_INTERNAL; 122 goto fail; 123 } 124 125 /* user verification */ 126 if (pin != NULL || (uv == FIDO_OPT_TRUE && 127 fido_dev_supports_permissions(dev))) { 128 if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh, 129 pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) { 130 fido_log_debug("%s: cbor_add_uv_params", __func__); 131 goto fail; 132 } 133 uv = FIDO_OPT_OMIT; 134 } 135 136 /* options */ 137 if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) 138 if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) { 139 fido_log_debug("%s: cbor_encode_assert_opt", __func__); 140 r = FIDO_ERR_INTERNAL; 141 goto fail; 142 } 143 144 /* frame and transmit */ 145 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || 146 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { 147 fido_log_debug("%s: fido_tx", __func__); 148 r = FIDO_ERR_TX; 149 goto fail; 150 } 151 152 r = FIDO_OK; 153fail: 154 cbor_vector_free(argv, nitems(argv)); 155 free(f.ptr); 156 157 return (r); 158} 159 160static int 161fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms) 162{ 163 unsigned char reply[FIDO_MAXMSG]; 164 int reply_len; 165 int r; 166 167 fido_assert_reset_rx(assert); 168 169 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 170 ms)) < 0) { 171 fido_log_debug("%s: fido_rx", __func__); 172 return (FIDO_ERR_RX); 173 } 174 175 /* start with room for a single assertion */ 176 if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL) 177 return (FIDO_ERR_INTERNAL); 178 179 assert->stmt_len = 0; 180 assert->stmt_cnt = 1; 181 182 /* adjust as needed */ 183 if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert, 184 adjust_assert_count)) != FIDO_OK) { 185 fido_log_debug("%s: adjust_assert_count", __func__); 186 return (r); 187 } 188 189 /* parse the first assertion */ 190 if ((r = cbor_parse_reply(reply, (size_t)reply_len, 191 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { 192 fido_log_debug("%s: parse_assert_reply", __func__); 193 return (r); 194 } 195 196 assert->stmt_len++; 197 198 return (FIDO_OK); 199} 200 201static int 202fido_get_next_assert_tx(fido_dev_t *dev, int *ms) 203{ 204 const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; 205 206 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) { 207 fido_log_debug("%s: fido_tx", __func__); 208 return (FIDO_ERR_TX); 209 } 210 211 return (FIDO_OK); 212} 213 214static int 215fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms) 216{ 217 unsigned char reply[FIDO_MAXMSG]; 218 int reply_len; 219 int r; 220 221 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 222 ms)) < 0) { 223 fido_log_debug("%s: fido_rx", __func__); 224 return (FIDO_ERR_RX); 225 } 226 227 /* sanity check */ 228 if (assert->stmt_len >= assert->stmt_cnt) { 229 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__, 230 assert->stmt_len, assert->stmt_cnt); 231 return (FIDO_ERR_INTERNAL); 232 } 233 234 if ((r = cbor_parse_reply(reply, (size_t)reply_len, 235 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { 236 fido_log_debug("%s: parse_assert_reply", __func__); 237 return (r); 238 } 239 240 return (FIDO_OK); 241} 242 243static int 244fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert, 245 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms) 246{ 247 int r; 248 249 if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin, 250 ms)) != FIDO_OK || 251 (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK) 252 return (r); 253 254 while (assert->stmt_len < assert->stmt_cnt) { 255 if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK || 256 (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK) 257 return (r); 258 assert->stmt_len++; 259 } 260 261 return (FIDO_OK); 262} 263 264static int 265decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert, 266 const fido_blob_t *key) 267{ 268 for (size_t i = 0; i < assert->stmt_cnt; i++) { 269 fido_assert_stmt *stmt = &assert->stmt[i]; 270 if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) { 271 if (aes256_cbc_dec(dev, key, 272 &stmt->authdata_ext.hmac_secret_enc, 273 &stmt->hmac_secret) < 0) { 274 fido_log_debug("%s: aes256_cbc_dec %zu", 275 __func__, i); 276 return (-1); 277 } 278 } 279 } 280 281 return (0); 282} 283 284int 285fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin) 286{ 287 fido_blob_t *ecdh = NULL; 288 es256_pk_t *pk = NULL; 289 int ms = dev->timeout_ms; 290 int r; 291 292#ifdef USE_WINHELLO 293 if (dev->flags & FIDO_DEV_WINHELLO) 294 return (fido_winhello_get_assert(dev, assert, pin, ms)); 295#endif 296 297 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { 298 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, 299 (void *)assert->rp_id, (void *)assert->cdh.ptr); 300 return (FIDO_ERR_INVALID_ARGUMENT); 301 } 302 303 if (fido_dev_is_fido2(dev) == false) { 304 if (pin != NULL || assert->ext.mask != 0) 305 return (FIDO_ERR_UNSUPPORTED_OPTION); 306 return (u2f_authenticate(dev, assert, &ms)); 307 } 308 309 if (pin != NULL || (assert->uv == FIDO_OPT_TRUE && 310 fido_dev_supports_permissions(dev)) || 311 (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) { 312 if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) { 313 fido_log_debug("%s: fido_do_ecdh", __func__); 314 goto fail; 315 } 316 } 317 318 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms); 319 if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) 320 if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) { 321 fido_log_debug("%s: decrypt_hmac_secrets", __func__); 322 r = FIDO_ERR_INTERNAL; 323 goto fail; 324 } 325 326fail: 327 es256_pk_free(&pk); 328 fido_blob_free(&ecdh); 329 330 return (r); 331} 332 333int 334fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv) 335{ 336 fido_log_debug("%s: flags=%02x", __func__, flags); 337 fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv); 338 339 if (up == FIDO_OPT_TRUE && 340 (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) { 341 fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__); 342 return (-1); /* user not present */ 343 } 344 345 if (uv == FIDO_OPT_TRUE && 346 (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) { 347 fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__); 348 return (-1); /* user not verified */ 349 } 350 351 return (0); 352} 353 354static int 355check_extensions(int authdata_ext, int ext) 356{ 357 /* XXX: largeBlobKey is not part of extensions map */ 358 ext &= ~FIDO_EXT_LARGEBLOB_KEY; 359 if (authdata_ext != ext) { 360 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__, 361 authdata_ext, ext); 362 return (-1); 363 } 364 365 return (0); 366} 367 368int 369fido_get_signed_hash(int cose_alg, fido_blob_t *dgst, 370 const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor) 371{ 372 cbor_item_t *item = NULL; 373 unsigned char *authdata_ptr = NULL; 374 size_t authdata_len; 375 struct cbor_load_result cbor; 376 const EVP_MD *md = NULL; 377 EVP_MD_CTX *ctx = NULL; 378 int ok = -1; 379 380 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len, 381 &cbor)) == NULL || cbor_isa_bytestring(item) == false || 382 cbor_bytestring_is_definite(item) == false) { 383 fido_log_debug("%s: authdata", __func__); 384 goto fail; 385 } 386 387 authdata_ptr = cbor_bytestring_handle(item); 388 authdata_len = cbor_bytestring_length(item); 389 390 if (cose_alg != COSE_EDDSA) { 391 if (dgst->len < SHA256_DIGEST_LENGTH || 392 (md = EVP_sha256()) == NULL || 393 (ctx = EVP_MD_CTX_new()) == NULL || 394 EVP_DigestInit_ex(ctx, md, NULL) != 1 || 395 EVP_DigestUpdate(ctx, authdata_ptr, authdata_len) != 1 || 396 EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || 397 EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { 398 fido_log_debug("%s: sha256", __func__); 399 goto fail; 400 } 401 dgst->len = SHA256_DIGEST_LENGTH; 402 } else { 403 if (SIZE_MAX - authdata_len < clientdata->len || 404 dgst->len < authdata_len + clientdata->len) { 405 fido_log_debug("%s: memcpy", __func__); 406 goto fail; 407 } 408 memcpy(dgst->ptr, authdata_ptr, authdata_len); 409 memcpy(dgst->ptr + authdata_len, clientdata->ptr, 410 clientdata->len); 411 dgst->len = authdata_len + clientdata->len; 412 } 413 414 ok = 0; 415fail: 416 if (item != NULL) 417 cbor_decref(&item); 418 419 EVP_MD_CTX_free(ctx); 420 421 return (ok); 422} 423 424int 425fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, 426 const void *pk) 427{ 428 unsigned char buf[1024]; /* XXX */ 429 fido_blob_t dgst; 430 const fido_assert_stmt *stmt = NULL; 431 int ok = -1; 432 int r; 433 434 dgst.ptr = buf; 435 dgst.len = sizeof(buf); 436 437 if (idx >= assert->stmt_len || pk == NULL) { 438 r = FIDO_ERR_INVALID_ARGUMENT; 439 goto out; 440 } 441 442 stmt = &assert->stmt[idx]; 443 444 /* do we have everything we need? */ 445 if (assert->cdh.ptr == NULL || assert->rp_id == NULL || 446 stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) { 447 fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p", 448 __func__, (void *)assert->cdh.ptr, assert->rp_id, 449 (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr); 450 r = FIDO_ERR_INVALID_ARGUMENT; 451 goto out; 452 } 453 454 if (fido_check_flags(stmt->authdata.flags, assert->up, 455 assert->uv) < 0) { 456 fido_log_debug("%s: fido_check_flags", __func__); 457 r = FIDO_ERR_INVALID_PARAM; 458 goto out; 459 } 460 461 if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) { 462 fido_log_debug("%s: check_extensions", __func__); 463 r = FIDO_ERR_INVALID_PARAM; 464 goto out; 465 } 466 467 if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) { 468 fido_log_debug("%s: fido_check_rp_id", __func__); 469 r = FIDO_ERR_INVALID_PARAM; 470 goto out; 471 } 472 473 if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh, 474 &stmt->authdata_cbor) < 0) { 475 fido_log_debug("%s: fido_get_signed_hash", __func__); 476 r = FIDO_ERR_INTERNAL; 477 goto out; 478 } 479 480 switch (cose_alg) { 481 case COSE_ES256: 482 ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig); 483 break; 484 case COSE_RS256: 485 ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig); 486 break; 487 case COSE_EDDSA: 488 ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig); 489 break; 490 default: 491 fido_log_debug("%s: unsupported cose_alg %d", __func__, 492 cose_alg); 493 r = FIDO_ERR_UNSUPPORTED_OPTION; 494 goto out; 495 } 496 497 if (ok < 0) 498 r = FIDO_ERR_INVALID_SIG; 499 else 500 r = FIDO_OK; 501out: 502 explicit_bzero(buf, sizeof(buf)); 503 504 return (r); 505} 506 507int 508fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data, 509 size_t data_len) 510{ 511 if (!fido_blob_is_empty(&assert->cdh) || 512 fido_blob_set(&assert->cd, data, data_len) < 0) { 513 return (FIDO_ERR_INVALID_ARGUMENT); 514 } 515 if (fido_sha256(&assert->cdh, data, data_len) < 0) { 516 fido_blob_reset(&assert->cd); 517 return (FIDO_ERR_INTERNAL); 518 } 519 520 return (FIDO_OK); 521} 522 523int 524fido_assert_set_clientdata_hash(fido_assert_t *assert, 525 const unsigned char *hash, size_t hash_len) 526{ 527 if (!fido_blob_is_empty(&assert->cd) || 528 fido_blob_set(&assert->cdh, hash, hash_len) < 0) 529 return (FIDO_ERR_INVALID_ARGUMENT); 530 531 return (FIDO_OK); 532} 533 534int 535fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt, 536 size_t salt_len) 537{ 538 if ((salt_len != 32 && salt_len != 64) || 539 fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0) 540 return (FIDO_ERR_INVALID_ARGUMENT); 541 542 return (FIDO_OK); 543} 544 545int 546fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx, 547 const unsigned char *secret, size_t secret_len) 548{ 549 if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) || 550 fido_blob_set(&assert->stmt[idx].hmac_secret, secret, 551 secret_len) < 0) 552 return (FIDO_ERR_INVALID_ARGUMENT); 553 554 return (FIDO_OK); 555} 556 557int 558fido_assert_set_rp(fido_assert_t *assert, const char *id) 559{ 560 if (assert->rp_id != NULL) { 561 free(assert->rp_id); 562 assert->rp_id = NULL; 563 } 564 565 if (id == NULL) 566 return (FIDO_ERR_INVALID_ARGUMENT); 567 568 if ((assert->rp_id = strdup(id)) == NULL) 569 return (FIDO_ERR_INTERNAL); 570 571 return (FIDO_OK); 572} 573 574int 575fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr, 576 size_t len) 577{ 578 fido_blob_t id; 579 fido_blob_t *list_ptr; 580 int r; 581 582 memset(&id, 0, sizeof(id)); 583 584 if (assert->allow_list.len == SIZE_MAX) { 585 r = FIDO_ERR_INVALID_ARGUMENT; 586 goto fail; 587 } 588 589 if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr = 590 recallocarray(assert->allow_list.ptr, assert->allow_list.len, 591 assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) { 592 r = FIDO_ERR_INVALID_ARGUMENT; 593 goto fail; 594 } 595 596 list_ptr[assert->allow_list.len++] = id; 597 assert->allow_list.ptr = list_ptr; 598 599 return (FIDO_OK); 600fail: 601 free(id.ptr); 602 603 return (r); 604 605} 606 607int 608fido_assert_set_extensions(fido_assert_t *assert, int ext) 609{ 610 if (ext == 0) 611 assert->ext.mask = 0; 612 else { 613 if ((ext & FIDO_EXT_ASSERT_MASK) != ext) 614 return (FIDO_ERR_INVALID_ARGUMENT); 615 assert->ext.mask |= ext; 616 } 617 618 return (FIDO_OK); 619} 620 621int 622fido_assert_set_options(fido_assert_t *assert, bool up, bool uv) 623{ 624 assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; 625 assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; 626 627 return (FIDO_OK); 628} 629 630int 631fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) 632{ 633 assert->up = up; 634 635 return (FIDO_OK); 636} 637 638int 639fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) 640{ 641 assert->uv = uv; 642 643 return (FIDO_OK); 644} 645 646const unsigned char * 647fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) 648{ 649 return (assert->cdh.ptr); 650} 651 652size_t 653fido_assert_clientdata_hash_len(const fido_assert_t *assert) 654{ 655 return (assert->cdh.len); 656} 657 658fido_assert_t * 659fido_assert_new(void) 660{ 661 return (calloc(1, sizeof(fido_assert_t))); 662} 663 664void 665fido_assert_reset_tx(fido_assert_t *assert) 666{ 667 free(assert->rp_id); 668 fido_blob_reset(&assert->cd); 669 fido_blob_reset(&assert->cdh); 670 fido_blob_reset(&assert->ext.hmac_salt); 671 fido_free_blob_array(&assert->allow_list); 672 memset(&assert->ext, 0, sizeof(assert->ext)); 673 memset(&assert->allow_list, 0, sizeof(assert->allow_list)); 674 assert->rp_id = NULL; 675 assert->up = FIDO_OPT_OMIT; 676 assert->uv = FIDO_OPT_OMIT; 677} 678 679static void fido_assert_reset_extattr(fido_assert_extattr_t *ext) 680{ 681 fido_blob_reset(&ext->hmac_secret_enc); 682 fido_blob_reset(&ext->blob); 683 memset(ext, 0, sizeof(*ext)); 684} 685 686void 687fido_assert_reset_rx(fido_assert_t *assert) 688{ 689 for (size_t i = 0; i < assert->stmt_cnt; i++) { 690 free(assert->stmt[i].user.icon); 691 free(assert->stmt[i].user.name); 692 free(assert->stmt[i].user.display_name); 693 fido_blob_reset(&assert->stmt[i].user.id); 694 fido_blob_reset(&assert->stmt[i].id); 695 fido_blob_reset(&assert->stmt[i].hmac_secret); 696 fido_blob_reset(&assert->stmt[i].authdata_cbor); 697 fido_blob_reset(&assert->stmt[i].largeblob_key); 698 fido_blob_reset(&assert->stmt[i].sig); 699 fido_assert_reset_extattr(&assert->stmt[i].authdata_ext); 700 memset(&assert->stmt[i], 0, sizeof(assert->stmt[i])); 701 } 702 free(assert->stmt); 703 assert->stmt = NULL; 704 assert->stmt_len = 0; 705 assert->stmt_cnt = 0; 706} 707 708void 709fido_assert_free(fido_assert_t **assert_p) 710{ 711 fido_assert_t *assert; 712 713 if (assert_p == NULL || (assert = *assert_p) == NULL) 714 return; 715 fido_assert_reset_tx(assert); 716 fido_assert_reset_rx(assert); 717 free(assert); 718 *assert_p = NULL; 719} 720 721size_t 722fido_assert_count(const fido_assert_t *assert) 723{ 724 return (assert->stmt_len); 725} 726 727const char * 728fido_assert_rp_id(const fido_assert_t *assert) 729{ 730 return (assert->rp_id); 731} 732 733uint8_t 734fido_assert_flags(const fido_assert_t *assert, size_t idx) 735{ 736 if (idx >= assert->stmt_len) 737 return (0); 738 739 return (assert->stmt[idx].authdata.flags); 740} 741 742uint32_t 743fido_assert_sigcount(const fido_assert_t *assert, size_t idx) 744{ 745 if (idx >= assert->stmt_len) 746 return (0); 747 748 return (assert->stmt[idx].authdata.sigcount); 749} 750 751const unsigned char * 752fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx) 753{ 754 if (idx >= assert->stmt_len) 755 return (NULL); 756 757 return (assert->stmt[idx].authdata_cbor.ptr); 758} 759 760size_t 761fido_assert_authdata_len(const fido_assert_t *assert, size_t idx) 762{ 763 if (idx >= assert->stmt_len) 764 return (0); 765 766 return (assert->stmt[idx].authdata_cbor.len); 767} 768 769const unsigned char * 770fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx) 771{ 772 if (idx >= assert->stmt_len) 773 return (NULL); 774 775 return (assert->stmt[idx].sig.ptr); 776} 777 778size_t 779fido_assert_sig_len(const fido_assert_t *assert, size_t idx) 780{ 781 if (idx >= assert->stmt_len) 782 return (0); 783 784 return (assert->stmt[idx].sig.len); 785} 786 787const unsigned char * 788fido_assert_id_ptr(const fido_assert_t *assert, size_t idx) 789{ 790 if (idx >= assert->stmt_len) 791 return (NULL); 792 793 return (assert->stmt[idx].id.ptr); 794} 795 796size_t 797fido_assert_id_len(const fido_assert_t *assert, size_t idx) 798{ 799 if (idx >= assert->stmt_len) 800 return (0); 801 802 return (assert->stmt[idx].id.len); 803} 804 805const unsigned char * 806fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx) 807{ 808 if (idx >= assert->stmt_len) 809 return (NULL); 810 811 return (assert->stmt[idx].user.id.ptr); 812} 813 814size_t 815fido_assert_user_id_len(const fido_assert_t *assert, size_t idx) 816{ 817 if (idx >= assert->stmt_len) 818 return (0); 819 820 return (assert->stmt[idx].user.id.len); 821} 822 823const char * 824fido_assert_user_icon(const fido_assert_t *assert, size_t idx) 825{ 826 if (idx >= assert->stmt_len) 827 return (NULL); 828 829 return (assert->stmt[idx].user.icon); 830} 831 832const char * 833fido_assert_user_name(const fido_assert_t *assert, size_t idx) 834{ 835 if (idx >= assert->stmt_len) 836 return (NULL); 837 838 return (assert->stmt[idx].user.name); 839} 840 841const char * 842fido_assert_user_display_name(const fido_assert_t *assert, size_t idx) 843{ 844 if (idx >= assert->stmt_len) 845 return (NULL); 846 847 return (assert->stmt[idx].user.display_name); 848} 849 850const unsigned char * 851fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx) 852{ 853 if (idx >= assert->stmt_len) 854 return (NULL); 855 856 return (assert->stmt[idx].hmac_secret.ptr); 857} 858 859size_t 860fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx) 861{ 862 if (idx >= assert->stmt_len) 863 return (0); 864 865 return (assert->stmt[idx].hmac_secret.len); 866} 867 868const unsigned char * 869fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx) 870{ 871 if (idx >= assert->stmt_len) 872 return (NULL); 873 874 return (assert->stmt[idx].largeblob_key.ptr); 875} 876 877size_t 878fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx) 879{ 880 if (idx >= assert->stmt_len) 881 return (0); 882 883 return (assert->stmt[idx].largeblob_key.len); 884} 885 886const unsigned char * 887fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx) 888{ 889 if (idx >= assert->stmt_len) 890 return (NULL); 891 892 return (assert->stmt[idx].authdata_ext.blob.ptr); 893} 894 895size_t 896fido_assert_blob_len(const fido_assert_t *assert, size_t idx) 897{ 898 if (idx >= assert->stmt_len) 899 return (0); 900 901 return (assert->stmt[idx].authdata_ext.blob.len); 902} 903 904static void 905fido_assert_clean_authdata(fido_assert_stmt *stmt) 906{ 907 fido_blob_reset(&stmt->authdata_cbor); 908 fido_assert_reset_extattr(&stmt->authdata_ext); 909 memset(&stmt->authdata, 0, sizeof(stmt->authdata)); 910} 911 912int 913fido_assert_set_authdata(fido_assert_t *assert, size_t idx, 914 const unsigned char *ptr, size_t len) 915{ 916 cbor_item_t *item = NULL; 917 fido_assert_stmt *stmt = NULL; 918 struct cbor_load_result cbor; 919 int r; 920 921 if (idx >= assert->stmt_len || ptr == NULL || len == 0) 922 return (FIDO_ERR_INVALID_ARGUMENT); 923 924 stmt = &assert->stmt[idx]; 925 fido_assert_clean_authdata(stmt); 926 927 if ((item = cbor_load(ptr, len, &cbor)) == NULL) { 928 fido_log_debug("%s: cbor_load", __func__); 929 r = FIDO_ERR_INVALID_ARGUMENT; 930 goto fail; 931 } 932 933 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, 934 &stmt->authdata, &stmt->authdata_ext) < 0) { 935 fido_log_debug("%s: cbor_decode_assert_authdata", __func__); 936 r = FIDO_ERR_INVALID_ARGUMENT; 937 goto fail; 938 } 939 940 r = FIDO_OK; 941fail: 942 if (item != NULL) 943 cbor_decref(&item); 944 945 if (r != FIDO_OK) 946 fido_assert_clean_authdata(stmt); 947 948 return (r); 949} 950 951int 952fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx, 953 const unsigned char *ptr, size_t len) 954{ 955 cbor_item_t *item = NULL; 956 fido_assert_stmt *stmt = NULL; 957 int r; 958 959 if (idx >= assert->stmt_len || ptr == NULL || len == 0) 960 return (FIDO_ERR_INVALID_ARGUMENT); 961 962 stmt = &assert->stmt[idx]; 963 fido_assert_clean_authdata(stmt); 964 965 if ((item = cbor_build_bytestring(ptr, len)) == NULL) { 966 fido_log_debug("%s: cbor_build_bytestring", __func__); 967 r = FIDO_ERR_INTERNAL; 968 goto fail; 969 } 970 971 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, 972 &stmt->authdata, &stmt->authdata_ext) < 0) { 973 fido_log_debug("%s: cbor_decode_assert_authdata", __func__); 974 r = FIDO_ERR_INVALID_ARGUMENT; 975 goto fail; 976 } 977 978 r = FIDO_OK; 979fail: 980 if (item != NULL) 981 cbor_decref(&item); 982 983 if (r != FIDO_OK) 984 fido_assert_clean_authdata(stmt); 985 986 return (r); 987} 988 989int 990fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr, 991 size_t len) 992{ 993 if (idx >= a->stmt_len || ptr == NULL || len == 0) 994 return (FIDO_ERR_INVALID_ARGUMENT); 995 if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0) 996 return (FIDO_ERR_INTERNAL); 997 998 return (FIDO_OK); 999} 1000 1001/* XXX shrinking leaks memory; fortunately that shouldn't happen */ 1002int 1003fido_assert_set_count(fido_assert_t *assert, size_t n) 1004{ 1005 void *new_stmt; 1006 1007#ifdef FIDO_FUZZ 1008 if (n > UINT8_MAX) { 1009 fido_log_debug("%s: n > UINT8_MAX", __func__); 1010 return (FIDO_ERR_INTERNAL); 1011 } 1012#endif 1013 1014 new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n, 1015 sizeof(fido_assert_stmt)); 1016 if (new_stmt == NULL) 1017 return (FIDO_ERR_INTERNAL); 1018 1019 assert->stmt = new_stmt; 1020 assert->stmt_cnt = n; 1021 assert->stmt_len = n; 1022 1023 return (FIDO_OK); 1024}