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