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
22static int
23credman_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
54static int
55credman_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;
108fail:
109	cbor_vector_free(param_cbor, nitems(param_cbor));
110
111	return (ok);
112}
113
114static int
115credman_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;
168fail:
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
178static int
179credman_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
201static int
202credman_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;
228out:
229	freezero(msg, FIDO_MAXMSG);
230
231	return (r);
232}
233
234static int
235credman_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
248int
249fido_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
257static int
258credman_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
293static void
294credman_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
306static int
307credman_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
335static int
336credman_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;
377out:
378	freezero(msg, FIDO_MAXMSG);
379
380	return (r);
381}
382
383static int
384credman_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;
416out:
417	freezero(msg, FIDO_MAXMSG);
418
419	return (r);
420}
421
422static int
423credman_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
454int
455fido_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
463static int
464credman_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;
481fail:
482	free(cred.ptr);
483
484	return (r);
485}
486
487int
488fido_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
496static int
497credman_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
518static void
519credman_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
534static int
535credman_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
563static int
564credman_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;
605out:
606	freezero(msg, FIDO_MAXMSG);
607
608	return (r);
609}
610
611static int
612credman_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;
644out:
645	freezero(msg, FIDO_MAXMSG);
646
647	return (r);
648}
649
650static int
651credman_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
672int
673fido_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
680static int
681credman_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
694int
695fido_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
702fido_credman_rk_t *
703fido_credman_rk_new(void)
704{
705	return (calloc(1, sizeof(fido_credman_rk_t)));
706}
707
708void
709fido_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
721size_t
722fido_credman_rk_count(const fido_credman_rk_t *rk)
723{
724	return (rk->n_rx);
725}
726
727const fido_cred_t *
728fido_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
736fido_credman_metadata_t *
737fido_credman_metadata_new(void)
738{
739	return (calloc(1, sizeof(fido_credman_metadata_t)));
740}
741
742void
743fido_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
754uint64_t
755fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
756{
757	return (metadata->rk_existing);
758}
759
760uint64_t
761fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
762{
763	return (metadata->rk_remaining);
764}
765
766fido_credman_rp_t *
767fido_credman_rp_new(void)
768{
769	return (calloc(1, sizeof(fido_credman_rp_t)));
770}
771
772void
773fido_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
785size_t
786fido_credman_rp_count(const fido_credman_rp_t *rp)
787{
788	return (rp->n_rx);
789}
790
791const char *
792fido_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
800const char *
801fido_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
809size_t
810fido_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
818const unsigned char *
819fido_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