xref: /freebsd/contrib/libfido2/tools/largeblob.c (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
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 <sys/types.h>
90afa8e06SEd Maste #include <sys/stat.h>
100afa8e06SEd Maste 
110afa8e06SEd Maste #include <fido.h>
120afa8e06SEd Maste #include <fido/credman.h>
130afa8e06SEd Maste 
140afa8e06SEd Maste #include <cbor.h>
150afa8e06SEd Maste #include <fcntl.h>
160afa8e06SEd Maste #include <limits.h>
170afa8e06SEd Maste #include <stdio.h>
180afa8e06SEd Maste #include <stdlib.h>
190afa8e06SEd Maste #include <string.h>
200afa8e06SEd Maste #ifdef HAVE_UNISTD_H
210afa8e06SEd Maste #include <unistd.h>
220afa8e06SEd Maste #endif
230afa8e06SEd Maste #include <zlib.h>
240afa8e06SEd Maste 
250afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
260afa8e06SEd Maste #include "extern.h"
270afa8e06SEd Maste 
28*2ccfa855SEd Maste #define BOUND (1024UL * 1024UL)
29*2ccfa855SEd Maste 
300afa8e06SEd Maste struct rkmap {
310afa8e06SEd Maste 	fido_credman_rp_t  *rp; /* known rps */
320afa8e06SEd Maste 	fido_credman_rk_t **rk; /* rk per rp */
330afa8e06SEd Maste };
340afa8e06SEd Maste 
350afa8e06SEd Maste static void
free_rkmap(struct rkmap * map)360afa8e06SEd Maste free_rkmap(struct rkmap *map)
370afa8e06SEd Maste {
380afa8e06SEd Maste 	if (map->rp != NULL) {
390afa8e06SEd Maste 		for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++)
400afa8e06SEd Maste 			fido_credman_rk_free(&map->rk[i]);
410afa8e06SEd Maste 		fido_credman_rp_free(&map->rp);
420afa8e06SEd Maste 	}
430afa8e06SEd Maste 	free(map->rk);
440afa8e06SEd Maste }
450afa8e06SEd Maste 
460afa8e06SEd Maste static int
map_known_rps(fido_dev_t * dev,const char * path,struct rkmap * map)470afa8e06SEd Maste map_known_rps(fido_dev_t *dev, const char *path, struct rkmap *map)
480afa8e06SEd Maste {
490afa8e06SEd Maste 	const char *rp_id;
500afa8e06SEd Maste 	char *pin = NULL;
510afa8e06SEd Maste 	size_t n;
520afa8e06SEd Maste 	int r, ok = -1;
530afa8e06SEd Maste 
540afa8e06SEd Maste 	if ((map->rp = fido_credman_rp_new()) == NULL) {
550afa8e06SEd Maste 		warnx("%s: fido_credman_rp_new", __func__);
560afa8e06SEd Maste 		goto out;
570afa8e06SEd Maste 	}
580afa8e06SEd Maste 	if ((pin = get_pin(path)) == NULL)
590afa8e06SEd Maste 		goto out;
600afa8e06SEd Maste 	if ((r = fido_credman_get_dev_rp(dev, map->rp, pin)) != FIDO_OK) {
610afa8e06SEd Maste 		warnx("fido_credman_get_dev_rp: %s", fido_strerr(r));
620afa8e06SEd Maste 		goto out;
630afa8e06SEd Maste 	}
640afa8e06SEd Maste 	if ((n = fido_credman_rp_count(map->rp)) > UINT8_MAX) {
650afa8e06SEd Maste 		warnx("%s: fido_credman_rp_count > UINT8_MAX", __func__);
660afa8e06SEd Maste 		goto out;
670afa8e06SEd Maste 	}
680afa8e06SEd Maste 	if ((map->rk = calloc(n, sizeof(*map->rk))) == NULL) {
690afa8e06SEd Maste 		warnx("%s: calloc", __func__);
700afa8e06SEd Maste 		goto out;
710afa8e06SEd Maste 	}
720afa8e06SEd Maste 	for (size_t i = 0; i < n; i++) {
730afa8e06SEd Maste 		if ((rp_id = fido_credman_rp_id(map->rp, i)) == NULL) {
740afa8e06SEd Maste 			warnx("%s: fido_credman_rp_id %zu", __func__, i);
750afa8e06SEd Maste 			goto out;
760afa8e06SEd Maste 		}
770afa8e06SEd Maste 		if ((map->rk[i] = fido_credman_rk_new()) == NULL) {
780afa8e06SEd Maste 			warnx("%s: fido_credman_rk_new", __func__);
790afa8e06SEd Maste 			goto out;
800afa8e06SEd Maste 		}
810afa8e06SEd Maste 		if ((r = fido_credman_get_dev_rk(dev, rp_id, map->rk[i],
820afa8e06SEd Maste 		    pin)) != FIDO_OK) {
830afa8e06SEd Maste 			warnx("%s: fido_credman_get_dev_rk %s: %s", __func__,
840afa8e06SEd Maste 			    rp_id, fido_strerr(r));
850afa8e06SEd Maste 			goto out;
860afa8e06SEd Maste 		}
870afa8e06SEd Maste 	}
880afa8e06SEd Maste 
890afa8e06SEd Maste 	ok = 0;
900afa8e06SEd Maste out:
910afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
920afa8e06SEd Maste 
930afa8e06SEd Maste 	return ok;
940afa8e06SEd Maste }
950afa8e06SEd Maste 
960afa8e06SEd Maste static int
lookup_key(const char * path,fido_dev_t * dev,const char * rp_id,const struct blob * cred_id,char ** pin,struct blob * key)970afa8e06SEd Maste lookup_key(const char *path, fido_dev_t *dev, const char *rp_id,
980afa8e06SEd Maste     const struct blob *cred_id, char **pin, struct blob *key)
990afa8e06SEd Maste {
1000afa8e06SEd Maste 	fido_credman_rk_t *rk = NULL;
1010afa8e06SEd Maste 	const fido_cred_t *cred = NULL;
1020afa8e06SEd Maste 	size_t i, n;
1030afa8e06SEd Maste 	int r, ok = -1;
1040afa8e06SEd Maste 
1050afa8e06SEd Maste 	if ((rk = fido_credman_rk_new()) == NULL) {
1060afa8e06SEd Maste 		warnx("%s: fido_credman_rk_new", __func__);
1070afa8e06SEd Maste 		goto out;
1080afa8e06SEd Maste 	}
1090afa8e06SEd Maste 	if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin)) != FIDO_OK &&
1100afa8e06SEd Maste 	    *pin == NULL && should_retry_with_pin(dev, r)) {
1110afa8e06SEd Maste 		if ((*pin = get_pin(path)) == NULL)
1120afa8e06SEd Maste 			goto out;
1130afa8e06SEd Maste 		r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin);
1140afa8e06SEd Maste 	}
1150afa8e06SEd Maste 	if (r != FIDO_OK) {
1160afa8e06SEd Maste 		warnx("%s: fido_credman_get_dev_rk: %s", __func__,
1170afa8e06SEd Maste 		    fido_strerr(r));
1180afa8e06SEd Maste 		goto out;
1190afa8e06SEd Maste 	}
1200afa8e06SEd Maste 	if ((n = fido_credman_rk_count(rk)) == 0) {
1210afa8e06SEd Maste 		warnx("%s: rp id not found", __func__);
1220afa8e06SEd Maste 		goto out;
1230afa8e06SEd Maste 	}
1240afa8e06SEd Maste 	if (n == 1 && cred_id->len == 0) {
1250afa8e06SEd Maste 		/* use the credential we found */
1260afa8e06SEd Maste 		cred = fido_credman_rk(rk, 0);
1270afa8e06SEd Maste 	} else {
1280afa8e06SEd Maste 		if (cred_id->len == 0) {
1290afa8e06SEd Maste 			warnx("%s: multiple credentials found", __func__);
1300afa8e06SEd Maste 			goto out;
1310afa8e06SEd Maste 		}
1320afa8e06SEd Maste 		for (i = 0; i < n; i++) {
1330afa8e06SEd Maste 			const fido_cred_t *x = fido_credman_rk(rk, i);
1340afa8e06SEd Maste 			if (fido_cred_id_len(x) <= cred_id->len &&
1350afa8e06SEd Maste 			    !memcmp(fido_cred_id_ptr(x), cred_id->ptr,
1360afa8e06SEd Maste 			    fido_cred_id_len(x))) {
1370afa8e06SEd Maste 				cred = x;
1380afa8e06SEd Maste 				break;
1390afa8e06SEd Maste 			}
1400afa8e06SEd Maste 		}
1410afa8e06SEd Maste 	}
1420afa8e06SEd Maste 	if (cred == NULL) {
1430afa8e06SEd Maste 		warnx("%s: credential not found", __func__);
1440afa8e06SEd Maste 		goto out;
1450afa8e06SEd Maste 	}
1460afa8e06SEd Maste 	if (fido_cred_largeblob_key_ptr(cred) == NULL) {
1470afa8e06SEd Maste 		warnx("%s: no associated blob key", __func__);
1480afa8e06SEd Maste 		goto out;
1490afa8e06SEd Maste 	}
1500afa8e06SEd Maste 	key->len = fido_cred_largeblob_key_len(cred);
1510afa8e06SEd Maste 	if ((key->ptr = malloc(key->len)) == NULL) {
1520afa8e06SEd Maste 		warnx("%s: malloc", __func__);
1530afa8e06SEd Maste 		goto out;
1540afa8e06SEd Maste 	}
1550afa8e06SEd Maste 	memcpy(key->ptr, fido_cred_largeblob_key_ptr(cred), key->len);
1560afa8e06SEd Maste 
1570afa8e06SEd Maste 	ok = 0;
1580afa8e06SEd Maste out:
1590afa8e06SEd Maste 	fido_credman_rk_free(&rk);
1600afa8e06SEd Maste 
1610afa8e06SEd Maste 	return ok;
1620afa8e06SEd Maste }
1630afa8e06SEd Maste 
1640afa8e06SEd Maste static int
load_key(const char * keyf,const char * cred_id64,const char * rp_id,const char * path,fido_dev_t * dev,char ** pin,struct blob * key)1650afa8e06SEd Maste load_key(const char *keyf, const char *cred_id64, const char *rp_id,
1660afa8e06SEd Maste     const char *path, fido_dev_t *dev, char **pin, struct blob *key)
1670afa8e06SEd Maste {
1680afa8e06SEd Maste 	struct blob cred_id;
1690afa8e06SEd Maste 	FILE *fp;
1700afa8e06SEd Maste 	int r;
1710afa8e06SEd Maste 
1720afa8e06SEd Maste 	memset(&cred_id, 0, sizeof(cred_id));
1730afa8e06SEd Maste 
1740afa8e06SEd Maste 	if (keyf != NULL) {
1750afa8e06SEd Maste 		if (rp_id != NULL || cred_id64 != NULL)
1760afa8e06SEd Maste 			usage();
1770afa8e06SEd Maste 		fp = open_read(keyf);
1780afa8e06SEd Maste 		if ((r = base64_read(fp, key)) < 0)
1790afa8e06SEd Maste 			warnx("%s: base64_read %s", __func__, keyf);
1800afa8e06SEd Maste 		fclose(fp);
1810afa8e06SEd Maste 		return r;
1820afa8e06SEd Maste 	}
1830afa8e06SEd Maste 	if (rp_id == NULL)
1840afa8e06SEd Maste 		usage();
1850afa8e06SEd Maste 	if (cred_id64 != NULL && base64_decode(cred_id64, (void *)&cred_id.ptr,
1860afa8e06SEd Maste 	    &cred_id.len) < 0) {
1870afa8e06SEd Maste 		warnx("%s: base64_decode %s", __func__, cred_id64);
1880afa8e06SEd Maste 		return -1;
1890afa8e06SEd Maste 	}
1900afa8e06SEd Maste 	r = lookup_key(path, dev, rp_id, &cred_id, pin, key);
1910afa8e06SEd Maste 	free(cred_id.ptr);
1920afa8e06SEd Maste 
1930afa8e06SEd Maste 	return r;
1940afa8e06SEd Maste }
1950afa8e06SEd Maste 
1960afa8e06SEd Maste int
blob_set(const char * path,const char * keyf,const char * rp_id,const char * cred_id64,const char * blobf)1970afa8e06SEd Maste blob_set(const char *path, const char *keyf, const char *rp_id,
1980afa8e06SEd Maste     const char *cred_id64, const char *blobf)
1990afa8e06SEd Maste {
2000afa8e06SEd Maste 	fido_dev_t *dev;
2010afa8e06SEd Maste 	struct blob key, blob;
2020afa8e06SEd Maste 	char *pin = NULL;
2030afa8e06SEd Maste 	int r, ok = 1;
2040afa8e06SEd Maste 
2050afa8e06SEd Maste 	dev = open_dev(path);
2060afa8e06SEd Maste 	memset(&key, 0, sizeof(key));
2070afa8e06SEd Maste 	memset(&blob, 0, sizeof(blob));
2080afa8e06SEd Maste 
2090afa8e06SEd Maste 	if (read_file(blobf, &blob.ptr, &blob.len) < 0 ||
2100afa8e06SEd Maste 	    load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
2110afa8e06SEd Maste 		goto out;
2120afa8e06SEd Maste 	if ((r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
2130afa8e06SEd Maste 	    blob.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
2140afa8e06SEd Maste 		if ((pin = get_pin(path)) == NULL)
2150afa8e06SEd Maste 			goto out;
2160afa8e06SEd Maste 		r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
2170afa8e06SEd Maste 		    blob.len, pin);
2180afa8e06SEd Maste 	}
2190afa8e06SEd Maste 	if (r != FIDO_OK) {
2200afa8e06SEd Maste 		warnx("fido_dev_largeblob_set: %s", fido_strerr(r));
2210afa8e06SEd Maste 		goto out;
2220afa8e06SEd Maste 	}
2230afa8e06SEd Maste 
2240afa8e06SEd Maste 	ok = 0; /* success */
2250afa8e06SEd Maste out:
2260afa8e06SEd Maste 	freezero(key.ptr, key.len);
2270afa8e06SEd Maste 	freezero(blob.ptr, blob.len);
2280afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
2290afa8e06SEd Maste 
2300afa8e06SEd Maste 	fido_dev_close(dev);
2310afa8e06SEd Maste 	fido_dev_free(&dev);
2320afa8e06SEd Maste 
2330afa8e06SEd Maste 	exit(ok);
2340afa8e06SEd Maste }
2350afa8e06SEd Maste 
2360afa8e06SEd Maste int
blob_get(const char * path,const char * keyf,const char * rp_id,const char * cred_id64,const char * blobf)2370afa8e06SEd Maste blob_get(const char *path, const char *keyf, const char *rp_id,
2380afa8e06SEd Maste     const char *cred_id64, const char *blobf)
2390afa8e06SEd Maste {
2400afa8e06SEd Maste 	fido_dev_t *dev;
2410afa8e06SEd Maste 	struct blob key, blob;
2420afa8e06SEd Maste 	char *pin = NULL;
2430afa8e06SEd Maste 	int r, ok = 1;
2440afa8e06SEd Maste 
2450afa8e06SEd Maste 	dev = open_dev(path);
2460afa8e06SEd Maste 	memset(&key, 0, sizeof(key));
2470afa8e06SEd Maste 	memset(&blob, 0, sizeof(blob));
2480afa8e06SEd Maste 
2490afa8e06SEd Maste 	if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
2500afa8e06SEd Maste 		goto out;
2510afa8e06SEd Maste 	if ((r = fido_dev_largeblob_get(dev, key.ptr, key.len, &blob.ptr,
2520afa8e06SEd Maste 	    &blob.len)) != FIDO_OK) {
2530afa8e06SEd Maste 		warnx("fido_dev_largeblob_get: %s", fido_strerr(r));
2540afa8e06SEd Maste 		goto out;
2550afa8e06SEd Maste 	}
2560afa8e06SEd Maste 	if (write_file(blobf, blob.ptr, blob.len) < 0)
2570afa8e06SEd Maste 		goto out;
2580afa8e06SEd Maste 
2590afa8e06SEd Maste 	ok = 0; /* success */
2600afa8e06SEd Maste out:
2610afa8e06SEd Maste 	freezero(key.ptr, key.len);
2620afa8e06SEd Maste 	freezero(blob.ptr, blob.len);
2630afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
2640afa8e06SEd Maste 
2650afa8e06SEd Maste 	fido_dev_close(dev);
2660afa8e06SEd Maste 	fido_dev_free(&dev);
2670afa8e06SEd Maste 
2680afa8e06SEd Maste 	exit(ok);
2690afa8e06SEd Maste }
2700afa8e06SEd Maste 
2710afa8e06SEd Maste int
blob_delete(const char * path,const char * keyf,const char * rp_id,const char * cred_id64)2720afa8e06SEd Maste blob_delete(const char *path, const char *keyf, const char *rp_id,
2730afa8e06SEd Maste     const char *cred_id64)
2740afa8e06SEd Maste {
2750afa8e06SEd Maste 	fido_dev_t *dev;
2760afa8e06SEd Maste 	struct blob key;
2770afa8e06SEd Maste 	char *pin = NULL;
2780afa8e06SEd Maste 	int r, ok = 1;
2790afa8e06SEd Maste 
2800afa8e06SEd Maste 	dev = open_dev(path);
2810afa8e06SEd Maste 	memset(&key, 0, sizeof(key));
2820afa8e06SEd Maste 
2830afa8e06SEd Maste 	if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
2840afa8e06SEd Maste 		goto out;
2850afa8e06SEd Maste 	if ((r = fido_dev_largeblob_remove(dev, key.ptr, key.len,
2860afa8e06SEd Maste 	    pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
2870afa8e06SEd Maste 		if ((pin = get_pin(path)) == NULL)
2880afa8e06SEd Maste 			goto out;
2890afa8e06SEd Maste 		r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin);
2900afa8e06SEd Maste 	}
2910afa8e06SEd Maste 	if (r != FIDO_OK) {
2920afa8e06SEd Maste 		warnx("fido_dev_largeblob_remove: %s", fido_strerr(r));
2930afa8e06SEd Maste 		goto out;
2940afa8e06SEd Maste 	}
2950afa8e06SEd Maste 
2960afa8e06SEd Maste 	ok = 0; /* success */
2970afa8e06SEd Maste out:
2980afa8e06SEd Maste 	freezero(key.ptr, key.len);
2990afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
3000afa8e06SEd Maste 
3010afa8e06SEd Maste 	fido_dev_close(dev);
3020afa8e06SEd Maste 	fido_dev_free(&dev);
3030afa8e06SEd Maste 
3040afa8e06SEd Maste 	exit(ok);
3050afa8e06SEd Maste }
3060afa8e06SEd Maste 
3070afa8e06SEd Maste static int
try_decompress(const struct blob * in,uint64_t origsiz,int wbits)308*2ccfa855SEd Maste try_decompress(const struct blob *in, uint64_t origsiz, int wbits)
3090afa8e06SEd Maste {
310*2ccfa855SEd Maste 	struct blob out;
311*2ccfa855SEd Maste 	z_stream zs;
312*2ccfa855SEd Maste 	u_int ilen, olen;
3130afa8e06SEd Maste 	int ok = -1;
3140afa8e06SEd Maste 
315*2ccfa855SEd Maste 	memset(&zs, 0, sizeof(zs));
316*2ccfa855SEd Maste 	memset(&out, 0, sizeof(out));
3170afa8e06SEd Maste 
318*2ccfa855SEd Maste 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND)
3190afa8e06SEd Maste 		return -1;
320*2ccfa855SEd Maste 	if (origsiz > SIZE_MAX || origsiz > UINT_MAX ||
321*2ccfa855SEd Maste 	    (olen = (u_int)origsiz) > BOUND)
3220afa8e06SEd Maste 		return -1;
323*2ccfa855SEd Maste 	if (inflateInit2(&zs, wbits) != Z_OK)
3240afa8e06SEd Maste 		return -1;
3250afa8e06SEd Maste 
326*2ccfa855SEd Maste 	if ((out.ptr = calloc(1, olen)) == NULL)
327*2ccfa855SEd Maste 		goto fail;
328*2ccfa855SEd Maste 
329*2ccfa855SEd Maste 	out.len = olen;
330*2ccfa855SEd Maste 	zs.next_in = in->ptr;
331*2ccfa855SEd Maste 	zs.avail_in = ilen;
332*2ccfa855SEd Maste 	zs.next_out = out.ptr;
333*2ccfa855SEd Maste 	zs.avail_out = olen;
334*2ccfa855SEd Maste 
335*2ccfa855SEd Maste 	if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
336*2ccfa855SEd Maste 		goto fail;
337*2ccfa855SEd Maste 	if (zs.avail_out != 0)
338*2ccfa855SEd Maste 		goto fail;
339*2ccfa855SEd Maste 
340*2ccfa855SEd Maste 	ok = 0;
341*2ccfa855SEd Maste fail:
342*2ccfa855SEd Maste 	if (inflateEnd(&zs) != Z_OK)
343*2ccfa855SEd Maste 		ok = -1;
344*2ccfa855SEd Maste 
345*2ccfa855SEd Maste 	freezero(out.ptr, out.len);
3460afa8e06SEd Maste 
3470afa8e06SEd Maste 	return ok;
3480afa8e06SEd Maste }
3490afa8e06SEd Maste 
3500afa8e06SEd Maste static int
decompress(const struct blob * plaintext,uint64_t origsiz)351*2ccfa855SEd Maste decompress(const struct blob *plaintext, uint64_t origsiz)
352*2ccfa855SEd Maste {
353*2ccfa855SEd Maste 	if (try_decompress(plaintext, origsiz, MAX_WBITS) == 0) /* rfc1950 */
354*2ccfa855SEd Maste 		return 0;
355*2ccfa855SEd Maste 	return try_decompress(plaintext, origsiz, -MAX_WBITS); /* rfc1951 */
356*2ccfa855SEd Maste }
357*2ccfa855SEd Maste 
358*2ccfa855SEd Maste static int
decode(const struct blob * ciphertext,const struct blob * nonce,uint64_t origsiz,const fido_cred_t * cred)3590afa8e06SEd Maste decode(const struct blob *ciphertext, const struct blob *nonce,
3600afa8e06SEd Maste     uint64_t origsiz, const fido_cred_t *cred)
3610afa8e06SEd Maste {
3620afa8e06SEd Maste 	uint8_t aad[4 + sizeof(uint64_t)];
3630afa8e06SEd Maste 	EVP_CIPHER_CTX *ctx = NULL;
3640afa8e06SEd Maste 	const EVP_CIPHER *cipher;
3650afa8e06SEd Maste 	struct blob plaintext;
3660afa8e06SEd Maste 	uint64_t tmp;
3670afa8e06SEd Maste 	int ok = -1;
3680afa8e06SEd Maste 
3690afa8e06SEd Maste 	memset(&plaintext, 0, sizeof(plaintext));
3700afa8e06SEd Maste 
3710afa8e06SEd Maste 	if (nonce->len != 12)
3720afa8e06SEd Maste 		return -1;
3730afa8e06SEd Maste 	if (cred == NULL ||
3740afa8e06SEd Maste 	    fido_cred_largeblob_key_ptr(cred) == NULL ||
3750afa8e06SEd Maste 	    fido_cred_largeblob_key_len(cred) != 32)
3760afa8e06SEd Maste 		return -1;
3770afa8e06SEd Maste 	if (ciphertext->len > UINT_MAX ||
3780afa8e06SEd Maste 	    ciphertext->len > SIZE_MAX - 16 ||
3790afa8e06SEd Maste 	    ciphertext->len < 16)
3800afa8e06SEd Maste 		return -1;
3810afa8e06SEd Maste 	plaintext.len = ciphertext->len - 16;
3820afa8e06SEd Maste 	if ((plaintext.ptr = calloc(1, plaintext.len)) == NULL)
3830afa8e06SEd Maste 		return -1;
3840afa8e06SEd Maste 	if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
3850afa8e06SEd Maste 	    (cipher = EVP_aes_256_gcm()) == NULL ||
3860afa8e06SEd Maste 	    EVP_CipherInit(ctx, cipher, fido_cred_largeblob_key_ptr(cred),
3870afa8e06SEd Maste 	    nonce->ptr, 0) == 0)
3880afa8e06SEd Maste 		goto out;
3890afa8e06SEd Maste 	if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
3900afa8e06SEd Maste 	    ciphertext->ptr + ciphertext->len - 16) == 0)
3910afa8e06SEd Maste 		goto out;
3920afa8e06SEd Maste 	aad[0] = 0x62; /* b */
3930afa8e06SEd Maste 	aad[1] = 0x6c; /* l */
3940afa8e06SEd Maste 	aad[2] = 0x6f; /* o */
3950afa8e06SEd Maste 	aad[3] = 0x62; /* b */
3960afa8e06SEd Maste 	tmp = htole64(origsiz);
3970afa8e06SEd Maste 	memcpy(&aad[4], &tmp, sizeof(uint64_t));
3980afa8e06SEd Maste 	if (EVP_Cipher(ctx, NULL, aad, (u_int)sizeof(aad)) < 0 ||
3990afa8e06SEd Maste 	    EVP_Cipher(ctx, plaintext.ptr, ciphertext->ptr,
4000afa8e06SEd Maste 	    (u_int)plaintext.len) < 0 ||
4010afa8e06SEd Maste 	    EVP_Cipher(ctx, NULL, NULL, 0) < 0)
4020afa8e06SEd Maste 		goto out;
4030afa8e06SEd Maste 	if (decompress(&plaintext, origsiz) < 0)
4040afa8e06SEd Maste 		goto out;
4050afa8e06SEd Maste 
4060afa8e06SEd Maste 	ok = 0;
4070afa8e06SEd Maste out:
4080afa8e06SEd Maste 	freezero(plaintext.ptr, plaintext.len);
4090afa8e06SEd Maste 
4100afa8e06SEd Maste 	if (ctx != NULL)
4110afa8e06SEd Maste 		EVP_CIPHER_CTX_free(ctx);
4120afa8e06SEd Maste 
4130afa8e06SEd Maste 	return ok;
4140afa8e06SEd Maste }
4150afa8e06SEd Maste 
4160afa8e06SEd Maste static const fido_cred_t *
try_rp(const fido_credman_rk_t * rk,const struct blob * ciphertext,const struct blob * nonce,uint64_t origsiz)4170afa8e06SEd Maste try_rp(const fido_credman_rk_t *rk, const struct blob *ciphertext,
4180afa8e06SEd Maste     const struct blob *nonce, uint64_t origsiz)
4190afa8e06SEd Maste {
4200afa8e06SEd Maste 	const fido_cred_t *cred;
4210afa8e06SEd Maste 
4220afa8e06SEd Maste 	for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
4230afa8e06SEd Maste 		if ((cred = fido_credman_rk(rk, i)) != NULL &&
4240afa8e06SEd Maste 		    decode(ciphertext, nonce, origsiz, cred) == 0)
4250afa8e06SEd Maste 			return cred;
4260afa8e06SEd Maste 
4270afa8e06SEd Maste 	return NULL;
4280afa8e06SEd Maste }
4290afa8e06SEd Maste 
4300afa8e06SEd Maste static int
decode_cbor_blob(struct blob * out,const cbor_item_t * item)4310afa8e06SEd Maste decode_cbor_blob(struct blob *out, const cbor_item_t *item)
4320afa8e06SEd Maste {
4330afa8e06SEd Maste 	if (out->ptr != NULL ||
4340afa8e06SEd Maste 	    cbor_isa_bytestring(item) == false ||
4350afa8e06SEd Maste 	    cbor_bytestring_is_definite(item) == false)
4360afa8e06SEd Maste 		return -1;
4370afa8e06SEd Maste 	out->len = cbor_bytestring_length(item);
4380afa8e06SEd Maste 	if ((out->ptr = malloc(out->len)) == NULL)
4390afa8e06SEd Maste 		return -1;
4400afa8e06SEd Maste 	memcpy(out->ptr, cbor_bytestring_handle(item), out->len);
4410afa8e06SEd Maste 
4420afa8e06SEd Maste 	return 0;
4430afa8e06SEd Maste }
4440afa8e06SEd Maste 
4450afa8e06SEd Maste static int
decode_blob_entry(const cbor_item_t * item,struct blob * ciphertext,struct blob * nonce,uint64_t * origsiz)4460afa8e06SEd Maste decode_blob_entry(const cbor_item_t *item, struct blob *ciphertext,
4470afa8e06SEd Maste     struct blob *nonce, uint64_t *origsiz)
4480afa8e06SEd Maste {
4490afa8e06SEd Maste 	struct cbor_pair *v;
4500afa8e06SEd Maste 
4510afa8e06SEd Maste 	if (item == NULL)
4520afa8e06SEd Maste 		return -1;
4530afa8e06SEd Maste 	if (cbor_isa_map(item) == false ||
4540afa8e06SEd Maste 	    cbor_map_is_definite(item) == false ||
4550afa8e06SEd Maste 	    (v = cbor_map_handle(item)) == NULL)
4560afa8e06SEd Maste 		return -1;
4570afa8e06SEd Maste 	if (cbor_map_size(item) > UINT8_MAX)
4580afa8e06SEd Maste 		return -1;
4590afa8e06SEd Maste 
4600afa8e06SEd Maste 	for (size_t i = 0; i < cbor_map_size(item); i++) {
4610afa8e06SEd Maste 		if (cbor_isa_uint(v[i].key) == false ||
4620afa8e06SEd Maste 		    cbor_int_get_width(v[i].key) != CBOR_INT_8)
4630afa8e06SEd Maste 			continue; /* ignore */
4640afa8e06SEd Maste 		switch (cbor_get_uint8(v[i].key)) {
4650afa8e06SEd Maste 		case 1: /* ciphertext */
4660afa8e06SEd Maste 			if (decode_cbor_blob(ciphertext, v[i].value) < 0)
4670afa8e06SEd Maste 				return -1;
4680afa8e06SEd Maste 			break;
4690afa8e06SEd Maste 		case 2: /* nonce */
4700afa8e06SEd Maste 			if (decode_cbor_blob(nonce, v[i].value) < 0)
4710afa8e06SEd Maste 				return -1;
4720afa8e06SEd Maste 			break;
4730afa8e06SEd Maste 		case 3: /* origSize */
4740afa8e06SEd Maste 			if (*origsiz != 0 ||
4750afa8e06SEd Maste 			    cbor_isa_uint(v[i].value) == false ||
4760afa8e06SEd Maste 			    (*origsiz = cbor_get_int(v[i].value)) > SIZE_MAX)
4770afa8e06SEd Maste 				return -1;
4780afa8e06SEd Maste 		}
4790afa8e06SEd Maste 	}
4800afa8e06SEd Maste 	if (ciphertext->ptr == NULL || nonce->ptr == NULL || *origsiz == 0)
4810afa8e06SEd Maste 		return -1;
4820afa8e06SEd Maste 
4830afa8e06SEd Maste 	return 0;
4840afa8e06SEd Maste }
4850afa8e06SEd Maste 
4860afa8e06SEd Maste static void
print_blob_entry(size_t idx,const cbor_item_t * item,const struct rkmap * map)4870afa8e06SEd Maste print_blob_entry(size_t idx, const cbor_item_t *item, const struct rkmap *map)
4880afa8e06SEd Maste {
4890afa8e06SEd Maste 	struct blob ciphertext, nonce;
4900afa8e06SEd Maste 	const fido_cred_t *cred = NULL;
4910afa8e06SEd Maste 	const char *rp_id = NULL;
4920afa8e06SEd Maste 	char *cred_id = NULL;
4930afa8e06SEd Maste 	uint64_t origsiz = 0;
4940afa8e06SEd Maste 
4950afa8e06SEd Maste 	memset(&ciphertext, 0, sizeof(ciphertext));
4960afa8e06SEd Maste 	memset(&nonce, 0, sizeof(nonce));
4970afa8e06SEd Maste 
4980afa8e06SEd Maste 	if (decode_blob_entry(item, &ciphertext, &nonce, &origsiz) < 0) {
4990afa8e06SEd Maste 		printf("%02zu: <skipped: bad cbor>\n", idx);
5000afa8e06SEd Maste 		goto out;
5010afa8e06SEd Maste 	}
5020afa8e06SEd Maste 	for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) {
5030afa8e06SEd Maste 		if ((cred = try_rp(map->rk[i], &ciphertext, &nonce,
5040afa8e06SEd Maste 		    origsiz)) != NULL) {
5050afa8e06SEd Maste 			rp_id = fido_credman_rp_id(map->rp, i);
5060afa8e06SEd Maste 			break;
5070afa8e06SEd Maste 		}
5080afa8e06SEd Maste 	}
5090afa8e06SEd Maste 	if (cred == NULL) {
5100afa8e06SEd Maste 		if ((cred_id = strdup("<unknown>")) == NULL) {
5110afa8e06SEd Maste 			printf("%02zu: <skipped: strdup failed>\n", idx);
5120afa8e06SEd Maste 			goto out;
5130afa8e06SEd Maste 		}
5140afa8e06SEd Maste 	} else {
5150afa8e06SEd Maste 		if (base64_encode(fido_cred_id_ptr(cred),
5160afa8e06SEd Maste 		    fido_cred_id_len(cred), &cred_id) < 0) {
5170afa8e06SEd Maste 			printf("%02zu: <skipped: base64_encode failed>\n", idx);
5180afa8e06SEd Maste 			goto out;
5190afa8e06SEd Maste 		}
5200afa8e06SEd Maste 	}
5210afa8e06SEd Maste 	if (rp_id == NULL)
5220afa8e06SEd Maste 		rp_id = "<unknown>";
5230afa8e06SEd Maste 
5240afa8e06SEd Maste 	printf("%02zu: %4zu %4zu %s %s\n", idx, ciphertext.len,
5250afa8e06SEd Maste 	    (size_t)origsiz, cred_id, rp_id);
5260afa8e06SEd Maste out:
5270afa8e06SEd Maste 	free(ciphertext.ptr);
5280afa8e06SEd Maste 	free(nonce.ptr);
5290afa8e06SEd Maste 	free(cred_id);
5300afa8e06SEd Maste }
5310afa8e06SEd Maste 
5320afa8e06SEd Maste static cbor_item_t *
get_cbor_array(fido_dev_t * dev)5330afa8e06SEd Maste get_cbor_array(fido_dev_t *dev)
5340afa8e06SEd Maste {
5350afa8e06SEd Maste 	struct cbor_load_result cbor_result;
5360afa8e06SEd Maste 	cbor_item_t *item = NULL;
5370afa8e06SEd Maste 	u_char *cbor_ptr = NULL;
5380afa8e06SEd Maste 	size_t cbor_len;
5390afa8e06SEd Maste 	int r, ok = -1;
5400afa8e06SEd Maste 
5410afa8e06SEd Maste 	if ((r = fido_dev_largeblob_get_array(dev, &cbor_ptr,
5420afa8e06SEd Maste 	    &cbor_len)) != FIDO_OK) {
5430afa8e06SEd Maste 		warnx("%s: fido_dev_largeblob_get_array: %s", __func__,
5440afa8e06SEd Maste 		    fido_strerr(r));
5450afa8e06SEd Maste 		goto out;
5460afa8e06SEd Maste 	}
5470afa8e06SEd Maste 	if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
5480afa8e06SEd Maste 		warnx("%s: cbor_load", __func__);
5490afa8e06SEd Maste 		goto out;
5500afa8e06SEd Maste 	}
5510afa8e06SEd Maste 	if (cbor_result.read != cbor_len) {
5520afa8e06SEd Maste 		warnx("%s: cbor_result.read (%zu) != cbor_len (%zu)", __func__,
5530afa8e06SEd Maste 		    cbor_result.read, cbor_len);
5540afa8e06SEd Maste 		/* continue */
5550afa8e06SEd Maste 	}
5560afa8e06SEd Maste 	if (cbor_isa_array(item) == false ||
5570afa8e06SEd Maste 	    cbor_array_is_definite(item) == false) {
5580afa8e06SEd Maste 		warnx("%s: cbor type", __func__);
5590afa8e06SEd Maste 		goto out;
5600afa8e06SEd Maste 	}
5610afa8e06SEd Maste 	if (cbor_array_size(item) > UINT8_MAX) {
5620afa8e06SEd Maste 		warnx("%s: cbor_array_size > UINT8_MAX", __func__);
5630afa8e06SEd Maste 		goto out;
5640afa8e06SEd Maste 	}
5650afa8e06SEd Maste 	if (cbor_array_size(item) == 0) {
5660afa8e06SEd Maste 		ok = 0; /* nothing to do */
5670afa8e06SEd Maste 		goto out;
5680afa8e06SEd Maste 	}
5690afa8e06SEd Maste 
5700afa8e06SEd Maste 	printf("total map size: %zu byte%s\n", cbor_len, plural(cbor_len));
5710afa8e06SEd Maste 
5720afa8e06SEd Maste 	ok = 0;
5730afa8e06SEd Maste out:
5740afa8e06SEd Maste 	if (ok < 0 && item != NULL) {
5750afa8e06SEd Maste 		cbor_decref(&item);
5760afa8e06SEd Maste 		item = NULL;
5770afa8e06SEd Maste 	}
5780afa8e06SEd Maste 	free(cbor_ptr);
5790afa8e06SEd Maste 
5800afa8e06SEd Maste 	return item;
5810afa8e06SEd Maste }
5820afa8e06SEd Maste 
5830afa8e06SEd Maste int
blob_list(const char * path)5840afa8e06SEd Maste blob_list(const char *path)
5850afa8e06SEd Maste {
5860afa8e06SEd Maste 	struct rkmap map;
5870afa8e06SEd Maste 	fido_dev_t *dev = NULL;
5880afa8e06SEd Maste 	cbor_item_t *item = NULL, **v;
5890afa8e06SEd Maste 	int ok = 1;
5900afa8e06SEd Maste 
5910afa8e06SEd Maste 	memset(&map, 0, sizeof(map));
5920afa8e06SEd Maste 	dev = open_dev(path);
5930afa8e06SEd Maste 	if (map_known_rps(dev, path, &map) < 0 ||
5940afa8e06SEd Maste 	    (item = get_cbor_array(dev)) == NULL)
5950afa8e06SEd Maste 		goto out;
5960afa8e06SEd Maste 	if (cbor_array_size(item) == 0) {
5970afa8e06SEd Maste 		ok = 0; /* nothing to do */
5980afa8e06SEd Maste 		goto out;
5990afa8e06SEd Maste 	}
6000afa8e06SEd Maste 	if ((v = cbor_array_handle(item)) == NULL) {
6010afa8e06SEd Maste 		warnx("%s: cbor_array_handle", __func__);
6020afa8e06SEd Maste 		goto out;
6030afa8e06SEd Maste 	}
6040afa8e06SEd Maste 	for (size_t i = 0; i < cbor_array_size(item); i++)
6050afa8e06SEd Maste 		print_blob_entry(i, v[i], &map);
6060afa8e06SEd Maste 
6070afa8e06SEd Maste 	ok = 0; /* success */
6080afa8e06SEd Maste out:
6090afa8e06SEd Maste 	free_rkmap(&map);
6100afa8e06SEd Maste 
6110afa8e06SEd Maste 	if (item != NULL)
6120afa8e06SEd Maste 		cbor_decref(&item);
6130afa8e06SEd Maste 
6140afa8e06SEd Maste 	fido_dev_close(dev);
6150afa8e06SEd Maste 	fido_dev_free(&dev);
6160afa8e06SEd Maste 
6170afa8e06SEd Maste 	exit(ok);
6180afa8e06SEd Maste }
619