xref: /openbsd/lib/libfido2/src/bio.c (revision e5dd7070)
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 <string.h>
8 
9 #include "fido.h"
10 #include "fido/bio.h"
11 #include "fido/es256.h"
12 
13 #define CMD_ENROLL_BEGIN	0x01
14 #define CMD_ENROLL_NEXT		0x02
15 #define CMD_ENROLL_CANCEL	0x03
16 #define CMD_ENUM		0x04
17 #define CMD_SET_NAME		0x05
18 #define CMD_ENROLL_REMOVE	0x06
19 #define CMD_GET_INFO		0x07
20 
21 static int
22 bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
23     cbor_item_t **param, fido_blob_t *hmac_data)
24 {
25 	const uint8_t	 prefix[2] = { 0x01 /* modality */, cmd };
26 	int		 ok = -1;
27 	size_t		 cbor_alloc_len;
28 	size_t		 cbor_len;
29 	unsigned char	*cbor = NULL;
30 
31 	if (argv == NULL || param == NULL)
32 		return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
33 
34 	if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
35 		fido_log_debug("%s: cbor_flatten_vector", __func__);
36 		goto fail;
37 	}
38 
39 	if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
40 	    &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
41 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
42 		goto fail;
43 	}
44 
45 	if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
46 		fido_log_debug("%s: malloc", __func__);
47 		goto fail;
48 	}
49 
50 	memcpy(hmac_data->ptr, prefix, sizeof(prefix));
51 	memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
52 	hmac_data->len = cbor_len + sizeof(prefix);
53 
54 	ok = 0;
55 fail:
56 	free(cbor);
57 
58 	return (ok);
59 }
60 
61 static int
62 bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
63     const char *pin, const fido_blob_t *token)
64 {
65 	cbor_item_t	*argv[5];
66 	es256_pk_t	*pk = NULL;
67 	fido_blob_t	*ecdh = NULL;
68 	fido_blob_t	 f;
69 	fido_blob_t	 hmac;
70 	int		 r = FIDO_ERR_INTERNAL;
71 
72 	memset(&f, 0, sizeof(f));
73 	memset(&hmac, 0, sizeof(hmac));
74 	memset(&argv, 0, sizeof(argv));
75 
76 	/* modality, subCommand */
77 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
78 	    (argv[1] = cbor_build_uint8(cmd)) == NULL) {
79 		fido_log_debug("%s: cbor encode", __func__);
80 		goto fail;
81 	}
82 
83 	/* subParams */
84 	if (pin || token) {
85 		if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2],
86 		    &hmac) < 0) {
87 			fido_log_debug("%s: bio_prepare_hmac", __func__);
88 			goto fail;
89 		}
90 	}
91 
92 	/* pinProtocol, pinAuth */
93 	if (pin) {
94 		if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
95 			fido_log_debug("%s: fido_do_ecdh", __func__);
96 			goto fail;
97 		}
98 		if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
99 		    &argv[4], &argv[3])) != FIDO_OK) {
100 			fido_log_debug("%s: cbor_add_pin_params", __func__);
101 			goto fail;
102 		}
103 	} else if (token) {
104 		if ((argv[3] = cbor_encode_pin_opt()) == NULL ||
105 		    (argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) {
106 			fido_log_debug("%s: encode pin", __func__);
107 			goto fail;
108 		}
109 	}
110 
111 	/* framing and transmission */
112 	if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, nitems(argv),
113 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
114 		fido_log_debug("%s: fido_tx", __func__);
115 		r = FIDO_ERR_TX;
116 		goto fail;
117 	}
118 
119 	r = FIDO_OK;
120 fail:
121 	cbor_vector_free(argv, nitems(argv));
122 	es256_pk_free(&pk);
123 	fido_blob_free(&ecdh);
124 	free(f.ptr);
125 	free(hmac.ptr);
126 
127 	return (r);
128 }
129 
130 static void
131 bio_reset_template(fido_bio_template_t *t)
132 {
133 	free(t->name);
134 	free(t->id.ptr);
135 	t->name = NULL;
136 	memset(&t->id, 0, sizeof(t->id));
137 }
138 
139 static void
140 bio_reset_template_array(fido_bio_template_array_t *ta)
141 {
142 	for (size_t i = 0; i < ta->n_alloc; i++)
143 		bio_reset_template(&ta->ptr[i]);
144 
145 	free(ta->ptr);
146 	ta->ptr = NULL;
147 	memset(ta, 0, sizeof(*ta));
148 }
149 
150 static int
151 decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
152 {
153 	fido_bio_template_t *t = arg;
154 
155 	if (cbor_isa_uint(key) == false ||
156 	    cbor_int_get_width(key) != CBOR_INT_8) {
157 		fido_log_debug("%s: cbor type", __func__);
158 		return (0); /* ignore */
159 	}
160 
161 	switch (cbor_get_uint8(key)) {
162 	case 1: /* id */
163 		return (fido_blob_decode(val, &t->id));
164 	case 2: /* name */
165 		return (cbor_string_copy(val, &t->name));
166 	}
167 
168 	return (0); /* ignore */
169 }
170 
171 static int
172 decode_template_array(const cbor_item_t *item, void *arg)
173 {
174 	fido_bio_template_array_t *ta = arg;
175 
176 	if (cbor_isa_map(item) == false ||
177 	    cbor_map_is_definite(item) == false) {
178 		fido_log_debug("%s: cbor type", __func__);
179 		return (-1);
180 	}
181 
182 	if (ta->n_rx >= ta->n_alloc) {
183 		fido_log_debug("%s: n_rx >= n_alloc", __func__);
184 		return (-1);
185 	}
186 
187 	if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
188 		fido_log_debug("%s: decode_template", __func__);
189 		return (-1);
190 	}
191 
192 	ta->n_rx++;
193 
194 	return (0);
195 }
196 
197 static int
198 bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
199     void *arg)
200 {
201 	fido_bio_template_array_t *ta = arg;
202 
203 	if (cbor_isa_uint(key) == false ||
204 	    cbor_int_get_width(key) != CBOR_INT_8 ||
205 	    cbor_get_uint8(key) != 7) {
206 		fido_log_debug("%s: cbor type", __func__);
207 		return (0); /* ignore */
208 	}
209 
210 	if (cbor_isa_array(val) == false ||
211 	    cbor_array_is_definite(val) == false) {
212 		fido_log_debug("%s: cbor type", __func__);
213 		return (-1);
214 	}
215 
216 	if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
217 		fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
218 		    __func__);
219 		return (-1);
220 	}
221 
222 	if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
223 		return (-1);
224 
225 	ta->n_alloc = cbor_array_size(val);
226 
227 	if (cbor_array_iter(val, ta, decode_template_array) < 0) {
228 		fido_log_debug("%s: decode_template_array", __func__);
229 		return (-1);
230 	}
231 
232 	return (0);
233 }
234 
235 static int
236 bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
237 {
238 	unsigned char	reply[FIDO_MAXMSG];
239 	int		reply_len;
240 	int		r;
241 
242 	bio_reset_template_array(ta);
243 
244 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
245 	    ms)) < 0) {
246 		fido_log_debug("%s: fido_rx", __func__);
247 		return (FIDO_ERR_RX);
248 	}
249 
250 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, ta,
251 	    bio_parse_template_array)) != FIDO_OK) {
252 		fido_log_debug("%s: bio_parse_template_array" , __func__);
253 		return (r);
254 	}
255 
256 	return (FIDO_OK);
257 }
258 
259 static int
260 bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
261     const char *pin, int ms)
262 {
263 	int r;
264 
265 	if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK ||
266 	    (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
267 		return (r);
268 
269 	return (FIDO_OK);
270 }
271 
272 int
273 fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
274     const char *pin)
275 {
276 	if (pin == NULL)
277 		return (FIDO_ERR_INVALID_ARGUMENT);
278 
279 	return (bio_get_template_array_wait(dev, ta, pin, -1));
280 }
281 
282 static int
283 bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
284     const char *pin, int ms)
285 {
286 	cbor_item_t	*argv[2];
287 	int		 r = FIDO_ERR_INTERNAL;
288 
289 	memset(&argv, 0, sizeof(argv));
290 
291 	if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
292 	    (argv[1] = cbor_build_string(t->name)) == NULL) {
293 		fido_log_debug("%s: cbor encode", __func__);
294 		goto fail;
295 	}
296 
297 	if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK ||
298 	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
299 		fido_log_debug("%s: tx/rx", __func__);
300 		goto fail;
301 	}
302 
303 	r = FIDO_OK;
304 fail:
305 	cbor_vector_free(argv, nitems(argv));
306 
307 	return (r);
308 }
309 
310 int
311 fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
312     const char *pin)
313 {
314 	if (pin == NULL || t->name == NULL)
315 		return (FIDO_ERR_INVALID_ARGUMENT);
316 
317 	return (bio_set_template_name_wait(dev, t, pin, -1));
318 }
319 
320 static void
321 bio_reset_enroll(fido_bio_enroll_t *e)
322 {
323 	e->remaining_samples = 0;
324 	e->last_status = 0;
325 
326 	if (e->token)
327 		fido_blob_free(&e->token);
328 }
329 
330 static int
331 bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
332     void *arg)
333 {
334 	fido_bio_enroll_t *e = arg;
335 	uint64_t x;
336 
337 	if (cbor_isa_uint(key) == false ||
338 	    cbor_int_get_width(key) != CBOR_INT_8) {
339 		fido_log_debug("%s: cbor type", __func__);
340 		return (0); /* ignore */
341 	}
342 
343 	switch (cbor_get_uint8(key)) {
344 	case 5:
345 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
346 			fido_log_debug("%s: cbor_decode_uint64", __func__);
347 			return (-1);
348 		}
349 		e->last_status = (uint8_t)x;
350 		break;
351 	case 6:
352 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
353 			fido_log_debug("%s: cbor_decode_uint64", __func__);
354 			return (-1);
355 		}
356 		e->remaining_samples = (uint8_t)x;
357 		break;
358 	default:
359 		return (0); /* ignore */
360 	}
361 
362 	return (0);
363 }
364 
365 static int
366 bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
367     void *arg)
368 {
369 	fido_blob_t *id = arg;
370 
371 	if (cbor_isa_uint(key) == false ||
372 	    cbor_int_get_width(key) != CBOR_INT_8 ||
373 	    cbor_get_uint8(key) != 4) {
374 		fido_log_debug("%s: cbor type", __func__);
375 		return (0); /* ignore */
376 	}
377 
378 	return (fido_blob_decode(val, id));
379 }
380 
381 static int
382 bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
383     fido_bio_enroll_t *e, int ms)
384 {
385 	unsigned char	reply[FIDO_MAXMSG];
386 	int		reply_len;
387 	int		r;
388 
389 	bio_reset_template(t);
390 
391 	e->remaining_samples = 0;
392 	e->last_status = 0;
393 
394 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
395 	    ms)) < 0) {
396 		fido_log_debug("%s: fido_rx", __func__);
397 		return (FIDO_ERR_RX);
398 	}
399 
400 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
401 	    bio_parse_enroll_status)) != FIDO_OK) {
402 		fido_log_debug("%s: bio_parse_enroll_status", __func__);
403 		return (r);
404 	}
405 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id,
406 	    bio_parse_template_id)) != FIDO_OK) {
407 		fido_log_debug("%s: bio_parse_template_id", __func__);
408 		return (r);
409 	}
410 
411 	return (FIDO_OK);
412 }
413 
414 static int
415 bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
416     fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
417 {
418 	cbor_item_t	*argv[3];
419 	const uint8_t	 cmd = CMD_ENROLL_BEGIN;
420 	int		 r = FIDO_ERR_INTERNAL;
421 
422 	memset(&argv, 0, sizeof(argv));
423 
424 	if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
425 		fido_log_debug("%s: cbor encode", __func__);
426 		goto fail;
427 	}
428 
429 	if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
430 	    (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
431 		fido_log_debug("%s: tx/rx", __func__);
432 		goto fail;
433 	}
434 
435 	r = FIDO_OK;
436 fail:
437 	cbor_vector_free(argv, nitems(argv));
438 
439 	return (r);
440 }
441 
442 int
443 fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
444     fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
445 {
446 	es256_pk_t	*pk = NULL;
447 	fido_blob_t	*ecdh = NULL;
448 	fido_blob_t	*token = NULL;
449 	int		 r;
450 
451 	if (pin == NULL || e->token != NULL)
452 		return (FIDO_ERR_INVALID_ARGUMENT);
453 
454 	if ((token = fido_blob_new()) == NULL) {
455 		r = FIDO_ERR_INTERNAL;
456 		goto fail;
457 	}
458 
459 	if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
460 		fido_log_debug("%s: fido_do_ecdh", __func__);
461 		goto fail;
462 	}
463 
464 	if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
465 		fido_log_debug("%s: fido_dev_get_pin_token", __func__);
466 		goto fail;
467 	}
468 
469 	e->token = token;
470 	token = NULL;
471 fail:
472 	es256_pk_free(&pk);
473 	fido_blob_free(&ecdh);
474 	fido_blob_free(&token);
475 
476 	if (r != FIDO_OK)
477 		return (r);
478 
479 	return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1));
480 }
481 
482 static int
483 bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
484 {
485 	unsigned char	reply[FIDO_MAXMSG];
486 	int		reply_len;
487 	int		r;
488 
489 	e->remaining_samples = 0;
490 	e->last_status = 0;
491 
492 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
493 	    ms)) < 0) {
494 		fido_log_debug("%s: fido_rx", __func__);
495 		return (FIDO_ERR_RX);
496 	}
497 
498 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
499 	    bio_parse_enroll_status)) != FIDO_OK) {
500 		fido_log_debug("%s: bio_parse_enroll_status", __func__);
501 		return (r);
502 	}
503 
504 	return (FIDO_OK);
505 }
506 
507 static int
508 bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
509     fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
510 {
511 	cbor_item_t	*argv[3];
512 	const uint8_t	 cmd = CMD_ENROLL_NEXT;
513 	int		 r = FIDO_ERR_INTERNAL;
514 
515 	memset(&argv, 0, sizeof(argv));
516 
517 	if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
518 	    (argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
519 		fido_log_debug("%s: cbor encode", __func__);
520 		goto fail;
521 	}
522 
523 	if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
524 	    (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
525 		fido_log_debug("%s: tx/rx", __func__);
526 		goto fail;
527 	}
528 
529 	r = FIDO_OK;
530 fail:
531 	cbor_vector_free(argv, nitems(argv));
532 
533 	return (r);
534 }
535 
536 int
537 fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
538     fido_bio_enroll_t *e, uint32_t timo_ms)
539 {
540 	if (e->token == NULL)
541 		return (FIDO_ERR_INVALID_ARGUMENT);
542 
543 	return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1));
544 }
545 
546 static int
547 bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
548 {
549 	const uint8_t	cmd = CMD_ENROLL_CANCEL;
550 	int		r;
551 
552 	if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK ||
553 	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
554 		fido_log_debug("%s: tx/rx", __func__);
555 		return (r);
556 	}
557 
558 	return (FIDO_OK);
559 }
560 
561 int
562 fido_bio_dev_enroll_cancel(fido_dev_t *dev)
563 {
564 	return (bio_enroll_cancel_wait(dev, -1));
565 }
566 
567 static int
568 bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
569     const char *pin, int ms)
570 {
571 	cbor_item_t	*argv[1];
572 	const uint8_t	 cmd = CMD_ENROLL_REMOVE;
573 	int		 r = FIDO_ERR_INTERNAL;
574 
575 	memset(&argv, 0, sizeof(argv));
576 
577 	if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
578 		fido_log_debug("%s: cbor encode", __func__);
579 		goto fail;
580 	}
581 
582 	if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK ||
583 	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
584 		fido_log_debug("%s: tx/rx", __func__);
585 		goto fail;
586 	}
587 
588 	r = FIDO_OK;
589 fail:
590 	cbor_vector_free(argv, nitems(argv));
591 
592 	return (r);
593 }
594 
595 int
596 fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
597     const char *pin)
598 {
599 	return (bio_enroll_remove_wait(dev, t, pin, -1));
600 }
601 
602 static void
603 bio_reset_info(fido_bio_info_t *i)
604 {
605 	i->type = 0;
606 	i->max_samples = 0;
607 }
608 
609 static int
610 bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
611 {
612 	fido_bio_info_t	*i = arg;
613 	uint64_t	 x;
614 
615 	if (cbor_isa_uint(key) == false ||
616 	    cbor_int_get_width(key) != CBOR_INT_8) {
617 		fido_log_debug("%s: cbor type", __func__);
618 		return (0); /* ignore */
619 	}
620 
621 	switch (cbor_get_uint8(key)) {
622 	case 2:
623 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
624 			fido_log_debug("%s: cbor_decode_uint64", __func__);
625 			return (-1);
626 		}
627 		i->type = (uint8_t)x;
628 		break;
629 	case 3:
630 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
631 			fido_log_debug("%s: cbor_decode_uint64", __func__);
632 			return (-1);
633 		}
634 		i->max_samples = (uint8_t)x;
635 		break;
636 	default:
637 		return (0); /* ignore */
638 	}
639 
640 	return (0);
641 }
642 
643 static int
644 bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
645 {
646 	unsigned char	reply[FIDO_MAXMSG];
647 	int		reply_len;
648 	int		r;
649 
650 	bio_reset_info(i);
651 
652 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
653 	    ms)) < 0) {
654 		fido_log_debug("%s: fido_rx", __func__);
655 		return (FIDO_ERR_RX);
656 	}
657 
658 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, i,
659 	    bio_parse_info)) != FIDO_OK) {
660 		fido_log_debug("%s: bio_parse_info" , __func__);
661 		return (r);
662 	}
663 
664 	return (FIDO_OK);
665 }
666 
667 static int
668 bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
669 {
670 	int r;
671 
672 	if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK ||
673 	    (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
674 		fido_log_debug("%s: tx/rx", __func__);
675 		return (r);
676 	}
677 
678 	return (FIDO_OK);
679 }
680 
681 int
682 fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
683 {
684 	return (bio_get_info_wait(dev, i, -1));
685 }
686 
687 const char *
688 fido_bio_template_name(const fido_bio_template_t *t)
689 {
690 	return (t->name);
691 }
692 
693 const unsigned char *
694 fido_bio_template_id_ptr(const fido_bio_template_t *t)
695 {
696 	return (t->id.ptr);
697 }
698 
699 size_t
700 fido_bio_template_id_len(const fido_bio_template_t *t)
701 {
702 	return (t->id.len);
703 }
704 
705 size_t
706 fido_bio_template_array_count(const fido_bio_template_array_t *ta)
707 {
708 	return (ta->n_rx);
709 }
710 
711 fido_bio_template_array_t *
712 fido_bio_template_array_new(void)
713 {
714 	return (calloc(1, sizeof(fido_bio_template_array_t)));
715 }
716 
717 fido_bio_template_t *
718 fido_bio_template_new(void)
719 {
720 	return (calloc(1, sizeof(fido_bio_template_t)));
721 }
722 
723 void
724 fido_bio_template_array_free(fido_bio_template_array_t **tap)
725 {
726 	fido_bio_template_array_t *ta;
727 
728 	if (tap == NULL || (ta = *tap) == NULL)
729 		return;
730 
731 	bio_reset_template_array(ta);
732 	free(ta);
733 	*tap = NULL;
734 }
735 
736 void
737 fido_bio_template_free(fido_bio_template_t **tp)
738 {
739 	fido_bio_template_t *t;
740 
741 	if (tp == NULL || (t = *tp) == NULL)
742 		return;
743 
744 	bio_reset_template(t);
745 	free(t);
746 	*tp = NULL;
747 }
748 
749 int
750 fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
751 {
752 	free(t->name);
753 	t->name = NULL;
754 
755 	if (name && (t->name = strdup(name)) == NULL)
756 		return (FIDO_ERR_INTERNAL);
757 
758 	return (FIDO_OK);
759 }
760 
761 int
762 fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
763     size_t len)
764 {
765 	free(t->id.ptr);
766 	t->id.ptr = NULL;
767 	t->id.len = 0;
768 
769 	if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
770 		return (FIDO_ERR_INTERNAL);
771 
772 	return (FIDO_OK);
773 }
774 
775 const fido_bio_template_t *
776 fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
777 {
778 	if (idx >= ta->n_alloc)
779 		return (NULL);
780 
781 	return (&ta->ptr[idx]);
782 }
783 
784 fido_bio_enroll_t *
785 fido_bio_enroll_new(void)
786 {
787 	return (calloc(1, sizeof(fido_bio_enroll_t)));
788 }
789 
790 fido_bio_info_t *
791 fido_bio_info_new(void)
792 {
793 	return (calloc(1, sizeof(fido_bio_info_t)));
794 }
795 
796 uint8_t
797 fido_bio_info_type(const fido_bio_info_t *i)
798 {
799 	return (i->type);
800 }
801 
802 uint8_t
803 fido_bio_info_max_samples(const fido_bio_info_t *i)
804 {
805 	return (i->max_samples);
806 }
807 
808 void
809 fido_bio_enroll_free(fido_bio_enroll_t **ep)
810 {
811 	fido_bio_enroll_t *e;
812 
813 	if (ep == NULL || (e = *ep) == NULL)
814 		return;
815 
816 	bio_reset_enroll(e);
817 
818 	free(e);
819 	*ep = NULL;
820 }
821 
822 void
823 fido_bio_info_free(fido_bio_info_t **ip)
824 {
825 	fido_bio_info_t *i;
826 
827 	if (ip == NULL || (i = *ip) == NULL)
828 		return;
829 
830 	free(i);
831 	*ip = NULL;
832 }
833 
834 uint8_t
835 fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
836 {
837 	return (e->remaining_samples);
838 }
839 
840 uint8_t
841 fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
842 {
843 	return (e->last_status);
844 }
845