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