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