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