Coverage Report

Created: 2021-07-20 18:14

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