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