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 14 static int 15 adjust_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 50 static int 51 parse_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 79 static int 80 fido_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; 153 fail: 154 cbor_vector_free(argv, nitems(argv)); 155 free(f.ptr); 156 157 return (r); 158 } 159 160 static int 161 fido_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 201 static int 202 fido_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 214 static int 215 fido_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 243 static int 244 fido_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 264 static int 265 decrypt_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 284 int 285 fido_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 326 fail: 327 es256_pk_free(&pk); 328 fido_blob_free(&ecdh); 329 330 return (r); 331 } 332 333 int 334 fido_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 354 static int 355 check_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 368 int 369 fido_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; 415 fail: 416 if (item != NULL) 417 cbor_decref(&item); 418 419 EVP_MD_CTX_free(ctx); 420 421 return (ok); 422 } 423 424 int 425 fido_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; 501 out: 502 explicit_bzero(buf, sizeof(buf)); 503 504 return (r); 505 } 506 507 int 508 fido_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 523 int 524 fido_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 534 int 535 fido_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 545 int 546 fido_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 557 int 558 fido_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 574 int 575 fido_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); 600 fail: 601 free(id.ptr); 602 603 return (r); 604 605 } 606 607 int 608 fido_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 621 int 622 fido_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 630 int 631 fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) 632 { 633 assert->up = up; 634 635 return (FIDO_OK); 636 } 637 638 int 639 fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) 640 { 641 assert->uv = uv; 642 643 return (FIDO_OK); 644 } 645 646 const unsigned char * 647 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) 648 { 649 return (assert->cdh.ptr); 650 } 651 652 size_t 653 fido_assert_clientdata_hash_len(const fido_assert_t *assert) 654 { 655 return (assert->cdh.len); 656 } 657 658 fido_assert_t * 659 fido_assert_new(void) 660 { 661 return (calloc(1, sizeof(fido_assert_t))); 662 } 663 664 void 665 fido_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 679 static 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 686 void 687 fido_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 708 void 709 fido_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 721 size_t 722 fido_assert_count(const fido_assert_t *assert) 723 { 724 return (assert->stmt_len); 725 } 726 727 const char * 728 fido_assert_rp_id(const fido_assert_t *assert) 729 { 730 return (assert->rp_id); 731 } 732 733 uint8_t 734 fido_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 742 uint32_t 743 fido_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 751 const unsigned char * 752 fido_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 760 size_t 761 fido_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 769 const unsigned char * 770 fido_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 778 size_t 779 fido_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 787 const unsigned char * 788 fido_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 796 size_t 797 fido_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 805 const unsigned char * 806 fido_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 814 size_t 815 fido_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 823 const char * 824 fido_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 832 const char * 833 fido_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 841 const char * 842 fido_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 850 const unsigned char * 851 fido_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 859 size_t 860 fido_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 868 const unsigned char * 869 fido_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 877 size_t 878 fido_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 886 const unsigned char * 887 fido_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 895 size_t 896 fido_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 904 static void 905 fido_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 912 int 913 fido_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; 941 fail: 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 951 int 952 fido_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; 979 fail: 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 989 int 990 fido_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 */ 1002 int 1003 fido_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 } 1025