Coverage Report

Created: 2022-01-17 10:46

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