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