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
16typedef struct largeblob {
17	size_t origsiz;
18	fido_blob_t ciphertext;
19	fido_blob_t nonce;
20} largeblob_t;
21
22static largeblob_t *
23largeblob_new(void)
24{
25	return calloc(1, sizeof(largeblob_t));
26}
27
28static void
29largeblob_reset(largeblob_t *blob)
30{
31	fido_blob_reset(&blob->ciphertext);
32	fido_blob_reset(&blob->nonce);
33	blob->origsiz = 0;
34}
35
36static void
37largeblob_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
48static int
49largeblob_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
63static fido_blob_t *
64largeblob_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;
85fail:
86	fido_blob_free(&aad);
87
88	if (ok < 0)
89		fido_blob_free(&plaintext);
90
91	return plaintext;
92}
93
94static int
95largeblob_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;
110fail:
111	explicit_bzero(buf, sizeof(buf));
112
113	return ok;
114}
115
116static int
117largeblob_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;
148fail:
149	fido_blob_free(&plaintext);
150	fido_blob_free(&aad);
151
152	return ok;
153}
154
155static int
156largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms)
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, ms) < 0) {
173		fido_log_debug("%s: fido_tx", __func__);
174		r = FIDO_ERR_TX;
175		goto fail;
176	}
177
178	r = FIDO_OK;
179fail:
180	cbor_vector_free(argv, nitems(argv));
181	free(f.ptr);
182
183	return r;
184}
185
186static int
187parse_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
200static int
201largeblob_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
226static cbor_item_t *
227largeblob_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
250static size_t
251get_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
264static int
265largeblob_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
299static int
300largeblob_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
319static cbor_item_t *
320largeblob_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));
338fail:
339	cbor_vector_free(argv, nitems(argv));
340	largeblob_free(&blob);
341
342	return item;
343}
344
345static int
346largeblob_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
385static int
386largeblob_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
400static int
401largeblob_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
421static int
422largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms)
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, ms)) != FIDO_OK ||
436		    (r = largeblob_get_rx(dev, &chunk, ms)) != 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;
456fail:
457	fido_blob_free(&array);
458	fido_blob_free(&chunk);
459
460	return r;
461}
462
463static int
464prepare_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
492static int
493largeblob_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{
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, ms) < 0) {
522		fido_log_debug("%s: fido_tx", __func__);
523		r = FIDO_ERR_TX;
524		goto fail;
525	}
526
527	r = FIDO_OK;
528fail:
529	cbor_vector_free(argv, nitems(argv));
530	fido_blob_free(&hmac);
531	free(f.ptr);
532
533	return r;
534}
535
536static int
537largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token,
538    int *ms)
539{
540	es256_pk_t *pk = NULL;
541	fido_blob_t *ecdh = NULL;
542	int r;
543
544	if ((*token = fido_blob_new()) == NULL)
545		return FIDO_ERR_INTERNAL;
546	if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
547		fido_log_debug("%s: fido_do_ecdh", __func__);
548		goto fail;
549	}
550	if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk,
551	    NULL, *token, ms)) != FIDO_OK) {
552		fido_log_debug("%s: fido_dev_get_uv_token", __func__);
553		goto fail;
554	}
555
556	r = FIDO_OK;
557fail:
558	if (r != FIDO_OK)
559		fido_blob_free(token);
560
561	fido_blob_free(&ecdh);
562	es256_pk_free(&pk);
563
564	return r;
565}
566
567static int
568largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin,
569    int *ms)
570{
571	unsigned char dgst[SHA256_DIGEST_LENGTH];
572	fido_blob_t cbor, *token = NULL;
573	size_t chunklen, maxchunklen, totalsize;
574	int r;
575
576	memset(&cbor, 0, sizeof(cbor));
577
578	if ((maxchunklen = get_chunklen(dev)) == 0) {
579		fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen);
580		r = FIDO_ERR_INVALID_ARGUMENT;
581		goto fail;
582	}
583	if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
584		fido_log_debug("%s: cbor type", __func__);
585		r = FIDO_ERR_INVALID_ARGUMENT;
586		goto fail;
587	}
588	if ((fido_blob_serialise(&cbor, item)) < 0) {
589		fido_log_debug("%s: fido_blob_serialise", __func__);
590		r = FIDO_ERR_INTERNAL;
591		goto fail;
592	}
593	if (cbor.len > SIZE_MAX - sizeof(dgst)) {
594		fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len);
595		r = FIDO_ERR_INVALID_ARGUMENT;
596		goto fail;
597	}
598	if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) {
599		fido_log_debug("%s: SHA256", __func__);
600		r = FIDO_ERR_INTERNAL;
601		goto fail;
602	}
603	totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */
604	if (pin != NULL || fido_dev_supports_permissions(dev)) {
605		if ((r = largeblob_get_uv_token(dev, pin, &token,
606		    ms)) != FIDO_OK) {
607			fido_log_debug("%s: largeblob_get_uv_token", __func__);
608			goto fail;
609		}
610	}
611	for (size_t offset = 0; offset < cbor.len; offset += chunklen) {
612		if ((chunklen = cbor.len - offset) > maxchunklen)
613			chunklen = maxchunklen;
614		if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset,
615		    chunklen, offset, totalsize, ms)) != FIDO_OK ||
616		    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
617			fido_log_debug("%s: body", __func__);
618			goto fail;
619		}
620	}
621	if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len,
622	    totalsize, ms)) != FIDO_OK ||
623	    (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
624		fido_log_debug("%s: dgst", __func__);
625		goto fail;
626	}
627
628	r = FIDO_OK;
629fail:
630	fido_blob_free(&token);
631	fido_blob_reset(&cbor);
632
633	return r;
634}
635
636static int
637largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
638    const char *pin, int *ms)
639{
640	cbor_item_t *array = NULL;
641	size_t idx;
642	int r;
643
644	if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
645		fido_log_debug("%s: largeblob_get_array", __func__);
646		goto fail;
647	}
648
649	switch (r = largeblob_array_lookup(NULL, &idx, array, key)) {
650	case FIDO_OK:
651		if (!cbor_array_replace(array, idx, item)) {
652			r = FIDO_ERR_INTERNAL;
653			goto fail;
654		}
655		break;
656	case FIDO_ERR_NOTFOUND:
657		if (cbor_array_append(&array, item) < 0) {
658			r = FIDO_ERR_INTERNAL;
659			goto fail;
660		}
661		break;
662	default:
663		fido_log_debug("%s: largeblob_array_lookup", __func__);
664		goto fail;
665	}
666
667	if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
668		fido_log_debug("%s: largeblob_set_array", __func__);
669		goto fail;
670	}
671
672	r = FIDO_OK;
673fail:
674	if (array != NULL)
675		cbor_decref(&array);
676
677	return r;
678}
679
680static int
681largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin,
682    int *ms)
683{
684	cbor_item_t *array = NULL;
685	size_t idx;
686	int r;
687
688	if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
689		fido_log_debug("%s: largeblob_get_array", __func__);
690		goto fail;
691	}
692	if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) {
693		fido_log_debug("%s: largeblob_array_lookup", __func__);
694		goto fail;
695	}
696	if (cbor_array_drop(&array, idx) < 0) {
697		fido_log_debug("%s: cbor_array_drop", __func__);
698		r = FIDO_ERR_INTERNAL;
699		goto fail;
700	}
701	if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
702		fido_log_debug("%s: largeblob_set_array", __func__);
703		goto fail;
704	}
705
706	r = FIDO_OK;
707fail:
708	if (array != NULL)
709		cbor_decref(&array);
710
711	return r;
712}
713
714int
715fido_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{
718	cbor_item_t *item = NULL;
719	fido_blob_t key, body;
720	int ms = dev->timeout_ms;
721	int r;
722
723	memset(&key, 0, sizeof(key));
724	memset(&body, 0, sizeof(body));
725
726	if (key_len != 32) {
727		fido_log_debug("%s: invalid key len %zu", __func__, key_len);
728		return FIDO_ERR_INVALID_ARGUMENT;
729	}
730	if (blob_ptr == NULL || blob_len == NULL) {
731		fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__,
732		    (const void *)blob_ptr, (const void *)blob_len);
733		return FIDO_ERR_INVALID_ARGUMENT;
734	}
735	*blob_ptr = NULL;
736	*blob_len = 0;
737	if (fido_blob_set(&key, key_ptr, key_len) < 0) {
738		fido_log_debug("%s: fido_blob_set", __func__);
739		return FIDO_ERR_INTERNAL;
740	}
741	if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
742		fido_log_debug("%s: largeblob_get_array", __func__);
743		goto fail;
744	}
745	if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK)
746		fido_log_debug("%s: largeblob_array_lookup", __func__);
747	else {
748		*blob_ptr = body.ptr;
749		*blob_len = body.len;
750	}
751fail:
752	if (item != NULL)
753		cbor_decref(&item);
754
755	fido_blob_reset(&key);
756
757	return r;
758}
759
760int
761fido_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{
765	cbor_item_t *item = NULL;
766	fido_blob_t key, body;
767	int ms = dev->timeout_ms;
768	int r;
769
770	memset(&key, 0, sizeof(key));
771	memset(&body, 0, sizeof(body));
772
773	if (key_len != 32) {
774		fido_log_debug("%s: invalid key len %zu", __func__, key_len);
775		return FIDO_ERR_INVALID_ARGUMENT;
776	}
777	if (blob_ptr == NULL || blob_len == 0) {
778		fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__,
779		    (const void *)blob_ptr, blob_len);
780		return FIDO_ERR_INVALID_ARGUMENT;
781	}
782	if (fido_blob_set(&key, key_ptr, key_len) < 0 ||
783	    fido_blob_set(&body, blob_ptr, blob_len) < 0) {
784		fido_log_debug("%s: fido_blob_set", __func__);
785		r = FIDO_ERR_INTERNAL;
786		goto fail;
787	}
788	if ((item = largeblob_encode(&body, &key)) == NULL) {
789		fido_log_debug("%s: largeblob_encode", __func__);
790		r = FIDO_ERR_INTERNAL;
791		goto fail;
792	}
793	if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK)
794		fido_log_debug("%s: largeblob_add", __func__);
795fail:
796	if (item != NULL)
797		cbor_decref(&item);
798
799	fido_blob_reset(&key);
800	fido_blob_reset(&body);
801
802	return r;
803}
804
805int
806fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
807    size_t key_len, const char *pin)
808{
809	fido_blob_t key;
810	int ms = dev->timeout_ms;
811	int r;
812
813	memset(&key, 0, sizeof(key));
814
815	if (key_len != 32) {
816		fido_log_debug("%s: invalid key len %zu", __func__, key_len);
817		return FIDO_ERR_INVALID_ARGUMENT;
818	}
819	if (fido_blob_set(&key, key_ptr, key_len) < 0) {
820		fido_log_debug("%s: fido_blob_set", __func__);
821		return FIDO_ERR_INTERNAL;
822	}
823	if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK)
824		fido_log_debug("%s: largeblob_drop", __func__);
825
826	fido_blob_reset(&key);
827
828	return r;
829}
830
831int
832fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
833    size_t *cbor_len)
834{
835	cbor_item_t *item = NULL;
836	fido_blob_t cbor;
837	int ms = dev->timeout_ms;
838	int r;
839
840	memset(&cbor, 0, sizeof(cbor));
841
842	if (cbor_ptr == NULL || cbor_len == NULL) {
843		fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__,
844		    (const void *)cbor_ptr, (const void *)cbor_len);
845		return FIDO_ERR_INVALID_ARGUMENT;
846	}
847	*cbor_ptr = NULL;
848	*cbor_len = 0;
849	if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
850		fido_log_debug("%s: largeblob_get_array", __func__);
851		return r;
852	}
853	if (fido_blob_serialise(&cbor, item) < 0) {
854		fido_log_debug("%s: fido_blob_serialise", __func__);
855		r = FIDO_ERR_INTERNAL;
856	} else {
857		*cbor_ptr = cbor.ptr;
858		*cbor_len = cbor.len;
859	}
860
861	cbor_decref(&item);
862
863	return r;
864}
865
866int
867fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
868    size_t cbor_len, const char *pin)
869{
870	cbor_item_t *item = NULL;
871	struct cbor_load_result cbor_result;
872	int ms = dev->timeout_ms;
873	int r;
874
875	if (cbor_ptr == NULL || cbor_len == 0) {
876		fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__,
877		    (const void *)cbor_ptr, cbor_len);
878		return FIDO_ERR_INVALID_ARGUMENT;
879	}
880	if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
881		fido_log_debug("%s: cbor_load", __func__);
882		return FIDO_ERR_INVALID_ARGUMENT;
883	}
884	if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK)
885		fido_log_debug("%s: largeblob_set_array", __func__);
886
887	cbor_decref(&item);
888
889	return r;
890}
891