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