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