xref: /freebsd/contrib/libfido2/src/credman.c (revision febb0da5bf4bc99828ebede7abcb039514ac367a)
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
credman_grow_array(void ** ptr,size_t * n_alloc,const size_t * n_rx,size_t n,size_t size)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
credman_prepare_hmac(uint8_t cmd,const void * body,cbor_item_t ** param,fido_blob_t * hmac_data)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
credman_get_cmd(const fido_dev_t * dev)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
credman_tx(fido_dev_t * dev,uint8_t subcmd,const void * param,const char * pin,const char * rp_id,fido_opt_t uv,int * ms)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
credman_parse_metadata(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
credman_rx_metadata(fido_dev_t * dev,fido_credman_metadata_t * metadata,int * ms)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
credman_get_metadata_wait(fido_dev_t * dev,fido_credman_metadata_t * metadata,const char * pin,int * ms)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
fido_credman_get_dev_metadata(fido_dev_t * dev,fido_credman_metadata_t * metadata,const char * pin)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
credman_parse_rk(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
credman_reset_rk(fido_credman_rk_t * rk)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
credman_parse_rk_count(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
credman_rx_rk(fido_dev_t * dev,fido_credman_rk_t * rk,int * ms)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
credman_rx_next_rk(fido_dev_t * dev,fido_credman_rk_t * rk,int * ms)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
credman_get_rk_wait(fido_dev_t * dev,const char * rp_id,fido_credman_rk_t * rk,const char * pin,int * ms)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
fido_credman_get_dev_rk(fido_dev_t * dev,const char * rp_id,fido_credman_rk_t * rk,const char * pin)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
credman_del_rk_wait(fido_dev_t * dev,const unsigned char * cred_id,size_t cred_id_len,const char * pin,int * ms)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
fido_credman_del_dev_rk(fido_dev_t * dev,const unsigned char * cred_id,size_t cred_id_len,const char * pin)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
credman_parse_rp(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
credman_reset_rp(fido_credman_rp_t * rp)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
credman_parse_rp_count(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
credman_rx_rp(fido_dev_t * dev,fido_credman_rp_t * rp,int * ms)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
credman_rx_next_rp(fido_dev_t * dev,fido_credman_rp_t * rp,int * ms)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
credman_get_rp_wait(fido_dev_t * dev,fido_credman_rp_t * rp,const char * pin,int * ms)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
fido_credman_get_dev_rp(fido_dev_t * dev,fido_credman_rp_t * rp,const char * pin)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
credman_set_dev_rk_wait(fido_dev_t * dev,fido_cred_t * cred,const char * pin,int * ms)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
fido_credman_set_dev_rk(fido_dev_t * dev,fido_cred_t * cred,const char * pin)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 *
fido_credman_rk_new(void)712 fido_credman_rk_new(void)
713 {
714 	return (calloc(1, sizeof(fido_credman_rk_t)));
715 }
716 
717 void
fido_credman_rk_free(fido_credman_rk_t ** rk_p)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
fido_credman_rk_count(const fido_credman_rk_t * rk)731 fido_credman_rk_count(const fido_credman_rk_t *rk)
732 {
733 	return (rk->n_rx);
734 }
735 
736 const fido_cred_t *
fido_credman_rk(const fido_credman_rk_t * rk,size_t idx)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 *
fido_credman_metadata_new(void)746 fido_credman_metadata_new(void)
747 {
748 	return (calloc(1, sizeof(fido_credman_metadata_t)));
749 }
750 
751 void
fido_credman_metadata_free(fido_credman_metadata_t ** metadata_p)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
fido_credman_rk_existing(const fido_credman_metadata_t * metadata)764 fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
765 {
766 	return (metadata->rk_existing);
767 }
768 
769 uint64_t
fido_credman_rk_remaining(const fido_credman_metadata_t * metadata)770 fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
771 {
772 	return (metadata->rk_remaining);
773 }
774 
775 fido_credman_rp_t *
fido_credman_rp_new(void)776 fido_credman_rp_new(void)
777 {
778 	return (calloc(1, sizeof(fido_credman_rp_t)));
779 }
780 
781 void
fido_credman_rp_free(fido_credman_rp_t ** rp_p)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
fido_credman_rp_count(const fido_credman_rp_t * rp)795 fido_credman_rp_count(const fido_credman_rp_t *rp)
796 {
797 	return (rp->n_rx);
798 }
799 
800 const char *
fido_credman_rp_id(const fido_credman_rp_t * rp,size_t idx)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 *
fido_credman_rp_name(const fido_credman_rp_t * rp,size_t idx)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
fido_credman_rp_id_hash_len(const fido_credman_rp_t * rp,size_t idx)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 *
fido_credman_rp_id_hash_ptr(const fido_credman_rp_t * rp,size_t idx)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