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