Coverage Report

Created: 2022-04-27 14:33

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