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