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