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 15 static int 16 adjust_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 51 static int 52 parse_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 84 static int 85 fido_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; 158 fail: 159 cbor_vector_free(argv, nitems(argv)); 160 free(f.ptr); 161 162 return (r); 163 } 164 165 static int 166 fido_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; 209 out: 210 freezero(msg, FIDO_MAXMSG); 211 212 return (r); 213 } 214 215 static int 216 fido_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 228 static int 229 fido_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; 261 out: 262 freezero(msg, FIDO_MAXMSG); 263 264 return (r); 265 } 266 267 static int 268 fido_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 288 static int 289 decrypt_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 308 int 309 fido_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 350 fail: 351 es256_pk_free(&pk); 352 fido_blob_free(&ecdh); 353 354 return (r); 355 } 356 357 int 358 fido_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 378 static int 379 check_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 392 static int 393 get_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 416 static int 417 get_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 440 static int 441 get_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 455 int 456 fido_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 } 490 fail: 491 if (item != NULL) 492 cbor_decref(&item); 493 494 return (ok); 495 } 496 497 int 498 fido_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; 577 out: 578 explicit_bzero(buf, sizeof(buf)); 579 580 return (r); 581 } 582 583 int 584 fido_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 599 int 600 fido_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 610 int 611 fido_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 621 int 622 fido_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 633 int 634 fido_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 651 int 652 fido_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 668 int 669 fido_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 678 int 679 fido_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); 704 fail: 705 free(id.ptr); 706 707 return (r); 708 } 709 710 int 711 fido_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 719 int 720 fido_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 733 int 734 fido_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 742 int 743 fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) 744 { 745 assert->up = up; 746 747 return (FIDO_OK); 748 } 749 750 int 751 fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) 752 { 753 assert->uv = uv; 754 755 return (FIDO_OK); 756 } 757 758 const unsigned char * 759 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) 760 { 761 return (assert->cdh.ptr); 762 } 763 764 size_t 765 fido_assert_clientdata_hash_len(const fido_assert_t *assert) 766 { 767 return (assert->cdh.len); 768 } 769 770 fido_assert_t * 771 fido_assert_new(void) 772 { 773 return (calloc(1, sizeof(fido_assert_t))); 774 } 775 776 void 777 fido_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 792 static void 793 fido_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 800 void 801 fido_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 823 void 824 fido_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 836 size_t 837 fido_assert_count(const fido_assert_t *assert) 838 { 839 return (assert->stmt_len); 840 } 841 842 const char * 843 fido_assert_rp_id(const fido_assert_t *assert) 844 { 845 return (assert->rp_id); 846 } 847 848 uint8_t 849 fido_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 857 uint32_t 858 fido_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 866 const unsigned char * 867 fido_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 875 size_t 876 fido_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 884 const unsigned char * 885 fido_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 893 size_t 894 fido_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 902 const unsigned char * 903 fido_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 911 size_t 912 fido_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 920 const unsigned char * 921 fido_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 929 size_t 930 fido_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 938 const unsigned char * 939 fido_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 947 size_t 948 fido_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 956 const char * 957 fido_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 965 const char * 966 fido_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 974 const char * 975 fido_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 983 const unsigned char * 984 fido_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 992 size_t 993 fido_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 1001 const unsigned char * 1002 fido_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 1010 size_t 1011 fido_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 1019 const unsigned char * 1020 fido_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 1028 size_t 1029 fido_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 1037 static void 1038 fido_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 1046 int 1047 fido_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; 1081 fail: 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 1091 int 1092 fido_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; 1125 fail: 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 1135 int 1136 fido_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 */ 1148 int 1149 fido_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