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