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