## Coverage Report

#### Created: 2025-03-01 02:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /libfido2/src/credman.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 <openssl/sha.h>
    9
    10
    #include "fido.h"
    11
    #include "fido/credman.h"
    12
    #include "fido/es256.h"
    13
    14
    11.2k
    #define CMD_CRED_METADATA       0x01
    15
    6.39k
    #define CMD_RP_BEGIN            0x02
    16
    2.61k
    #define CMD_RP_NEXT             0x03
    17
    18.3k
    #define CMD_RK_BEGIN            0x04
    18
    7.21k
    #define CMD_RK_NEXT             0x05
    19
    12.6k
    #define CMD_DELETE_CRED         0x06
    20
    12.9k
    #define CMD_UPDATE_CRED         0x07
    21
    22
    static int
    23
    credman_grow_array(void **ptr, size_t *n_alloc, const size_t *n_rx, size_t n,
    24
        size_t size)
    25
    5.81k
    {
    26
    5.81k
            void *new_ptr;
    27
    28
    5.81k
    #ifdef FIDO_FUZZ
    29
    5.81k
            if (n > UINT8_MAX) {
    30
    455
                    fido_log_debug("%s: n > UINT8_MAX", __func__);
    31
    455
                    return (-1);
    32
    455
            }
    33
    5.35k
    #endif
    34
    35
    5.35k
            if (n < *n_alloc)
    36
    0
                    return (0);
    37
    38
            /* sanity check */
    39
    5.35k
            if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) {
    40
    0
                    fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n,
    41
    0
                        *n_rx, *n_alloc);
    42
    0
                    return (-1);
    43
    0
            }
    44
    45
    5.35k
            if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL)
    46
    7
                    return (-1);
    47
    48
    5.35k
            *ptr = new_ptr;
    49
    5.35k
            *n_alloc = n;
    50
    51
    5.35k
            return (0);
    52
    5.35k
    }
    53
    54
    static int
    55
    credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param,
    56
        fido_blob_t *hmac_data)
    57
    24.1k
    {
    58
    24.1k
            cbor_item_t *param_cbor[3];
    59
    24.1k
            const fido_cred_t *cred;
    60
    24.1k
            size_t n;
    61
    24.1k
            int ok = -1;
    62
    63
    24.1k
            memset(&param_cbor, 0, sizeof(param_cbor));
    64
    65
    24.1k
            if (body == NULL)
    66
    8.59k
                    return (fido_blob_set(hmac_data, &cmd, sizeof(cmd)));
    67
    68
    15.5k
            switch (cmd) {
    69
    8.25k
            case CMD_RK_BEGIN:
    70
    8.25k
                    n = 1;
    71
    8.25k
                    if ((param_cbor[0] = fido_blob_encode(body)) == NULL) {
    72
    7
                            fido_log_debug("%s: cbor encode", __func__);
    73
    7
                            goto fail;
    74
    7
                    }
    75
    8.25k
                    break;
    76
    8.25k
            case CMD_DELETE_CRED:
    77
    3.64k
                    n = 2;
    78
    3.64k
                    if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) {
    79
    55
                            fido_log_debug("%s: cbor encode", __func__);
    80
    55
                            goto fail;
    81
    55
                    }
    82
    3.59k
                    break;
    83
    3.64k
            case CMD_UPDATE_CRED:
    84
    3.64k
                    n = 3;
    85
    3.64k
                    cred = body;
    86
    3.64k
                    param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id);
    87
    3.64k
                    param_cbor[2] = cbor_encode_user_entity(&cred->user);
    88
    3.64k
                    if (param_cbor[1] == NULL || param_cbor[2] == NULL) {
    89
    82
                            fido_log_debug("%s: cbor encode", __func__);
    90
    82
                            goto fail;
    91
    82
                    }
    92
    3.56k
                    break;
    93
    3.56k
            default:
    94
    0
                    fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
    95
    0
                    return (-1);
    96
    15.5k
            }
    97
    98
    15.4k
            if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
    99
    93
                    fido_log_debug("%s: cbor_flatten_vector", __func__);
    100
    93
                    goto fail;
    101
    93
            }
    102
    15.3k
            if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) {
    103
    74
                    fido_log_debug("%s: cbor_build_frame", __func__);
    104
    74
                    goto fail;
    105
    74
            }
    106
    107
    15.2k
            ok = 0;
    108
    15.5k
    fail:
    109
    15.5k
            cbor_vector_free(param_cbor, nitems(param_cbor));
    110
    111
    15.5k
            return (ok);
    112
    15.2k
    }
    113
    114
    static uint8_t
    115
    credman_get_cmd(const fido_dev_t *dev)
    116
    55.8k
    {
    117
    55.8k
            if (dev->flags & FIDO_DEV_CREDMAN)
    118
    76
                    return (CTAP_CBOR_CRED_MGMT);
    119
    120
    55.7k
            return (CTAP_CBOR_CRED_MGMT_PRE);
    121
    55.8k
    }
    122
    123
    static int
    124
    credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
    125
        const char *rp_id, fido_opt_t uv, int *ms)
    126
    55.8k
    {
    127
    55.8k
            fido_blob_t      f;
    128
    55.8k
            fido_blob_t     *ecdh = NULL;
    129
    55.8k
            fido_blob_t      hmac;
    130
    55.8k
            es256_pk_t      *pk = NULL;
    131
    55.8k
            cbor_item_t     *argv[4];
    132
    55.8k
            const uint8_t    cmd = credman_get_cmd(dev);
    133
    55.8k
            int              r = FIDO_ERR_INTERNAL;
    134
    135
    55.8k
            memset(&f, 0, sizeof(f));
    136
    55.8k
            memset(&hmac, 0, sizeof(hmac));
    137
    55.8k
            memset(&argv, 0, sizeof(argv));
    138
    139
    55.8k
            if (fido_dev_is_fido2(dev) == false) {
    140
    21.8k
                    fido_log_debug("%s: fido_dev_is_fido2", __func__);
    141
    21.8k
                    r = FIDO_ERR_INVALID_COMMAND;
    142
    21.8k
                    goto fail;
    143
    21.8k
            }
    144
    145
            /* subCommand */
    146
    34.0k
            if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
    147
    91
                    fido_log_debug("%s: cbor encode", __func__);
    148
    91
                    goto fail;
    149
    91
            }
    150
    151
            /* pinProtocol, pinAuth */
    152
    33.9k
            if (pin != NULL || uv == FIDO_OPT_TRUE) {
    153
    24.1k
                    if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) {
    154
    313
                            fido_log_debug("%s: credman_prepare_hmac", __func__);
    155
    313
                            goto fail;
    156
    313
                    }
    157
    23.8k
                    if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
    158
    15.4k
                            fido_log_debug("%s: fido_do_ecdh", __func__);
    159
    15.4k
                            goto fail;
    160
    15.4k
                    }
    161
    8.41k
                    if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
    162
    8.41k
                        rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) {
    163
    1.55k
                            fido_log_debug("%s: cbor_add_uv_params", __func__);
    164
    1.55k
                            goto fail;
    165
    1.55k
                    }
    166
    8.41k
            }
    167
    168
            /* framing and transmission */
    169
    16.6k
            if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
    170
    16.6k
                fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    171
    175
                    fido_log_debug("%s: fido_tx", __func__);
    172
    175
                    r = FIDO_ERR_TX;
    173
    175
                    goto fail;
    174
    175
            }
    175
    176
    16.4k
            r = FIDO_OK;
    177
    55.8k
    fail:
    178
    55.8k
            es256_pk_free(&pk);
    179
    55.8k
            fido_blob_free(&ecdh);
    180
    55.8k
            cbor_vector_free(argv, nitems(argv));
    181
    55.8k
            free(f.ptr);
    182
    55.8k
            free(hmac.ptr);
    183
    184
    55.8k
            return (r);
    185
    16.4k
    }
    186
    187
    static int
    188
    credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
    189
        void *arg)
    190
    204
    {
    191
    204
            fido_credman_metadata_t *metadata = arg;
    192
    193
    204
            if (cbor_isa_uint(key) == false ||
    194
    204
                cbor_int_get_width(key) != CBOR_INT_8) {
    195
    76
                    fido_log_debug("%s: cbor type", __func__);
    196
    76
                    return (0); /* ignore */
    197
    76
            }
    198
    199
    128
            switch (cbor_get_uint8(key)) {
    200
    16
            case 1:
    201
    16
                    return (cbor_decode_uint64(val, &metadata->rk_existing));
    202
    16
            case 2:
    203
    16
                    return (cbor_decode_uint64(val, &metadata->rk_remaining));
    204
    96
            default:
    205
    96
                    fido_log_debug("%s: cbor type", __func__);
    206
    96
                    return (0); /* ignore */
    207
    128
            }
    208
    128
    }
    209
    210
    static int
    211
    credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms)
    212
    106
    {
    213
    106
            unsigned char   *msg;
    214
    106
            int              msglen;
    215
    106
            int              r;
    216
    217
    106
            memset(metadata, 0, sizeof(*metadata));
    218
    219
    106
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    220
    3
                    r = FIDO_ERR_INTERNAL;
    221
    3
                    goto out;
    222
    3
            }
    223
    224
    103
            if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    225
    7
                    fido_log_debug("%s: fido_rx", __func__);
    226
    7
                    r = FIDO_ERR_RX;
    227
    7
                    goto out;
    228
    7
            }
    229
    230
    96
            if ((r = cbor_parse_reply(msg, (size_t)msglen, metadata,
    231
    96
                credman_parse_metadata)) != FIDO_OK) {
    232
    60
                    fido_log_debug("%s: credman_parse_metadata", __func__);
    233
    60
                    goto out;
    234
    60
            }
    235
    236
    36
            r = FIDO_OK;
    237
    106
    out:
    238
    106
            freezero(msg, FIDO_MAXMSG);
    239
    240
    106
            return (r);
    241
    36
    }
    242
    243
    static int
    244
    credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
    245
        const char *pin, int *ms)
    246
    11.2k
    {
    247
    11.2k
            int r;
    248
    249
    11.2k
            if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL,
    250
    11.2k
                FIDO_OPT_TRUE, ms)) != FIDO_OK ||
    251
    11.2k
                (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
    252
    11.1k
                    return (r);
    253
    254
    36
            return (FIDO_OK);
    255
    11.2k
    }
    256
    257
    int
    258
    fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
    259
        const char *pin)
    260
    11.2k
    {
    261
    11.2k
            int ms = dev->timeout_ms;
    262
    263
    11.2k
            return (credman_get_metadata_wait(dev, metadata, pin, &ms));
    264
    11.2k
    }
    265
    266
    static int
    267
    credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    268
    31.6k
    {
    269
    31.6k
            fido_cred_t     *cred = arg;
    270
    31.6k
            uint64_t         prot;
    271
    272
    31.6k
            if (cbor_isa_uint(key) == false ||
    273
    31.6k
                cbor_int_get_width(key) != CBOR_INT_8) {
    274
    811
                    fido_log_debug("%s: cbor type", __func__);
    275
    811
                    return (0); /* ignore */
    276
    811
            }
    277
    278
    30.8k
            switch (cbor_get_uint8(key)) {
    279
    6.94k
            case 6:
    280
    6.94k
                    return (cbor_decode_user(val, &cred->user));
    281
    6.51k
            case 7:
    282
    6.51k
                    return (cbor_decode_cred_id(val, &cred->attcred.id));
    283
    6.44k
            case 8:
    284
    6.44k
                    if (cbor_decode_pubkey(val, &cred->attcred.type,
    285
    6.44k
                        &cred->attcred.pubkey) < 0)
    286
    1.51k
                            return (-1);
    287
    4.93k
                    cred->type = cred->attcred.type; /* XXX */
    288
    4.93k
                    return (0);
    289
    4.02k
            case 10:
    290
    4.02k
                    if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX ||
    291
    4.02k
                        fido_cred_set_prot(cred, (int)prot) != FIDO_OK)
    292
    325
                            return (-1);
    293
    3.70k
                    return (0);
    294
    13
            case 11:
    295
    13
                    return (fido_blob_decode(val, &cred->largeblob_key));
    296
    6.87k
            default:
    297
    6.87k
                    fido_log_debug("%s: cbor type", __func__);
    298
    6.87k
                    return (0); /* ignore */
    299
    30.8k
            }
    300
    30.8k
    }
    301
    302
    static void
    303
    credman_reset_rk(fido_credman_rk_t *rk)
    304
    14.7k
    {
    305
    87.1k
            for (size_t i = 0; i < rk->n_alloc; i++) {
    306
    72.4k
                    fido_cred_reset_tx(&rk->ptr[i]);
    307
    72.4k
                    fido_cred_reset_rx(&rk->ptr[i]);
    308
    72.4k
            }
    309
    310
    14.7k
            free(rk->ptr);
    311
    14.7k
            rk->ptr = NULL;
    312
    14.7k
            memset(rk, 0, sizeof(*rk));
    313
    14.7k
    }
    314
    315
    static int
    316
    credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
    317
        void *arg)
    318
    21.3k
    {
    319
    21.3k
            fido_credman_rk_t *rk = arg;
    320
    21.3k
            uint64_t n;
    321
    322
            /* totalCredentials */
    323
    21.3k
            if (cbor_isa_uint(key) == false ||
    324
    21.3k
                cbor_int_get_width(key) != CBOR_INT_8 ||
    325
    21.3k
                cbor_get_uint8(key) != 9) {
    326
    17.1k
                    fido_log_debug("%s: cbor_type", __func__);
    327
    17.1k
                    return (0); /* ignore */
    328
    17.1k
            }
    329
    330
    4.25k
            if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
    331
    3
                    fido_log_debug("%s: cbor_decode_uint64", __func__);
    332
    3
                    return (-1);
    333
    3
            }
    334
    335
    4.24k
            if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx,
    336
    4.24k
                (size_t)n, sizeof(*rk->ptr)) < 0) {
    337
    225
                    fido_log_debug("%s: credman_grow_array", __func__);
    338
    225
                    return (-1);
    339
    225
            }
    340
    341
    4.02k
            return (0);
    342
    4.24k
    }
    343
    344
    static int
    345
    credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
    346
    4.65k
    {
    347
    4.65k
            unsigned char   *msg;
    348
    4.65k
            int              msglen;
    349
    4.65k
            int              r;
    350
    351
    4.65k
            credman_reset_rk(rk);
    352
    353
    4.65k
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    354
    2
                    r = FIDO_ERR_INTERNAL;
    355
    2
                    goto out;
    356
    2
            }
    357
    358
    4.64k
            if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    359
    76
                    fido_log_debug("%s: fido_rx", __func__);
    360
    76
                    r = FIDO_ERR_RX;
    361
    76
                    goto out;
    362
    76
            }
    363
    364
            /* adjust as needed */
    365
    4.57k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, rk,
    366
    4.57k
                credman_parse_rk_count)) != FIDO_OK) {
    367
    549
                    fido_log_debug("%s: credman_parse_rk_count", __func__);
    368
    549
                    goto out;
    369
    549
            }
    370
    371
    4.02k
            if (rk->n_alloc == 0) {
    372
    13
                    fido_log_debug("%s: n_alloc=0", __func__);
    373
    13
                    r = FIDO_OK;
    374
    13
                    goto out;
    375
    13
            }
    376
    377
            /* parse the first rk */
    378
    4.01k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[0],
    379
    4.01k
                credman_parse_rk)) != FIDO_OK) {
    380
    788
                    fido_log_debug("%s: credman_parse_rk", __func__);
    381
    788
                    goto out;
    382
    788
            }
    383
    3.22k
            rk->n_rx = 1;
    384
    385
    3.22k
            r = FIDO_OK;
    386
    4.65k
    out:
    387
    4.65k
            freezero(msg, FIDO_MAXMSG);
    388
    389
    4.65k
            return (r);
    390
    3.22k
    }
    391
    392
    static int
    393
    credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
    394
    7.18k
    {
    395
    7.18k
            unsigned char   *msg;
    396
    7.18k
            int              msglen;
    397
    7.18k
            int              r;
    398
    399
    7.18k
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    400
    5
                    r = FIDO_ERR_INTERNAL;
    401
    5
                    goto out;
    402
    5
            }
    403
    404
    7.17k
            if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    405
    1.17k
                    fido_log_debug("%s: fido_rx", __func__);
    406
    1.17k
                    r = FIDO_ERR_RX;
    407
    1.17k
                    goto out;
    408
    1.17k
            }
    409
    410
            /* sanity check */
    411
    6.00k
            if (rk->n_rx >= rk->n_alloc) {
    412
    0
                    fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx,
    413
    0
                        rk->n_alloc);
    414
    0
                    r = FIDO_ERR_INTERNAL;
    415
    0
                    goto out;
    416
    0
            }
    417
    418
    6.00k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[rk->n_rx],
    419
    6.00k
                credman_parse_rk)) != FIDO_OK) {
    420
    1.98k
                    fido_log_debug("%s: credman_parse_rk", __func__);
    421
    1.98k
                    goto out;
    422
    1.98k
            }
    423
    424
    4.01k
            r = FIDO_OK;
    425
    7.18k
    out:
    426
    7.18k
            freezero(msg, FIDO_MAXMSG);
    427
    428
    7.18k
            return (r);
    429
    4.01k
    }
    430
    431
    static int
    432
    credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
    433
        const char *pin, int *ms)
    434
    10.0k
    {
    435
    10.0k
            fido_blob_t     rp_dgst;
    436
    10.0k
            uint8_t         dgst[SHA256_DIGEST_LENGTH];
    437
    10.0k
            int             r;
    438
    439
    10.0k
            if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) {
    440
    7
                    fido_log_debug("%s: sha256", __func__);
    441
    7
                    return (FIDO_ERR_INTERNAL);
    442
    7
            }
    443
    444
    10.0k
            rp_dgst.ptr = dgst;
    445
    10.0k
            rp_dgst.len = sizeof(dgst);
    446
    447
    10.0k
            if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id,
    448
    10.0k
                FIDO_OPT_TRUE, ms)) != FIDO_OK ||
    449
    10.0k
                (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
    450
    6.82k
                    return (r);
    451
    452
    7.25k
            while (rk->n_rx < rk->n_alloc) {
    453
    7.21k
                    if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL,
    454
    7.21k
                        FIDO_OPT_FALSE, ms)) != FIDO_OK ||
    455
    7.21k
                        (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
    456
    3.19k
                            return (r);
    457
    4.01k
                    rk->n_rx++;
    458
    4.01k
            }
    459
    460
    39
            return (FIDO_OK);
    461
    3.23k
    }
    462
    463
    int
    464
    fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
    465
        fido_credman_rk_t *rk, const char *pin)
    466
    10.0k
    {
    467
    10.0k
            int ms = dev->timeout_ms;
    468
    469
    10.0k
            return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms));
    470
    10.0k
    }
    471
    472
    static int
    473
    credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
    474
        size_t cred_id_len, const char *pin, int *ms)
    475
    9.07k
    {
    476
    9.07k
            fido_blob_t cred;
    477
    9.07k
            int r;
    478
    479
    9.07k
            memset(&cred, 0, sizeof(cred));
    480
    481
    9.07k
            if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
    482
    56
                    return (FIDO_ERR_INVALID_ARGUMENT);
    483
    484
    9.02k
            if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL,
    485
    9.02k
                FIDO_OPT_TRUE, ms)) != FIDO_OK ||
    486
    9.02k
                (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
    487
    9.00k
                    goto fail;
    488
    489
    18
            r = FIDO_OK;
    490
    9.02k
    fail:
    491
    9.02k
            free(cred.ptr);
    492
    493
    9.02k
            return (r);
    494
    18
    }
    495
    496
    int
    497
    fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
    498
        size_t cred_id_len, const char *pin)
    499
    9.07k
    {
    500
    9.07k
            int ms = dev->timeout_ms;
    501
    502
    9.07k
            return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms));
    503
    9.07k
    }
    504
    505
    static int
    506
    credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    507
    7.27k
    {
    508
    7.27k
            struct fido_credman_single_rp *rp = arg;
    509
    510
    7.27k
            if (cbor_isa_uint(key) == false ||
    511
    7.27k
                cbor_int_get_width(key) != CBOR_INT_8) {
    512
    659
                    fido_log_debug("%s: cbor type", __func__);
    513
    659
                    return (0); /* ignore */
    514
    659
            }
    515
    516
    6.61k
            switch (cbor_get_uint8(key)) {
    517
    2.83k
            case 3:
    518
    2.83k
                    return (cbor_decode_rp_entity(val, &rp->rp_entity));
    519
    1.77k
            case 4:
    520
    1.77k
                    return (fido_blob_decode(val, &rp->rp_id_hash));
    521
    2.00k
            default:
    522
    2.00k
                    fido_log_debug("%s: cbor type", __func__);
    523
    2.00k
                    return (0); /* ignore */
    524
    6.61k
            }
    525
    6.61k
    }
    526
    527
    static void
    528
    credman_reset_rp(fido_credman_rp_t *rp)
    529
    8.07k
    {
    530
    54.9k
            for (size_t i = 0; i < rp->n_alloc; i++) {
    531
    46.8k
                    free(rp->ptr[i].rp_entity.id);
    532
    46.8k
                    free(rp->ptr[i].rp_entity.name);
    533
    46.8k
                    rp->ptr[i].rp_entity.id = NULL;
    534
    46.8k
                    rp->ptr[i].rp_entity.name = NULL;
    535
    46.8k
                    fido_blob_reset(&rp->ptr[i].rp_id_hash);
    536
    46.8k
            }
    537
    538
    8.07k
            free(rp->ptr);
    539
    8.07k
            rp->ptr = NULL;
    540
    8.07k
            memset(rp, 0, sizeof(*rp));
    541
    8.07k
    }
    542
    543
    static int
    544
    credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
    545
        void *arg)
    546
    5.41k
    {
    547
    5.41k
            fido_credman_rp_t *rp = arg;
    548
    5.41k
            uint64_t n;
    549
    550
            /* totalRPs */
    551
    5.41k
            if (cbor_isa_uint(key) == false ||
    552
    5.41k
                cbor_int_get_width(key) != CBOR_INT_8 ||
    553
    5.41k
                cbor_get_uint8(key) != 5) {
    554
    3.84k
                    fido_log_debug("%s: cbor_type", __func__);
    555
    3.84k
                    return (0); /* ignore */
    556
    3.84k
            }
    557
    558
    1.57k
            if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
    559
    3
                    fido_log_debug("%s: cbor_decode_uint64", __func__);
    560
    3
                    return (-1);
    561
    3
            }
    562
    563
    1.56k
            if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx,
    564
    1.56k
                (size_t)n, sizeof(*rp->ptr)) < 0) {
    565
    237
                    fido_log_debug("%s: credman_grow_array", __func__);
    566
    237
                    return (-1);
    567
    237
            }
    568
    569
    1.33k
            return (0);
    570
    1.56k
    }
    571
    572
    static int
    573
    credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
    574
    1.68k
    {
    575
    1.68k
            unsigned char   *msg;
    576
    1.68k
            int              msglen;
    577
    1.68k
            int              r;
    578
    579
    1.68k
            credman_reset_rp(rp);
    580
    581
    1.68k
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    582
    3
                    r = FIDO_ERR_INTERNAL;
    583
    3
                    goto out;
    584
    3
            }
    585
    586
    1.68k
            if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    587
    15
                    fido_log_debug("%s: fido_rx", __func__);
    588
    15
                    r = FIDO_ERR_RX;
    589
    15
                    goto out;
    590
    15
            }
    591
    592
            /* adjust as needed */
    593
    1.66k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, rp,
    594
    1.66k
                credman_parse_rp_count)) != FIDO_OK) {
    595
    315
                    fido_log_debug("%s: credman_parse_rp_count", __func__);
    596
    315
                    goto out;
    597
    315
            }
    598
    599
    1.35k
            if (rp->n_alloc == 0) {
    600
    29
                    fido_log_debug("%s: n_alloc=0", __func__);
    601
    29
                    r = FIDO_OK;
    602
    29
                    goto out;
    603
    29
            }
    604
    605
            /* parse the first rp */
    606
    1.32k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[0],
    607
    1.32k
                credman_parse_rp)) != FIDO_OK) {
    608
    24
                    fido_log_debug("%s: credman_parse_rp", __func__);
    609
    24
                    goto out;
    610
    24
            }
    611
    1.29k
            rp->n_rx = 1;
    612
    613
    1.29k
            r = FIDO_OK;
    614
    1.68k
    out:
    615
    1.68k
            freezero(msg, FIDO_MAXMSG);
    616
    617
    1.68k
            return (r);
    618
    1.29k
    }
    619
    620
    static int
    621
    credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
    622
    2.56k
    {
    623
    2.56k
            unsigned char   *msg;
    624
    2.56k
            int              msglen;
    625
    2.56k
            int              r;
    626
    627
    2.56k
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    628
    4
                    r = FIDO_ERR_INTERNAL;
    629
    4
                    goto out;
    630
    4
            }
    631
    632
    2.55k
            if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    633
    436
                    fido_log_debug("%s: fido_rx", __func__);
    634
    436
                    r = FIDO_ERR_RX;
    635
    436
                    goto out;
    636
    436
            }
    637
    638
            /* sanity check */
    639
    2.12k
            if (rp->n_rx >= rp->n_alloc) {
    640
    0
                    fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx,
    641
    0
                        rp->n_alloc);
    642
    0
                    r = FIDO_ERR_INTERNAL;
    643
    0
                    goto out;
    644
    0
            }
    645
    646
    2.12k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[rp->n_rx],
    647
    2.12k
                credman_parse_rp)) != FIDO_OK) {
    648
    732
                    fido_log_debug("%s: credman_parse_rp", __func__);
    649
    732
                    goto out;
    650
    732
            }
    651
    652
    1.38k
            r = FIDO_OK;
    653
    2.56k
    out:
    654
    2.56k
            freezero(msg, FIDO_MAXMSG);
    655
    656
    2.56k
            return (r);
    657
    1.38k
    }
    658
    659
    static int
    660
    credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
    661
        int *ms)
    662
    6.39k
    {
    663
    6.39k
            int r;
    664
    665
    6.39k
            if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL,
    666
    6.39k
                FIDO_OPT_TRUE, ms)) != FIDO_OK ||
    667
    6.39k
                (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
    668
    5.06k
                    return (r);
    669
    670
    2.71k
            while (rp->n_rx < rp->n_alloc) {
    671
    2.61k
                    if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL,
    672
    2.61k
                        FIDO_OPT_FALSE, ms)) != FIDO_OK ||
    673
    2.61k
                        (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
    674
    1.22k
                            return (r);
    675
    1.38k
                    rp->n_rx++;
    676
    1.38k
            }
    677
    678
    98
            return (FIDO_OK);
    679
    1.32k
    }
    680
    681
    int
    682
    fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
    683
    6.39k
    {
    684
    6.39k
            int ms = dev->timeout_ms;
    685
    686
    6.39k
            return (credman_get_rp_wait(dev, rp, pin, &ms));
    687
    6.39k
    }
    688
    689
    static int
    690
    credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
    691
        int *ms)
    692
    9.33k
    {
    693
    9.33k
            int r;
    694
    695
    9.33k
            if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL,
    696
    9.33k
                FIDO_OPT_TRUE, ms)) != FIDO_OK ||
    697
    9.33k
                (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
    698
    9.30k
                    return (r);
    699
    700
    32
            return (FIDO_OK);
    701
    9.33k
    }
    702
    703
    int
    704
    fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
    705
    9.33k
    {
    706
    9.33k
            int ms = dev->timeout_ms;
    707
    708
    9.33k
            return (credman_set_dev_rk_wait(dev, cred, pin, &ms));
    709
    9.33k
    }
    710
    711
    fido_credman_rk_t *
    712
    fido_credman_rk_new(void)
    713
    10.1k
    {
    714
    10.1k
            return (calloc(1, sizeof(fido_credman_rk_t)));
    715
    10.1k
    }
    716
    717
    void
    718
    fido_credman_rk_free(fido_credman_rk_t **rk_p)
    719
    10.0k
    {
    720
    10.0k
            fido_credman_rk_t *rk;
    721
    722
    10.0k
            if (rk_p == NULL || (rk = *rk_p) == NULL)
    723
    0
                    return;
    724
    725
    10.0k
            credman_reset_rk(rk);
    726
    10.0k
            free(rk);
    727
    10.0k
            *rk_p = NULL;
    728
    10.0k
    }
    729
    730
    size_t
    731
    fido_credman_rk_count(const fido_credman_rk_t *rk)
    732
    33.4k
    {
    733
    33.4k
            return (rk->n_rx);
    734
    33.4k
    }
    735
    736
    const fido_cred_t *
    737
    fido_credman_rk(const fido_credman_rk_t *rk, size_t idx)
    738
    17.3k
    {
    739
    17.3k
            if (idx >= rk->n_alloc)
    740
    6.07k
                    return (NULL);
    741
    742
    11.2k
            return (&rk->ptr[idx]);
    743
    17.3k
    }
    744
    745
    fido_credman_metadata_t *
    746
    fido_credman_metadata_new(void)
    747
    11.2k
    {
    748
    11.2k
            return (calloc(1, sizeof(fido_credman_metadata_t)));
    749
    11.2k
    }
    750
    751
    void
    752
    fido_credman_metadata_free(fido_credman_metadata_t **metadata_p)
    753
    11.2k
    {
    754
    11.2k
            fido_credman_metadata_t *metadata;
    755
    756
    11.2k
            if (metadata_p == NULL || (metadata = *metadata_p) == NULL)
    757
    0
                    return;
    758
    759
    11.2k
            free(metadata);
    760
    11.2k
            *metadata_p = NULL;
    761
    11.2k
    }
    762
    763
    uint64_t
    764
    fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
    765
    11.2k
    {
    766
    11.2k
            return (metadata->rk_existing);
    767
    11.2k
    }
    768
    769
    uint64_t
    770
    fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
    771
    11.2k
    {
    772
    11.2k
            return (metadata->rk_remaining);
    773
    11.2k
    }
    774
    775
    fido_credman_rp_t *
    776
    fido_credman_rp_new(void)
    777
    6.43k
    {
    778
    6.43k
            return (calloc(1, sizeof(fido_credman_rp_t)));
    779
    6.43k
    }
    780
    781
    void
    782
    fido_credman_rp_free(fido_credman_rp_t **rp_p)
    783
    6.39k
    {
    784
    6.39k
            fido_credman_rp_t *rp;
    785
    786
    6.39k
            if (rp_p == NULL || (rp = *rp_p) == NULL)
    787
    0
                    return;
    788
    789
    6.39k
            credman_reset_rp(rp);
    790
    6.39k
            free(rp);
    791
    6.39k
            *rp_p = NULL;
    792
    6.39k
    }
    793
    794
    size_t
    795
    fido_credman_rp_count(const fido_credman_rp_t *rp)
    796
    15.4k
    {
    797
    15.4k
            return (rp->n_rx);
    798
    15.4k
    }
    799
    800
    const char *
    801
    fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx)
    802
    9.08k
    {
    803
    9.08k
            if (idx >= rp->n_alloc)
    804
    5.13k
                    return (NULL);
    805
    806
    3.94k
            return (rp->ptr[idx].rp_entity.id);
    807
    9.08k
    }
    808
    809
    const char *
    810
    fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx)
    811
    9.08k
    {
    812
    9.08k
            if (idx >= rp->n_alloc)
    813
    5.13k
                    return (NULL);
    814
    815
    3.94k
            return (rp->ptr[idx].rp_entity.name);
    816
    9.08k
    }
    817
    818
    size_t
    819
    fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx)
    820
    9.08k
    {
    821
    9.08k
            if (idx >= rp->n_alloc)
    822
    5.13k
                    return (0);
    823
    824
    3.94k
            return (rp->ptr[idx].rp_id_hash.len);
    825
    9.08k
    }
    826
    827
    const unsigned char *
    828
    fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx)
    829
    9.08k
    {
    830
    9.08k
            if (idx >= rp->n_alloc)
    831
    5.13k
                    return (NULL);
    832
    833
    3.94k
            return (rp->ptr[idx].rp_id_hash.ptr);
    834
    9.08k
    }