Coverage Report

Created: 2026-04-08 06:32

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