1 /* 2 * Copyright (c) 2019-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 9 #include "fido.h" 10 #include "fido/credman.h" 11 #include "fido/es256.h" 12 13 #define CMD_CRED_METADATA 0x01 14 #define CMD_RP_BEGIN 0x02 15 #define CMD_RP_NEXT 0x03 16 #define CMD_RK_BEGIN 0x04 17 #define CMD_RK_NEXT 0x05 18 #define CMD_DELETE_CRED 0x06 19 #define CMD_UPDATE_CRED 0x07 20 21 static int 22 credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n, 23 size_t size) 24 { 25 void *new_ptr; 26 27 #ifdef FIDO_FUZZ 28 if (n > UINT8_MAX) { 29 fido_log_debug("%s: n > UINT8_MAX", __func__); 30 return (-1); 31 } 32 #endif 33 34 if (n < *n_alloc) 35 return (0); 36 37 /* sanity check */ 38 if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) { 39 fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n, 40 *n_rx, *n_alloc); 41 return (-1); 42 } 43 44 if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL) 45 return (-1); 46 47 *ptr = new_ptr; 48 *n_alloc = n; 49 50 return (0); 51 } 52 53 static int 54 credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param, 55 fido_blob_t *hmac_data) 56 { 57 cbor_item_t *param_cbor[3]; 58 const fido_cred_t *cred; 59 size_t n; 60 int ok = -1; 61 62 memset(¶m_cbor, 0, sizeof(param_cbor)); 63 64 if (body == NULL) 65 return (fido_blob_set(hmac_data, &cmd, sizeof(cmd))); 66 67 switch (cmd) { 68 case CMD_RK_BEGIN: 69 n = 1; 70 if ((param_cbor[0] = fido_blob_encode(body)) == NULL) { 71 fido_log_debug("%s: cbor encode", __func__); 72 goto fail; 73 } 74 break; 75 case CMD_DELETE_CRED: 76 n = 2; 77 if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) { 78 fido_log_debug("%s: cbor encode", __func__); 79 goto fail; 80 } 81 break; 82 case CMD_UPDATE_CRED: 83 n = 3; 84 cred = body; 85 param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id); 86 param_cbor[2] = cbor_encode_user_entity(&cred->user); 87 if (param_cbor[1] == NULL || param_cbor[2] == NULL) { 88 fido_log_debug("%s: cbor encode", __func__); 89 goto fail; 90 } 91 break; 92 default: 93 fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd); 94 return (-1); 95 } 96 97 if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) { 98 fido_log_debug("%s: cbor_flatten_vector", __func__); 99 goto fail; 100 } 101 if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) { 102 fido_log_debug("%s: cbor_build_frame", __func__); 103 goto fail; 104 } 105 106 ok = 0; 107 fail: 108 cbor_vector_free(param_cbor, nitems(param_cbor)); 109 110 return (ok); 111 } 112 113 static int 114 credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin, 115 const char *rp_id, fido_opt_t uv, int *ms) 116 { 117 fido_blob_t f; 118 fido_blob_t *ecdh = NULL; 119 fido_blob_t hmac; 120 es256_pk_t *pk = NULL; 121 cbor_item_t *argv[4]; 122 const uint8_t cmd = CTAP_CBOR_CRED_MGMT_PRE; 123 int r = FIDO_ERR_INTERNAL; 124 125 memset(&f, 0, sizeof(f)); 126 memset(&hmac, 0, sizeof(hmac)); 127 memset(&argv, 0, sizeof(argv)); 128 129 if (fido_dev_is_fido2(dev) == false) { 130 fido_log_debug("%s: fido_dev_is_fido2", __func__); 131 r = FIDO_ERR_INVALID_COMMAND; 132 goto fail; 133 } 134 135 /* subCommand */ 136 if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) { 137 fido_log_debug("%s: cbor encode", __func__); 138 goto fail; 139 } 140 141 /* pinProtocol, pinAuth */ 142 if (pin != NULL || uv == FIDO_OPT_TRUE) { 143 if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) { 144 fido_log_debug("%s: credman_prepare_hmac", __func__); 145 goto fail; 146 } 147 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { 148 fido_log_debug("%s: fido_do_ecdh", __func__); 149 goto fail; 150 } 151 if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, 152 rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) { 153 fido_log_debug("%s: cbor_add_uv_params", __func__); 154 goto fail; 155 } 156 } 157 158 /* framing and transmission */ 159 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || 160 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { 161 fido_log_debug("%s: fido_tx", __func__); 162 r = FIDO_ERR_TX; 163 goto fail; 164 } 165 166 r = FIDO_OK; 167 fail: 168 es256_pk_free(&pk); 169 fido_blob_free(&ecdh); 170 cbor_vector_free(argv, nitems(argv)); 171 free(f.ptr); 172 free(hmac.ptr); 173 174 return (r); 175 } 176 177 static int 178 credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val, 179 void *arg) 180 { 181 fido_credman_metadata_t *metadata = arg; 182 183 if (cbor_isa_uint(key) == false || 184 cbor_int_get_width(key) != CBOR_INT_8) { 185 fido_log_debug("%s: cbor type", __func__); 186 return (0); /* ignore */ 187 } 188 189 switch (cbor_get_uint8(key)) { 190 case 1: 191 return (cbor_decode_uint64(val, &metadata->rk_existing)); 192 case 2: 193 return (cbor_decode_uint64(val, &metadata->rk_remaining)); 194 default: 195 fido_log_debug("%s: cbor type", __func__); 196 return (0); /* ignore */ 197 } 198 } 199 200 static int 201 credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms) 202 { 203 unsigned char reply[FIDO_MAXMSG]; 204 int reply_len; 205 int r; 206 207 memset(metadata, 0, sizeof(*metadata)); 208 209 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 210 ms)) < 0) { 211 fido_log_debug("%s: fido_rx", __func__); 212 return (FIDO_ERR_RX); 213 } 214 215 if ((r = cbor_parse_reply(reply, (size_t)reply_len, metadata, 216 credman_parse_metadata)) != FIDO_OK) { 217 fido_log_debug("%s: credman_parse_metadata", __func__); 218 return (r); 219 } 220 221 return (FIDO_OK); 222 } 223 224 static int 225 credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata, 226 const char *pin, int *ms) 227 { 228 int r; 229 230 if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL, 231 FIDO_OPT_TRUE, ms)) != FIDO_OK || 232 (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK) 233 return (r); 234 235 return (FIDO_OK); 236 } 237 238 int 239 fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, 240 const char *pin) 241 { 242 int ms = dev->timeout_ms; 243 244 return (credman_get_metadata_wait(dev, metadata, pin, &ms)); 245 } 246 247 static int 248 credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) 249 { 250 fido_cred_t *cred = arg; 251 uint64_t prot; 252 253 if (cbor_isa_uint(key) == false || 254 cbor_int_get_width(key) != CBOR_INT_8) { 255 fido_log_debug("%s: cbor type", __func__); 256 return (0); /* ignore */ 257 } 258 259 switch (cbor_get_uint8(key)) { 260 case 6: 261 return (cbor_decode_user(val, &cred->user)); 262 case 7: 263 return (cbor_decode_cred_id(val, &cred->attcred.id)); 264 case 8: 265 if (cbor_decode_pubkey(val, &cred->attcred.type, 266 &cred->attcred.pubkey) < 0) 267 return (-1); 268 cred->type = cred->attcred.type; /* XXX */ 269 return (0); 270 case 10: 271 if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX || 272 fido_cred_set_prot(cred, (int)prot) != FIDO_OK) 273 return (-1); 274 return (0); 275 case 11: 276 return (fido_blob_decode(val, &cred->largeblob_key)); 277 default: 278 fido_log_debug("%s: cbor type", __func__); 279 return (0); /* ignore */ 280 } 281 } 282 283 static void 284 credman_reset_rk(fido_credman_rk_t *rk) 285 { 286 for (size_t i = 0; i < rk->n_alloc; i++) { 287 fido_cred_reset_tx(&rk->ptr[i]); 288 fido_cred_reset_rx(&rk->ptr[i]); 289 } 290 291 free(rk->ptr); 292 rk->ptr = NULL; 293 memset(rk, 0, sizeof(*rk)); 294 } 295 296 static int 297 credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val, 298 void *arg) 299 { 300 fido_credman_rk_t *rk = arg; 301 uint64_t n; 302 303 /* totalCredentials */ 304 if (cbor_isa_uint(key) == false || 305 cbor_int_get_width(key) != CBOR_INT_8 || 306 cbor_get_uint8(key) != 9) { 307 fido_log_debug("%s: cbor_type", __func__); 308 return (0); /* ignore */ 309 } 310 311 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { 312 fido_log_debug("%s: cbor_decode_uint64", __func__); 313 return (-1); 314 } 315 316 if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx, 317 (size_t)n, sizeof(*rk->ptr)) < 0) { 318 fido_log_debug("%s: credman_grow_array", __func__); 319 return (-1); 320 } 321 322 return (0); 323 } 324 325 static int 326 credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) 327 { 328 unsigned char reply[FIDO_MAXMSG]; 329 int reply_len; 330 int r; 331 332 credman_reset_rk(rk); 333 334 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 335 ms)) < 0) { 336 fido_log_debug("%s: fido_rx", __func__); 337 return (FIDO_ERR_RX); 338 } 339 340 /* adjust as needed */ 341 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rk, 342 credman_parse_rk_count)) != FIDO_OK) { 343 fido_log_debug("%s: credman_parse_rk_count", __func__); 344 return (r); 345 } 346 347 if (rk->n_alloc == 0) { 348 fido_log_debug("%s: n_alloc=0", __func__); 349 return (FIDO_OK); 350 } 351 352 /* parse the first rk */ 353 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[0], 354 credman_parse_rk)) != FIDO_OK) { 355 fido_log_debug("%s: credman_parse_rk", __func__); 356 return (r); 357 } 358 359 rk->n_rx++; 360 361 return (FIDO_OK); 362 } 363 364 static int 365 credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) 366 { 367 unsigned char reply[FIDO_MAXMSG]; 368 int reply_len; 369 int r; 370 371 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 372 ms)) < 0) { 373 fido_log_debug("%s: fido_rx", __func__); 374 return (FIDO_ERR_RX); 375 } 376 377 /* sanity check */ 378 if (rk->n_rx >= rk->n_alloc) { 379 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx, 380 rk->n_alloc); 381 return (FIDO_ERR_INTERNAL); 382 } 383 384 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx], 385 credman_parse_rk)) != FIDO_OK) { 386 fido_log_debug("%s: credman_parse_rk", __func__); 387 return (r); 388 } 389 390 return (FIDO_OK); 391 } 392 393 static int 394 credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk, 395 const char *pin, int *ms) 396 { 397 fido_blob_t rp_dgst; 398 uint8_t dgst[SHA256_DIGEST_LENGTH]; 399 int r; 400 401 if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) { 402 fido_log_debug("%s: sha256", __func__); 403 return (FIDO_ERR_INTERNAL); 404 } 405 406 rp_dgst.ptr = dgst; 407 rp_dgst.len = sizeof(dgst); 408 409 if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id, 410 FIDO_OPT_TRUE, ms)) != FIDO_OK || 411 (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK) 412 return (r); 413 414 while (rk->n_rx < rk->n_alloc) { 415 if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL, 416 FIDO_OPT_FALSE, ms)) != FIDO_OK || 417 (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK) 418 return (r); 419 rk->n_rx++; 420 } 421 422 return (FIDO_OK); 423 } 424 425 int 426 fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id, 427 fido_credman_rk_t *rk, const char *pin) 428 { 429 int ms = dev->timeout_ms; 430 431 return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms)); 432 } 433 434 static int 435 credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id, 436 size_t cred_id_len, const char *pin, int *ms) 437 { 438 fido_blob_t cred; 439 int r; 440 441 memset(&cred, 0, sizeof(cred)); 442 443 if (fido_blob_set(&cred, cred_id, cred_id_len) < 0) 444 return (FIDO_ERR_INVALID_ARGUMENT); 445 446 if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL, 447 FIDO_OPT_TRUE, ms)) != FIDO_OK || 448 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) 449 goto fail; 450 451 r = FIDO_OK; 452 fail: 453 free(cred.ptr); 454 455 return (r); 456 } 457 458 int 459 fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id, 460 size_t cred_id_len, const char *pin) 461 { 462 int ms = dev->timeout_ms; 463 464 return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms)); 465 } 466 467 static int 468 credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg) 469 { 470 struct fido_credman_single_rp *rp = arg; 471 472 if (cbor_isa_uint(key) == false || 473 cbor_int_get_width(key) != CBOR_INT_8) { 474 fido_log_debug("%s: cbor type", __func__); 475 return (0); /* ignore */ 476 } 477 478 switch (cbor_get_uint8(key)) { 479 case 3: 480 return (cbor_decode_rp_entity(val, &rp->rp_entity)); 481 case 4: 482 return (fido_blob_decode(val, &rp->rp_id_hash)); 483 default: 484 fido_log_debug("%s: cbor type", __func__); 485 return (0); /* ignore */ 486 } 487 } 488 489 static void 490 credman_reset_rp(fido_credman_rp_t *rp) 491 { 492 for (size_t i = 0; i < rp->n_alloc; i++) { 493 free(rp->ptr[i].rp_entity.id); 494 free(rp->ptr[i].rp_entity.name); 495 rp->ptr[i].rp_entity.id = NULL; 496 rp->ptr[i].rp_entity.name = NULL; 497 fido_blob_reset(&rp->ptr[i].rp_id_hash); 498 } 499 500 free(rp->ptr); 501 rp->ptr = NULL; 502 memset(rp, 0, sizeof(*rp)); 503 } 504 505 static int 506 credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val, 507 void *arg) 508 { 509 fido_credman_rp_t *rp = arg; 510 uint64_t n; 511 512 /* totalRPs */ 513 if (cbor_isa_uint(key) == false || 514 cbor_int_get_width(key) != CBOR_INT_8 || 515 cbor_get_uint8(key) != 5) { 516 fido_log_debug("%s: cbor_type", __func__); 517 return (0); /* ignore */ 518 } 519 520 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { 521 fido_log_debug("%s: cbor_decode_uint64", __func__); 522 return (-1); 523 } 524 525 if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx, 526 (size_t)n, sizeof(*rp->ptr)) < 0) { 527 fido_log_debug("%s: credman_grow_array", __func__); 528 return (-1); 529 } 530 531 return (0); 532 } 533 534 static int 535 credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) 536 { 537 unsigned char reply[FIDO_MAXMSG]; 538 int reply_len; 539 int r; 540 541 credman_reset_rp(rp); 542 543 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 544 ms)) < 0) { 545 fido_log_debug("%s: fido_rx", __func__); 546 return (FIDO_ERR_RX); 547 } 548 549 /* adjust as needed */ 550 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rp, 551 credman_parse_rp_count)) != FIDO_OK) { 552 fido_log_debug("%s: credman_parse_rp_count", __func__); 553 return (r); 554 } 555 556 if (rp->n_alloc == 0) { 557 fido_log_debug("%s: n_alloc=0", __func__); 558 return (FIDO_OK); 559 } 560 561 /* parse the first rp */ 562 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[0], 563 credman_parse_rp)) != FIDO_OK) { 564 fido_log_debug("%s: credman_parse_rp", __func__); 565 return (r); 566 } 567 568 rp->n_rx++; 569 570 return (FIDO_OK); 571 } 572 573 static int 574 credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) 575 { 576 unsigned char reply[FIDO_MAXMSG]; 577 int reply_len; 578 int r; 579 580 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 581 ms)) < 0) { 582 fido_log_debug("%s: fido_rx", __func__); 583 return (FIDO_ERR_RX); 584 } 585 586 /* sanity check */ 587 if (rp->n_rx >= rp->n_alloc) { 588 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx, 589 rp->n_alloc); 590 return (FIDO_ERR_INTERNAL); 591 } 592 593 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx], 594 credman_parse_rp)) != FIDO_OK) { 595 fido_log_debug("%s: credman_parse_rp", __func__); 596 return (r); 597 } 598 599 return (FIDO_OK); 600 } 601 602 static int 603 credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin, 604 int *ms) 605 { 606 int r; 607 608 if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL, 609 FIDO_OPT_TRUE, ms)) != FIDO_OK || 610 (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK) 611 return (r); 612 613 while (rp->n_rx < rp->n_alloc) { 614 if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL, 615 FIDO_OPT_FALSE, ms)) != FIDO_OK || 616 (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK) 617 return (r); 618 rp->n_rx++; 619 } 620 621 return (FIDO_OK); 622 } 623 624 int 625 fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin) 626 { 627 int ms = dev->timeout_ms; 628 629 return (credman_get_rp_wait(dev, rp, pin, &ms)); 630 } 631 632 static int 633 credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, 634 int *ms) 635 { 636 int r; 637 638 if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL, 639 FIDO_OPT_TRUE, ms)) != FIDO_OK || 640 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) 641 return (r); 642 643 return (FIDO_OK); 644 } 645 646 int 647 fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin) 648 { 649 int ms = dev->timeout_ms; 650 651 return (credman_set_dev_rk_wait(dev, cred, pin, &ms)); 652 } 653 654 fido_credman_rk_t * 655 fido_credman_rk_new(void) 656 { 657 return (calloc(1, sizeof(fido_credman_rk_t))); 658 } 659 660 void 661 fido_credman_rk_free(fido_credman_rk_t **rk_p) 662 { 663 fido_credman_rk_t *rk; 664 665 if (rk_p == NULL || (rk = *rk_p) == NULL) 666 return; 667 668 credman_reset_rk(rk); 669 free(rk); 670 *rk_p = NULL; 671 } 672 673 size_t 674 fido_credman_rk_count(const fido_credman_rk_t *rk) 675 { 676 return (rk->n_rx); 677 } 678 679 const fido_cred_t * 680 fido_credman_rk(const fido_credman_rk_t *rk, size_t idx) 681 { 682 if (idx >= rk->n_alloc) 683 return (NULL); 684 685 return (&rk->ptr[idx]); 686 } 687 688 fido_credman_metadata_t * 689 fido_credman_metadata_new(void) 690 { 691 return (calloc(1, sizeof(fido_credman_metadata_t))); 692 } 693 694 void 695 fido_credman_metadata_free(fido_credman_metadata_t **metadata_p) 696 { 697 fido_credman_metadata_t *metadata; 698 699 if (metadata_p == NULL || (metadata = *metadata_p) == NULL) 700 return; 701 702 free(metadata); 703 *metadata_p = NULL; 704 } 705 706 uint64_t 707 fido_credman_rk_existing(const fido_credman_metadata_t *metadata) 708 { 709 return (metadata->rk_existing); 710 } 711 712 uint64_t 713 fido_credman_rk_remaining(const fido_credman_metadata_t *metadata) 714 { 715 return (metadata->rk_remaining); 716 } 717 718 fido_credman_rp_t * 719 fido_credman_rp_new(void) 720 { 721 return (calloc(1, sizeof(fido_credman_rp_t))); 722 } 723 724 void 725 fido_credman_rp_free(fido_credman_rp_t **rp_p) 726 { 727 fido_credman_rp_t *rp; 728 729 if (rp_p == NULL || (rp = *rp_p) == NULL) 730 return; 731 732 credman_reset_rp(rp); 733 free(rp); 734 *rp_p = NULL; 735 } 736 737 size_t 738 fido_credman_rp_count(const fido_credman_rp_t *rp) 739 { 740 return (rp->n_rx); 741 } 742 743 const char * 744 fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx) 745 { 746 if (idx >= rp->n_alloc) 747 return (NULL); 748 749 return (rp->ptr[idx].rp_entity.id); 750 } 751 752 const char * 753 fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx) 754 { 755 if (idx >= rp->n_alloc) 756 return (NULL); 757 758 return (rp->ptr[idx].rp_entity.name); 759 } 760 761 size_t 762 fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx) 763 { 764 if (idx >= rp->n_alloc) 765 return (0); 766 767 return (rp->ptr[idx].rp_id_hash.len); 768 } 769 770 const unsigned char * 771 fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx) 772 { 773 if (idx >= rp->n_alloc) 774 return (NULL); 775 776 return (rp->ptr[idx].rp_id_hash.ptr); 777 } 778