## Coverage Report

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

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

    /libfido2/src/largeblob.c

    Line
    Count
    Source
    1
    /*
    2
     * Copyright (c) 2020-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/es256.h"
    12
    13
    7.91k
    #define LARGEBLOB_DIGEST_LENGTH 16
    14
    3.18k
    #define LARGEBLOB_NONCE_LENGTH  12
    15
    3.20k
    #define LARGEBLOB_TAG_LENGTH    16
    16
    17
    typedef struct largeblob {
    18
            size_t origsiz;
    19
            fido_blob_t ciphertext;
    20
            fido_blob_t nonce;
    21
    } largeblob_t;
    22
    23
    static largeblob_t *
    24
    largeblob_new(void)
    25
    10.9k
    {
    26
    10.9k
            return calloc(1, sizeof(largeblob_t));
    27
    10.9k
    }
    28
    29
    static void
    30
    largeblob_reset(largeblob_t *blob)
    31
    14.1k
    {
    32
    14.1k
            fido_blob_reset(&blob->ciphertext);
    33
    14.1k
            fido_blob_reset(&blob->nonce);
    34
    14.1k
            blob->origsiz = 0;
    35
    14.1k
    }
    36
    37
    static void
    38
    largeblob_free(largeblob_t **blob_ptr)
    39
    10.9k
    {
    40
    10.9k
            largeblob_t *blob;
    41
    42
    10.9k
            if (blob_ptr == NULL || (blob = *blob_ptr) == NULL)
    43
    13
                    return;
    44
    10.9k
            largeblob_reset(blob);
    45
    10.9k
            free(blob);
    46
    10.9k
            *blob_ptr = NULL;
    47
    10.9k
    }
    48
    49
    static int
    50
    largeblob_aad(fido_blob_t *aad, uint64_t size)
    51
    13.9k
    {
    52
    13.9k
            uint8_t buf[4 + sizeof(uint64_t)];
    53
    54
    13.9k
            buf[0] = 0x62; /* b */
    55
    13.9k
            buf[1] = 0x6c; /* l */
    56
    13.9k
            buf[2] = 0x6f; /* o */
    57
    13.9k
            buf[3] = 0x62; /* b */
    58
    13.9k
            size = htole64(size);
    59
    13.9k
            memcpy(&buf[4], &size, sizeof(uint64_t));
    60
    61
    13.9k
            return fido_blob_set(aad, buf, sizeof(buf));
    62
    13.9k
    }
    63
    64
    static fido_blob_t *
    65
    largeblob_decrypt(const largeblob_t *blob, const fido_blob_t *key)
    66
    3.18k
    {
    67
    3.18k
            fido_blob_t *plaintext = NULL, *aad = NULL;
    68
    3.18k
            int ok = -1;
    69
    70
    3.18k
            if ((plaintext = fido_blob_new()) == NULL ||
    71
    3.18k
                (aad = fido_blob_new()) == NULL) {
    72
    47
                    fido_log_debug("%s: fido_blob_new", __func__);
    73
    47
                    goto fail;
    74
    47
            }
    75
    3.13k
            if (largeblob_aad(aad, blob->origsiz) < 0) {
    76
    53
                    fido_log_debug("%s: largeblob_aad", __func__);
    77
    53
                    goto fail;
    78
    53
            }
    79
    3.08k
            if (aes256_gcm_dec(key, &blob->nonce, aad, &blob->ciphertext,
    80
    3.08k
                plaintext) < 0) {
    81
    1.43k
                    fido_log_debug("%s: aes256_gcm_dec", __func__);
    82
    1.43k
                    goto fail;
    83
    1.43k
            }
    84
    85
    1.65k
            ok = 0;
    86
    3.18k
    fail:
    87
    3.18k
            fido_blob_free(&aad);
    88
    89
    3.18k
            if (ok < 0)
    90
    1.53k
                    fido_blob_free(&plaintext);
    91
    92
    3.18k
            return plaintext;
    93
    1.65k
    }
    94
    95
    static int
    96
    largeblob_get_nonce(largeblob_t *blob)
    97
    10.8k
    {
    98
    10.8k
            uint8_t buf[LARGEBLOB_NONCE_LENGTH];
    99
    10.8k
            int ok = -1;
    100
    101
    10.8k
            if (fido_get_random(buf, sizeof(buf)) < 0) {
    102
    18
                    fido_log_debug("%s: fido_get_random", __func__);
    103
    18
                    goto fail;
    104
    18
            }
    105
    10.7k
            if (fido_blob_set(&blob->nonce, buf, sizeof(buf)) < 0) {
    106
    15
                    fido_log_debug("%s: fido_blob_set", __func__);
    107
    15
                    goto fail;
    108
    15
            }
    109
    110
    10.7k
            ok = 0;
    111
    10.8k
    fail:
    112
    10.8k
            explicit_bzero(buf, sizeof(buf));
    113
    114
    10.8k
            return ok;
    115
    10.7k
    }
    116
    117
    static int
    118
    largeblob_seal(largeblob_t *blob, const fido_blob_t *body,
    119
        const fido_blob_t *key)
    120
    10.9k
    {
    121
    10.9k
            fido_blob_t *plaintext = NULL, *aad = NULL;
    122
    10.9k
            int ok = -1;
    123
    124
    10.9k
            if ((plaintext = fido_blob_new()) == NULL ||
    125
    10.9k
                (aad = fido_blob_new()) == NULL) {
    126
    51
                    fido_log_debug("%s: fido_blob_new", __func__);
    127
    51
                    goto fail;
    128
    51
            }
    129
    10.8k
            if (fido_compress(plaintext, body) != FIDO_OK) {
    130
    35
                    fido_log_debug("%s: fido_compress", __func__);
    131
    35
                    goto fail;
    132
    35
            }
    133
    10.8k
            if (largeblob_aad(aad, body->len) < 0) {
    134
    11
                    fido_log_debug("%s: largeblob_aad", __func__);
    135
    11
                    goto fail;
    136
    11
            }
    137
    10.8k
            if (largeblob_get_nonce(blob) < 0) {
    138
    33
                    fido_log_debug("%s: largeblob_get_nonce", __func__);
    139
    33
                    goto fail;
    140
    33
            }
    141
    10.7k
            if (aes256_gcm_enc(key, &blob->nonce, aad, plaintext,
    142
    10.7k
                &blob->ciphertext) < 0) {
    143
    206
                    fido_log_debug("%s: aes256_gcm_enc", __func__);
    144
    206
                    goto fail;
    145
    206
            }
    146
    10.5k
            blob->origsiz = body->len;
    147
    148
    10.5k
            ok = 0;
    149
    10.9k
    fail:
    150
    10.9k
            fido_blob_free(&plaintext);
    151
    10.9k
            fido_blob_free(&aad);
    152
    153
    10.9k
            return ok;
    154
    10.5k
    }
    155
    156
    static int
    157
    largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms)
    158
    10.2k
    {
    159
    10.2k
            fido_blob_t f;
    160
    10.2k
            cbor_item_t *argv[3];
    161
    10.2k
            int r;
    162
    163
    10.2k
            memset(argv, 0, sizeof(argv));
    164
    10.2k
            memset(&f, 0, sizeof(f));
    165
    166
    10.2k
            if ((argv[0] = cbor_build_uint(count)) == NULL ||
    167
    10.2k
                (argv[2] = cbor_build_uint(offset)) == NULL) {
    168
    86
                    fido_log_debug("%s: cbor encode", __func__);
    169
    86
                    r = FIDO_ERR_INTERNAL;
    170
    86
                    goto fail;
    171
    86
            }
    172
    10.1k
            if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
    173
    10.1k
                fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    174
    274
                    fido_log_debug("%s: fido_tx", __func__);
    175
    274
                    r = FIDO_ERR_TX;
    176
    274
                    goto fail;
    177
    274
            }
    178
    179
    9.86k
            r = FIDO_OK;
    180
    10.2k
    fail:
    181
    10.2k
            cbor_vector_free(argv, nitems(argv));
    182
    10.2k
            free(f.ptr);
    183
    184
    10.2k
            return r;
    185
    9.86k
    }
    186
    187
    static int
    188
    parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val,
    189
        void *arg)
    190
    4.98k
    {
    191
    4.98k
            if (cbor_isa_uint(key) == false ||
    192
    4.98k
                cbor_int_get_width(key) != CBOR_INT_8 ||
    193
    4.98k
                cbor_get_uint8(key) != 1) {
    194
    834
                    fido_log_debug("%s: cbor type", __func__);
    195
    834
                    return 0; /* ignore */
    196
    834
            }
    197
    198
    4.15k
            return fido_blob_decode(val, arg);
    199
    4.98k
    }
    200
    201
    static int
    202
    largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int *ms)
    203
    9.86k
    {
    204
    9.86k
            unsigned char *msg;
    205
    9.86k
            int msglen, r;
    206
    207
    9.86k
            *chunk = NULL;
    208
    9.86k
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    209
    159
                    r = FIDO_ERR_INTERNAL;
    210
    159
                    goto out;
    211
    159
            }
    212
    9.70k
            if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    213
    4.91k
                    fido_log_debug("%s: fido_rx", __func__);
    214
    4.91k
                    r = FIDO_ERR_RX;
    215
    4.91k
                    goto out;
    216
    4.91k
            }
    217
    4.79k
            if ((*chunk = fido_blob_new()) == NULL) {
    218
    13
                    fido_log_debug("%s: fido_blob_new", __func__);
    219
    13
                    r = FIDO_ERR_INTERNAL;
    220
    13
                    goto out;
    221
    13
            }
    222
    4.77k
            if ((r = cbor_parse_reply(msg, (size_t)msglen, *chunk,
    223
    4.77k
                parse_largeblob_reply)) != FIDO_OK) {
    224
    618
                    fido_log_debug("%s: parse_largeblob_reply", __func__);
    225
    618
                    goto out;
    226
    618
            }
    227
    228
    4.16k
            r = FIDO_OK;
    229
    9.86k
    out:
    230
    9.86k
            if (r != FIDO_OK)
    231
    5.70k
                    fido_blob_free(chunk);
    232
    233
    9.86k
            freezero(msg, FIDO_MAXMSG);
    234
    235
    9.86k
            return r;
    236
    4.16k
    }
    237
    238
    static cbor_item_t *
    239
    largeblob_array_load(const uint8_t *ptr, size_t len)
    240
    2.06k
    {
    241
    2.06k
            struct cbor_load_result cbor;
    242
    2.06k
            cbor_item_t *item;
    243
    244
    2.06k
            if (len < LARGEBLOB_DIGEST_LENGTH) {
    245
    0
                    fido_log_debug("%s: len", __func__);
    246
    0
                    return NULL;
    247
    0
            }
    248
    2.06k
            len -= LARGEBLOB_DIGEST_LENGTH;
    249
    2.06k
            if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
    250
    12
                    fido_log_debug("%s: cbor_load", __func__);
    251
    12
                    return NULL;
    252
    12
            }
    253
    2.05k
            if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
    254
    0
                    fido_log_debug("%s: cbor type", __func__);
    255
    0
                    cbor_decref(&item);
    256
    0
                    return NULL;
    257
    0
            }
    258
    259
    2.05k
            return item;
    260
    2.05k
    }
    261
    262
    static size_t
    263
    get_chunklen(fido_dev_t *dev)
    264
    39.1k
    {
    265
    39.1k
            uint64_t maxchunklen;
    266
    267
    39.1k
            if ((maxchunklen = fido_dev_maxmsgsize(dev)) > SIZE_MAX)
    268
    0
                    maxchunklen = SIZE_MAX;
    269
    39.1k
            if (maxchunklen > FIDO_MAXMSG)
    270
    3.31k
                    maxchunklen = FIDO_MAXMSG;
    271
    39.1k
            maxchunklen = maxchunklen > 64 ? maxchunklen - 64 : 0;
    272
    273
    39.1k
            return (size_t)maxchunklen;
    274
    39.1k
    }
    275
    276
    static int
    277
    largeblob_do_decode(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    278
    9.63k
    {
    279
    9.63k
            largeblob_t *blob = arg;
    280
    9.63k
            uint64_t origsiz;
    281
    282
    9.63k
            if (cbor_isa_uint(key) == false ||
    283
    9.63k
                cbor_int_get_width(key) != CBOR_INT_8) {
    284
    0
                    fido_log_debug("%s: cbor type", __func__);
    285
    0
                    return 0; /* ignore */
    286
    0
            }
    287
    288
    9.63k
            switch (cbor_get_uint8(key)) {
    289
    3.24k
            case 1: /* ciphertext */
    290
    3.24k
                    if (fido_blob_decode(val, &blob->ciphertext) < 0 ||
    291
    3.24k
                        blob->ciphertext.len < LARGEBLOB_TAG_LENGTH)
    292
    31
                            return -1;
    293
    3.20k
                    return 0;
    294
    3.20k
            case 2: /* nonce */
    295
    3.20k
                    if (fido_blob_decode(val, &blob->nonce) < 0 ||
    296
    3.20k
                        blob->nonce.len != LARGEBLOB_NONCE_LENGTH)
    297
    27
                            return -1;
    298
    3.18k
                    return 0;
    299
    3.18k
            case 3: /* origSize */
    300
    3.18k
                    if (!cbor_isa_uint(val) ||
    301
    3.18k
                        (origsiz = cbor_get_int(val)) > SIZE_MAX)
    302
    0
                            return -1;
    303
    3.18k
                    blob->origsiz = (size_t)origsiz;
    304
    3.18k
                    return 0;
    305
    0
            default: /* ignore */
    306
    0
                    fido_log_debug("%s: cbor type", __func__);
    307
    0
                    return 0;
    308
    9.63k
            }
    309
    9.63k
    }
    310
    311
    static int
    312
    largeblob_decode(largeblob_t *blob, const cbor_item_t *item)
    313
    3.24k
    {
    314
    3.24k
            if (!cbor_isa_map(item) || !cbor_map_is_definite(item)) {
    315
    0
                    fido_log_debug("%s: cbor type", __func__);
    316
    0
                    return -1;
    317
    0
            }
    318
    3.24k
            if (cbor_map_iter(item, blob, largeblob_do_decode) < 0) {
    319
    67
                    fido_log_debug("%s: cbor_map_iter", __func__);
    320
    67
                    return -1;
    321
    67
            }
    322
    3.18k
            if (fido_blob_is_empty(&blob->ciphertext) ||
    323
    3.18k
                fido_blob_is_empty(&blob->nonce) || blob->origsiz == 0) {
    324
    0
                    fido_log_debug("%s: incomplete blob", __func__);
    325
    0
                    return -1;
    326
    0
            }
    327
    328
    3.18k
            return 0;
    329
    3.18k
    }
    330
    331
    static cbor_item_t *
    332
    largeblob_encode(const fido_blob_t *body, const fido_blob_t *key)
    333
    10.9k
    {
    334
    10.9k
            largeblob_t *blob;
    335
    10.9k
            cbor_item_t *argv[3], *item = NULL;
    336
    337
    10.9k
            memset(argv, 0, sizeof(argv));
    338
    10.9k
            if ((blob = largeblob_new()) == NULL ||
    339
    10.9k
                largeblob_seal(blob, body, key) < 0) {
    340
    349
                    fido_log_debug("%s: largeblob_seal", __func__);
    341
    349
                    goto fail;
    342
    349
            }
    343
    10.5k
            if ((argv[0] = fido_blob_encode(&blob->ciphertext)) == NULL ||
    344
    10.5k
                (argv[1] = fido_blob_encode(&blob->nonce)) == NULL ||
    345
    10.5k
                (argv[2] = cbor_build_uint(blob->origsiz)) == NULL) {
    346
    76
                    fido_log_debug("%s: cbor encode", __func__);
    347
    76
                    goto fail;
    348
    76
            }
    349
    10.4k
            item = cbor_flatten_vector(argv, nitems(argv));
    350
    10.9k
    fail:
    351
    10.9k
            cbor_vector_free(argv, nitems(argv));
    352
    10.9k
            largeblob_free(&blob);
    353
    354
    10.9k
            return item;
    355
    10.4k
    }
    356
    357
    static int
    358
    largeblob_array_lookup(fido_blob_t *out, size_t *idx, const cbor_item_t *item,
    359
        const fido_blob_t *key)
    360
    3.81k
    {
    361
    3.81k
            cbor_item_t **v;
    362
    3.81k
            fido_blob_t *plaintext = NULL;
    363
    3.81k
            largeblob_t blob;
    364
    3.81k
            int r;
    365
    366
    3.81k
            memset(&blob, 0, sizeof(blob));
    367
    3.81k
            if (idx != NULL)
    368
    3.72k
                    *idx = 0;
    369
    3.81k
            if ((v = cbor_array_handle(item)) == NULL)
    370
    18
                    return FIDO_ERR_INVALID_ARGUMENT;
    371
    5.39k
            for (size_t i = 0; i < cbor_array_size(item); i++) {
    372
    3.24k
                    if (largeblob_decode(&blob, v[i]) < 0 ||
    373
    3.24k
                        (plaintext = largeblob_decrypt(&blob, key)) == NULL) {
    374
    1.59k
                            fido_log_debug("%s: largeblob_decode", __func__);
    375
    1.59k
                            largeblob_reset(&blob);
    376
    1.59k
                            continue;
    377
    1.59k
                    }
    378
    1.65k
                    if (idx != NULL)
    379
    1.61k
                            *idx = i;
    380
    1.65k
                    break;
    381
    3.24k
            }
    382
    3.79k
            if (plaintext == NULL) {
    383
    2.14k
                    fido_log_debug("%s: not found", __func__);
    384
    2.14k
                    return FIDO_ERR_NOTFOUND;
    385
    2.14k
            }
    386
    1.65k
            if (out != NULL)
    387
    33
                    r = fido_uncompress(out, plaintext, blob.origsiz);
    388
    1.61k
            else
    389
    1.61k
                    r = FIDO_OK;
    390
    391
    1.65k
            fido_blob_free(&plaintext);
    392
    1.65k
            largeblob_reset(&blob);
    393
    394
    1.65k
            return r;
    395
    3.79k
    }
    396
    397
    static int
    398
    largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH], const u_char *data,
    399
        size_t len)
    400
    3.83k
    {
    401
    3.83k
            u_char dgst[SHA256_DIGEST_LENGTH];
    402
    403
    3.83k
            if (data == NULL || len == 0)
    404
    11
                    return -1;
    405
    3.82k
            if (SHA256(data, len, dgst) != dgst)
    406
    37
                    return -1;
    407
    3.78k
            memcpy(out, dgst, LARGEBLOB_DIGEST_LENGTH);
    408
    409
    3.78k
            return 0;
    410
    3.82k
    }
    411
    412
    static int
    413
    largeblob_array_check(const fido_blob_t *array)
    414
    3.89k
    {
    415
    3.89k
            u_char expected_hash[LARGEBLOB_DIGEST_LENGTH];
    416
    3.89k
            size_t body_len;
    417
    418
    3.89k
            fido_log_xxd(array->ptr, array->len, __func__);
    419
    3.89k
            if (array->len < sizeof(expected_hash)) {
    420
    61
                    fido_log_debug("%s: len %zu", __func__, array->len);
    421
    61
                    return -1;
    422
    61
            }
    423
    3.83k
            body_len = array->len - sizeof(expected_hash);
    424
    3.83k
            if (largeblob_array_digest(expected_hash, array->ptr, body_len) < 0) {
    425
    48
                    fido_log_debug("%s: largeblob_array_digest", __func__);
    426
    48
                    return -1;
    427
    48
            }
    428
    429
    3.78k
            return timingsafe_bcmp(expected_hash, array->ptr + body_len,
    430
    3.78k
                sizeof(expected_hash));
    431
    3.83k
    }
    432
    433
    static int
    434
    largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms)
    435
    28.6k
    {
    436
    28.6k
            fido_blob_t *array, *chunk = NULL;
    437
    28.6k
            size_t n;
    438
    28.6k
            int r;
    439
    440
    28.6k
            *item = NULL;
    441
    28.6k
            if ((n = get_chunklen(dev)) == 0)
    442
    18.5k
                    return FIDO_ERR_INVALID_ARGUMENT;
    443
    10.0k
            if ((array = fido_blob_new()) == NULL)
    444
    57
                    return FIDO_ERR_INTERNAL;
    445
    10.2k
            do {
    446
    10.2k
                    fido_blob_free(&chunk);
    447
    10.2k
                    if ((r = largeblob_get_tx(dev, array->len, n, ms)) != FIDO_OK ||
    448
    10.2k
                        (r = largeblob_get_rx(dev, &chunk, ms)) != FIDO_OK) {
    449
    6.06k
                            fido_log_debug("%s: largeblob_get_wait %zu/%zu",
    450
    6.06k
                                __func__, array->len, n);
    451
    6.06k
                            goto fail;
    452
    6.06k
                    }
    453
    4.16k
                    if (fido_blob_append(array, chunk->ptr, chunk->len) < 0) {
    454
    74
                            fido_log_debug("%s: fido_blob_append", __func__);
    455
    74
                            r = FIDO_ERR_INTERNAL;
    456
    74
                            goto fail;
    457
    74
                    }
    458
    4.16k
            } while (chunk->len == n);
    459
    460
    3.89k
            if (largeblob_array_check(array) != 0)
    461
    1.82k
                    *item = cbor_new_definite_array(0); /* per spec */
    462
    2.06k
            else
    463
    2.06k
                    *item = largeblob_array_load(array->ptr, array->len);
    464
    3.89k
            if (*item == NULL)
    465
    36
                    r = FIDO_ERR_INTERNAL;
    466
    3.85k
            else
    467
    3.85k
                    r = FIDO_OK;
    468
    10.0k
    fail:
    469
    10.0k
            fido_blob_free(&array);
    470
    10.0k
            fido_blob_free(&chunk);
    471
    472
    10.0k
            return r;
    473
    3.89k
    }
    474
    475
    static int
    476
    prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac)
    477
    286
    {
    478
    286
            uint8_t buf[32 + 2 + sizeof(uint32_t) + SHA256_DIGEST_LENGTH];
    479
    286
            uint32_t u32_offset;
    480
    481
    286
            if (data == NULL || len == 0) {
    482
    0
                    fido_log_debug("%s: invalid data=%p, len=%zu", __func__,
    483
    0
                        (const void *)data, len);
    484
    0
                    return -1;
    485
    0
            }
    486
    286
            if (offset > UINT32_MAX) {
    487
    0
                    fido_log_debug("%s: invalid offset=%zu", __func__, offset);
    488
    0
                    return -1;
    489
    0
            }
    490
    491
    286
            memset(buf, 0xff, 32);
    492
    286
            buf[32] = CTAP_CBOR_LARGEBLOB;
    493
    286
            buf[33] = 0x00;
    494
    286
            u32_offset = htole32((uint32_t)offset);
    495
    286
            memcpy(&buf[34], &u32_offset, sizeof(uint32_t));
    496
    286
            if (SHA256(data, len, &buf[38]) != &buf[38]) {
    497
    9
                    fido_log_debug("%s: SHA256", __func__);
    498
    9
                    return -1;
    499
    9
            }
    500
    501
    277
            return fido_blob_set(hmac, buf, sizeof(buf));
    502
    286
    }
    503
    504
    static int
    505
    largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk,
    506
        size_t chunk_len, size_t offset, size_t totalsiz, int *ms)
    507
    2.97k
    {
    508
    2.97k
            fido_blob_t *hmac = NULL, f;
    509
    2.97k
            cbor_item_t *argv[6];
    510
    2.97k
            int r;
    511
    512
    2.97k
            memset(argv, 0, sizeof(argv));
    513
    2.97k
            memset(&f, 0, sizeof(f));
    514
    515
    2.97k
            if ((argv[1] = cbor_build_bytestring(chunk, chunk_len)) == NULL ||
    516
    2.97k
                (argv[2] = cbor_build_uint(offset)) == NULL ||
    517
    2.97k
                (offset == 0 && (argv[3] = cbor_build_uint(totalsiz)) == NULL)) {
    518
    61
                    fido_log_debug("%s: cbor encode", __func__);
    519
    61
                    r = FIDO_ERR_INTERNAL;
    520
    61
                    goto fail;
    521
    61
            }
    522
    2.91k
            if (token != NULL) {
    523
    290
                    if ((hmac = fido_blob_new()) == NULL ||
    524
    290
                        prepare_hmac(offset, chunk, chunk_len, hmac) < 0 ||
    525
    290
                        (argv[4] = cbor_encode_pin_auth(dev, token, hmac)) == NULL ||
    526
    290
                        (argv[5] = cbor_encode_pin_opt(dev)) == NULL) {
    527
    41
                            fido_log_debug("%s: cbor_encode_pin_auth", __func__);
    528
    41
                            r = FIDO_ERR_INTERNAL;
    529
    41
                            goto fail;
    530
    41
                    }
    531
    290
            }
    532
    2.87k
            if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
    533
    2.87k
                fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    534
    198
                    fido_log_debug("%s: fido_tx", __func__);
    535
    198
                    r = FIDO_ERR_TX;
    536
    198
                    goto fail;
    537
    198
            }
    538
    539
    2.67k
            r = FIDO_OK;
    540
    2.97k
    fail:
    541
    2.97k
            cbor_vector_free(argv, nitems(argv));
    542
    2.97k
            fido_blob_free(&hmac);
    543
    2.97k
            free(f.ptr);
    544
    545
    2.97k
            return r;
    546
    2.67k
    }
    547
    548
    static int
    549
    largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token,
    550
        int *ms)
    551
    4.03k
    {
    552
    4.03k
            es256_pk_t *pk = NULL;
    553
    4.03k
            fido_blob_t *ecdh = NULL;
    554
    4.03k
            int r;
    555
    556
    4.03k
            if ((*token = fido_blob_new()) == NULL)
    557
    16
                    return FIDO_ERR_INTERNAL;
    558
    4.01k
            if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
    559
    2.75k
                    fido_log_debug("%s: fido_do_ecdh", __func__);
    560
    2.75k
                    goto fail;
    561
    2.75k
            }
    562
    1.25k
            if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk,
    563
    1.25k
                NULL, *token, ms)) != FIDO_OK) {
    564
    1.06k
                    fido_log_debug("%s: fido_dev_get_uv_token", __func__);
    565
    1.06k
                    goto fail;
    566
    1.06k
            }
    567
    568
    190
            r = FIDO_OK;
    569
    4.01k
    fail:
    570
    4.01k
            if (r != FIDO_OK)
    571
    3.82k
                    fido_blob_free(token);
    572
    573
    4.01k
            fido_blob_free(&ecdh);
    574
    4.01k
            es256_pk_free(&pk);
    575
    576
    4.01k
            return r;
    577
    190
    }
    578
    579
    static int
    580
    largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin,
    581
        int *ms)
    582
    10.4k
    {
    583
    10.4k
            unsigned char dgst[SHA256_DIGEST_LENGTH];
    584
    10.4k
            fido_blob_t cbor, *token = NULL;
    585
    10.4k
            size_t chunklen, maxchunklen, totalsize;
    586
    10.4k
            int r;
    587
    588
    10.4k
            memset(&cbor, 0, sizeof(cbor));
    589
    590
    10.4k
            if ((maxchunklen = get_chunklen(dev)) == 0) {
    591
    4.10k
                    fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen);
    592
    4.10k
                    r = FIDO_ERR_INVALID_ARGUMENT;
    593
    4.10k
                    goto fail;
    594
    4.10k
            }
    595
    6.38k
            if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
    596
    696
                    fido_log_debug("%s: cbor type", __func__);
    597
    696
                    r = FIDO_ERR_INVALID_ARGUMENT;
    598
    696
                    goto fail;
    599
    696
            }
    600
    5.68k
            if ((fido_blob_serialise(&cbor, item)) < 0) {
    601
    15
                    fido_log_debug("%s: fido_blob_serialise", __func__);
    602
    15
                    r = FIDO_ERR_INTERNAL;
    603
    15
                    goto fail;
    604
    15
            }
    605
    5.66k
            if (cbor.len > SIZE_MAX - sizeof(dgst)) {
    606
    0
                    fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len);
    607
    0
                    r = FIDO_ERR_INVALID_ARGUMENT;
    608
    0
                    goto fail;
    609
    0
            }
    610
    5.66k
            if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) {
    611
    24
                    fido_log_debug("%s: SHA256", __func__);
    612
    24
                    r = FIDO_ERR_INTERNAL;
    613
    24
                    goto fail;
    614
    24
            }
    615
    5.64k
            totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */
    616
    5.64k
            if (pin != NULL || fido_dev_supports_permissions(dev)) {
    617
    4.03k
                    if ((r = largeblob_get_uv_token(dev, pin, &token,
    618
    4.03k
                        ms)) != FIDO_OK) {
    619
    3.84k
                            fido_log_debug("%s: largeblob_get_uv_token", __func__);
    620
    3.84k
                            goto fail;
    621
    3.84k
                    }
    622
    4.03k
            }
    623
    2.97k
            for (size_t offset = 0; offset < cbor.len; offset += chunklen) {
    624
    2.65k
                    if ((chunklen = cbor.len - offset) > maxchunklen)
    625
    954
                            chunklen = maxchunklen;
    626
    2.65k
                    if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset,
    627
    2.65k
                        chunklen, offset, totalsize, ms)) != FIDO_OK ||
    628
    2.65k
                        (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
    629
    1.48k
                            fido_log_debug("%s: body", __func__);
    630
    1.48k
                            goto fail;
    631
    1.48k
                    }
    632
    2.65k
            }
    633
    322
            if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len,
    634
    322
                totalsize, ms)) != FIDO_OK ||
    635
    322
                (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
    636
    268
                    fido_log_debug("%s: dgst", __func__);
    637
    268
                    goto fail;
    638
    268
            }
    639
    640
    54
            r = FIDO_OK;
    641
    10.4k
    fail:
    642
    10.4k
            fido_blob_free(&token);
    643
    10.4k
            fido_blob_reset(&cbor);
    644
    645
    10.4k
            return r;
    646
    54
    }
    647
    648
    static int
    649
    largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
    650
        const char *pin, int *ms)
    651
    10.3k
    {
    652
    10.3k
            cbor_item_t *array = NULL;
    653
    10.3k
            size_t idx;
    654
    10.3k
            int r;
    655
    656
    10.3k
            if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
    657
    7.78k
                    fido_log_debug("%s: largeblob_get_array", __func__);
    658
    7.78k
                    goto fail;
    659
    7.78k
            }
    660
    661
    2.57k
            switch (r = largeblob_array_lookup(NULL, &idx, array, key)) {
    662
    694
            case FIDO_OK:
    663
    694
                    if (!cbor_array_replace(array, idx, item)) {
    664
    0
                            r = FIDO_ERR_INTERNAL;
    665
    0
                            goto fail;
    666
    0
                    }
    667
    694
                    break;
    668
    1.87k
            case FIDO_ERR_NOTFOUND:
    669
    1.87k
                    if (cbor_array_append(&array, item) < 0) {
    670
    60
                            r = FIDO_ERR_INTERNAL;
    671
    60
                            goto fail;
    672
    60
                    }
    673
    1.81k
                    break;
    674
    1.81k
            default:
    675
    8
                    fido_log_debug("%s: largeblob_array_lookup", __func__);
    676
    8
                    goto fail;
    677
    2.57k
            }
    678
    679
    2.50k
            if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
    680
    2.47k
                    fido_log_debug("%s: largeblob_set_array", __func__);
    681
    2.47k
                    goto fail;
    682
    2.47k
            }
    683
    684
    31
            r = FIDO_OK;
    685
    10.3k
    fail:
    686
    10.3k
            if (array != NULL)
    687
    2.57k
                    cbor_decref(&array);
    688
    689
    10.3k
            return r;
    690
    31
    }
    691
    692
    static int
    693
    largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin,
    694
        int *ms)
    695
    8.98k
    {
    696
    8.98k
            cbor_item_t *array = NULL;
    697
    8.98k
            size_t idx;
    698
    8.98k
            int r;
    699
    700
    8.98k
            if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
    701
    7.83k
                    fido_log_debug("%s: largeblob_get_array", __func__);
    702
    7.83k
                    goto fail;
    703
    7.83k
            }
    704
    1.14k
            if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) {
    705
    224
                    fido_log_debug("%s: largeblob_array_lookup", __func__);
    706
    224
                    goto fail;
    707
    224
            }
    708
    925
            if (cbor_array_drop(&array, idx) < 0) {
    709
    23
                    fido_log_debug("%s: cbor_array_drop", __func__);
    710
    23
                    r = FIDO_ERR_INTERNAL;
    711
    23
                    goto fail;
    712
    23
            }
    713
    902
            if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
    714
    891
                    fido_log_debug("%s: largeblob_set_array", __func__);
    715
    891
                    goto fail;
    716
    891
            }
    717
    718
    11
            r = FIDO_OK;
    719
    8.98k
    fail:
    720
    8.98k
            if (array != NULL)
    721
    1.14k
                    cbor_decref(&array);
    722
    723
    8.98k
            return r;
    724
    11
    }
    725
    726
    int
    727
    fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
    728
        size_t key_len, unsigned char **blob_ptr, size_t *blob_len)
    729
    5.81k
    {
    730
    5.81k
            cbor_item_t *item = NULL;
    731
    5.81k
            fido_blob_t key, body;
    732
    5.81k
            int ms = dev->timeout_ms;
    733
    5.81k
            int r;
    734
    735
    5.81k
            memset(&key, 0, sizeof(key));
    736
    5.81k
            memset(&body, 0, sizeof(body));
    737
    738
    5.81k
            if (key_len != 32) {
    739
    2.35k
                    fido_log_debug("%s: invalid key len %zu", __func__, key_len);
    740
    2.35k
                    return FIDO_ERR_INVALID_ARGUMENT;
    741
    2.35k
            }
    742
    3.45k
            if (blob_ptr == NULL || blob_len == NULL) {
    743
    0
                    fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__,
    744
    0
                        (const void *)blob_ptr, (const void *)blob_len);
    745
    0
                    return FIDO_ERR_INVALID_ARGUMENT;
    746
    0
            }
    747
    3.45k
            *blob_ptr = NULL;
    748
    3.45k
            *blob_len = 0;
    749
    3.45k
            if (fido_blob_set(&key, key_ptr, key_len) < 0) {
    750
    6
                    fido_log_debug("%s: fido_blob_set", __func__);
    751
    6
                    return FIDO_ERR_INTERNAL;
    752
    6
            }
    753
    3.45k
            if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
    754
    3.36k
                    fido_log_debug("%s: largeblob_get_array", __func__);
    755
    3.36k
                    goto fail;
    756
    3.36k
            }
    757
    86
            if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK)
    758
    59
                    fido_log_debug("%s: largeblob_array_lookup", __func__);
    759
    27
            else {
    760
    27
                    *blob_ptr = body.ptr;
    761
    27
                    *blob_len = body.len;
    762
    27
            }
    763
    3.45k
    fail:
    764
    3.45k
            if (item != NULL)
    765
    86
                    cbor_decref(&item);
    766
    767
    3.45k
            fido_blob_reset(&key);
    768
    769
    3.45k
            return r;
    770
    86
    }
    771
    772
    int
    773
    fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
    774
        size_t key_len, const unsigned char *blob_ptr, size_t blob_len,
    775
        const char *pin)
    776
    17.6k
    {
    777
    17.6k
            cbor_item_t *item = NULL;
    778
    17.6k
            fido_blob_t key, body;
    779
    17.6k
            int ms = dev->timeout_ms;
    780
    17.6k
            int r;
    781
    782
    17.6k
            memset(&key, 0, sizeof(key));
    783
    17.6k
            memset(&body, 0, sizeof(body));
    784
    785
    17.6k
            if (key_len != 32) {
    786
    6.68k
                    fido_log_debug("%s: invalid key len %zu", __func__, key_len);
    787
    6.68k
                    return FIDO_ERR_INVALID_ARGUMENT;
    788
    6.68k
            }
    789
    10.9k
            if (blob_ptr == NULL || blob_len == 0) {
    790
    6
                    fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__,
    791
    6
                        (const void *)blob_ptr, blob_len);
    792
    6
                    return FIDO_ERR_INVALID_ARGUMENT;
    793
    6
            }
    794
    10.9k
            if (fido_blob_set(&key, key_ptr, key_len) < 0 ||
    795
    10.9k
                fido_blob_set(&body, blob_ptr, blob_len) < 0) {
    796
    27
                    fido_log_debug("%s: fido_blob_set", __func__);
    797
    27
                    r = FIDO_ERR_INTERNAL;
    798
    27
                    goto fail;
    799
    27
            }
    800
    10.9k
            if ((item = largeblob_encode(&body, &key)) == NULL) {
    801
    561
                    fido_log_debug("%s: largeblob_encode", __func__);
    802
    561
                    r = FIDO_ERR_INTERNAL;
    803
    561
                    goto fail;
    804
    561
            }
    805
    10.3k
            if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK)
    806
    10.3k
                    fido_log_debug("%s: largeblob_add", __func__);
    807
    10.9k
    fail:
    808
    10.9k
            if (item != NULL)
    809
    10.3k
                    cbor_decref(&item);
    810
    811
    10.9k
            fido_blob_reset(&key);
    812
    10.9k
            fido_blob_reset(&body);
    813
    814
    10.9k
            return r;
    815
    10.3k
    }
    816
    817
    int
    818
    fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
    819
        size_t key_len, const char *pin)
    820
    15.4k
    {
    821
    15.4k
            fido_blob_t key;
    822
    15.4k
            int ms = dev->timeout_ms;
    823
    15.4k
            int r;
    824
    825
    15.4k
            memset(&key, 0, sizeof(key));
    826
    827
    15.4k
            if (key_len != 32) {
    828
    6.46k
                    fido_log_debug("%s: invalid key len %zu", __func__, key_len);
    829
    6.46k
                    return FIDO_ERR_INVALID_ARGUMENT;
    830
    6.46k
            }
    831
    8.99k
            if (fido_blob_set(&key, key_ptr, key_len) < 0) {
    832
    13
                    fido_log_debug("%s: fido_blob_set", __func__);
    833
    13
                    return FIDO_ERR_INTERNAL;
    834
    13
            }
    835
    8.98k
            if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK)
    836
    8.97k
                    fido_log_debug("%s: largeblob_drop", __func__);
    837
    838
    8.98k
            fido_blob_reset(&key);
    839
    840
    8.98k
            return r;
    841
    8.99k
    }
    842
    843
    int
    844
    fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
    845
        size_t *cbor_len)
    846
    5.86k
    {
    847
    5.86k
            cbor_item_t *item = NULL;
    848
    5.86k
            fido_blob_t cbor;
    849
    5.86k
            int ms = dev->timeout_ms;
    850
    5.86k
            int r;
    851
    852
    5.86k
            memset(&cbor, 0, sizeof(cbor));
    853
    854
    5.86k
            if (cbor_ptr == NULL || cbor_len == NULL) {
    855
    0
                    fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__,
    856
    0
                        (const void *)cbor_ptr, (const void *)cbor_len);
    857
    0
                    return FIDO_ERR_INVALID_ARGUMENT;
    858
    0
            }
    859
    5.86k
            *cbor_ptr = NULL;
    860
    5.86k
            *cbor_len = 0;
    861
    5.86k
            if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
    862
    5.82k
                    fido_log_debug("%s: largeblob_get_array", __func__);
    863
    5.82k
                    return r;
    864
    5.82k
            }
    865
    46
            if (fido_blob_serialise(&cbor, item) < 0) {
    866
    3
                    fido_log_debug("%s: fido_blob_serialise", __func__);
    867
    3
                    r = FIDO_ERR_INTERNAL;
    868
    43
            } else {
    869
    43
                    *cbor_ptr = cbor.ptr;
    870
    43
                    *cbor_len = cbor.len;
    871
    43
            }
    872
    873
    46
            cbor_decref(&item);
    874
    875
    46
            return r;
    876
    5.86k
    }
    877
    878
    int
    879
    fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
    880
        size_t cbor_len, const char *pin)
    881
    16.1k
    {
    882
    16.1k
            cbor_item_t *item = NULL;
    883
    16.1k
            struct cbor_load_result cbor_result;
    884
    16.1k
            int ms = dev->timeout_ms;
    885
    16.1k
            int r;
    886
    887
    16.1k
            if (cbor_ptr == NULL || cbor_len == 0) {
    888
    6
                    fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__,
    889
    6
                        (const void *)cbor_ptr, cbor_len);
    890
    6
                    return FIDO_ERR_INVALID_ARGUMENT;
    891
    6
            }
    892
    16.0k
            if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
    893
    9.02k
                    fido_log_debug("%s: cbor_load", __func__);
    894
    9.02k
                    return FIDO_ERR_INVALID_ARGUMENT;
    895
    9.02k
            }
    896
    7.07k
            if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK)
    897
    7.05k
                    fido_log_debug("%s: largeblob_set_array", __func__);
    898
    899
    7.07k
            cbor_decref(&item);
    900
    901
    7.07k
            return r;
    902
    16.0k
    }