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