Coverage Report

Created: 2024-06-03 09:43

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