Coverage Report

Created: 2021-10-21 13:35

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