## Coverage Report

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

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