Coverage Report

Created: 2026-04-08 06:32

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