Coverage Report

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