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
bio_prepare_hmac(uint8_t cmd,cbor_item_t ** argv,size_t argc,cbor_item_t ** param,fido_blob_t * hmac_data)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
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)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, int *ms)
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, ms)) != 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], ms)) != 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, ms) < 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
bio_reset_template(fido_bio_template_t * t)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
bio_reset_template_array(fido_bio_template_array_t * ta)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
decode_template(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
decode_template_array(const cbor_item_t * item,void * arg)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
bio_parse_template_array(const cbor_item_t * key,const cbor_item_t * val,void * arg)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
bio_rx_template_array(fido_dev_t * dev,fido_bio_template_array_t * ta,int * ms)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
bio_get_template_array_wait(fido_dev_t * dev,fido_bio_template_array_t * ta,const char * pin,int * ms)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, ms)) != 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
fido_bio_dev_get_template_array(fido_dev_t * dev,fido_bio_template_array_t * ta,const char * pin)271 fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
272 const char *pin)
273 {
274 int ms = dev->timeout_ms;
275
276 if (pin == NULL)
277 return (FIDO_ERR_INVALID_ARGUMENT);
278
279 return (bio_get_template_array_wait(dev, ta, pin, &ms));
280 }
281
282 static int
bio_set_template_name_wait(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin,int * ms)283 bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
284 const char *pin, int *ms)
285 {
286 cbor_item_t *argv[2];
287 int r = FIDO_ERR_INTERNAL;
288
289 memset(&argv, 0, sizeof(argv));
290
291 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
292 (argv[1] = cbor_build_string(t->name)) == NULL) {
293 fido_log_debug("%s: cbor encode", __func__);
294 goto fail;
295 }
296
297 if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL,
298 ms)) != FIDO_OK ||
299 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
300 fido_log_debug("%s: tx/rx", __func__);
301 goto fail;
302 }
303
304 r = FIDO_OK;
305 fail:
306 cbor_vector_free(argv, nitems(argv));
307
308 return (r);
309 }
310
311 int
fido_bio_dev_set_template_name(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin)312 fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
313 const char *pin)
314 {
315 int ms = dev->timeout_ms;
316
317 if (pin == NULL || t->name == NULL)
318 return (FIDO_ERR_INVALID_ARGUMENT);
319
320 return (bio_set_template_name_wait(dev, t, pin, &ms));
321 }
322
323 static void
bio_reset_enroll(fido_bio_enroll_t * e)324 bio_reset_enroll(fido_bio_enroll_t *e)
325 {
326 e->remaining_samples = 0;
327 e->last_status = 0;
328
329 if (e->token)
330 fido_blob_free(&e->token);
331 }
332
333 static int
bio_parse_enroll_status(const cbor_item_t * key,const cbor_item_t * val,void * arg)334 bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
335 void *arg)
336 {
337 fido_bio_enroll_t *e = arg;
338 uint64_t x;
339
340 if (cbor_isa_uint(key) == false ||
341 cbor_int_get_width(key) != CBOR_INT_8) {
342 fido_log_debug("%s: cbor type", __func__);
343 return (0); /* ignore */
344 }
345
346 switch (cbor_get_uint8(key)) {
347 case 5:
348 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
349 fido_log_debug("%s: cbor_decode_uint64", __func__);
350 return (-1);
351 }
352 e->last_status = (uint8_t)x;
353 break;
354 case 6:
355 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
356 fido_log_debug("%s: cbor_decode_uint64", __func__);
357 return (-1);
358 }
359 e->remaining_samples = (uint8_t)x;
360 break;
361 default:
362 return (0); /* ignore */
363 }
364
365 return (0);
366 }
367
368 static int
bio_parse_template_id(const cbor_item_t * key,const cbor_item_t * val,void * arg)369 bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
370 void *arg)
371 {
372 fido_blob_t *id = arg;
373
374 if (cbor_isa_uint(key) == false ||
375 cbor_int_get_width(key) != CBOR_INT_8 ||
376 cbor_get_uint8(key) != 4) {
377 fido_log_debug("%s: cbor type", __func__);
378 return (0); /* ignore */
379 }
380
381 return (fido_blob_decode(val, id));
382 }
383
384 static int
bio_rx_enroll_begin(fido_dev_t * dev,fido_bio_template_t * t,fido_bio_enroll_t * e,int * ms)385 bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
386 fido_bio_enroll_t *e, int *ms)
387 {
388 unsigned char reply[FIDO_MAXMSG];
389 int reply_len;
390 int r;
391
392 bio_reset_template(t);
393
394 e->remaining_samples = 0;
395 e->last_status = 0;
396
397 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
398 ms)) < 0) {
399 fido_log_debug("%s: fido_rx", __func__);
400 return (FIDO_ERR_RX);
401 }
402
403 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
404 bio_parse_enroll_status)) != FIDO_OK) {
405 fido_log_debug("%s: bio_parse_enroll_status", __func__);
406 return (r);
407 }
408 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id,
409 bio_parse_template_id)) != FIDO_OK) {
410 fido_log_debug("%s: bio_parse_template_id", __func__);
411 return (r);
412 }
413
414 return (FIDO_OK);
415 }
416
417 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)418 bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
419 fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
420 {
421 cbor_item_t *argv[3];
422 const uint8_t cmd = CMD_ENROLL_BEGIN;
423 int r = FIDO_ERR_INTERNAL;
424
425 memset(&argv, 0, sizeof(argv));
426
427 if ((argv[2] = cbor_build_uint(timo_ms)) == NULL) {
428 fido_log_debug("%s: cbor encode", __func__);
429 goto fail;
430 }
431
432 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
433 (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
434 fido_log_debug("%s: tx/rx", __func__);
435 goto fail;
436 }
437
438 r = FIDO_OK;
439 fail:
440 cbor_vector_free(argv, nitems(argv));
441
442 return (r);
443 }
444
445 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)446 fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
447 fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
448 {
449 es256_pk_t *pk = NULL;
450 fido_blob_t *ecdh = NULL;
451 fido_blob_t *token = NULL;
452 int ms = dev->timeout_ms;
453 int r;
454
455 if (pin == NULL || e->token != NULL)
456 return (FIDO_ERR_INVALID_ARGUMENT);
457
458 if ((token = fido_blob_new()) == NULL) {
459 r = FIDO_ERR_INTERNAL;
460 goto fail;
461 }
462
463 if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
464 fido_log_debug("%s: fido_do_ecdh", __func__);
465 goto fail;
466 }
467
468 if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
469 pk, NULL, token, &ms)) != FIDO_OK) {
470 fido_log_debug("%s: fido_dev_get_uv_token", __func__);
471 goto fail;
472 }
473
474 e->token = token;
475 token = NULL;
476 fail:
477 es256_pk_free(&pk);
478 fido_blob_free(&ecdh);
479 fido_blob_free(&token);
480
481 if (r != FIDO_OK)
482 return (r);
483
484 return (bio_enroll_begin_wait(dev, t, e, timo_ms, &ms));
485 }
486
487 static int
bio_rx_enroll_continue(fido_dev_t * dev,fido_bio_enroll_t * e,int * ms)488 bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms)
489 {
490 unsigned char reply[FIDO_MAXMSG];
491 int reply_len;
492 int r;
493
494 e->remaining_samples = 0;
495 e->last_status = 0;
496
497 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
498 ms)) < 0) {
499 fido_log_debug("%s: fido_rx", __func__);
500 return (FIDO_ERR_RX);
501 }
502
503 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
504 bio_parse_enroll_status)) != FIDO_OK) {
505 fido_log_debug("%s: bio_parse_enroll_status", __func__);
506 return (r);
507 }
508
509 return (FIDO_OK);
510 }
511
512 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)513 bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
514 fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
515 {
516 cbor_item_t *argv[3];
517 const uint8_t cmd = CMD_ENROLL_NEXT;
518 int r = FIDO_ERR_INTERNAL;
519
520 memset(&argv, 0, sizeof(argv));
521
522 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
523 (argv[2] = cbor_build_uint(timo_ms)) == NULL) {
524 fido_log_debug("%s: cbor encode", __func__);
525 goto fail;
526 }
527
528 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
529 (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
530 fido_log_debug("%s: tx/rx", __func__);
531 goto fail;
532 }
533
534 r = FIDO_OK;
535 fail:
536 cbor_vector_free(argv, nitems(argv));
537
538 return (r);
539 }
540
541 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)542 fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
543 fido_bio_enroll_t *e, uint32_t timo_ms)
544 {
545 int ms = dev->timeout_ms;
546
547 if (e->token == NULL)
548 return (FIDO_ERR_INVALID_ARGUMENT);
549
550 return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms));
551 }
552
553 static int
bio_enroll_cancel_wait(fido_dev_t * dev,int * ms)554 bio_enroll_cancel_wait(fido_dev_t *dev, int *ms)
555 {
556 const uint8_t cmd = CMD_ENROLL_CANCEL;
557 int r;
558
559 if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL, ms)) != FIDO_OK ||
560 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
561 fido_log_debug("%s: tx/rx", __func__);
562 return (r);
563 }
564
565 return (FIDO_OK);
566 }
567
568 int
fido_bio_dev_enroll_cancel(fido_dev_t * dev)569 fido_bio_dev_enroll_cancel(fido_dev_t *dev)
570 {
571 int ms = dev->timeout_ms;
572
573 return (bio_enroll_cancel_wait(dev, &ms));
574 }
575
576 static int
bio_enroll_remove_wait(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin,int * ms)577 bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
578 const char *pin, int *ms)
579 {
580 cbor_item_t *argv[1];
581 const uint8_t cmd = CMD_ENROLL_REMOVE;
582 int r = FIDO_ERR_INTERNAL;
583
584 memset(&argv, 0, sizeof(argv));
585
586 if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
587 fido_log_debug("%s: cbor encode", __func__);
588 goto fail;
589 }
590
591 if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL, ms)) != FIDO_OK ||
592 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
593 fido_log_debug("%s: tx/rx", __func__);
594 goto fail;
595 }
596
597 r = FIDO_OK;
598 fail:
599 cbor_vector_free(argv, nitems(argv));
600
601 return (r);
602 }
603
604 int
fido_bio_dev_enroll_remove(fido_dev_t * dev,const fido_bio_template_t * t,const char * pin)605 fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
606 const char *pin)
607 {
608 int ms = dev->timeout_ms;
609
610 return (bio_enroll_remove_wait(dev, t, pin, &ms));
611 }
612
613 static void
bio_reset_info(fido_bio_info_t * i)614 bio_reset_info(fido_bio_info_t *i)
615 {
616 i->type = 0;
617 i->max_samples = 0;
618 }
619
620 static int
bio_parse_info(const cbor_item_t * key,const cbor_item_t * val,void * arg)621 bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
622 {
623 fido_bio_info_t *i = arg;
624 uint64_t x;
625
626 if (cbor_isa_uint(key) == false ||
627 cbor_int_get_width(key) != CBOR_INT_8) {
628 fido_log_debug("%s: cbor type", __func__);
629 return (0); /* ignore */
630 }
631
632 switch (cbor_get_uint8(key)) {
633 case 2:
634 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
635 fido_log_debug("%s: cbor_decode_uint64", __func__);
636 return (-1);
637 }
638 i->type = (uint8_t)x;
639 break;
640 case 3:
641 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
642 fido_log_debug("%s: cbor_decode_uint64", __func__);
643 return (-1);
644 }
645 i->max_samples = (uint8_t)x;
646 break;
647 default:
648 return (0); /* ignore */
649 }
650
651 return (0);
652 }
653
654 static int
bio_rx_info(fido_dev_t * dev,fido_bio_info_t * i,int * ms)655 bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
656 {
657 unsigned char reply[FIDO_MAXMSG];
658 int reply_len;
659 int r;
660
661 bio_reset_info(i);
662
663 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
664 ms)) < 0) {
665 fido_log_debug("%s: fido_rx", __func__);
666 return (FIDO_ERR_RX);
667 }
668
669 if ((r = cbor_parse_reply(reply, (size_t)reply_len, i,
670 bio_parse_info)) != FIDO_OK) {
671 fido_log_debug("%s: bio_parse_info" , __func__);
672 return (r);
673 }
674
675 return (FIDO_OK);
676 }
677
678 static int
bio_get_info_wait(fido_dev_t * dev,fido_bio_info_t * i,int * ms)679 bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
680 {
681 int r;
682
683 if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL,
684 ms)) != FIDO_OK ||
685 (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
686 fido_log_debug("%s: tx/rx", __func__);
687 return (r);
688 }
689
690 return (FIDO_OK);
691 }
692
693 int
fido_bio_dev_get_info(fido_dev_t * dev,fido_bio_info_t * i)694 fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
695 {
696 int ms = dev->timeout_ms;
697
698 return (bio_get_info_wait(dev, i, &ms));
699 }
700
701 const char *
fido_bio_template_name(const fido_bio_template_t * t)702 fido_bio_template_name(const fido_bio_template_t *t)
703 {
704 return (t->name);
705 }
706
707 const unsigned char *
fido_bio_template_id_ptr(const fido_bio_template_t * t)708 fido_bio_template_id_ptr(const fido_bio_template_t *t)
709 {
710 return (t->id.ptr);
711 }
712
713 size_t
fido_bio_template_id_len(const fido_bio_template_t * t)714 fido_bio_template_id_len(const fido_bio_template_t *t)
715 {
716 return (t->id.len);
717 }
718
719 size_t
fido_bio_template_array_count(const fido_bio_template_array_t * ta)720 fido_bio_template_array_count(const fido_bio_template_array_t *ta)
721 {
722 return (ta->n_rx);
723 }
724
725 fido_bio_template_array_t *
fido_bio_template_array_new(void)726 fido_bio_template_array_new(void)
727 {
728 return (calloc(1, sizeof(fido_bio_template_array_t)));
729 }
730
731 fido_bio_template_t *
fido_bio_template_new(void)732 fido_bio_template_new(void)
733 {
734 return (calloc(1, sizeof(fido_bio_template_t)));
735 }
736
737 void
fido_bio_template_array_free(fido_bio_template_array_t ** tap)738 fido_bio_template_array_free(fido_bio_template_array_t **tap)
739 {
740 fido_bio_template_array_t *ta;
741
742 if (tap == NULL || (ta = *tap) == NULL)
743 return;
744
745 bio_reset_template_array(ta);
746 free(ta);
747 *tap = NULL;
748 }
749
750 void
fido_bio_template_free(fido_bio_template_t ** tp)751 fido_bio_template_free(fido_bio_template_t **tp)
752 {
753 fido_bio_template_t *t;
754
755 if (tp == NULL || (t = *tp) == NULL)
756 return;
757
758 bio_reset_template(t);
759 free(t);
760 *tp = NULL;
761 }
762
763 int
fido_bio_template_set_name(fido_bio_template_t * t,const char * name)764 fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
765 {
766 free(t->name);
767 t->name = NULL;
768
769 if (name && (t->name = strdup(name)) == NULL)
770 return (FIDO_ERR_INTERNAL);
771
772 return (FIDO_OK);
773 }
774
775 int
fido_bio_template_set_id(fido_bio_template_t * t,const unsigned char * ptr,size_t len)776 fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
777 size_t len)
778 {
779 fido_blob_reset(&t->id);
780
781 if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
782 return (FIDO_ERR_INTERNAL);
783
784 return (FIDO_OK);
785 }
786
787 const fido_bio_template_t *
fido_bio_template(const fido_bio_template_array_t * ta,size_t idx)788 fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
789 {
790 if (idx >= ta->n_alloc)
791 return (NULL);
792
793 return (&ta->ptr[idx]);
794 }
795
796 fido_bio_enroll_t *
fido_bio_enroll_new(void)797 fido_bio_enroll_new(void)
798 {
799 return (calloc(1, sizeof(fido_bio_enroll_t)));
800 }
801
802 fido_bio_info_t *
fido_bio_info_new(void)803 fido_bio_info_new(void)
804 {
805 return (calloc(1, sizeof(fido_bio_info_t)));
806 }
807
808 uint8_t
fido_bio_info_type(const fido_bio_info_t * i)809 fido_bio_info_type(const fido_bio_info_t *i)
810 {
811 return (i->type);
812 }
813
814 uint8_t
fido_bio_info_max_samples(const fido_bio_info_t * i)815 fido_bio_info_max_samples(const fido_bio_info_t *i)
816 {
817 return (i->max_samples);
818 }
819
820 void
fido_bio_enroll_free(fido_bio_enroll_t ** ep)821 fido_bio_enroll_free(fido_bio_enroll_t **ep)
822 {
823 fido_bio_enroll_t *e;
824
825 if (ep == NULL || (e = *ep) == NULL)
826 return;
827
828 bio_reset_enroll(e);
829
830 free(e);
831 *ep = NULL;
832 }
833
834 void
fido_bio_info_free(fido_bio_info_t ** ip)835 fido_bio_info_free(fido_bio_info_t **ip)
836 {
837 fido_bio_info_t *i;
838
839 if (ip == NULL || (i = *ip) == NULL)
840 return;
841
842 free(i);
843 *ip = NULL;
844 }
845
846 uint8_t
fido_bio_enroll_remaining_samples(const fido_bio_enroll_t * e)847 fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
848 {
849 return (e->remaining_samples);
850 }
851
852 uint8_t
fido_bio_enroll_last_status(const fido_bio_enroll_t * e)853 fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
854 {
855 return (e->last_status);
856 }
857