Coverage Report

Created: 2022-04-27 14:33

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