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 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)61 bio_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;
120 fail:
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
130 static void
bio_reset_template(fido_bio_template_t * t)131 bio_reset_template(fido_bio_template_t *t)
132 {
133 free(t->name);
134 t->name = NULL;
135 fido_blob_reset(&t->id);
136 }
137
138 static void
bio_reset_template_array(fido_bio_template_array_t * ta)139 bio_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
149 static int
decode_template(const cbor_item_t * key,const cbor_item_t * val,void * arg)150 decode_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
170 static int
decode_template_array(const cbor_item_t * item,void * arg)171 decode_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
196 static int
bio_parse_template_array(const cbor_item_t * key,const cbor_item_t * val,void * arg)197 bio_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
234 static int
bio_rx_template_array(fido_dev_t * dev,fido_bio_template_array_t * ta,int * ms)235 bio_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;
261 out:
262 freezero(msg, FIDO_MAXMSG);
263
264 return (r);
265 }
266
267 static int
bio_get_template_array_wait(fido_dev_t * dev,fido_bio_template_array_t * ta,const char * pin,int * ms)268 bio_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
280 int
fido_bio_dev_get_template_array(fido_dev_t * dev,fido_bio_template_array_t * ta,const char * pin)281 fido_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
292 static int
bio_set_template_name_wait(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin,int * ms)293 bio_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;
315 fail:
316 cbor_vector_free(argv, nitems(argv));
317
318 return (r);
319 }
320
321 int
fido_bio_dev_set_template_name(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin)322 fido_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
333 static void
bio_reset_enroll(fido_bio_enroll_t * e)334 bio_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
343 static int
bio_parse_enroll_status(const cbor_item_t * key,const cbor_item_t * val,void * arg)344 bio_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
378 static int
bio_parse_template_id(const cbor_item_t * key,const cbor_item_t * val,void * arg)379 bio_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
394 static int
bio_rx_enroll_begin(fido_dev_t * dev,fido_bio_template_t * t,fido_bio_enroll_t * e,int * ms)395 bio_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;
431 out:
432 freezero(msg, FIDO_MAXMSG);
433
434 return (r);
435 }
436
437 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)438 bio_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;
459 fail:
460 cbor_vector_free(argv, nitems(argv));
461
462 return (r);
463 }
464
465 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)466 fido_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;
496 fail:
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
507 static int
bio_rx_enroll_continue(fido_dev_t * dev,fido_bio_enroll_t * e,int * ms)508 bio_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;
535 out:
536 freezero(msg, FIDO_MAXMSG);
537
538 return (r);
539 }
540
541 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)542 bio_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;
564 fail:
565 cbor_vector_free(argv, nitems(argv));
566
567 return (r);
568 }
569
570 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)571 fido_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
582 static int
bio_enroll_cancel_wait(fido_dev_t * dev,int * ms)583 bio_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
597 int
fido_bio_dev_enroll_cancel(fido_dev_t * dev)598 fido_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
605 static int
bio_enroll_remove_wait(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin,int * ms)606 bio_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;
627 fail:
628 cbor_vector_free(argv, nitems(argv));
629
630 return (r);
631 }
632
633 int
fido_bio_dev_enroll_remove(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin)634 fido_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
642 static void
bio_reset_info(fido_bio_info_t * i)643 bio_reset_info(fido_bio_info_t *i)
644 {
645 i->type = 0;
646 i->max_samples = 0;
647 }
648
649 static int
bio_parse_info(const cbor_item_t * key,const cbor_item_t * val,void * arg)650 bio_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
683 static int
bio_rx_info(fido_dev_t * dev,fido_bio_info_t * i,int * ms)684 bio_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;
710 out:
711 freezero(msg, FIDO_MAXMSG);
712
713 return (r);
714 }
715
716 static int
bio_get_info_wait(fido_dev_t * dev,fido_bio_info_t * i,int * ms)717 bio_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
731 int
fido_bio_dev_get_info(fido_dev_t * dev,fido_bio_info_t * i)732 fido_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
739 const char *
fido_bio_template_name(const fido_bio_template_t * t)740 fido_bio_template_name(const fido_bio_template_t *t)
741 {
742 return (t->name);
743 }
744
745 const unsigned char *
fido_bio_template_id_ptr(const fido_bio_template_t * t)746 fido_bio_template_id_ptr(const fido_bio_template_t *t)
747 {
748 return (t->id.ptr);
749 }
750
751 size_t
fido_bio_template_id_len(const fido_bio_template_t * t)752 fido_bio_template_id_len(const fido_bio_template_t *t)
753 {
754 return (t->id.len);
755 }
756
757 size_t
fido_bio_template_array_count(const fido_bio_template_array_t * ta)758 fido_bio_template_array_count(const fido_bio_template_array_t *ta)
759 {
760 return (ta->n_rx);
761 }
762
763 fido_bio_template_array_t *
fido_bio_template_array_new(void)764 fido_bio_template_array_new(void)
765 {
766 return (calloc(1, sizeof(fido_bio_template_array_t)));
767 }
768
769 fido_bio_template_t *
fido_bio_template_new(void)770 fido_bio_template_new(void)
771 {
772 return (calloc(1, sizeof(fido_bio_template_t)));
773 }
774
775 void
fido_bio_template_array_free(fido_bio_template_array_t ** tap)776 fido_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
788 void
fido_bio_template_free(fido_bio_template_t ** tp)789 fido_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
801 int
fido_bio_template_set_name(fido_bio_template_t * t,const char * name)802 fido_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
813 int
fido_bio_template_set_id(fido_bio_template_t * t,const unsigned char * ptr,size_t len)814 fido_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
825 const fido_bio_template_t *
fido_bio_template(const fido_bio_template_array_t * ta,size_t idx)826 fido_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
834 fido_bio_enroll_t *
fido_bio_enroll_new(void)835 fido_bio_enroll_new(void)
836 {
837 return (calloc(1, sizeof(fido_bio_enroll_t)));
838 }
839
840 fido_bio_info_t *
fido_bio_info_new(void)841 fido_bio_info_new(void)
842 {
843 return (calloc(1, sizeof(fido_bio_info_t)));
844 }
845
846 uint8_t
fido_bio_info_type(const fido_bio_info_t * i)847 fido_bio_info_type(const fido_bio_info_t *i)
848 {
849 return (i->type);
850 }
851
852 uint8_t
fido_bio_info_max_samples(const fido_bio_info_t * i)853 fido_bio_info_max_samples(const fido_bio_info_t *i)
854 {
855 return (i->max_samples);
856 }
857
858 void
fido_bio_enroll_free(fido_bio_enroll_t ** ep)859 fido_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
872 void
fido_bio_info_free(fido_bio_info_t ** ip)873 fido_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
884 uint8_t
fido_bio_enroll_remaining_samples(const fido_bio_enroll_t * e)885 fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
886 {
887 return (e->remaining_samples);
888 }
889
890 uint8_t
fido_bio_enroll_last_status(const fido_bio_enroll_t * e)891 fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
892 {
893 return (e->last_status);
894 }
895