## Coverage Report

#### Created: 2023-09-23 17:42

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