## 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
    }