Coverage Report

Created: 2025-03-01 02:43

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