xref: /freebsd-src/contrib/libfido2/src/bio.c (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
10afa8e06SEd Maste /*
2*2ccfa855SEd Maste  * Copyright (c) 2019-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste  * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste  */
70afa8e06SEd Maste 
80afa8e06SEd Maste #include "fido.h"
90afa8e06SEd Maste #include "fido/bio.h"
100afa8e06SEd Maste #include "fido/es256.h"
110afa8e06SEd Maste 
120afa8e06SEd Maste #define CMD_ENROLL_BEGIN	0x01
130afa8e06SEd Maste #define CMD_ENROLL_NEXT		0x02
140afa8e06SEd Maste #define CMD_ENROLL_CANCEL	0x03
150afa8e06SEd Maste #define CMD_ENUM		0x04
160afa8e06SEd Maste #define CMD_SET_NAME		0x05
170afa8e06SEd Maste #define CMD_ENROLL_REMOVE	0x06
180afa8e06SEd Maste #define CMD_GET_INFO		0x07
190afa8e06SEd Maste 
200afa8e06SEd Maste static int
bio_prepare_hmac(uint8_t cmd,cbor_item_t ** argv,size_t argc,cbor_item_t ** param,fido_blob_t * hmac_data)210afa8e06SEd Maste bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
220afa8e06SEd Maste     cbor_item_t **param, fido_blob_t *hmac_data)
230afa8e06SEd Maste {
240afa8e06SEd Maste 	const uint8_t	 prefix[2] = { 0x01 /* modality */, cmd };
250afa8e06SEd Maste 	int		 ok = -1;
260afa8e06SEd Maste 	size_t		 cbor_alloc_len;
270afa8e06SEd Maste 	size_t		 cbor_len;
280afa8e06SEd Maste 	unsigned char	*cbor = NULL;
290afa8e06SEd Maste 
300afa8e06SEd Maste 	if (argv == NULL || param == NULL)
310afa8e06SEd Maste 		return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
320afa8e06SEd Maste 
330afa8e06SEd Maste 	if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
340afa8e06SEd Maste 		fido_log_debug("%s: cbor_flatten_vector", __func__);
350afa8e06SEd Maste 		goto fail;
360afa8e06SEd Maste 	}
370afa8e06SEd Maste 
380afa8e06SEd Maste 	if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
390afa8e06SEd Maste 	    &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
400afa8e06SEd Maste 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
410afa8e06SEd Maste 		goto fail;
420afa8e06SEd Maste 	}
430afa8e06SEd Maste 
440afa8e06SEd Maste 	if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
450afa8e06SEd Maste 		fido_log_debug("%s: malloc", __func__);
460afa8e06SEd Maste 		goto fail;
470afa8e06SEd Maste 	}
480afa8e06SEd Maste 
490afa8e06SEd Maste 	memcpy(hmac_data->ptr, prefix, sizeof(prefix));
500afa8e06SEd Maste 	memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
510afa8e06SEd Maste 	hmac_data->len = cbor_len + sizeof(prefix);
520afa8e06SEd Maste 
530afa8e06SEd Maste 	ok = 0;
540afa8e06SEd Maste fail:
550afa8e06SEd Maste 	free(cbor);
560afa8e06SEd Maste 
570afa8e06SEd Maste 	return (ok);
580afa8e06SEd Maste }
590afa8e06SEd Maste 
600afa8e06SEd Maste static int
bio_tx(fido_dev_t * dev,uint8_t subcmd,cbor_item_t ** sub_argv,size_t sub_argc,const char * pin,const fido_blob_t * token,int * ms)610afa8e06SEd Maste bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc,
62f540a430SEd Maste     const char *pin, const fido_blob_t *token, int *ms)
630afa8e06SEd Maste {
640afa8e06SEd Maste 	cbor_item_t	*argv[5];
650afa8e06SEd Maste 	es256_pk_t	*pk = NULL;
660afa8e06SEd Maste 	fido_blob_t	*ecdh = NULL;
670afa8e06SEd Maste 	fido_blob_t	 f;
680afa8e06SEd Maste 	fido_blob_t	 hmac;
690afa8e06SEd Maste 	const uint8_t	 cmd = CTAP_CBOR_BIO_ENROLL_PRE;
700afa8e06SEd Maste 	int		 r = FIDO_ERR_INTERNAL;
710afa8e06SEd Maste 
720afa8e06SEd Maste 	memset(&f, 0, sizeof(f));
730afa8e06SEd Maste 	memset(&hmac, 0, sizeof(hmac));
740afa8e06SEd Maste 	memset(&argv, 0, sizeof(argv));
750afa8e06SEd Maste 
760afa8e06SEd Maste 	/* modality, subCommand */
770afa8e06SEd Maste 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
780afa8e06SEd Maste 	    (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
790afa8e06SEd Maste 		fido_log_debug("%s: cbor encode", __func__);
800afa8e06SEd Maste 		goto fail;
810afa8e06SEd Maste 	}
820afa8e06SEd Maste 
830afa8e06SEd Maste 	/* subParams */
840afa8e06SEd Maste 	if (pin || token) {
850afa8e06SEd Maste 		if (bio_prepare_hmac(subcmd, sub_argv, sub_argc, &argv[2],
860afa8e06SEd Maste 		    &hmac) < 0) {
870afa8e06SEd Maste 			fido_log_debug("%s: bio_prepare_hmac", __func__);
880afa8e06SEd Maste 			goto fail;
890afa8e06SEd Maste 		}
900afa8e06SEd Maste 	}
910afa8e06SEd Maste 
920afa8e06SEd Maste 	/* pinProtocol, pinAuth */
930afa8e06SEd Maste 	if (pin) {
94f540a430SEd Maste 		if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
950afa8e06SEd Maste 			fido_log_debug("%s: fido_do_ecdh", __func__);
960afa8e06SEd Maste 			goto fail;
970afa8e06SEd Maste 		}
980afa8e06SEd Maste 		if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
99f540a430SEd Maste 		    NULL, &argv[4], &argv[3], ms)) != FIDO_OK) {
1000afa8e06SEd Maste 			fido_log_debug("%s: cbor_add_uv_params", __func__);
1010afa8e06SEd Maste 			goto fail;
1020afa8e06SEd Maste 		}
1030afa8e06SEd Maste 	} else if (token) {
1040afa8e06SEd Maste 		if ((argv[3] = cbor_encode_pin_opt(dev)) == NULL ||
1050afa8e06SEd Maste 		    (argv[4] = cbor_encode_pin_auth(dev, token, &hmac)) == NULL) {
1060afa8e06SEd Maste 			fido_log_debug("%s: encode pin", __func__);
1070afa8e06SEd Maste 			goto fail;
1080afa8e06SEd Maste 		}
1090afa8e06SEd Maste 	}
1100afa8e06SEd Maste 
1110afa8e06SEd Maste 	/* framing and transmission */
1120afa8e06SEd Maste 	if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
113f540a430SEd Maste 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
1140afa8e06SEd Maste 		fido_log_debug("%s: fido_tx", __func__);
1150afa8e06SEd Maste 		r = FIDO_ERR_TX;
1160afa8e06SEd Maste 		goto fail;
1170afa8e06SEd Maste 	}
1180afa8e06SEd Maste 
1190afa8e06SEd Maste 	r = FIDO_OK;
1200afa8e06SEd Maste fail:
1210afa8e06SEd Maste 	cbor_vector_free(argv, nitems(argv));
1220afa8e06SEd Maste 	es256_pk_free(&pk);
1230afa8e06SEd Maste 	fido_blob_free(&ecdh);
1240afa8e06SEd Maste 	free(f.ptr);
1250afa8e06SEd Maste 	free(hmac.ptr);
1260afa8e06SEd Maste 
1270afa8e06SEd Maste 	return (r);
1280afa8e06SEd Maste }
1290afa8e06SEd Maste 
1300afa8e06SEd Maste static void
bio_reset_template(fido_bio_template_t * t)1310afa8e06SEd Maste bio_reset_template(fido_bio_template_t *t)
1320afa8e06SEd Maste {
1330afa8e06SEd Maste 	free(t->name);
1340afa8e06SEd Maste 	t->name = NULL;
1350afa8e06SEd Maste 	fido_blob_reset(&t->id);
1360afa8e06SEd Maste }
1370afa8e06SEd Maste 
1380afa8e06SEd Maste static void
bio_reset_template_array(fido_bio_template_array_t * ta)1390afa8e06SEd Maste bio_reset_template_array(fido_bio_template_array_t *ta)
1400afa8e06SEd Maste {
1410afa8e06SEd Maste 	for (size_t i = 0; i < ta->n_alloc; i++)
1420afa8e06SEd Maste 		bio_reset_template(&ta->ptr[i]);
1430afa8e06SEd Maste 
1440afa8e06SEd Maste 	free(ta->ptr);
1450afa8e06SEd Maste 	ta->ptr = NULL;
1460afa8e06SEd Maste 	memset(ta, 0, sizeof(*ta));
1470afa8e06SEd Maste }
1480afa8e06SEd Maste 
1490afa8e06SEd Maste static int
decode_template(const cbor_item_t * key,const cbor_item_t * val,void * arg)1500afa8e06SEd Maste decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1510afa8e06SEd Maste {
1520afa8e06SEd Maste 	fido_bio_template_t *t = arg;
1530afa8e06SEd Maste 
1540afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
1550afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8) {
1560afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
1570afa8e06SEd Maste 		return (0); /* ignore */
1580afa8e06SEd Maste 	}
1590afa8e06SEd Maste 
1600afa8e06SEd Maste 	switch (cbor_get_uint8(key)) {
1610afa8e06SEd Maste 	case 1: /* id */
1620afa8e06SEd Maste 		return (fido_blob_decode(val, &t->id));
1630afa8e06SEd Maste 	case 2: /* name */
1640afa8e06SEd Maste 		return (cbor_string_copy(val, &t->name));
1650afa8e06SEd Maste 	}
1660afa8e06SEd Maste 
1670afa8e06SEd Maste 	return (0); /* ignore */
1680afa8e06SEd Maste }
1690afa8e06SEd Maste 
1700afa8e06SEd Maste static int
decode_template_array(const cbor_item_t * item,void * arg)1710afa8e06SEd Maste decode_template_array(const cbor_item_t *item, void *arg)
1720afa8e06SEd Maste {
1730afa8e06SEd Maste 	fido_bio_template_array_t *ta = arg;
1740afa8e06SEd Maste 
1750afa8e06SEd Maste 	if (cbor_isa_map(item) == false ||
1760afa8e06SEd Maste 	    cbor_map_is_definite(item) == false) {
1770afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
1780afa8e06SEd Maste 		return (-1);
1790afa8e06SEd Maste 	}
1800afa8e06SEd Maste 
1810afa8e06SEd Maste 	if (ta->n_rx >= ta->n_alloc) {
1820afa8e06SEd Maste 		fido_log_debug("%s: n_rx >= n_alloc", __func__);
1830afa8e06SEd Maste 		return (-1);
1840afa8e06SEd Maste 	}
1850afa8e06SEd Maste 
1860afa8e06SEd Maste 	if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
1870afa8e06SEd Maste 		fido_log_debug("%s: decode_template", __func__);
1880afa8e06SEd Maste 		return (-1);
1890afa8e06SEd Maste 	}
1900afa8e06SEd Maste 
1910afa8e06SEd Maste 	ta->n_rx++;
1920afa8e06SEd Maste 
1930afa8e06SEd Maste 	return (0);
1940afa8e06SEd Maste }
1950afa8e06SEd Maste 
1960afa8e06SEd Maste static int
bio_parse_template_array(const cbor_item_t * key,const cbor_item_t * val,void * arg)1970afa8e06SEd Maste bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
1980afa8e06SEd Maste     void *arg)
1990afa8e06SEd Maste {
2000afa8e06SEd Maste 	fido_bio_template_array_t *ta = arg;
2010afa8e06SEd Maste 
2020afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
2030afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8 ||
2040afa8e06SEd Maste 	    cbor_get_uint8(key) != 7) {
2050afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
2060afa8e06SEd Maste 		return (0); /* ignore */
2070afa8e06SEd Maste 	}
2080afa8e06SEd Maste 
2090afa8e06SEd Maste 	if (cbor_isa_array(val) == false ||
2100afa8e06SEd Maste 	    cbor_array_is_definite(val) == false) {
2110afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
2120afa8e06SEd Maste 		return (-1);
2130afa8e06SEd Maste 	}
2140afa8e06SEd Maste 
2150afa8e06SEd Maste 	if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
2160afa8e06SEd Maste 		fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
2170afa8e06SEd Maste 		    __func__);
2180afa8e06SEd Maste 		return (-1);
2190afa8e06SEd Maste 	}
2200afa8e06SEd Maste 
2210afa8e06SEd Maste 	if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
2220afa8e06SEd Maste 		return (-1);
2230afa8e06SEd Maste 
2240afa8e06SEd Maste 	ta->n_alloc = cbor_array_size(val);
2250afa8e06SEd Maste 
2260afa8e06SEd Maste 	if (cbor_array_iter(val, ta, decode_template_array) < 0) {
2270afa8e06SEd Maste 		fido_log_debug("%s: decode_template_array", __func__);
2280afa8e06SEd Maste 		return (-1);
2290afa8e06SEd Maste 	}
2300afa8e06SEd Maste 
2310afa8e06SEd Maste 	return (0);
2320afa8e06SEd Maste }
2330afa8e06SEd Maste 
2340afa8e06SEd Maste static int
bio_rx_template_array(fido_dev_t * dev,fido_bio_template_array_t * ta,int * ms)235f540a430SEd Maste bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int *ms)
2360afa8e06SEd Maste {
237*2ccfa855SEd Maste 	unsigned char	*msg;
238*2ccfa855SEd Maste 	int		 msglen;
2390afa8e06SEd Maste 	int		 r;
2400afa8e06SEd Maste 
2410afa8e06SEd Maste 	bio_reset_template_array(ta);
2420afa8e06SEd Maste 
243*2ccfa855SEd Maste 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
244*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
245*2ccfa855SEd Maste 		goto out;
2460afa8e06SEd Maste 	}
2470afa8e06SEd Maste 
248*2ccfa855SEd Maste 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
249*2ccfa855SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
250*2ccfa855SEd Maste 		r = FIDO_ERR_RX;
251*2ccfa855SEd Maste 		goto out;
252*2ccfa855SEd Maste 	}
253*2ccfa855SEd Maste 
254*2ccfa855SEd Maste 	if ((r = cbor_parse_reply(msg, (size_t)msglen, ta,
2550afa8e06SEd Maste 	    bio_parse_template_array)) != FIDO_OK) {
2560afa8e06SEd Maste 		fido_log_debug("%s: bio_parse_template_array" , __func__);
257*2ccfa855SEd Maste 		goto out;
2580afa8e06SEd Maste 	}
2590afa8e06SEd Maste 
260*2ccfa855SEd Maste 	r = FIDO_OK;
261*2ccfa855SEd Maste out:
262*2ccfa855SEd Maste 	freezero(msg, FIDO_MAXMSG);
263*2ccfa855SEd Maste 
264*2ccfa855SEd Maste 	return (r);
2650afa8e06SEd Maste }
2660afa8e06SEd Maste 
2670afa8e06SEd Maste static int
bio_get_template_array_wait(fido_dev_t * dev,fido_bio_template_array_t * ta,const char * pin,int * ms)2680afa8e06SEd Maste bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
269f540a430SEd Maste     const char *pin, int *ms)
2700afa8e06SEd Maste {
2710afa8e06SEd Maste 	int r;
2720afa8e06SEd Maste 
273f540a430SEd Maste 	if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL, ms)) != FIDO_OK ||
2740afa8e06SEd Maste 	    (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
2750afa8e06SEd Maste 		return (r);
2760afa8e06SEd Maste 
2770afa8e06SEd Maste 	return (FIDO_OK);
2780afa8e06SEd Maste }
2790afa8e06SEd Maste 
2800afa8e06SEd Maste int
fido_bio_dev_get_template_array(fido_dev_t * dev,fido_bio_template_array_t * ta,const char * pin)2810afa8e06SEd Maste fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
2820afa8e06SEd Maste     const char *pin)
2830afa8e06SEd Maste {
284f540a430SEd Maste 	int ms = dev->timeout_ms;
285f540a430SEd Maste 
2860afa8e06SEd Maste 	if (pin == NULL)
2870afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
2880afa8e06SEd Maste 
289f540a430SEd Maste 	return (bio_get_template_array_wait(dev, ta, pin, &ms));
2900afa8e06SEd Maste }
2910afa8e06SEd Maste 
2920afa8e06SEd Maste static int
bio_set_template_name_wait(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin,int * ms)2930afa8e06SEd Maste bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
294f540a430SEd Maste     const char *pin, int *ms)
2950afa8e06SEd Maste {
2960afa8e06SEd Maste 	cbor_item_t	*argv[2];
2970afa8e06SEd Maste 	int		 r = FIDO_ERR_INTERNAL;
2980afa8e06SEd Maste 
2990afa8e06SEd Maste 	memset(&argv, 0, sizeof(argv));
3000afa8e06SEd Maste 
3010afa8e06SEd Maste 	if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
3020afa8e06SEd Maste 	    (argv[1] = cbor_build_string(t->name)) == NULL) {
3030afa8e06SEd Maste 		fido_log_debug("%s: cbor encode", __func__);
3040afa8e06SEd Maste 		goto fail;
3050afa8e06SEd Maste 	}
3060afa8e06SEd Maste 
307f540a430SEd Maste 	if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL,
308f540a430SEd Maste 	    ms)) != FIDO_OK ||
3090afa8e06SEd Maste 	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
3100afa8e06SEd Maste 		fido_log_debug("%s: tx/rx", __func__);
3110afa8e06SEd Maste 		goto fail;
3120afa8e06SEd Maste 	}
3130afa8e06SEd Maste 
3140afa8e06SEd Maste 	r = FIDO_OK;
3150afa8e06SEd Maste fail:
3160afa8e06SEd Maste 	cbor_vector_free(argv, nitems(argv));
3170afa8e06SEd Maste 
3180afa8e06SEd Maste 	return (r);
3190afa8e06SEd Maste }
3200afa8e06SEd Maste 
3210afa8e06SEd Maste int
fido_bio_dev_set_template_name(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin)3220afa8e06SEd Maste fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
3230afa8e06SEd Maste     const char *pin)
3240afa8e06SEd Maste {
325f540a430SEd Maste 	int ms = dev->timeout_ms;
326f540a430SEd Maste 
3270afa8e06SEd Maste 	if (pin == NULL || t->name == NULL)
3280afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
3290afa8e06SEd Maste 
330f540a430SEd Maste 	return (bio_set_template_name_wait(dev, t, pin, &ms));
3310afa8e06SEd Maste }
3320afa8e06SEd Maste 
3330afa8e06SEd Maste static void
bio_reset_enroll(fido_bio_enroll_t * e)3340afa8e06SEd Maste bio_reset_enroll(fido_bio_enroll_t *e)
3350afa8e06SEd Maste {
3360afa8e06SEd Maste 	e->remaining_samples = 0;
3370afa8e06SEd Maste 	e->last_status = 0;
3380afa8e06SEd Maste 
3390afa8e06SEd Maste 	if (e->token)
3400afa8e06SEd Maste 		fido_blob_free(&e->token);
3410afa8e06SEd Maste }
3420afa8e06SEd Maste 
3430afa8e06SEd Maste static int
bio_parse_enroll_status(const cbor_item_t * key,const cbor_item_t * val,void * arg)3440afa8e06SEd Maste bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
3450afa8e06SEd Maste     void *arg)
3460afa8e06SEd Maste {
3470afa8e06SEd Maste 	fido_bio_enroll_t *e = arg;
3480afa8e06SEd Maste 	uint64_t x;
3490afa8e06SEd Maste 
3500afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
3510afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8) {
3520afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
3530afa8e06SEd Maste 		return (0); /* ignore */
3540afa8e06SEd Maste 	}
3550afa8e06SEd Maste 
3560afa8e06SEd Maste 	switch (cbor_get_uint8(key)) {
3570afa8e06SEd Maste 	case 5:
3580afa8e06SEd Maste 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
3590afa8e06SEd Maste 			fido_log_debug("%s: cbor_decode_uint64", __func__);
3600afa8e06SEd Maste 			return (-1);
3610afa8e06SEd Maste 		}
3620afa8e06SEd Maste 		e->last_status = (uint8_t)x;
3630afa8e06SEd Maste 		break;
3640afa8e06SEd Maste 	case 6:
3650afa8e06SEd Maste 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
3660afa8e06SEd Maste 			fido_log_debug("%s: cbor_decode_uint64", __func__);
3670afa8e06SEd Maste 			return (-1);
3680afa8e06SEd Maste 		}
3690afa8e06SEd Maste 		e->remaining_samples = (uint8_t)x;
3700afa8e06SEd Maste 		break;
3710afa8e06SEd Maste 	default:
3720afa8e06SEd Maste 		return (0); /* ignore */
3730afa8e06SEd Maste 	}
3740afa8e06SEd Maste 
3750afa8e06SEd Maste 	return (0);
3760afa8e06SEd Maste }
3770afa8e06SEd Maste 
3780afa8e06SEd Maste static int
bio_parse_template_id(const cbor_item_t * key,const cbor_item_t * val,void * arg)3790afa8e06SEd Maste bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
3800afa8e06SEd Maste     void *arg)
3810afa8e06SEd Maste {
3820afa8e06SEd Maste 	fido_blob_t *id = arg;
3830afa8e06SEd Maste 
3840afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
3850afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8 ||
3860afa8e06SEd Maste 	    cbor_get_uint8(key) != 4) {
3870afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
3880afa8e06SEd Maste 		return (0); /* ignore */
3890afa8e06SEd Maste 	}
3900afa8e06SEd Maste 
3910afa8e06SEd Maste 	return (fido_blob_decode(val, id));
3920afa8e06SEd Maste }
3930afa8e06SEd Maste 
3940afa8e06SEd Maste static int
bio_rx_enroll_begin(fido_dev_t * dev,fido_bio_template_t * t,fido_bio_enroll_t * e,int * ms)3950afa8e06SEd Maste bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
396f540a430SEd Maste     fido_bio_enroll_t *e, int *ms)
3970afa8e06SEd Maste {
398*2ccfa855SEd Maste 	unsigned char	*msg;
399*2ccfa855SEd Maste 	int		 msglen;
4000afa8e06SEd Maste 	int		 r;
4010afa8e06SEd Maste 
4020afa8e06SEd Maste 	bio_reset_template(t);
4030afa8e06SEd Maste 
4040afa8e06SEd Maste 	e->remaining_samples = 0;
4050afa8e06SEd Maste 	e->last_status = 0;
4060afa8e06SEd Maste 
407*2ccfa855SEd Maste 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
408*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
409*2ccfa855SEd Maste 		goto out;
4100afa8e06SEd Maste 	}
4110afa8e06SEd Maste 
412*2ccfa855SEd Maste 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
413*2ccfa855SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
414*2ccfa855SEd Maste 		r = FIDO_ERR_RX;
415*2ccfa855SEd Maste 		goto out;
416*2ccfa855SEd Maste 	}
417*2ccfa855SEd Maste 
418*2ccfa855SEd Maste 	if ((r = cbor_parse_reply(msg, (size_t)msglen, e,
4190afa8e06SEd Maste 	    bio_parse_enroll_status)) != FIDO_OK) {
4200afa8e06SEd Maste 		fido_log_debug("%s: bio_parse_enroll_status", __func__);
421*2ccfa855SEd Maste 		goto out;
4220afa8e06SEd Maste 	}
4230afa8e06SEd Maste 
424*2ccfa855SEd Maste 	if ((r = cbor_parse_reply(msg, (size_t)msglen, &t->id,
425*2ccfa855SEd Maste 	    bio_parse_template_id)) != FIDO_OK) {
426*2ccfa855SEd Maste 		fido_log_debug("%s: bio_parse_template_id", __func__);
427*2ccfa855SEd Maste 		goto out;
428*2ccfa855SEd Maste 	}
429*2ccfa855SEd Maste 
430*2ccfa855SEd Maste 	r = FIDO_OK;
431*2ccfa855SEd Maste out:
432*2ccfa855SEd Maste 	freezero(msg, FIDO_MAXMSG);
433*2ccfa855SEd Maste 
434*2ccfa855SEd Maste 	return (r);
4350afa8e06SEd Maste }
4360afa8e06SEd Maste 
4370afa8e06SEd Maste static int
bio_enroll_begin_wait(fido_dev_t * dev,fido_bio_template_t * t,fido_bio_enroll_t * e,uint32_t timo_ms,int * ms)4380afa8e06SEd Maste bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
439f540a430SEd Maste     fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
4400afa8e06SEd Maste {
4410afa8e06SEd Maste 	cbor_item_t	*argv[3];
4420afa8e06SEd Maste 	const uint8_t	 cmd = CMD_ENROLL_BEGIN;
4430afa8e06SEd Maste 	int		 r = FIDO_ERR_INTERNAL;
4440afa8e06SEd Maste 
4450afa8e06SEd Maste 	memset(&argv, 0, sizeof(argv));
4460afa8e06SEd Maste 
4473e696dfbSEd Maste 	if ((argv[2] = cbor_build_uint(timo_ms)) == NULL) {
4480afa8e06SEd Maste 		fido_log_debug("%s: cbor encode", __func__);
4490afa8e06SEd Maste 		goto fail;
4500afa8e06SEd Maste 	}
4510afa8e06SEd Maste 
452f540a430SEd Maste 	if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
4530afa8e06SEd Maste 	    (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
4540afa8e06SEd Maste 		fido_log_debug("%s: tx/rx", __func__);
4550afa8e06SEd Maste 		goto fail;
4560afa8e06SEd Maste 	}
4570afa8e06SEd Maste 
4580afa8e06SEd Maste 	r = FIDO_OK;
4590afa8e06SEd Maste fail:
4600afa8e06SEd Maste 	cbor_vector_free(argv, nitems(argv));
4610afa8e06SEd Maste 
4620afa8e06SEd Maste 	return (r);
4630afa8e06SEd Maste }
4640afa8e06SEd Maste 
4650afa8e06SEd Maste int
fido_bio_dev_enroll_begin(fido_dev_t * dev,fido_bio_template_t * t,fido_bio_enroll_t * e,uint32_t timo_ms,const char * pin)4660afa8e06SEd Maste fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
4670afa8e06SEd Maste     fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
4680afa8e06SEd Maste {
4690afa8e06SEd Maste 	es256_pk_t	*pk = NULL;
4700afa8e06SEd Maste 	fido_blob_t	*ecdh = NULL;
4710afa8e06SEd Maste 	fido_blob_t	*token = NULL;
472f540a430SEd Maste 	int		 ms = dev->timeout_ms;
4730afa8e06SEd Maste 	int		 r;
4740afa8e06SEd Maste 
4750afa8e06SEd Maste 	if (pin == NULL || e->token != NULL)
4760afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
4770afa8e06SEd Maste 
4780afa8e06SEd Maste 	if ((token = fido_blob_new()) == NULL) {
4790afa8e06SEd Maste 		r = FIDO_ERR_INTERNAL;
4800afa8e06SEd Maste 		goto fail;
4810afa8e06SEd Maste 	}
4820afa8e06SEd Maste 
483f540a430SEd Maste 	if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
4840afa8e06SEd Maste 		fido_log_debug("%s: fido_do_ecdh", __func__);
4850afa8e06SEd Maste 		goto fail;
4860afa8e06SEd Maste 	}
4870afa8e06SEd Maste 
4880afa8e06SEd Maste 	if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
489f540a430SEd Maste 	    pk, NULL, token, &ms)) != FIDO_OK) {
4900afa8e06SEd Maste 		fido_log_debug("%s: fido_dev_get_uv_token", __func__);
4910afa8e06SEd Maste 		goto fail;
4920afa8e06SEd Maste 	}
4930afa8e06SEd Maste 
4940afa8e06SEd Maste 	e->token = token;
4950afa8e06SEd Maste 	token = NULL;
4960afa8e06SEd Maste fail:
4970afa8e06SEd Maste 	es256_pk_free(&pk);
4980afa8e06SEd Maste 	fido_blob_free(&ecdh);
4990afa8e06SEd Maste 	fido_blob_free(&token);
5000afa8e06SEd Maste 
5010afa8e06SEd Maste 	if (r != FIDO_OK)
5020afa8e06SEd Maste 		return (r);
5030afa8e06SEd Maste 
504f540a430SEd Maste 	return (bio_enroll_begin_wait(dev, t, e, timo_ms, &ms));
5050afa8e06SEd Maste }
5060afa8e06SEd Maste 
5070afa8e06SEd Maste static int
bio_rx_enroll_continue(fido_dev_t * dev,fido_bio_enroll_t * e,int * ms)508f540a430SEd Maste bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms)
5090afa8e06SEd Maste {
510*2ccfa855SEd Maste 	unsigned char	*msg;
511*2ccfa855SEd Maste 	int		 msglen;
5120afa8e06SEd Maste 	int		 r;
5130afa8e06SEd Maste 
5140afa8e06SEd Maste 	e->remaining_samples = 0;
5150afa8e06SEd Maste 	e->last_status = 0;
5160afa8e06SEd Maste 
517*2ccfa855SEd Maste 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
518*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
519*2ccfa855SEd Maste 		goto out;
5200afa8e06SEd Maste 	}
5210afa8e06SEd Maste 
522*2ccfa855SEd Maste 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
523*2ccfa855SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
524*2ccfa855SEd Maste 		r = FIDO_ERR_RX;
525*2ccfa855SEd Maste 		goto out;
526*2ccfa855SEd Maste 	}
527*2ccfa855SEd Maste 
528*2ccfa855SEd Maste 	if ((r = cbor_parse_reply(msg, (size_t)msglen, e,
5290afa8e06SEd Maste 	    bio_parse_enroll_status)) != FIDO_OK) {
5300afa8e06SEd Maste 		fido_log_debug("%s: bio_parse_enroll_status", __func__);
531*2ccfa855SEd Maste 		goto out;
5320afa8e06SEd Maste 	}
5330afa8e06SEd Maste 
534*2ccfa855SEd Maste 	r = FIDO_OK;
535*2ccfa855SEd Maste out:
536*2ccfa855SEd Maste 	freezero(msg, FIDO_MAXMSG);
537*2ccfa855SEd Maste 
538*2ccfa855SEd Maste 	return (r);
5390afa8e06SEd Maste }
5400afa8e06SEd Maste 
5410afa8e06SEd Maste static int
bio_enroll_continue_wait(fido_dev_t * dev,const fido_bio_template_t * t,fido_bio_enroll_t * e,uint32_t timo_ms,int * ms)5420afa8e06SEd Maste bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
543f540a430SEd Maste     fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
5440afa8e06SEd Maste {
5450afa8e06SEd Maste 	cbor_item_t	*argv[3];
5460afa8e06SEd Maste 	const uint8_t	 cmd = CMD_ENROLL_NEXT;
5470afa8e06SEd Maste 	int		 r = FIDO_ERR_INTERNAL;
5480afa8e06SEd Maste 
5490afa8e06SEd Maste 	memset(&argv, 0, sizeof(argv));
5500afa8e06SEd Maste 
5510afa8e06SEd Maste 	if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
5523e696dfbSEd Maste 	    (argv[2] = cbor_build_uint(timo_ms)) == NULL) {
5530afa8e06SEd Maste 		fido_log_debug("%s: cbor encode", __func__);
5540afa8e06SEd Maste 		goto fail;
5550afa8e06SEd Maste 	}
5560afa8e06SEd Maste 
557f540a430SEd Maste 	if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
5580afa8e06SEd Maste 	    (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
5590afa8e06SEd Maste 		fido_log_debug("%s: tx/rx", __func__);
5600afa8e06SEd Maste 		goto fail;
5610afa8e06SEd Maste 	}
5620afa8e06SEd Maste 
5630afa8e06SEd Maste 	r = FIDO_OK;
5640afa8e06SEd Maste fail:
5650afa8e06SEd Maste 	cbor_vector_free(argv, nitems(argv));
5660afa8e06SEd Maste 
5670afa8e06SEd Maste 	return (r);
5680afa8e06SEd Maste }
5690afa8e06SEd Maste 
5700afa8e06SEd Maste int
fido_bio_dev_enroll_continue(fido_dev_t * dev,const fido_bio_template_t * t,fido_bio_enroll_t * e,uint32_t timo_ms)5710afa8e06SEd Maste fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
5720afa8e06SEd Maste     fido_bio_enroll_t *e, uint32_t timo_ms)
5730afa8e06SEd Maste {
574f540a430SEd Maste 	int ms = dev->timeout_ms;
575f540a430SEd Maste 
5760afa8e06SEd Maste 	if (e->token == NULL)
5770afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
5780afa8e06SEd Maste 
579f540a430SEd Maste 	return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms));
5800afa8e06SEd Maste }
5810afa8e06SEd Maste 
5820afa8e06SEd Maste static int
bio_enroll_cancel_wait(fido_dev_t * dev,int * ms)583f540a430SEd Maste bio_enroll_cancel_wait(fido_dev_t *dev, int *ms)
5840afa8e06SEd Maste {
5850afa8e06SEd Maste 	const uint8_t	cmd = CMD_ENROLL_CANCEL;
5860afa8e06SEd Maste 	int		r;
5870afa8e06SEd Maste 
588f540a430SEd Maste 	if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL, ms)) != FIDO_OK ||
5890afa8e06SEd Maste 	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
5900afa8e06SEd Maste 		fido_log_debug("%s: tx/rx", __func__);
5910afa8e06SEd Maste 		return (r);
5920afa8e06SEd Maste 	}
5930afa8e06SEd Maste 
5940afa8e06SEd Maste 	return (FIDO_OK);
5950afa8e06SEd Maste }
5960afa8e06SEd Maste 
5970afa8e06SEd Maste int
fido_bio_dev_enroll_cancel(fido_dev_t * dev)5980afa8e06SEd Maste fido_bio_dev_enroll_cancel(fido_dev_t *dev)
5990afa8e06SEd Maste {
600f540a430SEd Maste 	int ms = dev->timeout_ms;
601f540a430SEd Maste 
602f540a430SEd Maste 	return (bio_enroll_cancel_wait(dev, &ms));
6030afa8e06SEd Maste }
6040afa8e06SEd Maste 
6050afa8e06SEd Maste static int
bio_enroll_remove_wait(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin,int * ms)6060afa8e06SEd Maste bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
607f540a430SEd Maste     const char *pin, int *ms)
6080afa8e06SEd Maste {
6090afa8e06SEd Maste 	cbor_item_t	*argv[1];
6100afa8e06SEd Maste 	const uint8_t	 cmd = CMD_ENROLL_REMOVE;
6110afa8e06SEd Maste 	int		 r = FIDO_ERR_INTERNAL;
6120afa8e06SEd Maste 
6130afa8e06SEd Maste 	memset(&argv, 0, sizeof(argv));
6140afa8e06SEd Maste 
6150afa8e06SEd Maste 	if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
6160afa8e06SEd Maste 		fido_log_debug("%s: cbor encode", __func__);
6170afa8e06SEd Maste 		goto fail;
6180afa8e06SEd Maste 	}
6190afa8e06SEd Maste 
620f540a430SEd Maste 	if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL, ms)) != FIDO_OK ||
6210afa8e06SEd Maste 	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
6220afa8e06SEd Maste 		fido_log_debug("%s: tx/rx", __func__);
6230afa8e06SEd Maste 		goto fail;
6240afa8e06SEd Maste 	}
6250afa8e06SEd Maste 
6260afa8e06SEd Maste 	r = FIDO_OK;
6270afa8e06SEd Maste fail:
6280afa8e06SEd Maste 	cbor_vector_free(argv, nitems(argv));
6290afa8e06SEd Maste 
6300afa8e06SEd Maste 	return (r);
6310afa8e06SEd Maste }
6320afa8e06SEd Maste 
6330afa8e06SEd Maste int
fido_bio_dev_enroll_remove(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin)6340afa8e06SEd Maste fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
6350afa8e06SEd Maste     const char *pin)
6360afa8e06SEd Maste {
637f540a430SEd Maste 	int ms = dev->timeout_ms;
638f540a430SEd Maste 
639f540a430SEd Maste 	return (bio_enroll_remove_wait(dev, t, pin, &ms));
6400afa8e06SEd Maste }
6410afa8e06SEd Maste 
6420afa8e06SEd Maste static void
bio_reset_info(fido_bio_info_t * i)6430afa8e06SEd Maste bio_reset_info(fido_bio_info_t *i)
6440afa8e06SEd Maste {
6450afa8e06SEd Maste 	i->type = 0;
6460afa8e06SEd Maste 	i->max_samples = 0;
6470afa8e06SEd Maste }
6480afa8e06SEd Maste 
6490afa8e06SEd Maste static int
bio_parse_info(const cbor_item_t * key,const cbor_item_t * val,void * arg)6500afa8e06SEd Maste bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
6510afa8e06SEd Maste {
6520afa8e06SEd Maste 	fido_bio_info_t	*i = arg;
6530afa8e06SEd Maste 	uint64_t	 x;
6540afa8e06SEd Maste 
6550afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
6560afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8) {
6570afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
6580afa8e06SEd Maste 		return (0); /* ignore */
6590afa8e06SEd Maste 	}
6600afa8e06SEd Maste 
6610afa8e06SEd Maste 	switch (cbor_get_uint8(key)) {
6620afa8e06SEd Maste 	case 2:
6630afa8e06SEd Maste 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
6640afa8e06SEd Maste 			fido_log_debug("%s: cbor_decode_uint64", __func__);
6650afa8e06SEd Maste 			return (-1);
6660afa8e06SEd Maste 		}
6670afa8e06SEd Maste 		i->type = (uint8_t)x;
6680afa8e06SEd Maste 		break;
6690afa8e06SEd Maste 	case 3:
6700afa8e06SEd Maste 		if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
6710afa8e06SEd Maste 			fido_log_debug("%s: cbor_decode_uint64", __func__);
6720afa8e06SEd Maste 			return (-1);
6730afa8e06SEd Maste 		}
6740afa8e06SEd Maste 		i->max_samples = (uint8_t)x;
6750afa8e06SEd Maste 		break;
6760afa8e06SEd Maste 	default:
6770afa8e06SEd Maste 		return (0); /* ignore */
6780afa8e06SEd Maste 	}
6790afa8e06SEd Maste 
6800afa8e06SEd Maste 	return (0);
6810afa8e06SEd Maste }
6820afa8e06SEd Maste 
6830afa8e06SEd Maste static int
bio_rx_info(fido_dev_t * dev,fido_bio_info_t * i,int * ms)684f540a430SEd Maste bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
6850afa8e06SEd Maste {
686*2ccfa855SEd Maste 	unsigned char	*msg;
687*2ccfa855SEd Maste 	int		 msglen;
6880afa8e06SEd Maste 	int		 r;
6890afa8e06SEd Maste 
6900afa8e06SEd Maste 	bio_reset_info(i);
6910afa8e06SEd Maste 
692*2ccfa855SEd Maste 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
693*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
694*2ccfa855SEd Maste 		goto out;
6950afa8e06SEd Maste 	}
6960afa8e06SEd Maste 
697*2ccfa855SEd Maste 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
698*2ccfa855SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
699*2ccfa855SEd Maste 		r = FIDO_ERR_RX;
700*2ccfa855SEd Maste 		goto out;
701*2ccfa855SEd Maste 	}
702*2ccfa855SEd Maste 
703*2ccfa855SEd Maste 	if ((r = cbor_parse_reply(msg, (size_t)msglen, i,
7040afa8e06SEd Maste 	    bio_parse_info)) != FIDO_OK) {
7050afa8e06SEd Maste 		fido_log_debug("%s: bio_parse_info" , __func__);
706*2ccfa855SEd Maste 		goto out;
7070afa8e06SEd Maste 	}
7080afa8e06SEd Maste 
709*2ccfa855SEd Maste 	r = FIDO_OK;
710*2ccfa855SEd Maste out:
711*2ccfa855SEd Maste 	freezero(msg, FIDO_MAXMSG);
712*2ccfa855SEd Maste 
713*2ccfa855SEd Maste 	return (r);
7140afa8e06SEd Maste }
7150afa8e06SEd Maste 
7160afa8e06SEd Maste static int
bio_get_info_wait(fido_dev_t * dev,fido_bio_info_t * i,int * ms)717f540a430SEd Maste bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
7180afa8e06SEd Maste {
7190afa8e06SEd Maste 	int r;
7200afa8e06SEd Maste 
721f540a430SEd Maste 	if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL,
722f540a430SEd Maste 	    ms)) != FIDO_OK ||
7230afa8e06SEd Maste 	    (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
7240afa8e06SEd Maste 		fido_log_debug("%s: tx/rx", __func__);
7250afa8e06SEd Maste 		return (r);
7260afa8e06SEd Maste 	}
7270afa8e06SEd Maste 
7280afa8e06SEd Maste 	return (FIDO_OK);
7290afa8e06SEd Maste }
7300afa8e06SEd Maste 
7310afa8e06SEd Maste int
fido_bio_dev_get_info(fido_dev_t * dev,fido_bio_info_t * i)7320afa8e06SEd Maste fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
7330afa8e06SEd Maste {
734f540a430SEd Maste 	int ms = dev->timeout_ms;
735f540a430SEd Maste 
736f540a430SEd Maste 	return (bio_get_info_wait(dev, i, &ms));
7370afa8e06SEd Maste }
7380afa8e06SEd Maste 
7390afa8e06SEd Maste const char *
fido_bio_template_name(const fido_bio_template_t * t)7400afa8e06SEd Maste fido_bio_template_name(const fido_bio_template_t *t)
7410afa8e06SEd Maste {
7420afa8e06SEd Maste 	return (t->name);
7430afa8e06SEd Maste }
7440afa8e06SEd Maste 
7450afa8e06SEd Maste const unsigned char *
fido_bio_template_id_ptr(const fido_bio_template_t * t)7460afa8e06SEd Maste fido_bio_template_id_ptr(const fido_bio_template_t *t)
7470afa8e06SEd Maste {
7480afa8e06SEd Maste 	return (t->id.ptr);
7490afa8e06SEd Maste }
7500afa8e06SEd Maste 
7510afa8e06SEd Maste size_t
fido_bio_template_id_len(const fido_bio_template_t * t)7520afa8e06SEd Maste fido_bio_template_id_len(const fido_bio_template_t *t)
7530afa8e06SEd Maste {
7540afa8e06SEd Maste 	return (t->id.len);
7550afa8e06SEd Maste }
7560afa8e06SEd Maste 
7570afa8e06SEd Maste size_t
fido_bio_template_array_count(const fido_bio_template_array_t * ta)7580afa8e06SEd Maste fido_bio_template_array_count(const fido_bio_template_array_t *ta)
7590afa8e06SEd Maste {
7600afa8e06SEd Maste 	return (ta->n_rx);
7610afa8e06SEd Maste }
7620afa8e06SEd Maste 
7630afa8e06SEd Maste fido_bio_template_array_t *
fido_bio_template_array_new(void)7640afa8e06SEd Maste fido_bio_template_array_new(void)
7650afa8e06SEd Maste {
7660afa8e06SEd Maste 	return (calloc(1, sizeof(fido_bio_template_array_t)));
7670afa8e06SEd Maste }
7680afa8e06SEd Maste 
7690afa8e06SEd Maste fido_bio_template_t *
fido_bio_template_new(void)7700afa8e06SEd Maste fido_bio_template_new(void)
7710afa8e06SEd Maste {
7720afa8e06SEd Maste 	return (calloc(1, sizeof(fido_bio_template_t)));
7730afa8e06SEd Maste }
7740afa8e06SEd Maste 
7750afa8e06SEd Maste void
fido_bio_template_array_free(fido_bio_template_array_t ** tap)7760afa8e06SEd Maste fido_bio_template_array_free(fido_bio_template_array_t **tap)
7770afa8e06SEd Maste {
7780afa8e06SEd Maste 	fido_bio_template_array_t *ta;
7790afa8e06SEd Maste 
7800afa8e06SEd Maste 	if (tap == NULL || (ta = *tap) == NULL)
7810afa8e06SEd Maste 		return;
7820afa8e06SEd Maste 
7830afa8e06SEd Maste 	bio_reset_template_array(ta);
7840afa8e06SEd Maste 	free(ta);
7850afa8e06SEd Maste 	*tap = NULL;
7860afa8e06SEd Maste }
7870afa8e06SEd Maste 
7880afa8e06SEd Maste void
fido_bio_template_free(fido_bio_template_t ** tp)7890afa8e06SEd Maste fido_bio_template_free(fido_bio_template_t **tp)
7900afa8e06SEd Maste {
7910afa8e06SEd Maste 	fido_bio_template_t *t;
7920afa8e06SEd Maste 
7930afa8e06SEd Maste 	if (tp == NULL || (t = *tp) == NULL)
7940afa8e06SEd Maste 		return;
7950afa8e06SEd Maste 
7960afa8e06SEd Maste 	bio_reset_template(t);
7970afa8e06SEd Maste 	free(t);
7980afa8e06SEd Maste 	*tp = NULL;
7990afa8e06SEd Maste }
8000afa8e06SEd Maste 
8010afa8e06SEd Maste int
fido_bio_template_set_name(fido_bio_template_t * t,const char * name)8020afa8e06SEd Maste fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
8030afa8e06SEd Maste {
8040afa8e06SEd Maste 	free(t->name);
8050afa8e06SEd Maste 	t->name = NULL;
8060afa8e06SEd Maste 
8070afa8e06SEd Maste 	if (name && (t->name = strdup(name)) == NULL)
8080afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
8090afa8e06SEd Maste 
8100afa8e06SEd Maste 	return (FIDO_OK);
8110afa8e06SEd Maste }
8120afa8e06SEd Maste 
8130afa8e06SEd Maste int
fido_bio_template_set_id(fido_bio_template_t * t,const unsigned char * ptr,size_t len)8140afa8e06SEd Maste fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
8150afa8e06SEd Maste     size_t len)
8160afa8e06SEd Maste {
8170afa8e06SEd Maste 	fido_blob_reset(&t->id);
8180afa8e06SEd Maste 
8190afa8e06SEd Maste 	if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
8200afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
8210afa8e06SEd Maste 
8220afa8e06SEd Maste 	return (FIDO_OK);
8230afa8e06SEd Maste }
8240afa8e06SEd Maste 
8250afa8e06SEd Maste const fido_bio_template_t *
fido_bio_template(const fido_bio_template_array_t * ta,size_t idx)8260afa8e06SEd Maste fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
8270afa8e06SEd Maste {
8280afa8e06SEd Maste 	if (idx >= ta->n_alloc)
8290afa8e06SEd Maste 		return (NULL);
8300afa8e06SEd Maste 
8310afa8e06SEd Maste 	return (&ta->ptr[idx]);
8320afa8e06SEd Maste }
8330afa8e06SEd Maste 
8340afa8e06SEd Maste fido_bio_enroll_t *
fido_bio_enroll_new(void)8350afa8e06SEd Maste fido_bio_enroll_new(void)
8360afa8e06SEd Maste {
8370afa8e06SEd Maste 	return (calloc(1, sizeof(fido_bio_enroll_t)));
8380afa8e06SEd Maste }
8390afa8e06SEd Maste 
8400afa8e06SEd Maste fido_bio_info_t *
fido_bio_info_new(void)8410afa8e06SEd Maste fido_bio_info_new(void)
8420afa8e06SEd Maste {
8430afa8e06SEd Maste 	return (calloc(1, sizeof(fido_bio_info_t)));
8440afa8e06SEd Maste }
8450afa8e06SEd Maste 
8460afa8e06SEd Maste uint8_t
fido_bio_info_type(const fido_bio_info_t * i)8470afa8e06SEd Maste fido_bio_info_type(const fido_bio_info_t *i)
8480afa8e06SEd Maste {
8490afa8e06SEd Maste 	return (i->type);
8500afa8e06SEd Maste }
8510afa8e06SEd Maste 
8520afa8e06SEd Maste uint8_t
fido_bio_info_max_samples(const fido_bio_info_t * i)8530afa8e06SEd Maste fido_bio_info_max_samples(const fido_bio_info_t *i)
8540afa8e06SEd Maste {
8550afa8e06SEd Maste 	return (i->max_samples);
8560afa8e06SEd Maste }
8570afa8e06SEd Maste 
8580afa8e06SEd Maste void
fido_bio_enroll_free(fido_bio_enroll_t ** ep)8590afa8e06SEd Maste fido_bio_enroll_free(fido_bio_enroll_t **ep)
8600afa8e06SEd Maste {
8610afa8e06SEd Maste 	fido_bio_enroll_t *e;
8620afa8e06SEd Maste 
8630afa8e06SEd Maste 	if (ep == NULL || (e = *ep) == NULL)
8640afa8e06SEd Maste 		return;
8650afa8e06SEd Maste 
8660afa8e06SEd Maste 	bio_reset_enroll(e);
8670afa8e06SEd Maste 
8680afa8e06SEd Maste 	free(e);
8690afa8e06SEd Maste 	*ep = NULL;
8700afa8e06SEd Maste }
8710afa8e06SEd Maste 
8720afa8e06SEd Maste void
fido_bio_info_free(fido_bio_info_t ** ip)8730afa8e06SEd Maste fido_bio_info_free(fido_bio_info_t **ip)
8740afa8e06SEd Maste {
8750afa8e06SEd Maste 	fido_bio_info_t *i;
8760afa8e06SEd Maste 
8770afa8e06SEd Maste 	if (ip == NULL || (i = *ip) == NULL)
8780afa8e06SEd Maste 		return;
8790afa8e06SEd Maste 
8800afa8e06SEd Maste 	free(i);
8810afa8e06SEd Maste 	*ip = NULL;
8820afa8e06SEd Maste }
8830afa8e06SEd Maste 
8840afa8e06SEd Maste uint8_t
fido_bio_enroll_remaining_samples(const fido_bio_enroll_t * e)8850afa8e06SEd Maste fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
8860afa8e06SEd Maste {
8870afa8e06SEd Maste 	return (e->remaining_samples);
8880afa8e06SEd Maste }
8890afa8e06SEd Maste 
8900afa8e06SEd Maste uint8_t
fido_bio_enroll_last_status(const fido_bio_enroll_t * e)8910afa8e06SEd Maste fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
8920afa8e06SEd Maste {
8930afa8e06SEd Maste 	return (e->last_status);
8940afa8e06SEd Maste }
895