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