1/* 2 * Copyright (c) 2018-2021 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 */ 6 7#include <openssl/sha.h> 8#include <openssl/x509.h> 9 10#include "fido.h" 11#include "fido/es256.h" 12 13#ifndef FIDO_MAXMSG_CRED 14#define FIDO_MAXMSG_CRED 4096 15#endif 16 17static int 18parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) 19{ 20 fido_cred_t *cred = arg; 21 22 if (cbor_isa_uint(key) == false || 23 cbor_int_get_width(key) != CBOR_INT_8) { 24 fido_log_debug("%s: cbor type", __func__); 25 return (0); /* ignore */ 26 } 27 28 switch (cbor_get_uint8(key)) { 29 case 1: /* fmt */ 30 return (cbor_decode_fmt(val, &cred->fmt)); 31 case 2: /* authdata */ 32 if (fido_blob_decode(val, &cred->authdata_raw) < 0) { 33 fido_log_debug("%s: fido_blob_decode", __func__); 34 return (-1); 35 } 36 return (cbor_decode_cred_authdata(val, cred->type, 37 &cred->authdata_cbor, &cred->authdata, &cred->attcred, 38 &cred->authdata_ext)); 39 case 3: /* attestation statement */ 40 return (cbor_decode_attstmt(val, &cred->attstmt)); 41 case 5: /* large blob key */ 42 return (fido_blob_decode(val, &cred->largeblob_key)); 43 default: /* ignore */ 44 fido_log_debug("%s: cbor type", __func__); 45 return (0); 46 } 47} 48 49static int 50fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin, 51 int *ms) 52{ 53 fido_blob_t f; 54 fido_blob_t *ecdh = NULL; 55 fido_opt_t uv = cred->uv; 56 es256_pk_t *pk = NULL; 57 cbor_item_t *argv[9]; 58 const uint8_t cmd = CTAP_CBOR_MAKECRED; 59 int r; 60 61 memset(&f, 0, sizeof(f)); 62 memset(argv, 0, sizeof(argv)); 63 64 if (cred->cdh.ptr == NULL || cred->type == 0) { 65 fido_log_debug("%s: cdh=%p, type=%d", __func__, 66 (void *)cred->cdh.ptr, cred->type); 67 r = FIDO_ERR_INVALID_ARGUMENT; 68 goto fail; 69 } 70 71 if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL || 72 (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL || 73 (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL || 74 (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) { 75 fido_log_debug("%s: cbor encode", __func__); 76 r = FIDO_ERR_INTERNAL; 77 goto fail; 78 } 79 80 /* excluded credentials */ 81 if (cred->excl.len) 82 if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) { 83 fido_log_debug("%s: cbor_encode_pubkey_list", __func__); 84 r = FIDO_ERR_INTERNAL; 85 goto fail; 86 } 87 88 /* extensions */ 89 if (cred->ext.mask) 90 if ((argv[5] = cbor_encode_cred_ext(&cred->ext, 91 &cred->blob)) == NULL) { 92 fido_log_debug("%s: cbor_encode_cred_ext", __func__); 93 r = FIDO_ERR_INTERNAL; 94 goto fail; 95 } 96 97 /* user verification */ 98 if (pin != NULL || (uv == FIDO_OPT_TRUE && 99 fido_dev_supports_permissions(dev))) { 100 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { 101 fido_log_debug("%s: fido_do_ecdh", __func__); 102 goto fail; 103 } 104 if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh, 105 pin, cred->rp.id, &argv[7], &argv[8], ms)) != FIDO_OK) { 106 fido_log_debug("%s: cbor_add_uv_params", __func__); 107 goto fail; 108 } 109 uv = FIDO_OPT_OMIT; 110 } 111 112 /* options */ 113 if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) 114 if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) { 115 fido_log_debug("%s: cbor_encode_cred_opt", __func__); 116 r = FIDO_ERR_INTERNAL; 117 goto fail; 118 } 119 120 /* framing and transmission */ 121 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || 122 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { 123 fido_log_debug("%s: fido_tx", __func__); 124 r = FIDO_ERR_TX; 125 goto fail; 126 } 127 128 r = FIDO_OK; 129fail: 130 es256_pk_free(&pk); 131 fido_blob_free(&ecdh); 132 cbor_vector_free(argv, nitems(argv)); 133 free(f.ptr); 134 135 return (r); 136} 137 138static int 139fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int *ms) 140{ 141 unsigned char *reply; 142 int reply_len; 143 int r; 144 145 fido_cred_reset_rx(cred); 146 147 if ((reply = malloc(FIDO_MAXMSG_CRED)) == NULL) { 148 r = FIDO_ERR_INTERNAL; 149 goto fail; 150 } 151 152 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, reply, FIDO_MAXMSG_CRED, 153 ms)) < 0) { 154 fido_log_debug("%s: fido_rx", __func__); 155 r = FIDO_ERR_RX; 156 goto fail; 157 } 158 159 if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred, 160 parse_makecred_reply)) != FIDO_OK) { 161 fido_log_debug("%s: parse_makecred_reply", __func__); 162 goto fail; 163 } 164 165 if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) || 166 fido_blob_is_empty(&cred->attcred.id)) { 167 r = FIDO_ERR_INVALID_CBOR; 168 goto fail; 169 } 170 171 r = FIDO_OK; 172fail: 173 free(reply); 174 175 if (r != FIDO_OK) 176 fido_cred_reset_rx(cred); 177 178 return (r); 179} 180 181static int 182fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, 183 int *ms) 184{ 185 int r; 186 187 if ((r = fido_dev_make_cred_tx(dev, cred, pin, ms)) != FIDO_OK || 188 (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK) 189 return (r); 190 191 return (FIDO_OK); 192} 193 194int 195fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) 196{ 197 int ms = dev->timeout_ms; 198 199#ifdef USE_WINHELLO 200 if (dev->flags & FIDO_DEV_WINHELLO) 201 return (fido_winhello_make_cred(dev, cred, pin, ms)); 202#endif 203 if (fido_dev_is_fido2(dev) == false) { 204 if (pin != NULL || cred->rk == FIDO_OPT_TRUE || 205 cred->ext.mask != 0) 206 return (FIDO_ERR_UNSUPPORTED_OPTION); 207 return (u2f_register(dev, cred, &ms)); 208 } 209 210 return (fido_dev_make_cred_wait(dev, cred, pin, &ms)); 211} 212 213static int 214check_extensions(const fido_cred_ext_t *authdata_ext, 215 const fido_cred_ext_t *ext) 216{ 217 fido_cred_ext_t tmp; 218 219 /* XXX: largeBlobKey is not part of the extensions map */ 220 memcpy(&tmp, ext, sizeof(tmp)); 221 tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY; 222 223 return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext))); 224} 225 226int 227fido_check_rp_id(const char *id, const unsigned char *obtained_hash) 228{ 229 unsigned char expected_hash[SHA256_DIGEST_LENGTH]; 230 231 explicit_bzero(expected_hash, sizeof(expected_hash)); 232 233 if (SHA256((const unsigned char *)id, strlen(id), 234 expected_hash) != expected_hash) { 235 fido_log_debug("%s: sha256", __func__); 236 return (-1); 237 } 238 239 return (timingsafe_bcmp(expected_hash, obtained_hash, 240 SHA256_DIGEST_LENGTH)); 241} 242 243static int 244get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id, 245 size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id, 246 const es256_pk_t *pk) 247{ 248 const uint8_t zero = 0; 249 const uint8_t four = 4; /* uncompressed point */ 250 const EVP_MD *md = NULL; 251 EVP_MD_CTX *ctx = NULL; 252 int ok = -1; 253 254 if (dgst->len != SHA256_DIGEST_LENGTH || 255 (md = EVP_sha256()) == NULL || 256 (ctx = EVP_MD_CTX_new()) == NULL || 257 EVP_DigestInit_ex(ctx, md, NULL) != 1 || 258 EVP_DigestUpdate(ctx, &zero, sizeof(zero)) != 1 || 259 EVP_DigestUpdate(ctx, rp_id, rp_id_len) != 1 || 260 EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || 261 EVP_DigestUpdate(ctx, id->ptr, id->len) != 1 || 262 EVP_DigestUpdate(ctx, &four, sizeof(four)) != 1 || 263 EVP_DigestUpdate(ctx, pk->x, sizeof(pk->x)) != 1 || 264 EVP_DigestUpdate(ctx, pk->y, sizeof(pk->y)) != 1 || 265 EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { 266 fido_log_debug("%s: sha256", __func__); 267 goto fail; 268 } 269 270 ok = 0; 271fail: 272 EVP_MD_CTX_free(ctx); 273 274 return (ok); 275} 276 277static int 278verify_attstmt(const fido_blob_t *dgst, const fido_attstmt_t *attstmt) 279{ 280 BIO *rawcert = NULL; 281 X509 *cert = NULL; 282 EVP_PKEY *pkey = NULL; 283 int ok = -1; 284 285 /* openssl needs ints */ 286 if (attstmt->x5c.len > INT_MAX) { 287 fido_log_debug("%s: x5c.len=%zu", __func__, attstmt->x5c.len); 288 return (-1); 289 } 290 291 /* fetch key from x509 */ 292 if ((rawcert = BIO_new_mem_buf(attstmt->x5c.ptr, 293 (int)attstmt->x5c.len)) == NULL || 294 (cert = d2i_X509_bio(rawcert, NULL)) == NULL || 295 (pkey = X509_get_pubkey(cert)) == NULL) { 296 fido_log_debug("%s: x509 key", __func__); 297 goto fail; 298 } 299 300 switch (attstmt->alg) { 301 case COSE_UNSPEC: 302 case COSE_ES256: 303 ok = es256_verify_sig(dgst, pkey, &attstmt->sig); 304 break; 305 case COSE_RS256: 306 ok = rs256_verify_sig(dgst, pkey, &attstmt->sig); 307 break; 308 case COSE_RS1: 309 ok = rs1_verify_sig(dgst, pkey, &attstmt->sig); 310 break; 311 case COSE_EDDSA: 312 ok = eddsa_verify_sig(dgst, pkey, &attstmt->sig); 313 break; 314 default: 315 fido_log_debug("%s: unknown alg %d", __func__, attstmt->alg); 316 break; 317 } 318 319fail: 320 BIO_free(rawcert); 321 X509_free(cert); 322 EVP_PKEY_free(pkey); 323 324 return (ok); 325} 326 327int 328fido_cred_verify(const fido_cred_t *cred) 329{ 330 unsigned char buf[SHA256_DIGEST_LENGTH]; 331 fido_blob_t dgst; 332 int r; 333 334 dgst.ptr = buf; 335 dgst.len = sizeof(buf); 336 337 /* do we have everything we need? */ 338 if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL || 339 cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL || 340 cred->fmt == NULL || cred->attcred.id.ptr == NULL || 341 cred->rp.id == NULL) { 342 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, " 343 "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr, 344 (void *)cred->authdata_cbor.ptr, 345 (void *)cred->attstmt.x5c.ptr, 346 (void *)cred->attstmt.sig.ptr, (void *)cred->fmt, 347 (void *)cred->attcred.id.ptr, cred->rp.id); 348 r = FIDO_ERR_INVALID_ARGUMENT; 349 goto out; 350 } 351 352 if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) { 353 fido_log_debug("%s: fido_check_rp_id", __func__); 354 r = FIDO_ERR_INVALID_PARAM; 355 goto out; 356 } 357 358 if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE, 359 cred->uv) < 0) { 360 fido_log_debug("%s: fido_check_flags", __func__); 361 r = FIDO_ERR_INVALID_PARAM; 362 goto out; 363 } 364 365 if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) { 366 fido_log_debug("%s: check_extensions", __func__); 367 r = FIDO_ERR_INVALID_PARAM; 368 goto out; 369 } 370 371 if (!strcmp(cred->fmt, "packed")) { 372 if (fido_get_signed_hash(COSE_ES256, &dgst, &cred->cdh, 373 &cred->authdata_cbor) < 0) { 374 fido_log_debug("%s: fido_get_signed_hash", __func__); 375 r = FIDO_ERR_INTERNAL; 376 goto out; 377 } 378 } else if (!strcmp(cred->fmt, "fido-u2f")) { 379 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash, 380 sizeof(cred->authdata.rp_id_hash), &cred->cdh, 381 &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) { 382 fido_log_debug("%s: get_signed_hash_u2f", __func__); 383 r = FIDO_ERR_INTERNAL; 384 goto out; 385 } 386 } else if (!strcmp(cred->fmt, "tpm")) { 387 if (fido_get_signed_hash_tpm(&dgst, &cred->cdh, 388 &cred->authdata_raw, &cred->attstmt, &cred->attcred) < 0) { 389 fido_log_debug("%s: fido_get_signed_hash_tpm", __func__); 390 r = FIDO_ERR_INTERNAL; 391 goto out; 392 } 393 } else { 394 fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt); 395 r = FIDO_ERR_INVALID_ARGUMENT; 396 goto out; 397 } 398 399 if (verify_attstmt(&dgst, &cred->attstmt) < 0) { 400 fido_log_debug("%s: verify_attstmt", __func__); 401 r = FIDO_ERR_INVALID_SIG; 402 goto out; 403 } 404 405 r = FIDO_OK; 406out: 407 explicit_bzero(buf, sizeof(buf)); 408 409 return (r); 410} 411 412int 413fido_cred_verify_self(const fido_cred_t *cred) 414{ 415 unsigned char buf[1024]; /* XXX */ 416 fido_blob_t dgst; 417 int ok = -1; 418 int r; 419 420 dgst.ptr = buf; 421 dgst.len = sizeof(buf); 422 423 /* do we have everything we need? */ 424 if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL || 425 cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL || 426 cred->fmt == NULL || cred->attcred.id.ptr == NULL || 427 cred->rp.id == NULL) { 428 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, " 429 "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr, 430 (void *)cred->authdata_cbor.ptr, 431 (void *)cred->attstmt.x5c.ptr, 432 (void *)cred->attstmt.sig.ptr, (void *)cred->fmt, 433 (void *)cred->attcred.id.ptr, cred->rp.id); 434 r = FIDO_ERR_INVALID_ARGUMENT; 435 goto out; 436 } 437 438 if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) { 439 fido_log_debug("%s: fido_check_rp_id", __func__); 440 r = FIDO_ERR_INVALID_PARAM; 441 goto out; 442 } 443 444 if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE, 445 cred->uv) < 0) { 446 fido_log_debug("%s: fido_check_flags", __func__); 447 r = FIDO_ERR_INVALID_PARAM; 448 goto out; 449 } 450 451 if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) { 452 fido_log_debug("%s: check_extensions", __func__); 453 r = FIDO_ERR_INVALID_PARAM; 454 goto out; 455 } 456 457 if (!strcmp(cred->fmt, "packed")) { 458 if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh, 459 &cred->authdata_cbor) < 0) { 460 fido_log_debug("%s: fido_get_signed_hash", __func__); 461 r = FIDO_ERR_INTERNAL; 462 goto out; 463 } 464 } else if (!strcmp(cred->fmt, "fido-u2f")) { 465 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash, 466 sizeof(cred->authdata.rp_id_hash), &cred->cdh, 467 &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) { 468 fido_log_debug("%s: get_signed_hash_u2f", __func__); 469 r = FIDO_ERR_INTERNAL; 470 goto out; 471 } 472 } else { 473 fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt); 474 r = FIDO_ERR_INVALID_ARGUMENT; 475 goto out; 476 } 477 478 switch (cred->attcred.type) { 479 case COSE_ES256: 480 ok = es256_pk_verify_sig(&dgst, &cred->attcred.pubkey.es256, 481 &cred->attstmt.sig); 482 break; 483 case COSE_RS256: 484 ok = rs256_pk_verify_sig(&dgst, &cred->attcred.pubkey.rs256, 485 &cred->attstmt.sig); 486 break; 487 case COSE_EDDSA: 488 ok = eddsa_pk_verify_sig(&dgst, &cred->attcred.pubkey.eddsa, 489 &cred->attstmt.sig); 490 break; 491 default: 492 fido_log_debug("%s: unsupported cose_alg %d", __func__, 493 cred->attcred.type); 494 r = FIDO_ERR_UNSUPPORTED_OPTION; 495 goto out; 496 } 497 498 if (ok < 0) 499 r = FIDO_ERR_INVALID_SIG; 500 else 501 r = FIDO_OK; 502 503out: 504 explicit_bzero(buf, sizeof(buf)); 505 506 return (r); 507} 508 509fido_cred_t * 510fido_cred_new(void) 511{ 512 return (calloc(1, sizeof(fido_cred_t))); 513} 514 515static void 516fido_cred_clean_authdata(fido_cred_t *cred) 517{ 518 fido_blob_reset(&cred->authdata_cbor); 519 fido_blob_reset(&cred->authdata_raw); 520 fido_blob_reset(&cred->attcred.id); 521 522 memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext)); 523 memset(&cred->authdata, 0, sizeof(cred->authdata)); 524 memset(&cred->attcred, 0, sizeof(cred->attcred)); 525} 526 527static void 528fido_cred_clean_attstmt(fido_attstmt_t *attstmt) 529{ 530 fido_blob_reset(&attstmt->certinfo); 531 fido_blob_reset(&attstmt->pubarea); 532 fido_blob_reset(&attstmt->cbor); 533 fido_blob_reset(&attstmt->x5c); 534 fido_blob_reset(&attstmt->sig); 535 536 memset(attstmt, 0, sizeof(*attstmt)); 537} 538 539void 540fido_cred_reset_tx(fido_cred_t *cred) 541{ 542 fido_blob_reset(&cred->cd); 543 fido_blob_reset(&cred->cdh); 544 fido_blob_reset(&cred->user.id); 545 fido_blob_reset(&cred->blob); 546 547 free(cred->rp.id); 548 free(cred->rp.name); 549 free(cred->user.icon); 550 free(cred->user.name); 551 free(cred->user.display_name); 552 fido_free_blob_array(&cred->excl); 553 554 memset(&cred->rp, 0, sizeof(cred->rp)); 555 memset(&cred->user, 0, sizeof(cred->user)); 556 memset(&cred->excl, 0, sizeof(cred->excl)); 557 memset(&cred->ext, 0, sizeof(cred->ext)); 558 559 cred->type = 0; 560 cred->rk = FIDO_OPT_OMIT; 561 cred->uv = FIDO_OPT_OMIT; 562} 563 564void 565fido_cred_reset_rx(fido_cred_t *cred) 566{ 567 free(cred->fmt); 568 cred->fmt = NULL; 569 fido_cred_clean_authdata(cred); 570 fido_cred_clean_attstmt(&cred->attstmt); 571 fido_blob_reset(&cred->largeblob_key); 572} 573 574void 575fido_cred_free(fido_cred_t **cred_p) 576{ 577 fido_cred_t *cred; 578 579 if (cred_p == NULL || (cred = *cred_p) == NULL) 580 return; 581 fido_cred_reset_tx(cred); 582 fido_cred_reset_rx(cred); 583 free(cred); 584 *cred_p = NULL; 585} 586 587int 588fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len) 589{ 590 cbor_item_t *item = NULL; 591 struct cbor_load_result cbor; 592 int r = FIDO_ERR_INVALID_ARGUMENT; 593 594 fido_cred_clean_authdata(cred); 595 596 if (ptr == NULL || len == 0) 597 goto fail; 598 599 if ((item = cbor_load(ptr, len, &cbor)) == NULL) { 600 fido_log_debug("%s: cbor_load", __func__); 601 goto fail; 602 } 603 604 if (fido_blob_decode(item, &cred->authdata_raw) < 0) { 605 fido_log_debug("%s: fido_blob_decode", __func__); 606 goto fail; 607 } 608 609 if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor, 610 &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { 611 fido_log_debug("%s: cbor_decode_cred_authdata", __func__); 612 goto fail; 613 } 614 615 r = FIDO_OK; 616fail: 617 if (item != NULL) 618 cbor_decref(&item); 619 620 if (r != FIDO_OK) 621 fido_cred_clean_authdata(cred); 622 623 return (r); 624} 625 626int 627fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr, 628 size_t len) 629{ 630 cbor_item_t *item = NULL; 631 int r = FIDO_ERR_INVALID_ARGUMENT; 632 633 fido_cred_clean_authdata(cred); 634 635 if (ptr == NULL || len == 0) 636 goto fail; 637 638 if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) { 639 fido_log_debug("%s: fido_blob_set", __func__); 640 r = FIDO_ERR_INTERNAL; 641 goto fail; 642 } 643 644 if ((item = cbor_build_bytestring(ptr, len)) == NULL) { 645 fido_log_debug("%s: cbor_build_bytestring", __func__); 646 r = FIDO_ERR_INTERNAL; 647 goto fail; 648 } 649 650 if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor, 651 &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { 652 fido_log_debug("%s: cbor_decode_cred_authdata", __func__); 653 goto fail; 654 } 655 656 r = FIDO_OK; 657fail: 658 if (item != NULL) 659 cbor_decref(&item); 660 661 if (r != FIDO_OK) 662 fido_cred_clean_authdata(cred); 663 664 return (r); 665} 666 667int 668fido_cred_set_id(fido_cred_t *cred, const unsigned char *ptr, size_t len) 669{ 670 if (fido_blob_set(&cred->attcred.id, ptr, len) < 0) 671 return (FIDO_ERR_INVALID_ARGUMENT); 672 673 return (FIDO_OK); 674} 675 676int 677fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len) 678{ 679 if (fido_blob_set(&cred->attstmt.x5c, ptr, len) < 0) 680 return (FIDO_ERR_INVALID_ARGUMENT); 681 682 return (FIDO_OK); 683} 684 685int 686fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len) 687{ 688 if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0) 689 return (FIDO_ERR_INVALID_ARGUMENT); 690 691 return (FIDO_OK); 692} 693 694int 695fido_cred_set_attstmt(fido_cred_t *cred, const unsigned char *ptr, size_t len) 696{ 697 cbor_item_t *item = NULL; 698 struct cbor_load_result cbor; 699 int r = FIDO_ERR_INVALID_ARGUMENT; 700 701 fido_cred_clean_attstmt(&cred->attstmt); 702 703 if (ptr == NULL || len == 0) 704 goto fail; 705 706 if ((item = cbor_load(ptr, len, &cbor)) == NULL) { 707 fido_log_debug("%s: cbor_load", __func__); 708 goto fail; 709 } 710 711 if (cbor_decode_attstmt(item, &cred->attstmt) < 0) { 712 fido_log_debug("%s: cbor_decode_attstmt", __func__); 713 goto fail; 714 } 715 716 r = FIDO_OK; 717fail: 718 if (item != NULL) 719 cbor_decref(&item); 720 721 if (r != FIDO_OK) 722 fido_cred_clean_attstmt(&cred->attstmt); 723 724 return (r); 725} 726 727int 728fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len) 729{ 730 fido_blob_t id_blob; 731 fido_blob_t *list_ptr; 732 733 memset(&id_blob, 0, sizeof(id_blob)); 734 735 if (fido_blob_set(&id_blob, id_ptr, id_len) < 0) 736 return (FIDO_ERR_INVALID_ARGUMENT); 737 738 if (cred->excl.len == SIZE_MAX) { 739 free(id_blob.ptr); 740 return (FIDO_ERR_INVALID_ARGUMENT); 741 } 742 743 if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len, 744 cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) { 745 free(id_blob.ptr); 746 return (FIDO_ERR_INTERNAL); 747 } 748 749 list_ptr[cred->excl.len++] = id_blob; 750 cred->excl.ptr = list_ptr; 751 752 return (FIDO_OK); 753} 754 755int 756fido_cred_set_clientdata(fido_cred_t *cred, const unsigned char *data, 757 size_t data_len) 758{ 759 if (!fido_blob_is_empty(&cred->cdh) || 760 fido_blob_set(&cred->cd, data, data_len) < 0) { 761 return (FIDO_ERR_INVALID_ARGUMENT); 762 } 763 if (fido_sha256(&cred->cdh, data, data_len) < 0) { 764 fido_blob_reset(&cred->cd); 765 return (FIDO_ERR_INTERNAL); 766 } 767 768 return (FIDO_OK); 769} 770 771int 772fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash, 773 size_t hash_len) 774{ 775 if (!fido_blob_is_empty(&cred->cd) || 776 fido_blob_set(&cred->cdh, hash, hash_len) < 0) 777 return (FIDO_ERR_INVALID_ARGUMENT); 778 779 return (FIDO_OK); 780} 781 782int 783fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name) 784{ 785 fido_rp_t *rp = &cred->rp; 786 787 if (rp->id != NULL) { 788 free(rp->id); 789 rp->id = NULL; 790 } 791 if (rp->name != NULL) { 792 free(rp->name); 793 rp->name = NULL; 794 } 795 796 if (id != NULL && (rp->id = strdup(id)) == NULL) 797 goto fail; 798 if (name != NULL && (rp->name = strdup(name)) == NULL) 799 goto fail; 800 801 return (FIDO_OK); 802fail: 803 free(rp->id); 804 free(rp->name); 805 rp->id = NULL; 806 rp->name = NULL; 807 808 return (FIDO_ERR_INTERNAL); 809} 810 811int 812fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id, 813 size_t user_id_len, const char *name, const char *display_name, 814 const char *icon) 815{ 816 fido_user_t *up = &cred->user; 817 818 if (up->id.ptr != NULL) { 819 free(up->id.ptr); 820 up->id.ptr = NULL; 821 up->id.len = 0; 822 } 823 if (up->name != NULL) { 824 free(up->name); 825 up->name = NULL; 826 } 827 if (up->display_name != NULL) { 828 free(up->display_name); 829 up->display_name = NULL; 830 } 831 if (up->icon != NULL) { 832 free(up->icon); 833 up->icon = NULL; 834 } 835 836 if (user_id != NULL && fido_blob_set(&up->id, user_id, user_id_len) < 0) 837 goto fail; 838 if (name != NULL && (up->name = strdup(name)) == NULL) 839 goto fail; 840 if (display_name != NULL && 841 (up->display_name = strdup(display_name)) == NULL) 842 goto fail; 843 if (icon != NULL && (up->icon = strdup(icon)) == NULL) 844 goto fail; 845 846 return (FIDO_OK); 847fail: 848 free(up->id.ptr); 849 free(up->name); 850 free(up->display_name); 851 free(up->icon); 852 853 up->id.ptr = NULL; 854 up->id.len = 0; 855 up->name = NULL; 856 up->display_name = NULL; 857 up->icon = NULL; 858 859 return (FIDO_ERR_INTERNAL); 860} 861 862int 863fido_cred_set_extensions(fido_cred_t *cred, int ext) 864{ 865 if (ext == 0) 866 cred->ext.mask = 0; 867 else { 868 if ((ext & FIDO_EXT_CRED_MASK) != ext) 869 return (FIDO_ERR_INVALID_ARGUMENT); 870 cred->ext.mask |= ext; 871 } 872 873 return (FIDO_OK); 874} 875 876int 877fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv) 878{ 879 cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; 880 cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; 881 882 return (FIDO_OK); 883} 884 885int 886fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk) 887{ 888 cred->rk = rk; 889 890 return (FIDO_OK); 891} 892 893int 894fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv) 895{ 896 cred->uv = uv; 897 898 return (FIDO_OK); 899} 900 901int 902fido_cred_set_prot(fido_cred_t *cred, int prot) 903{ 904 if (prot == 0) { 905 cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT; 906 cred->ext.prot = 0; 907 } else { 908 if (prot != FIDO_CRED_PROT_UV_OPTIONAL && 909 prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID && 910 prot != FIDO_CRED_PROT_UV_REQUIRED) 911 return (FIDO_ERR_INVALID_ARGUMENT); 912 913 cred->ext.mask |= FIDO_EXT_CRED_PROTECT; 914 cred->ext.prot = prot; 915 } 916 917 return (FIDO_OK); 918} 919 920int 921fido_cred_set_pin_minlen(fido_cred_t *cred, size_t len) 922{ 923 if (len == 0) 924 cred->ext.mask &= ~FIDO_EXT_MINPINLEN; 925 else 926 cred->ext.mask |= FIDO_EXT_MINPINLEN; 927 928 cred->ext.minpinlen = len; 929 930 return (FIDO_OK); 931} 932 933int 934fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len) 935{ 936 if (ptr == NULL || len == 0) 937 return (FIDO_ERR_INVALID_ARGUMENT); 938 if (fido_blob_set(&cred->blob, ptr, len) < 0) 939 return (FIDO_ERR_INTERNAL); 940 941 cred->ext.mask |= FIDO_EXT_CRED_BLOB; 942 943 return (FIDO_OK); 944} 945 946int 947fido_cred_set_fmt(fido_cred_t *cred, const char *fmt) 948{ 949 free(cred->fmt); 950 cred->fmt = NULL; 951 952 if (fmt == NULL) 953 return (FIDO_ERR_INVALID_ARGUMENT); 954 955 if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") && 956 strcmp(fmt, "none") && strcmp(fmt, "tpm")) 957 return (FIDO_ERR_INVALID_ARGUMENT); 958 959 if ((cred->fmt = strdup(fmt)) == NULL) 960 return (FIDO_ERR_INTERNAL); 961 962 return (FIDO_OK); 963} 964 965int 966fido_cred_set_type(fido_cred_t *cred, int cose_alg) 967{ 968 if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 && 969 cose_alg != COSE_EDDSA) || cred->type != 0) 970 return (FIDO_ERR_INVALID_ARGUMENT); 971 972 cred->type = cose_alg; 973 974 return (FIDO_OK); 975} 976 977int 978fido_cred_type(const fido_cred_t *cred) 979{ 980 return (cred->type); 981} 982 983uint8_t 984fido_cred_flags(const fido_cred_t *cred) 985{ 986 return (cred->authdata.flags); 987} 988 989uint32_t 990fido_cred_sigcount(const fido_cred_t *cred) 991{ 992 return (cred->authdata.sigcount); 993} 994 995const unsigned char * 996fido_cred_clientdata_hash_ptr(const fido_cred_t *cred) 997{ 998 return (cred->cdh.ptr); 999} 1000 1001size_t 1002fido_cred_clientdata_hash_len(const fido_cred_t *cred) 1003{ 1004 return (cred->cdh.len); 1005} 1006 1007const unsigned char * 1008fido_cred_x5c_ptr(const fido_cred_t *cred) 1009{ 1010 return (cred->attstmt.x5c.ptr); 1011} 1012 1013size_t 1014fido_cred_x5c_len(const fido_cred_t *cred) 1015{ 1016 return (cred->attstmt.x5c.len); 1017} 1018 1019const unsigned char * 1020fido_cred_sig_ptr(const fido_cred_t *cred) 1021{ 1022 return (cred->attstmt.sig.ptr); 1023} 1024 1025size_t 1026fido_cred_sig_len(const fido_cred_t *cred) 1027{ 1028 return (cred->attstmt.sig.len); 1029} 1030 1031const unsigned char * 1032fido_cred_authdata_ptr(const fido_cred_t *cred) 1033{ 1034 return (cred->authdata_cbor.ptr); 1035} 1036 1037size_t 1038fido_cred_authdata_len(const fido_cred_t *cred) 1039{ 1040 return (cred->authdata_cbor.len); 1041} 1042 1043const unsigned char * 1044fido_cred_authdata_raw_ptr(const fido_cred_t *cred) 1045{ 1046 return (cred->authdata_raw.ptr); 1047} 1048 1049size_t 1050fido_cred_authdata_raw_len(const fido_cred_t *cred) 1051{ 1052 return (cred->authdata_raw.len); 1053} 1054 1055const unsigned char * 1056fido_cred_attstmt_ptr(const fido_cred_t *cred) 1057{ 1058 return (cred->attstmt.cbor.ptr); 1059} 1060 1061size_t 1062fido_cred_attstmt_len(const fido_cred_t *cred) 1063{ 1064 return (cred->attstmt.cbor.len); 1065} 1066 1067const unsigned char * 1068fido_cred_pubkey_ptr(const fido_cred_t *cred) 1069{ 1070 const void *ptr; 1071 1072 switch (cred->attcred.type) { 1073 case COSE_ES256: 1074 ptr = &cred->attcred.pubkey.es256; 1075 break; 1076 case COSE_RS256: 1077 ptr = &cred->attcred.pubkey.rs256; 1078 break; 1079 case COSE_EDDSA: 1080 ptr = &cred->attcred.pubkey.eddsa; 1081 break; 1082 default: 1083 ptr = NULL; 1084 break; 1085 } 1086 1087 return (ptr); 1088} 1089 1090size_t 1091fido_cred_pubkey_len(const fido_cred_t *cred) 1092{ 1093 size_t len; 1094 1095 switch (cred->attcred.type) { 1096 case COSE_ES256: 1097 len = sizeof(cred->attcred.pubkey.es256); 1098 break; 1099 case COSE_RS256: 1100 len = sizeof(cred->attcred.pubkey.rs256); 1101 break; 1102 case COSE_EDDSA: 1103 len = sizeof(cred->attcred.pubkey.eddsa); 1104 break; 1105 default: 1106 len = 0; 1107 break; 1108 } 1109 1110 return (len); 1111} 1112 1113const unsigned char * 1114fido_cred_id_ptr(const fido_cred_t *cred) 1115{ 1116 return (cred->attcred.id.ptr); 1117} 1118 1119size_t 1120fido_cred_id_len(const fido_cred_t *cred) 1121{ 1122 return (cred->attcred.id.len); 1123} 1124 1125const unsigned char * 1126fido_cred_aaguid_ptr(const fido_cred_t *cred) 1127{ 1128 return (cred->attcred.aaguid); 1129} 1130 1131size_t 1132fido_cred_aaguid_len(const fido_cred_t *cred) 1133{ 1134 return (sizeof(cred->attcred.aaguid)); 1135} 1136 1137int 1138fido_cred_prot(const fido_cred_t *cred) 1139{ 1140 return (cred->ext.prot); 1141} 1142 1143size_t 1144fido_cred_pin_minlen(const fido_cred_t *cred) 1145{ 1146 return (cred->ext.minpinlen); 1147} 1148 1149const char * 1150fido_cred_fmt(const fido_cred_t *cred) 1151{ 1152 return (cred->fmt); 1153} 1154 1155const char * 1156fido_cred_rp_id(const fido_cred_t *cred) 1157{ 1158 return (cred->rp.id); 1159} 1160 1161const char * 1162fido_cred_rp_name(const fido_cred_t *cred) 1163{ 1164 return (cred->rp.name); 1165} 1166 1167const char * 1168fido_cred_user_name(const fido_cred_t *cred) 1169{ 1170 return (cred->user.name); 1171} 1172 1173const char * 1174fido_cred_display_name(const fido_cred_t *cred) 1175{ 1176 return (cred->user.display_name); 1177} 1178 1179const unsigned char * 1180fido_cred_user_id_ptr(const fido_cred_t *cred) 1181{ 1182 return (cred->user.id.ptr); 1183} 1184 1185size_t 1186fido_cred_user_id_len(const fido_cred_t *cred) 1187{ 1188 return (cred->user.id.len); 1189} 1190 1191const unsigned char * 1192fido_cred_largeblob_key_ptr(const fido_cred_t *cred) 1193{ 1194 return (cred->largeblob_key.ptr); 1195} 1196 1197size_t 1198fido_cred_largeblob_key_len(const fido_cred_t *cred) 1199{ 1200 return (cred->largeblob_key.len); 1201} 1202