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