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