Coverage Report

Created: 2022-07-22 12:05

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