xref: /freebsd/contrib/libfido2/src/credman.c (revision 4d846d26)
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(&param_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