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 17 static int 18 parse_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 49 static int 50 fido_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; 129 fail: 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 138 static int 139 fido_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; 172 fail: 173 free(reply); 174 175 if (r != FIDO_OK) 176 fido_cred_reset_rx(cred); 177 178 return (r); 179 } 180 181 static int 182 fido_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 194 int 195 fido_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 213 static int 214 check_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 226 int 227 fido_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 243 static int 244 get_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; 271 fail: 272 EVP_MD_CTX_free(ctx); 273 274 return (ok); 275 } 276 277 static int 278 verify_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 319 fail: 320 BIO_free(rawcert); 321 X509_free(cert); 322 EVP_PKEY_free(pkey); 323 324 return (ok); 325 } 326 327 int 328 fido_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; 406 out: 407 explicit_bzero(buf, sizeof(buf)); 408 409 return (r); 410 } 411 412 int 413 fido_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 503 out: 504 explicit_bzero(buf, sizeof(buf)); 505 506 return (r); 507 } 508 509 fido_cred_t * 510 fido_cred_new(void) 511 { 512 return (calloc(1, sizeof(fido_cred_t))); 513 } 514 515 static void 516 fido_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 527 static void 528 fido_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 539 void 540 fido_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 564 void 565 fido_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 574 void 575 fido_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 587 int 588 fido_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; 616 fail: 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 626 int 627 fido_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; 657 fail: 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 667 int 668 fido_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 676 int 677 fido_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 685 int 686 fido_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 694 int 695 fido_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; 717 fail: 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 727 int 728 fido_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 755 int 756 fido_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 771 int 772 fido_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 782 int 783 fido_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); 802 fail: 803 free(rp->id); 804 free(rp->name); 805 rp->id = NULL; 806 rp->name = NULL; 807 808 return (FIDO_ERR_INTERNAL); 809 } 810 811 int 812 fido_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); 847 fail: 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 862 int 863 fido_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 876 int 877 fido_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 885 int 886 fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk) 887 { 888 cred->rk = rk; 889 890 return (FIDO_OK); 891 } 892 893 int 894 fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv) 895 { 896 cred->uv = uv; 897 898 return (FIDO_OK); 899 } 900 901 int 902 fido_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 920 int 921 fido_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 933 int 934 fido_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 946 int 947 fido_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 965 int 966 fido_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 977 int 978 fido_cred_type(const fido_cred_t *cred) 979 { 980 return (cred->type); 981 } 982 983 uint8_t 984 fido_cred_flags(const fido_cred_t *cred) 985 { 986 return (cred->authdata.flags); 987 } 988 989 uint32_t 990 fido_cred_sigcount(const fido_cred_t *cred) 991 { 992 return (cred->authdata.sigcount); 993 } 994 995 const unsigned char * 996 fido_cred_clientdata_hash_ptr(const fido_cred_t *cred) 997 { 998 return (cred->cdh.ptr); 999 } 1000 1001 size_t 1002 fido_cred_clientdata_hash_len(const fido_cred_t *cred) 1003 { 1004 return (cred->cdh.len); 1005 } 1006 1007 const unsigned char * 1008 fido_cred_x5c_ptr(const fido_cred_t *cred) 1009 { 1010 return (cred->attstmt.x5c.ptr); 1011 } 1012 1013 size_t 1014 fido_cred_x5c_len(const fido_cred_t *cred) 1015 { 1016 return (cred->attstmt.x5c.len); 1017 } 1018 1019 const unsigned char * 1020 fido_cred_sig_ptr(const fido_cred_t *cred) 1021 { 1022 return (cred->attstmt.sig.ptr); 1023 } 1024 1025 size_t 1026 fido_cred_sig_len(const fido_cred_t *cred) 1027 { 1028 return (cred->attstmt.sig.len); 1029 } 1030 1031 const unsigned char * 1032 fido_cred_authdata_ptr(const fido_cred_t *cred) 1033 { 1034 return (cred->authdata_cbor.ptr); 1035 } 1036 1037 size_t 1038 fido_cred_authdata_len(const fido_cred_t *cred) 1039 { 1040 return (cred->authdata_cbor.len); 1041 } 1042 1043 const unsigned char * 1044 fido_cred_authdata_raw_ptr(const fido_cred_t *cred) 1045 { 1046 return (cred->authdata_raw.ptr); 1047 } 1048 1049 size_t 1050 fido_cred_authdata_raw_len(const fido_cred_t *cred) 1051 { 1052 return (cred->authdata_raw.len); 1053 } 1054 1055 const unsigned char * 1056 fido_cred_attstmt_ptr(const fido_cred_t *cred) 1057 { 1058 return (cred->attstmt.cbor.ptr); 1059 } 1060 1061 size_t 1062 fido_cred_attstmt_len(const fido_cred_t *cred) 1063 { 1064 return (cred->attstmt.cbor.len); 1065 } 1066 1067 const unsigned char * 1068 fido_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 1090 size_t 1091 fido_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 1113 const unsigned char * 1114 fido_cred_id_ptr(const fido_cred_t *cred) 1115 { 1116 return (cred->attcred.id.ptr); 1117 } 1118 1119 size_t 1120 fido_cred_id_len(const fido_cred_t *cred) 1121 { 1122 return (cred->attcred.id.len); 1123 } 1124 1125 const unsigned char * 1126 fido_cred_aaguid_ptr(const fido_cred_t *cred) 1127 { 1128 return (cred->attcred.aaguid); 1129 } 1130 1131 size_t 1132 fido_cred_aaguid_len(const fido_cred_t *cred) 1133 { 1134 return (sizeof(cred->attcred.aaguid)); 1135 } 1136 1137 int 1138 fido_cred_prot(const fido_cred_t *cred) 1139 { 1140 return (cred->ext.prot); 1141 } 1142 1143 size_t 1144 fido_cred_pin_minlen(const fido_cred_t *cred) 1145 { 1146 return (cred->ext.minpinlen); 1147 } 1148 1149 const char * 1150 fido_cred_fmt(const fido_cred_t *cred) 1151 { 1152 return (cred->fmt); 1153 } 1154 1155 const char * 1156 fido_cred_rp_id(const fido_cred_t *cred) 1157 { 1158 return (cred->rp.id); 1159 } 1160 1161 const char * 1162 fido_cred_rp_name(const fido_cred_t *cred) 1163 { 1164 return (cred->rp.name); 1165 } 1166 1167 const char * 1168 fido_cred_user_name(const fido_cred_t *cred) 1169 { 1170 return (cred->user.name); 1171 } 1172 1173 const char * 1174 fido_cred_display_name(const fido_cred_t *cred) 1175 { 1176 return (cred->user.display_name); 1177 } 1178 1179 const unsigned char * 1180 fido_cred_user_id_ptr(const fido_cred_t *cred) 1181 { 1182 return (cred->user.id.ptr); 1183 } 1184 1185 size_t 1186 fido_cred_user_id_len(const fido_cred_t *cred) 1187 { 1188 return (cred->user.id.len); 1189 } 1190 1191 const unsigned char * 1192 fido_cred_largeblob_key_ptr(const fido_cred_t *cred) 1193 { 1194 return (cred->largeblob_key.ptr); 1195 } 1196 1197 size_t 1198 fido_cred_largeblob_key_len(const fido_cred_t *cred) 1199 { 1200 return (cred->largeblob_key.len); 1201 } 1202