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