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