10afa8e06SEd Maste /*
2*2ccfa855SEd Maste * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste */
70afa8e06SEd Maste
80afa8e06SEd Maste #include <openssl/sha.h>
90afa8e06SEd Maste
100afa8e06SEd Maste #include "fido.h"
110afa8e06SEd Maste #include "fido/es256.h"
120afa8e06SEd Maste
130afa8e06SEd Maste #define LARGEBLOB_DIGEST_LENGTH 16
140afa8e06SEd Maste #define LARGEBLOB_NONCE_LENGTH 12
150afa8e06SEd Maste #define LARGEBLOB_TAG_LENGTH 16
160afa8e06SEd Maste
170afa8e06SEd Maste typedef struct largeblob {
180afa8e06SEd Maste size_t origsiz;
190afa8e06SEd Maste fido_blob_t ciphertext;
200afa8e06SEd Maste fido_blob_t nonce;
210afa8e06SEd Maste } largeblob_t;
220afa8e06SEd Maste
230afa8e06SEd Maste static largeblob_t *
largeblob_new(void)240afa8e06SEd Maste largeblob_new(void)
250afa8e06SEd Maste {
260afa8e06SEd Maste return calloc(1, sizeof(largeblob_t));
270afa8e06SEd Maste }
280afa8e06SEd Maste
290afa8e06SEd Maste static void
largeblob_reset(largeblob_t * blob)300afa8e06SEd Maste largeblob_reset(largeblob_t *blob)
310afa8e06SEd Maste {
320afa8e06SEd Maste fido_blob_reset(&blob->ciphertext);
330afa8e06SEd Maste fido_blob_reset(&blob->nonce);
340afa8e06SEd Maste blob->origsiz = 0;
350afa8e06SEd Maste }
360afa8e06SEd Maste
370afa8e06SEd Maste static void
largeblob_free(largeblob_t ** blob_ptr)380afa8e06SEd Maste largeblob_free(largeblob_t **blob_ptr)
390afa8e06SEd Maste {
400afa8e06SEd Maste largeblob_t *blob;
410afa8e06SEd Maste
420afa8e06SEd Maste if (blob_ptr == NULL || (blob = *blob_ptr) == NULL)
430afa8e06SEd Maste return;
440afa8e06SEd Maste largeblob_reset(blob);
450afa8e06SEd Maste free(blob);
460afa8e06SEd Maste *blob_ptr = NULL;
470afa8e06SEd Maste }
480afa8e06SEd Maste
490afa8e06SEd Maste static int
largeblob_aad(fido_blob_t * aad,uint64_t size)500afa8e06SEd Maste largeblob_aad(fido_blob_t *aad, uint64_t size)
510afa8e06SEd Maste {
520afa8e06SEd Maste uint8_t buf[4 + sizeof(uint64_t)];
530afa8e06SEd Maste
540afa8e06SEd Maste buf[0] = 0x62; /* b */
550afa8e06SEd Maste buf[1] = 0x6c; /* l */
560afa8e06SEd Maste buf[2] = 0x6f; /* o */
570afa8e06SEd Maste buf[3] = 0x62; /* b */
580afa8e06SEd Maste size = htole64(size);
590afa8e06SEd Maste memcpy(&buf[4], &size, sizeof(uint64_t));
600afa8e06SEd Maste
610afa8e06SEd Maste return fido_blob_set(aad, buf, sizeof(buf));
620afa8e06SEd Maste }
630afa8e06SEd Maste
640afa8e06SEd Maste static fido_blob_t *
largeblob_decrypt(const largeblob_t * blob,const fido_blob_t * key)650afa8e06SEd Maste largeblob_decrypt(const largeblob_t *blob, const fido_blob_t *key)
660afa8e06SEd Maste {
670afa8e06SEd Maste fido_blob_t *plaintext = NULL, *aad = NULL;
680afa8e06SEd Maste int ok = -1;
690afa8e06SEd Maste
700afa8e06SEd Maste if ((plaintext = fido_blob_new()) == NULL ||
710afa8e06SEd Maste (aad = fido_blob_new()) == NULL) {
720afa8e06SEd Maste fido_log_debug("%s: fido_blob_new", __func__);
730afa8e06SEd Maste goto fail;
740afa8e06SEd Maste }
750afa8e06SEd Maste if (largeblob_aad(aad, blob->origsiz) < 0) {
760afa8e06SEd Maste fido_log_debug("%s: largeblob_aad", __func__);
770afa8e06SEd Maste goto fail;
780afa8e06SEd Maste }
790afa8e06SEd Maste if (aes256_gcm_dec(key, &blob->nonce, aad, &blob->ciphertext,
800afa8e06SEd Maste plaintext) < 0) {
810afa8e06SEd Maste fido_log_debug("%s: aes256_gcm_dec", __func__);
820afa8e06SEd Maste goto fail;
830afa8e06SEd Maste }
840afa8e06SEd Maste
850afa8e06SEd Maste ok = 0;
860afa8e06SEd Maste fail:
870afa8e06SEd Maste fido_blob_free(&aad);
880afa8e06SEd Maste
890afa8e06SEd Maste if (ok < 0)
900afa8e06SEd Maste fido_blob_free(&plaintext);
910afa8e06SEd Maste
920afa8e06SEd Maste return plaintext;
930afa8e06SEd Maste }
940afa8e06SEd Maste
950afa8e06SEd Maste static int
largeblob_get_nonce(largeblob_t * blob)960afa8e06SEd Maste largeblob_get_nonce(largeblob_t *blob)
970afa8e06SEd Maste {
980afa8e06SEd Maste uint8_t buf[LARGEBLOB_NONCE_LENGTH];
990afa8e06SEd Maste int ok = -1;
1000afa8e06SEd Maste
1010afa8e06SEd Maste if (fido_get_random(buf, sizeof(buf)) < 0) {
1020afa8e06SEd Maste fido_log_debug("%s: fido_get_random", __func__);
1030afa8e06SEd Maste goto fail;
1040afa8e06SEd Maste }
1050afa8e06SEd Maste if (fido_blob_set(&blob->nonce, buf, sizeof(buf)) < 0) {
1060afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__);
1070afa8e06SEd Maste goto fail;
1080afa8e06SEd Maste }
1090afa8e06SEd Maste
1100afa8e06SEd Maste ok = 0;
1110afa8e06SEd Maste fail:
1120afa8e06SEd Maste explicit_bzero(buf, sizeof(buf));
1130afa8e06SEd Maste
1140afa8e06SEd Maste return ok;
1150afa8e06SEd Maste }
1160afa8e06SEd Maste
1170afa8e06SEd Maste static int
largeblob_seal(largeblob_t * blob,const fido_blob_t * body,const fido_blob_t * key)1180afa8e06SEd Maste largeblob_seal(largeblob_t *blob, const fido_blob_t *body,
1190afa8e06SEd Maste const fido_blob_t *key)
1200afa8e06SEd Maste {
1210afa8e06SEd Maste fido_blob_t *plaintext = NULL, *aad = NULL;
1220afa8e06SEd Maste int ok = -1;
1230afa8e06SEd Maste
1240afa8e06SEd Maste if ((plaintext = fido_blob_new()) == NULL ||
1250afa8e06SEd Maste (aad = fido_blob_new()) == NULL) {
1260afa8e06SEd Maste fido_log_debug("%s: fido_blob_new", __func__);
1270afa8e06SEd Maste goto fail;
1280afa8e06SEd Maste }
1290afa8e06SEd Maste if (fido_compress(plaintext, body) != FIDO_OK) {
1300afa8e06SEd Maste fido_log_debug("%s: fido_compress", __func__);
1310afa8e06SEd Maste goto fail;
1320afa8e06SEd Maste }
1330afa8e06SEd Maste if (largeblob_aad(aad, body->len) < 0) {
1340afa8e06SEd Maste fido_log_debug("%s: largeblob_aad", __func__);
1350afa8e06SEd Maste goto fail;
1360afa8e06SEd Maste }
1370afa8e06SEd Maste if (largeblob_get_nonce(blob) < 0) {
1380afa8e06SEd Maste fido_log_debug("%s: largeblob_get_nonce", __func__);
1390afa8e06SEd Maste goto fail;
1400afa8e06SEd Maste }
1410afa8e06SEd Maste if (aes256_gcm_enc(key, &blob->nonce, aad, plaintext,
1420afa8e06SEd Maste &blob->ciphertext) < 0) {
1430afa8e06SEd Maste fido_log_debug("%s: aes256_gcm_enc", __func__);
1440afa8e06SEd Maste goto fail;
1450afa8e06SEd Maste }
1460afa8e06SEd Maste blob->origsiz = body->len;
1470afa8e06SEd Maste
1480afa8e06SEd Maste ok = 0;
1490afa8e06SEd Maste fail:
1500afa8e06SEd Maste fido_blob_free(&plaintext);
1510afa8e06SEd Maste fido_blob_free(&aad);
1520afa8e06SEd Maste
1530afa8e06SEd Maste return ok;
1540afa8e06SEd Maste }
1550afa8e06SEd Maste
1560afa8e06SEd Maste static int
largeblob_get_tx(fido_dev_t * dev,size_t offset,size_t count,int * ms)157f540a430SEd Maste largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms)
1580afa8e06SEd Maste {
1590afa8e06SEd Maste fido_blob_t f;
1600afa8e06SEd Maste cbor_item_t *argv[3];
1610afa8e06SEd Maste int r;
1620afa8e06SEd Maste
1630afa8e06SEd Maste memset(argv, 0, sizeof(argv));
1640afa8e06SEd Maste memset(&f, 0, sizeof(f));
1650afa8e06SEd Maste
1660afa8e06SEd Maste if ((argv[0] = cbor_build_uint(count)) == NULL ||
1670afa8e06SEd Maste (argv[2] = cbor_build_uint(offset)) == NULL) {
1680afa8e06SEd Maste fido_log_debug("%s: cbor encode", __func__);
1690afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
1700afa8e06SEd Maste goto fail;
1710afa8e06SEd Maste }
1720afa8e06SEd Maste if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
173f540a430SEd Maste fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
1740afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
1750afa8e06SEd Maste r = FIDO_ERR_TX;
1760afa8e06SEd Maste goto fail;
1770afa8e06SEd Maste }
1780afa8e06SEd Maste
1790afa8e06SEd Maste r = FIDO_OK;
1800afa8e06SEd Maste fail:
1810afa8e06SEd Maste cbor_vector_free(argv, nitems(argv));
1820afa8e06SEd Maste free(f.ptr);
1830afa8e06SEd Maste
1840afa8e06SEd Maste return r;
1850afa8e06SEd Maste }
1860afa8e06SEd Maste
1870afa8e06SEd Maste static int
parse_largeblob_reply(const cbor_item_t * key,const cbor_item_t * val,void * arg)1880afa8e06SEd Maste parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val,
1890afa8e06SEd Maste void *arg)
1900afa8e06SEd Maste {
1910afa8e06SEd Maste if (cbor_isa_uint(key) == false ||
1920afa8e06SEd Maste cbor_int_get_width(key) != CBOR_INT_8 ||
1930afa8e06SEd Maste cbor_get_uint8(key) != 1) {
1940afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__);
1950afa8e06SEd Maste return 0; /* ignore */
1960afa8e06SEd Maste }
1970afa8e06SEd Maste
1980afa8e06SEd Maste return fido_blob_decode(val, arg);
1990afa8e06SEd Maste }
2000afa8e06SEd Maste
2010afa8e06SEd Maste static int
largeblob_get_rx(fido_dev_t * dev,fido_blob_t ** chunk,int * ms)202f540a430SEd Maste largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int *ms)
2030afa8e06SEd Maste {
204*2ccfa855SEd Maste unsigned char *msg;
205*2ccfa855SEd Maste int msglen, r;
2060afa8e06SEd Maste
2070afa8e06SEd Maste *chunk = NULL;
208*2ccfa855SEd Maste if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
209*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
210*2ccfa855SEd Maste goto out;
211*2ccfa855SEd Maste }
212*2ccfa855SEd Maste if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
2130afa8e06SEd Maste fido_log_debug("%s: fido_rx", __func__);
214*2ccfa855SEd Maste r = FIDO_ERR_RX;
215*2ccfa855SEd Maste goto out;
2160afa8e06SEd Maste }
2170afa8e06SEd Maste if ((*chunk = fido_blob_new()) == NULL) {
2180afa8e06SEd Maste fido_log_debug("%s: fido_blob_new", __func__);
219*2ccfa855SEd Maste r = FIDO_ERR_INTERNAL;
220*2ccfa855SEd Maste goto out;
2210afa8e06SEd Maste }
222*2ccfa855SEd Maste if ((r = cbor_parse_reply(msg, (size_t)msglen, *chunk,
2230afa8e06SEd Maste parse_largeblob_reply)) != FIDO_OK) {
2240afa8e06SEd Maste fido_log_debug("%s: parse_largeblob_reply", __func__);
225*2ccfa855SEd Maste goto out;
2260afa8e06SEd Maste }
2270afa8e06SEd Maste
228*2ccfa855SEd Maste r = FIDO_OK;
229*2ccfa855SEd Maste out:
230*2ccfa855SEd Maste if (r != FIDO_OK)
231*2ccfa855SEd Maste fido_blob_free(chunk);
232*2ccfa855SEd Maste
233*2ccfa855SEd Maste freezero(msg, FIDO_MAXMSG);
234*2ccfa855SEd Maste
235*2ccfa855SEd Maste return r;
2360afa8e06SEd Maste }
2370afa8e06SEd Maste
2380afa8e06SEd Maste static cbor_item_t *
largeblob_array_load(const uint8_t * ptr,size_t len)2390afa8e06SEd Maste largeblob_array_load(const uint8_t *ptr, size_t len)
2400afa8e06SEd Maste {
2410afa8e06SEd Maste struct cbor_load_result cbor;
2420afa8e06SEd Maste cbor_item_t *item;
2430afa8e06SEd Maste
2440afa8e06SEd Maste if (len < LARGEBLOB_DIGEST_LENGTH) {
2450afa8e06SEd Maste fido_log_debug("%s: len", __func__);
2460afa8e06SEd Maste return NULL;
2470afa8e06SEd Maste }
2480afa8e06SEd Maste len -= LARGEBLOB_DIGEST_LENGTH;
2490afa8e06SEd Maste if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
2500afa8e06SEd Maste fido_log_debug("%s: cbor_load", __func__);
2510afa8e06SEd Maste return NULL;
2520afa8e06SEd Maste }
2530afa8e06SEd Maste if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
2540afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__);
2550afa8e06SEd Maste cbor_decref(&item);
2560afa8e06SEd Maste return NULL;
2570afa8e06SEd Maste }
2580afa8e06SEd Maste
2590afa8e06SEd Maste return item;
2600afa8e06SEd Maste }
2610afa8e06SEd Maste
2620afa8e06SEd Maste static size_t
get_chunklen(fido_dev_t * dev)2630afa8e06SEd Maste get_chunklen(fido_dev_t *dev)
2640afa8e06SEd Maste {
2650afa8e06SEd Maste uint64_t maxchunklen;
2660afa8e06SEd Maste
2670afa8e06SEd Maste if ((maxchunklen = fido_dev_maxmsgsize(dev)) > SIZE_MAX)
2680afa8e06SEd Maste maxchunklen = SIZE_MAX;
2690afa8e06SEd Maste if (maxchunklen > FIDO_MAXMSG)
2700afa8e06SEd Maste maxchunklen = FIDO_MAXMSG;
2710afa8e06SEd Maste maxchunklen = maxchunklen > 64 ? maxchunklen - 64 : 0;
2720afa8e06SEd Maste
2730afa8e06SEd Maste return (size_t)maxchunklen;
2740afa8e06SEd Maste }
2750afa8e06SEd Maste
2760afa8e06SEd Maste static int
largeblob_do_decode(const cbor_item_t * key,const cbor_item_t * val,void * arg)2770afa8e06SEd Maste largeblob_do_decode(const cbor_item_t *key, const cbor_item_t *val, void *arg)
2780afa8e06SEd Maste {
2790afa8e06SEd Maste largeblob_t *blob = arg;
2800afa8e06SEd Maste uint64_t origsiz;
2810afa8e06SEd Maste
2820afa8e06SEd Maste if (cbor_isa_uint(key) == false ||
2830afa8e06SEd Maste cbor_int_get_width(key) != CBOR_INT_8) {
2840afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__);
2850afa8e06SEd Maste return 0; /* ignore */
2860afa8e06SEd Maste }
2870afa8e06SEd Maste
2880afa8e06SEd Maste switch (cbor_get_uint8(key)) {
2890afa8e06SEd Maste case 1: /* ciphertext */
2900afa8e06SEd Maste if (fido_blob_decode(val, &blob->ciphertext) < 0 ||
2910afa8e06SEd Maste blob->ciphertext.len < LARGEBLOB_TAG_LENGTH)
2920afa8e06SEd Maste return -1;
2930afa8e06SEd Maste return 0;
2940afa8e06SEd Maste case 2: /* nonce */
2950afa8e06SEd Maste if (fido_blob_decode(val, &blob->nonce) < 0 ||
2960afa8e06SEd Maste blob->nonce.len != LARGEBLOB_NONCE_LENGTH)
2970afa8e06SEd Maste return -1;
2980afa8e06SEd Maste return 0;
2990afa8e06SEd Maste case 3: /* origSize */
3000afa8e06SEd Maste if (!cbor_isa_uint(val) ||
3010afa8e06SEd Maste (origsiz = cbor_get_int(val)) > SIZE_MAX)
3020afa8e06SEd Maste return -1;
3030afa8e06SEd Maste blob->origsiz = (size_t)origsiz;
3040afa8e06SEd Maste return 0;
3050afa8e06SEd Maste default: /* ignore */
3060afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__);
3070afa8e06SEd Maste return 0;
3080afa8e06SEd Maste }
3090afa8e06SEd Maste }
3100afa8e06SEd Maste
3110afa8e06SEd Maste static int
largeblob_decode(largeblob_t * blob,const cbor_item_t * item)3120afa8e06SEd Maste largeblob_decode(largeblob_t *blob, const cbor_item_t *item)
3130afa8e06SEd Maste {
3140afa8e06SEd Maste if (!cbor_isa_map(item) || !cbor_map_is_definite(item)) {
3150afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__);
3160afa8e06SEd Maste return -1;
3170afa8e06SEd Maste }
3180afa8e06SEd Maste if (cbor_map_iter(item, blob, largeblob_do_decode) < 0) {
3190afa8e06SEd Maste fido_log_debug("%s: cbor_map_iter", __func__);
3200afa8e06SEd Maste return -1;
3210afa8e06SEd Maste }
3220afa8e06SEd Maste if (fido_blob_is_empty(&blob->ciphertext) ||
3230afa8e06SEd Maste fido_blob_is_empty(&blob->nonce) || blob->origsiz == 0) {
3240afa8e06SEd Maste fido_log_debug("%s: incomplete blob", __func__);
3250afa8e06SEd Maste return -1;
3260afa8e06SEd Maste }
3270afa8e06SEd Maste
3280afa8e06SEd Maste return 0;
3290afa8e06SEd Maste }
3300afa8e06SEd Maste
3310afa8e06SEd Maste static cbor_item_t *
largeblob_encode(const fido_blob_t * body,const fido_blob_t * key)3320afa8e06SEd Maste largeblob_encode(const fido_blob_t *body, const fido_blob_t *key)
3330afa8e06SEd Maste {
3340afa8e06SEd Maste largeblob_t *blob;
3350afa8e06SEd Maste cbor_item_t *argv[3], *item = NULL;
3360afa8e06SEd Maste
3370afa8e06SEd Maste memset(argv, 0, sizeof(argv));
3380afa8e06SEd Maste if ((blob = largeblob_new()) == NULL ||
3390afa8e06SEd Maste largeblob_seal(blob, body, key) < 0) {
3400afa8e06SEd Maste fido_log_debug("%s: largeblob_seal", __func__);
3410afa8e06SEd Maste goto fail;
3420afa8e06SEd Maste }
3430afa8e06SEd Maste if ((argv[0] = fido_blob_encode(&blob->ciphertext)) == NULL ||
3440afa8e06SEd Maste (argv[1] = fido_blob_encode(&blob->nonce)) == NULL ||
3450afa8e06SEd Maste (argv[2] = cbor_build_uint(blob->origsiz)) == NULL) {
3460afa8e06SEd Maste fido_log_debug("%s: cbor encode", __func__);
3470afa8e06SEd Maste goto fail;
3480afa8e06SEd Maste }
3490afa8e06SEd Maste item = cbor_flatten_vector(argv, nitems(argv));
3500afa8e06SEd Maste fail:
3510afa8e06SEd Maste cbor_vector_free(argv, nitems(argv));
3520afa8e06SEd Maste largeblob_free(&blob);
3530afa8e06SEd Maste
3540afa8e06SEd Maste return item;
3550afa8e06SEd Maste }
3560afa8e06SEd Maste
3570afa8e06SEd Maste static int
largeblob_array_lookup(fido_blob_t * out,size_t * idx,const cbor_item_t * item,const fido_blob_t * key)3580afa8e06SEd Maste largeblob_array_lookup(fido_blob_t *out, size_t *idx, const cbor_item_t *item,
3590afa8e06SEd Maste const fido_blob_t *key)
3600afa8e06SEd Maste {
3610afa8e06SEd Maste cbor_item_t **v;
3620afa8e06SEd Maste fido_blob_t *plaintext = NULL;
3630afa8e06SEd Maste largeblob_t blob;
3640afa8e06SEd Maste int r;
3650afa8e06SEd Maste
3660afa8e06SEd Maste memset(&blob, 0, sizeof(blob));
3670afa8e06SEd Maste if (idx != NULL)
3680afa8e06SEd Maste *idx = 0;
3690afa8e06SEd Maste if ((v = cbor_array_handle(item)) == NULL)
3700afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
3710afa8e06SEd Maste for (size_t i = 0; i < cbor_array_size(item); i++) {
3720afa8e06SEd Maste if (largeblob_decode(&blob, v[i]) < 0 ||
3730afa8e06SEd Maste (plaintext = largeblob_decrypt(&blob, key)) == NULL) {
3740afa8e06SEd Maste fido_log_debug("%s: largeblob_decode", __func__);
3750afa8e06SEd Maste largeblob_reset(&blob);
3760afa8e06SEd Maste continue;
3770afa8e06SEd Maste }
3780afa8e06SEd Maste if (idx != NULL)
3790afa8e06SEd Maste *idx = i;
3800afa8e06SEd Maste break;
3810afa8e06SEd Maste }
3820afa8e06SEd Maste if (plaintext == NULL) {
3830afa8e06SEd Maste fido_log_debug("%s: not found", __func__);
3840afa8e06SEd Maste return FIDO_ERR_NOTFOUND;
3850afa8e06SEd Maste }
3860afa8e06SEd Maste if (out != NULL)
3870afa8e06SEd Maste r = fido_uncompress(out, plaintext, blob.origsiz);
3880afa8e06SEd Maste else
3890afa8e06SEd Maste r = FIDO_OK;
3900afa8e06SEd Maste
3910afa8e06SEd Maste fido_blob_free(&plaintext);
3920afa8e06SEd Maste largeblob_reset(&blob);
3930afa8e06SEd Maste
3940afa8e06SEd Maste return r;
3950afa8e06SEd Maste }
3960afa8e06SEd Maste
3970afa8e06SEd Maste static int
largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH],const u_char * data,size_t len)3980afa8e06SEd Maste largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH], const u_char *data,
3990afa8e06SEd Maste size_t len)
4000afa8e06SEd Maste {
4010afa8e06SEd Maste u_char dgst[SHA256_DIGEST_LENGTH];
4020afa8e06SEd Maste
4030afa8e06SEd Maste if (data == NULL || len == 0)
4040afa8e06SEd Maste return -1;
4050afa8e06SEd Maste if (SHA256(data, len, dgst) != dgst)
4060afa8e06SEd Maste return -1;
4070afa8e06SEd Maste memcpy(out, dgst, LARGEBLOB_DIGEST_LENGTH);
4080afa8e06SEd Maste
4090afa8e06SEd Maste return 0;
4100afa8e06SEd Maste }
4110afa8e06SEd Maste
4120afa8e06SEd Maste static int
largeblob_array_check(const fido_blob_t * array)4130afa8e06SEd Maste largeblob_array_check(const fido_blob_t *array)
4140afa8e06SEd Maste {
4150afa8e06SEd Maste u_char expected_hash[LARGEBLOB_DIGEST_LENGTH];
4160afa8e06SEd Maste size_t body_len;
4170afa8e06SEd Maste
4180afa8e06SEd Maste fido_log_xxd(array->ptr, array->len, __func__);
4190afa8e06SEd Maste if (array->len < sizeof(expected_hash)) {
4200afa8e06SEd Maste fido_log_debug("%s: len %zu", __func__, array->len);
4210afa8e06SEd Maste return -1;
4220afa8e06SEd Maste }
4230afa8e06SEd Maste body_len = array->len - sizeof(expected_hash);
4240afa8e06SEd Maste if (largeblob_array_digest(expected_hash, array->ptr, body_len) < 0) {
4250afa8e06SEd Maste fido_log_debug("%s: largeblob_array_digest", __func__);
4260afa8e06SEd Maste return -1;
4270afa8e06SEd Maste }
4280afa8e06SEd Maste
4290afa8e06SEd Maste return timingsafe_bcmp(expected_hash, array->ptr + body_len,
4300afa8e06SEd Maste sizeof(expected_hash));
4310afa8e06SEd Maste }
4320afa8e06SEd Maste
4330afa8e06SEd Maste static int
largeblob_get_array(fido_dev_t * dev,cbor_item_t ** item,int * ms)434f540a430SEd Maste largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms)
4350afa8e06SEd Maste {
4360afa8e06SEd Maste fido_blob_t *array, *chunk = NULL;
4370afa8e06SEd Maste size_t n;
4380afa8e06SEd Maste int r;
4390afa8e06SEd Maste
4400afa8e06SEd Maste *item = NULL;
4410afa8e06SEd Maste if ((n = get_chunklen(dev)) == 0)
4420afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
4430afa8e06SEd Maste if ((array = fido_blob_new()) == NULL)
4440afa8e06SEd Maste return FIDO_ERR_INTERNAL;
4450afa8e06SEd Maste do {
4460afa8e06SEd Maste fido_blob_free(&chunk);
447f540a430SEd Maste if ((r = largeblob_get_tx(dev, array->len, n, ms)) != FIDO_OK ||
448f540a430SEd Maste (r = largeblob_get_rx(dev, &chunk, ms)) != FIDO_OK) {
4490afa8e06SEd Maste fido_log_debug("%s: largeblob_get_wait %zu/%zu",
4500afa8e06SEd Maste __func__, array->len, n);
4510afa8e06SEd Maste goto fail;
4520afa8e06SEd Maste }
4530afa8e06SEd Maste if (fido_blob_append(array, chunk->ptr, chunk->len) < 0) {
4540afa8e06SEd Maste fido_log_debug("%s: fido_blob_append", __func__);
4550afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
4560afa8e06SEd Maste goto fail;
4570afa8e06SEd Maste }
4580afa8e06SEd Maste } while (chunk->len == n);
4590afa8e06SEd Maste
4600afa8e06SEd Maste if (largeblob_array_check(array) != 0)
4610afa8e06SEd Maste *item = cbor_new_definite_array(0); /* per spec */
4620afa8e06SEd Maste else
4630afa8e06SEd Maste *item = largeblob_array_load(array->ptr, array->len);
4640afa8e06SEd Maste if (*item == NULL)
4650afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
4660afa8e06SEd Maste else
4670afa8e06SEd Maste r = FIDO_OK;
4680afa8e06SEd Maste fail:
4690afa8e06SEd Maste fido_blob_free(&array);
4700afa8e06SEd Maste fido_blob_free(&chunk);
4710afa8e06SEd Maste
4720afa8e06SEd Maste return r;
4730afa8e06SEd Maste }
4740afa8e06SEd Maste
4750afa8e06SEd Maste static int
prepare_hmac(size_t offset,const u_char * data,size_t len,fido_blob_t * hmac)4760afa8e06SEd Maste prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac)
4770afa8e06SEd Maste {
4780afa8e06SEd Maste uint8_t buf[32 + 2 + sizeof(uint32_t) + SHA256_DIGEST_LENGTH];
4790afa8e06SEd Maste uint32_t u32_offset;
4800afa8e06SEd Maste
4810afa8e06SEd Maste if (data == NULL || len == 0) {
4820afa8e06SEd Maste fido_log_debug("%s: invalid data=%p, len=%zu", __func__,
4830afa8e06SEd Maste (const void *)data, len);
4840afa8e06SEd Maste return -1;
4850afa8e06SEd Maste }
4860afa8e06SEd Maste if (offset > UINT32_MAX) {
4870afa8e06SEd Maste fido_log_debug("%s: invalid offset=%zu", __func__, offset);
4880afa8e06SEd Maste return -1;
4890afa8e06SEd Maste }
4900afa8e06SEd Maste
4910afa8e06SEd Maste memset(buf, 0xff, 32);
4920afa8e06SEd Maste buf[32] = CTAP_CBOR_LARGEBLOB;
4930afa8e06SEd Maste buf[33] = 0x00;
4940afa8e06SEd Maste u32_offset = htole32((uint32_t)offset);
4950afa8e06SEd Maste memcpy(&buf[34], &u32_offset, sizeof(uint32_t));
4960afa8e06SEd Maste if (SHA256(data, len, &buf[38]) != &buf[38]) {
4970afa8e06SEd Maste fido_log_debug("%s: SHA256", __func__);
4980afa8e06SEd Maste return -1;
4990afa8e06SEd Maste }
5000afa8e06SEd Maste
5010afa8e06SEd Maste return fido_blob_set(hmac, buf, sizeof(buf));
5020afa8e06SEd Maste }
5030afa8e06SEd Maste
5040afa8e06SEd Maste 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)5050afa8e06SEd Maste largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk,
506f540a430SEd Maste size_t chunk_len, size_t offset, size_t totalsiz, int *ms)
5070afa8e06SEd Maste {
5080afa8e06SEd Maste fido_blob_t *hmac = NULL, f;
5090afa8e06SEd Maste cbor_item_t *argv[6];
5100afa8e06SEd Maste int r;
5110afa8e06SEd Maste
5120afa8e06SEd Maste memset(argv, 0, sizeof(argv));
5130afa8e06SEd Maste memset(&f, 0, sizeof(f));
5140afa8e06SEd Maste
5150afa8e06SEd Maste if ((argv[1] = cbor_build_bytestring(chunk, chunk_len)) == NULL ||
5160afa8e06SEd Maste (argv[2] = cbor_build_uint(offset)) == NULL ||
5170afa8e06SEd Maste (offset == 0 && (argv[3] = cbor_build_uint(totalsiz)) == NULL)) {
5180afa8e06SEd Maste fido_log_debug("%s: cbor encode", __func__);
5190afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
5200afa8e06SEd Maste goto fail;
5210afa8e06SEd Maste }
5220afa8e06SEd Maste if (token != NULL) {
5230afa8e06SEd Maste if ((hmac = fido_blob_new()) == NULL ||
5240afa8e06SEd Maste prepare_hmac(offset, chunk, chunk_len, hmac) < 0 ||
5250afa8e06SEd Maste (argv[4] = cbor_encode_pin_auth(dev, token, hmac)) == NULL ||
5260afa8e06SEd Maste (argv[5] = cbor_encode_pin_opt(dev)) == NULL) {
5270afa8e06SEd Maste fido_log_debug("%s: cbor_encode_pin_auth", __func__);
5280afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
5290afa8e06SEd Maste goto fail;
5300afa8e06SEd Maste }
5310afa8e06SEd Maste }
5320afa8e06SEd Maste if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
533f540a430SEd Maste fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
5340afa8e06SEd Maste fido_log_debug("%s: fido_tx", __func__);
5350afa8e06SEd Maste r = FIDO_ERR_TX;
5360afa8e06SEd Maste goto fail;
5370afa8e06SEd Maste }
5380afa8e06SEd Maste
5390afa8e06SEd Maste r = FIDO_OK;
5400afa8e06SEd Maste fail:
5410afa8e06SEd Maste cbor_vector_free(argv, nitems(argv));
5420afa8e06SEd Maste fido_blob_free(&hmac);
5430afa8e06SEd Maste free(f.ptr);
5440afa8e06SEd Maste
5450afa8e06SEd Maste return r;
5460afa8e06SEd Maste }
5470afa8e06SEd Maste
5480afa8e06SEd Maste static int
largeblob_get_uv_token(fido_dev_t * dev,const char * pin,fido_blob_t ** token,int * ms)549f540a430SEd Maste largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token,
550f540a430SEd Maste int *ms)
5510afa8e06SEd Maste {
5520afa8e06SEd Maste es256_pk_t *pk = NULL;
5530afa8e06SEd Maste fido_blob_t *ecdh = NULL;
5540afa8e06SEd Maste int r;
5550afa8e06SEd Maste
5560afa8e06SEd Maste if ((*token = fido_blob_new()) == NULL)
5570afa8e06SEd Maste return FIDO_ERR_INTERNAL;
558f540a430SEd Maste if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
5590afa8e06SEd Maste fido_log_debug("%s: fido_do_ecdh", __func__);
5600afa8e06SEd Maste goto fail;
5610afa8e06SEd Maste }
5620afa8e06SEd Maste if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk,
563f540a430SEd Maste NULL, *token, ms)) != FIDO_OK) {
5640afa8e06SEd Maste fido_log_debug("%s: fido_dev_get_uv_token", __func__);
5650afa8e06SEd Maste goto fail;
5660afa8e06SEd Maste }
5670afa8e06SEd Maste
5680afa8e06SEd Maste r = FIDO_OK;
5690afa8e06SEd Maste fail:
5700afa8e06SEd Maste if (r != FIDO_OK)
5710afa8e06SEd Maste fido_blob_free(token);
5720afa8e06SEd Maste
5730afa8e06SEd Maste fido_blob_free(&ecdh);
5740afa8e06SEd Maste es256_pk_free(&pk);
5750afa8e06SEd Maste
5760afa8e06SEd Maste return r;
5770afa8e06SEd Maste }
5780afa8e06SEd Maste
5790afa8e06SEd Maste static int
largeblob_set_array(fido_dev_t * dev,const cbor_item_t * item,const char * pin,int * ms)580f540a430SEd Maste largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin,
581f540a430SEd Maste int *ms)
5820afa8e06SEd Maste {
5830afa8e06SEd Maste unsigned char dgst[SHA256_DIGEST_LENGTH];
5840afa8e06SEd Maste fido_blob_t cbor, *token = NULL;
5850afa8e06SEd Maste size_t chunklen, maxchunklen, totalsize;
5860afa8e06SEd Maste int r;
5870afa8e06SEd Maste
5880afa8e06SEd Maste memset(&cbor, 0, sizeof(cbor));
5890afa8e06SEd Maste
5900afa8e06SEd Maste if ((maxchunklen = get_chunklen(dev)) == 0) {
5910afa8e06SEd Maste fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen);
5920afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT;
5930afa8e06SEd Maste goto fail;
5940afa8e06SEd Maste }
5950afa8e06SEd Maste if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
5960afa8e06SEd Maste fido_log_debug("%s: cbor type", __func__);
5970afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT;
5980afa8e06SEd Maste goto fail;
5990afa8e06SEd Maste }
6000afa8e06SEd Maste if ((fido_blob_serialise(&cbor, item)) < 0) {
6010afa8e06SEd Maste fido_log_debug("%s: fido_blob_serialise", __func__);
6020afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
6030afa8e06SEd Maste goto fail;
6040afa8e06SEd Maste }
6050afa8e06SEd Maste if (cbor.len > SIZE_MAX - sizeof(dgst)) {
6060afa8e06SEd Maste fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len);
6070afa8e06SEd Maste r = FIDO_ERR_INVALID_ARGUMENT;
6080afa8e06SEd Maste goto fail;
6090afa8e06SEd Maste }
6100afa8e06SEd Maste if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) {
6110afa8e06SEd Maste fido_log_debug("%s: SHA256", __func__);
6120afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
6130afa8e06SEd Maste goto fail;
6140afa8e06SEd Maste }
6150afa8e06SEd Maste totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */
6160afa8e06SEd Maste if (pin != NULL || fido_dev_supports_permissions(dev)) {
617f540a430SEd Maste if ((r = largeblob_get_uv_token(dev, pin, &token,
618f540a430SEd Maste ms)) != FIDO_OK) {
6190afa8e06SEd Maste fido_log_debug("%s: largeblob_get_uv_token", __func__);
6200afa8e06SEd Maste goto fail;
6210afa8e06SEd Maste }
6220afa8e06SEd Maste }
6230afa8e06SEd Maste for (size_t offset = 0; offset < cbor.len; offset += chunklen) {
6240afa8e06SEd Maste if ((chunklen = cbor.len - offset) > maxchunklen)
6250afa8e06SEd Maste chunklen = maxchunklen;
6260afa8e06SEd Maste if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset,
627f540a430SEd Maste chunklen, offset, totalsize, ms)) != FIDO_OK ||
628f540a430SEd Maste (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
6290afa8e06SEd Maste fido_log_debug("%s: body", __func__);
6300afa8e06SEd Maste goto fail;
6310afa8e06SEd Maste }
6320afa8e06SEd Maste }
6330afa8e06SEd Maste if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len,
634f540a430SEd Maste totalsize, ms)) != FIDO_OK ||
635f540a430SEd Maste (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
6360afa8e06SEd Maste fido_log_debug("%s: dgst", __func__);
6370afa8e06SEd Maste goto fail;
6380afa8e06SEd Maste }
6390afa8e06SEd Maste
6400afa8e06SEd Maste r = FIDO_OK;
6410afa8e06SEd Maste fail:
6420afa8e06SEd Maste fido_blob_free(&token);
6430afa8e06SEd Maste fido_blob_reset(&cbor);
6440afa8e06SEd Maste
6450afa8e06SEd Maste return r;
6460afa8e06SEd Maste }
6470afa8e06SEd Maste
6480afa8e06SEd Maste static int
largeblob_add(fido_dev_t * dev,const fido_blob_t * key,cbor_item_t * item,const char * pin,int * ms)6490afa8e06SEd Maste largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
650f540a430SEd Maste const char *pin, int *ms)
6510afa8e06SEd Maste {
6520afa8e06SEd Maste cbor_item_t *array = NULL;
6530afa8e06SEd Maste size_t idx;
6540afa8e06SEd Maste int r;
6550afa8e06SEd Maste
656f540a430SEd Maste if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
6570afa8e06SEd Maste fido_log_debug("%s: largeblob_get_array", __func__);
6580afa8e06SEd Maste goto fail;
6590afa8e06SEd Maste }
6600afa8e06SEd Maste
6610afa8e06SEd Maste switch (r = largeblob_array_lookup(NULL, &idx, array, key)) {
6620afa8e06SEd Maste case FIDO_OK:
6630afa8e06SEd Maste if (!cbor_array_replace(array, idx, item)) {
6640afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
6650afa8e06SEd Maste goto fail;
6660afa8e06SEd Maste }
6670afa8e06SEd Maste break;
6680afa8e06SEd Maste case FIDO_ERR_NOTFOUND:
6690afa8e06SEd Maste if (cbor_array_append(&array, item) < 0) {
6700afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
6710afa8e06SEd Maste goto fail;
6720afa8e06SEd Maste }
6730afa8e06SEd Maste break;
6740afa8e06SEd Maste default:
6750afa8e06SEd Maste fido_log_debug("%s: largeblob_array_lookup", __func__);
6760afa8e06SEd Maste goto fail;
6770afa8e06SEd Maste }
6780afa8e06SEd Maste
679f540a430SEd Maste if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
6800afa8e06SEd Maste fido_log_debug("%s: largeblob_set_array", __func__);
6810afa8e06SEd Maste goto fail;
6820afa8e06SEd Maste }
6830afa8e06SEd Maste
6840afa8e06SEd Maste r = FIDO_OK;
6850afa8e06SEd Maste fail:
6860afa8e06SEd Maste if (array != NULL)
6870afa8e06SEd Maste cbor_decref(&array);
6880afa8e06SEd Maste
6890afa8e06SEd Maste return r;
6900afa8e06SEd Maste }
6910afa8e06SEd Maste
6920afa8e06SEd Maste static int
largeblob_drop(fido_dev_t * dev,const fido_blob_t * key,const char * pin,int * ms)693f540a430SEd Maste largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin,
694f540a430SEd Maste int *ms)
6950afa8e06SEd Maste {
6960afa8e06SEd Maste cbor_item_t *array = NULL;
6970afa8e06SEd Maste size_t idx;
6980afa8e06SEd Maste int r;
6990afa8e06SEd Maste
700f540a430SEd Maste if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
7010afa8e06SEd Maste fido_log_debug("%s: largeblob_get_array", __func__);
7020afa8e06SEd Maste goto fail;
7030afa8e06SEd Maste }
7040afa8e06SEd Maste if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) {
7050afa8e06SEd Maste fido_log_debug("%s: largeblob_array_lookup", __func__);
7060afa8e06SEd Maste goto fail;
7070afa8e06SEd Maste }
7080afa8e06SEd Maste if (cbor_array_drop(&array, idx) < 0) {
7090afa8e06SEd Maste fido_log_debug("%s: cbor_array_drop", __func__);
7100afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
7110afa8e06SEd Maste goto fail;
7120afa8e06SEd Maste }
713f540a430SEd Maste if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
7140afa8e06SEd Maste fido_log_debug("%s: largeblob_set_array", __func__);
7150afa8e06SEd Maste goto fail;
7160afa8e06SEd Maste }
7170afa8e06SEd Maste
7180afa8e06SEd Maste r = FIDO_OK;
7190afa8e06SEd Maste fail:
7200afa8e06SEd Maste if (array != NULL)
7210afa8e06SEd Maste cbor_decref(&array);
7220afa8e06SEd Maste
7230afa8e06SEd Maste return r;
7240afa8e06SEd Maste }
7250afa8e06SEd Maste
7260afa8e06SEd Maste 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)7270afa8e06SEd Maste fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
7280afa8e06SEd Maste size_t key_len, unsigned char **blob_ptr, size_t *blob_len)
7290afa8e06SEd Maste {
7300afa8e06SEd Maste cbor_item_t *item = NULL;
7310afa8e06SEd Maste fido_blob_t key, body;
732f540a430SEd Maste int ms = dev->timeout_ms;
7330afa8e06SEd Maste int r;
7340afa8e06SEd Maste
7350afa8e06SEd Maste memset(&key, 0, sizeof(key));
7360afa8e06SEd Maste memset(&body, 0, sizeof(body));
7370afa8e06SEd Maste
7380afa8e06SEd Maste if (key_len != 32) {
7390afa8e06SEd Maste fido_log_debug("%s: invalid key len %zu", __func__, key_len);
7400afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
7410afa8e06SEd Maste }
7420afa8e06SEd Maste if (blob_ptr == NULL || blob_len == NULL) {
7430afa8e06SEd Maste fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__,
7440afa8e06SEd Maste (const void *)blob_ptr, (const void *)blob_len);
7450afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
7460afa8e06SEd Maste }
7470afa8e06SEd Maste *blob_ptr = NULL;
7480afa8e06SEd Maste *blob_len = 0;
7490afa8e06SEd Maste if (fido_blob_set(&key, key_ptr, key_len) < 0) {
7500afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__);
7510afa8e06SEd Maste return FIDO_ERR_INTERNAL;
7520afa8e06SEd Maste }
753f540a430SEd Maste if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
7540afa8e06SEd Maste fido_log_debug("%s: largeblob_get_array", __func__);
7550afa8e06SEd Maste goto fail;
7560afa8e06SEd Maste }
7570afa8e06SEd Maste if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK)
7580afa8e06SEd Maste fido_log_debug("%s: largeblob_array_lookup", __func__);
7590afa8e06SEd Maste else {
7600afa8e06SEd Maste *blob_ptr = body.ptr;
7610afa8e06SEd Maste *blob_len = body.len;
7620afa8e06SEd Maste }
7630afa8e06SEd Maste fail:
7640afa8e06SEd Maste if (item != NULL)
7650afa8e06SEd Maste cbor_decref(&item);
7660afa8e06SEd Maste
7670afa8e06SEd Maste fido_blob_reset(&key);
7680afa8e06SEd Maste
7690afa8e06SEd Maste return r;
7700afa8e06SEd Maste }
7710afa8e06SEd Maste
7720afa8e06SEd Maste 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)7730afa8e06SEd Maste fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
7740afa8e06SEd Maste size_t key_len, const unsigned char *blob_ptr, size_t blob_len,
7750afa8e06SEd Maste const char *pin)
7760afa8e06SEd Maste {
7770afa8e06SEd Maste cbor_item_t *item = NULL;
7780afa8e06SEd Maste fido_blob_t key, body;
779f540a430SEd Maste int ms = dev->timeout_ms;
7800afa8e06SEd Maste int r;
7810afa8e06SEd Maste
7820afa8e06SEd Maste memset(&key, 0, sizeof(key));
7830afa8e06SEd Maste memset(&body, 0, sizeof(body));
7840afa8e06SEd Maste
7850afa8e06SEd Maste if (key_len != 32) {
7860afa8e06SEd Maste fido_log_debug("%s: invalid key len %zu", __func__, key_len);
7870afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
7880afa8e06SEd Maste }
7890afa8e06SEd Maste if (blob_ptr == NULL || blob_len == 0) {
7900afa8e06SEd Maste fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__,
7910afa8e06SEd Maste (const void *)blob_ptr, blob_len);
7920afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
7930afa8e06SEd Maste }
7940afa8e06SEd Maste if (fido_blob_set(&key, key_ptr, key_len) < 0 ||
7950afa8e06SEd Maste fido_blob_set(&body, blob_ptr, blob_len) < 0) {
7960afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__);
7970afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
7980afa8e06SEd Maste goto fail;
7990afa8e06SEd Maste }
8000afa8e06SEd Maste if ((item = largeblob_encode(&body, &key)) == NULL) {
8010afa8e06SEd Maste fido_log_debug("%s: largeblob_encode", __func__);
8020afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
8030afa8e06SEd Maste goto fail;
8040afa8e06SEd Maste }
805f540a430SEd Maste if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK)
8060afa8e06SEd Maste fido_log_debug("%s: largeblob_add", __func__);
8070afa8e06SEd Maste fail:
8080afa8e06SEd Maste if (item != NULL)
8090afa8e06SEd Maste cbor_decref(&item);
8100afa8e06SEd Maste
8110afa8e06SEd Maste fido_blob_reset(&key);
8120afa8e06SEd Maste fido_blob_reset(&body);
8130afa8e06SEd Maste
8140afa8e06SEd Maste return r;
8150afa8e06SEd Maste }
8160afa8e06SEd Maste
8170afa8e06SEd Maste int
fido_dev_largeblob_remove(fido_dev_t * dev,const unsigned char * key_ptr,size_t key_len,const char * pin)8180afa8e06SEd Maste fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
8190afa8e06SEd Maste size_t key_len, const char *pin)
8200afa8e06SEd Maste {
8210afa8e06SEd Maste fido_blob_t key;
822f540a430SEd Maste int ms = dev->timeout_ms;
8230afa8e06SEd Maste int r;
8240afa8e06SEd Maste
8250afa8e06SEd Maste memset(&key, 0, sizeof(key));
8260afa8e06SEd Maste
8270afa8e06SEd Maste if (key_len != 32) {
8280afa8e06SEd Maste fido_log_debug("%s: invalid key len %zu", __func__, key_len);
8290afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
8300afa8e06SEd Maste }
8310afa8e06SEd Maste if (fido_blob_set(&key, key_ptr, key_len) < 0) {
8320afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__);
8330afa8e06SEd Maste return FIDO_ERR_INTERNAL;
8340afa8e06SEd Maste }
835f540a430SEd Maste if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK)
8360afa8e06SEd Maste fido_log_debug("%s: largeblob_drop", __func__);
8370afa8e06SEd Maste
8380afa8e06SEd Maste fido_blob_reset(&key);
8390afa8e06SEd Maste
8400afa8e06SEd Maste return r;
8410afa8e06SEd Maste }
8420afa8e06SEd Maste
8430afa8e06SEd Maste int
fido_dev_largeblob_get_array(fido_dev_t * dev,unsigned char ** cbor_ptr,size_t * cbor_len)8440afa8e06SEd Maste fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
8450afa8e06SEd Maste size_t *cbor_len)
8460afa8e06SEd Maste {
8470afa8e06SEd Maste cbor_item_t *item = NULL;
8480afa8e06SEd Maste fido_blob_t cbor;
849f540a430SEd Maste int ms = dev->timeout_ms;
8500afa8e06SEd Maste int r;
8510afa8e06SEd Maste
8520afa8e06SEd Maste memset(&cbor, 0, sizeof(cbor));
8530afa8e06SEd Maste
8540afa8e06SEd Maste if (cbor_ptr == NULL || cbor_len == NULL) {
8550afa8e06SEd Maste fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__,
8560afa8e06SEd Maste (const void *)cbor_ptr, (const void *)cbor_len);
8570afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
8580afa8e06SEd Maste }
8590afa8e06SEd Maste *cbor_ptr = NULL;
8600afa8e06SEd Maste *cbor_len = 0;
861f540a430SEd Maste if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
8620afa8e06SEd Maste fido_log_debug("%s: largeblob_get_array", __func__);
8630afa8e06SEd Maste return r;
8640afa8e06SEd Maste }
8650afa8e06SEd Maste if (fido_blob_serialise(&cbor, item) < 0) {
8660afa8e06SEd Maste fido_log_debug("%s: fido_blob_serialise", __func__);
8670afa8e06SEd Maste r = FIDO_ERR_INTERNAL;
8680afa8e06SEd Maste } else {
8690afa8e06SEd Maste *cbor_ptr = cbor.ptr;
8700afa8e06SEd Maste *cbor_len = cbor.len;
8710afa8e06SEd Maste }
8720afa8e06SEd Maste
8730afa8e06SEd Maste cbor_decref(&item);
8740afa8e06SEd Maste
8750afa8e06SEd Maste return r;
8760afa8e06SEd Maste }
8770afa8e06SEd Maste
8780afa8e06SEd Maste int
fido_dev_largeblob_set_array(fido_dev_t * dev,const unsigned char * cbor_ptr,size_t cbor_len,const char * pin)8790afa8e06SEd Maste fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
8800afa8e06SEd Maste size_t cbor_len, const char *pin)
8810afa8e06SEd Maste {
8820afa8e06SEd Maste cbor_item_t *item = NULL;
8830afa8e06SEd Maste struct cbor_load_result cbor_result;
884f540a430SEd Maste int ms = dev->timeout_ms;
8850afa8e06SEd Maste int r;
8860afa8e06SEd Maste
8870afa8e06SEd Maste if (cbor_ptr == NULL || cbor_len == 0) {
8880afa8e06SEd Maste fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__,
8890afa8e06SEd Maste (const void *)cbor_ptr, cbor_len);
8900afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
8910afa8e06SEd Maste }
8920afa8e06SEd Maste if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
8930afa8e06SEd Maste fido_log_debug("%s: cbor_load", __func__);
8940afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT;
8950afa8e06SEd Maste }
896f540a430SEd Maste if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK)
8970afa8e06SEd Maste fido_log_debug("%s: largeblob_set_array", __func__);
8980afa8e06SEd Maste
8990afa8e06SEd Maste cbor_decref(&item);
9000afa8e06SEd Maste
9010afa8e06SEd Maste return r;
9020afa8e06SEd Maste }
903