xref: /freebsd/contrib/libfido2/tools/largeblob.c (revision 0afa8e065e14bb8fd338d75690e0238c00167d40)
1*0afa8e06SEd Maste /*
2*0afa8e06SEd Maste  * Copyright (c) 2020 Yubico AB. All rights reserved.
3*0afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
4*0afa8e06SEd Maste  * license that can be found in the LICENSE file.
5*0afa8e06SEd Maste  */
6*0afa8e06SEd Maste 
7*0afa8e06SEd Maste #include <sys/types.h>
8*0afa8e06SEd Maste #include <sys/stat.h>
9*0afa8e06SEd Maste 
10*0afa8e06SEd Maste #include <fido.h>
11*0afa8e06SEd Maste #include <fido/credman.h>
12*0afa8e06SEd Maste 
13*0afa8e06SEd Maste #include <cbor.h>
14*0afa8e06SEd Maste #include <fcntl.h>
15*0afa8e06SEd Maste #include <limits.h>
16*0afa8e06SEd Maste #include <stdio.h>
17*0afa8e06SEd Maste #include <stdlib.h>
18*0afa8e06SEd Maste #include <string.h>
19*0afa8e06SEd Maste #ifdef HAVE_UNISTD_H
20*0afa8e06SEd Maste #include <unistd.h>
21*0afa8e06SEd Maste #endif
22*0afa8e06SEd Maste #include <zlib.h>
23*0afa8e06SEd Maste 
24*0afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
25*0afa8e06SEd Maste #include "extern.h"
26*0afa8e06SEd Maste 
27*0afa8e06SEd Maste struct rkmap {
28*0afa8e06SEd Maste 	fido_credman_rp_t  *rp; /* known rps */
29*0afa8e06SEd Maste 	fido_credman_rk_t **rk; /* rk per rp */
30*0afa8e06SEd Maste };
31*0afa8e06SEd Maste 
32*0afa8e06SEd Maste static void
33*0afa8e06SEd Maste free_rkmap(struct rkmap *map)
34*0afa8e06SEd Maste {
35*0afa8e06SEd Maste 	if (map->rp != NULL) {
36*0afa8e06SEd Maste 		for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++)
37*0afa8e06SEd Maste 			fido_credman_rk_free(&map->rk[i]);
38*0afa8e06SEd Maste 		fido_credman_rp_free(&map->rp);
39*0afa8e06SEd Maste 	}
40*0afa8e06SEd Maste 	free(map->rk);
41*0afa8e06SEd Maste }
42*0afa8e06SEd Maste 
43*0afa8e06SEd Maste static int
44*0afa8e06SEd Maste map_known_rps(fido_dev_t *dev, const char *path, struct rkmap *map)
45*0afa8e06SEd Maste {
46*0afa8e06SEd Maste 	const char *rp_id;
47*0afa8e06SEd Maste 	char *pin = NULL;
48*0afa8e06SEd Maste 	size_t n;
49*0afa8e06SEd Maste 	int r, ok = -1;
50*0afa8e06SEd Maste 
51*0afa8e06SEd Maste 	if ((map->rp = fido_credman_rp_new()) == NULL) {
52*0afa8e06SEd Maste 		warnx("%s: fido_credman_rp_new", __func__);
53*0afa8e06SEd Maste 		goto out;
54*0afa8e06SEd Maste 	}
55*0afa8e06SEd Maste 	if ((pin = get_pin(path)) == NULL)
56*0afa8e06SEd Maste 		goto out;
57*0afa8e06SEd Maste 	if ((r = fido_credman_get_dev_rp(dev, map->rp, pin)) != FIDO_OK) {
58*0afa8e06SEd Maste 		warnx("fido_credman_get_dev_rp: %s", fido_strerr(r));
59*0afa8e06SEd Maste 		goto out;
60*0afa8e06SEd Maste 	}
61*0afa8e06SEd Maste 	if ((n = fido_credman_rp_count(map->rp)) > UINT8_MAX) {
62*0afa8e06SEd Maste 		warnx("%s: fido_credman_rp_count > UINT8_MAX", __func__);
63*0afa8e06SEd Maste 		goto out;
64*0afa8e06SEd Maste 	}
65*0afa8e06SEd Maste 	if ((map->rk = calloc(n, sizeof(*map->rk))) == NULL) {
66*0afa8e06SEd Maste 		warnx("%s: calloc", __func__);
67*0afa8e06SEd Maste 		goto out;
68*0afa8e06SEd Maste 	}
69*0afa8e06SEd Maste 	for (size_t i = 0; i < n; i++) {
70*0afa8e06SEd Maste 		if ((rp_id = fido_credman_rp_id(map->rp, i)) == NULL) {
71*0afa8e06SEd Maste 			warnx("%s: fido_credman_rp_id %zu", __func__, i);
72*0afa8e06SEd Maste 			goto out;
73*0afa8e06SEd Maste 		}
74*0afa8e06SEd Maste 		if ((map->rk[i] = fido_credman_rk_new()) == NULL) {
75*0afa8e06SEd Maste 			warnx("%s: fido_credman_rk_new", __func__);
76*0afa8e06SEd Maste 			goto out;
77*0afa8e06SEd Maste 		}
78*0afa8e06SEd Maste 		if ((r = fido_credman_get_dev_rk(dev, rp_id, map->rk[i],
79*0afa8e06SEd Maste 		    pin)) != FIDO_OK) {
80*0afa8e06SEd Maste 			warnx("%s: fido_credman_get_dev_rk %s: %s", __func__,
81*0afa8e06SEd Maste 			    rp_id, fido_strerr(r));
82*0afa8e06SEd Maste 			goto out;
83*0afa8e06SEd Maste 		}
84*0afa8e06SEd Maste 	}
85*0afa8e06SEd Maste 
86*0afa8e06SEd Maste 	ok = 0;
87*0afa8e06SEd Maste out:
88*0afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
89*0afa8e06SEd Maste 
90*0afa8e06SEd Maste 	return ok;
91*0afa8e06SEd Maste }
92*0afa8e06SEd Maste 
93*0afa8e06SEd Maste static int
94*0afa8e06SEd Maste lookup_key(const char *path, fido_dev_t *dev, const char *rp_id,
95*0afa8e06SEd Maste     const struct blob *cred_id, char **pin, struct blob *key)
96*0afa8e06SEd Maste {
97*0afa8e06SEd Maste 	fido_credman_rk_t *rk = NULL;
98*0afa8e06SEd Maste 	const fido_cred_t *cred = NULL;
99*0afa8e06SEd Maste 	size_t i, n;
100*0afa8e06SEd Maste 	int r, ok = -1;
101*0afa8e06SEd Maste 
102*0afa8e06SEd Maste 	if ((rk = fido_credman_rk_new()) == NULL) {
103*0afa8e06SEd Maste 		warnx("%s: fido_credman_rk_new", __func__);
104*0afa8e06SEd Maste 		goto out;
105*0afa8e06SEd Maste 	}
106*0afa8e06SEd Maste 	if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin)) != FIDO_OK &&
107*0afa8e06SEd Maste 	    *pin == NULL && should_retry_with_pin(dev, r)) {
108*0afa8e06SEd Maste 		if ((*pin = get_pin(path)) == NULL)
109*0afa8e06SEd Maste 			goto out;
110*0afa8e06SEd Maste 		r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin);
111*0afa8e06SEd Maste 	}
112*0afa8e06SEd Maste 	if (r != FIDO_OK) {
113*0afa8e06SEd Maste 		warnx("%s: fido_credman_get_dev_rk: %s", __func__,
114*0afa8e06SEd Maste 		    fido_strerr(r));
115*0afa8e06SEd Maste 		goto out;
116*0afa8e06SEd Maste 	}
117*0afa8e06SEd Maste 	if ((n = fido_credman_rk_count(rk)) == 0) {
118*0afa8e06SEd Maste 		warnx("%s: rp id not found", __func__);
119*0afa8e06SEd Maste 		goto out;
120*0afa8e06SEd Maste 	}
121*0afa8e06SEd Maste 	if (n == 1 && cred_id->len == 0) {
122*0afa8e06SEd Maste 		/* use the credential we found */
123*0afa8e06SEd Maste 		cred = fido_credman_rk(rk, 0);
124*0afa8e06SEd Maste 	} else {
125*0afa8e06SEd Maste 		if (cred_id->len == 0) {
126*0afa8e06SEd Maste 			warnx("%s: multiple credentials found", __func__);
127*0afa8e06SEd Maste 			goto out;
128*0afa8e06SEd Maste 		}
129*0afa8e06SEd Maste 		for (i = 0; i < n; i++) {
130*0afa8e06SEd Maste 			const fido_cred_t *x = fido_credman_rk(rk, i);
131*0afa8e06SEd Maste 			if (fido_cred_id_len(x) <= cred_id->len &&
132*0afa8e06SEd Maste 			    !memcmp(fido_cred_id_ptr(x), cred_id->ptr,
133*0afa8e06SEd Maste 			    fido_cred_id_len(x))) {
134*0afa8e06SEd Maste 				cred = x;
135*0afa8e06SEd Maste 				break;
136*0afa8e06SEd Maste 			}
137*0afa8e06SEd Maste 		}
138*0afa8e06SEd Maste 	}
139*0afa8e06SEd Maste 	if (cred == NULL) {
140*0afa8e06SEd Maste 		warnx("%s: credential not found", __func__);
141*0afa8e06SEd Maste 		goto out;
142*0afa8e06SEd Maste 	}
143*0afa8e06SEd Maste 	if (fido_cred_largeblob_key_ptr(cred) == NULL) {
144*0afa8e06SEd Maste 		warnx("%s: no associated blob key", __func__);
145*0afa8e06SEd Maste 		goto out;
146*0afa8e06SEd Maste 	}
147*0afa8e06SEd Maste 	key->len = fido_cred_largeblob_key_len(cred);
148*0afa8e06SEd Maste 	if ((key->ptr = malloc(key->len)) == NULL) {
149*0afa8e06SEd Maste 		warnx("%s: malloc", __func__);
150*0afa8e06SEd Maste 		goto out;
151*0afa8e06SEd Maste 	}
152*0afa8e06SEd Maste 	memcpy(key->ptr, fido_cred_largeblob_key_ptr(cred), key->len);
153*0afa8e06SEd Maste 
154*0afa8e06SEd Maste 	ok = 0;
155*0afa8e06SEd Maste out:
156*0afa8e06SEd Maste 	fido_credman_rk_free(&rk);
157*0afa8e06SEd Maste 
158*0afa8e06SEd Maste 	return ok;
159*0afa8e06SEd Maste }
160*0afa8e06SEd Maste 
161*0afa8e06SEd Maste static int
162*0afa8e06SEd Maste load_key(const char *keyf, const char *cred_id64, const char *rp_id,
163*0afa8e06SEd Maste     const char *path, fido_dev_t *dev, char **pin, struct blob *key)
164*0afa8e06SEd Maste {
165*0afa8e06SEd Maste 	struct blob cred_id;
166*0afa8e06SEd Maste 	FILE *fp;
167*0afa8e06SEd Maste 	int r;
168*0afa8e06SEd Maste 
169*0afa8e06SEd Maste 	memset(&cred_id, 0, sizeof(cred_id));
170*0afa8e06SEd Maste 
171*0afa8e06SEd Maste 	if (keyf != NULL) {
172*0afa8e06SEd Maste 		if (rp_id != NULL || cred_id64 != NULL)
173*0afa8e06SEd Maste 			usage();
174*0afa8e06SEd Maste 		fp = open_read(keyf);
175*0afa8e06SEd Maste 		if ((r = base64_read(fp, key)) < 0)
176*0afa8e06SEd Maste 			warnx("%s: base64_read %s", __func__, keyf);
177*0afa8e06SEd Maste 		fclose(fp);
178*0afa8e06SEd Maste 		return r;
179*0afa8e06SEd Maste 	}
180*0afa8e06SEd Maste 	if (rp_id == NULL)
181*0afa8e06SEd Maste 		usage();
182*0afa8e06SEd Maste 	if (cred_id64 != NULL && base64_decode(cred_id64, (void *)&cred_id.ptr,
183*0afa8e06SEd Maste 	    &cred_id.len) < 0) {
184*0afa8e06SEd Maste 		warnx("%s: base64_decode %s", __func__, cred_id64);
185*0afa8e06SEd Maste 		return -1;
186*0afa8e06SEd Maste 	}
187*0afa8e06SEd Maste 	r = lookup_key(path, dev, rp_id, &cred_id, pin, key);
188*0afa8e06SEd Maste 	free(cred_id.ptr);
189*0afa8e06SEd Maste 
190*0afa8e06SEd Maste 	return r;
191*0afa8e06SEd Maste }
192*0afa8e06SEd Maste 
193*0afa8e06SEd Maste int
194*0afa8e06SEd Maste blob_set(const char *path, const char *keyf, const char *rp_id,
195*0afa8e06SEd Maste     const char *cred_id64, const char *blobf)
196*0afa8e06SEd Maste {
197*0afa8e06SEd Maste 	fido_dev_t *dev;
198*0afa8e06SEd Maste 	struct blob key, blob;
199*0afa8e06SEd Maste 	char *pin = NULL;
200*0afa8e06SEd Maste 	int r, ok = 1;
201*0afa8e06SEd Maste 
202*0afa8e06SEd Maste 	dev = open_dev(path);
203*0afa8e06SEd Maste 	memset(&key, 0, sizeof(key));
204*0afa8e06SEd Maste 	memset(&blob, 0, sizeof(blob));
205*0afa8e06SEd Maste 
206*0afa8e06SEd Maste 	if (read_file(blobf, &blob.ptr, &blob.len) < 0 ||
207*0afa8e06SEd Maste 	    load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
208*0afa8e06SEd Maste 		goto out;
209*0afa8e06SEd Maste 	if ((r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
210*0afa8e06SEd Maste 	    blob.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
211*0afa8e06SEd Maste 		if ((pin = get_pin(path)) == NULL)
212*0afa8e06SEd Maste 			goto out;
213*0afa8e06SEd Maste 		r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
214*0afa8e06SEd Maste 		    blob.len, pin);
215*0afa8e06SEd Maste 	}
216*0afa8e06SEd Maste 	if (r != FIDO_OK) {
217*0afa8e06SEd Maste 		warnx("fido_dev_largeblob_set: %s", fido_strerr(r));
218*0afa8e06SEd Maste 		goto out;
219*0afa8e06SEd Maste 	}
220*0afa8e06SEd Maste 
221*0afa8e06SEd Maste 	ok = 0; /* success */
222*0afa8e06SEd Maste out:
223*0afa8e06SEd Maste 	freezero(key.ptr, key.len);
224*0afa8e06SEd Maste 	freezero(blob.ptr, blob.len);
225*0afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
226*0afa8e06SEd Maste 
227*0afa8e06SEd Maste 	fido_dev_close(dev);
228*0afa8e06SEd Maste 	fido_dev_free(&dev);
229*0afa8e06SEd Maste 
230*0afa8e06SEd Maste 	exit(ok);
231*0afa8e06SEd Maste }
232*0afa8e06SEd Maste 
233*0afa8e06SEd Maste int
234*0afa8e06SEd Maste blob_get(const char *path, const char *keyf, const char *rp_id,
235*0afa8e06SEd Maste     const char *cred_id64, const char *blobf)
236*0afa8e06SEd Maste {
237*0afa8e06SEd Maste 	fido_dev_t *dev;
238*0afa8e06SEd Maste 	struct blob key, blob;
239*0afa8e06SEd Maste 	char *pin = NULL;
240*0afa8e06SEd Maste 	int r, ok = 1;
241*0afa8e06SEd Maste 
242*0afa8e06SEd Maste 	dev = open_dev(path);
243*0afa8e06SEd Maste 	memset(&key, 0, sizeof(key));
244*0afa8e06SEd Maste 	memset(&blob, 0, sizeof(blob));
245*0afa8e06SEd Maste 
246*0afa8e06SEd Maste 	if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
247*0afa8e06SEd Maste 		goto out;
248*0afa8e06SEd Maste 	if ((r = fido_dev_largeblob_get(dev, key.ptr, key.len, &blob.ptr,
249*0afa8e06SEd Maste 	    &blob.len)) != FIDO_OK) {
250*0afa8e06SEd Maste 		warnx("fido_dev_largeblob_get: %s", fido_strerr(r));
251*0afa8e06SEd Maste 		goto out;
252*0afa8e06SEd Maste 	}
253*0afa8e06SEd Maste 	if (write_file(blobf, blob.ptr, blob.len) < 0)
254*0afa8e06SEd Maste 		goto out;
255*0afa8e06SEd Maste 
256*0afa8e06SEd Maste 	ok = 0; /* success */
257*0afa8e06SEd Maste out:
258*0afa8e06SEd Maste 	freezero(key.ptr, key.len);
259*0afa8e06SEd Maste 	freezero(blob.ptr, blob.len);
260*0afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
261*0afa8e06SEd Maste 
262*0afa8e06SEd Maste 	fido_dev_close(dev);
263*0afa8e06SEd Maste 	fido_dev_free(&dev);
264*0afa8e06SEd Maste 
265*0afa8e06SEd Maste 	exit(ok);
266*0afa8e06SEd Maste }
267*0afa8e06SEd Maste 
268*0afa8e06SEd Maste int
269*0afa8e06SEd Maste blob_delete(const char *path, const char *keyf, const char *rp_id,
270*0afa8e06SEd Maste     const char *cred_id64)
271*0afa8e06SEd Maste {
272*0afa8e06SEd Maste 	fido_dev_t *dev;
273*0afa8e06SEd Maste 	struct blob key;
274*0afa8e06SEd Maste 	char *pin = NULL;
275*0afa8e06SEd Maste 	int r, ok = 1;
276*0afa8e06SEd Maste 
277*0afa8e06SEd Maste 	dev = open_dev(path);
278*0afa8e06SEd Maste 	memset(&key, 0, sizeof(key));
279*0afa8e06SEd Maste 
280*0afa8e06SEd Maste 	if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
281*0afa8e06SEd Maste 		goto out;
282*0afa8e06SEd Maste 	if ((r = fido_dev_largeblob_remove(dev, key.ptr, key.len,
283*0afa8e06SEd Maste 	    pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
284*0afa8e06SEd Maste 		if ((pin = get_pin(path)) == NULL)
285*0afa8e06SEd Maste 			goto out;
286*0afa8e06SEd Maste 		r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin);
287*0afa8e06SEd Maste 	}
288*0afa8e06SEd Maste 	if (r != FIDO_OK) {
289*0afa8e06SEd Maste 		warnx("fido_dev_largeblob_remove: %s", fido_strerr(r));
290*0afa8e06SEd Maste 		goto out;
291*0afa8e06SEd Maste 	}
292*0afa8e06SEd Maste 
293*0afa8e06SEd Maste 	ok = 0; /* success */
294*0afa8e06SEd Maste out:
295*0afa8e06SEd Maste 	freezero(key.ptr, key.len);
296*0afa8e06SEd Maste 	freezero(pin, PINBUF_LEN);
297*0afa8e06SEd Maste 
298*0afa8e06SEd Maste 	fido_dev_close(dev);
299*0afa8e06SEd Maste 	fido_dev_free(&dev);
300*0afa8e06SEd Maste 
301*0afa8e06SEd Maste 	exit(ok);
302*0afa8e06SEd Maste }
303*0afa8e06SEd Maste 
304*0afa8e06SEd Maste static int
305*0afa8e06SEd Maste decompress(const struct blob *plaintext, uint64_t origsiz)
306*0afa8e06SEd Maste {
307*0afa8e06SEd Maste 	struct blob inflated;
308*0afa8e06SEd Maste 	u_long ilen, plen;
309*0afa8e06SEd Maste 	int ok = -1;
310*0afa8e06SEd Maste 
311*0afa8e06SEd Maste 	memset(&inflated, 0, sizeof(inflated));
312*0afa8e06SEd Maste 
313*0afa8e06SEd Maste 	if (plaintext->len > ULONG_MAX)
314*0afa8e06SEd Maste 		return -1;
315*0afa8e06SEd Maste 	if (origsiz > ULONG_MAX || origsiz > SIZE_MAX)
316*0afa8e06SEd Maste 		return -1;
317*0afa8e06SEd Maste 	plen = (u_long)plaintext->len;
318*0afa8e06SEd Maste 	ilen = (u_long)origsiz;
319*0afa8e06SEd Maste 	inflated.len = (size_t)origsiz;
320*0afa8e06SEd Maste 	if ((inflated.ptr = calloc(1, inflated.len)) == NULL)
321*0afa8e06SEd Maste 		return -1;
322*0afa8e06SEd Maste 	if (uncompress(inflated.ptr, &ilen, plaintext->ptr, plen) != Z_OK ||
323*0afa8e06SEd Maste 	    ilen > SIZE_MAX || (size_t)ilen != (size_t)origsiz)
324*0afa8e06SEd Maste 		goto out;
325*0afa8e06SEd Maste 
326*0afa8e06SEd Maste 	ok = 0; /* success */
327*0afa8e06SEd Maste out:
328*0afa8e06SEd Maste 	freezero(inflated.ptr, inflated.len);
329*0afa8e06SEd Maste 
330*0afa8e06SEd Maste 	return ok;
331*0afa8e06SEd Maste }
332*0afa8e06SEd Maste 
333*0afa8e06SEd Maste static int
334*0afa8e06SEd Maste decode(const struct blob *ciphertext, const struct blob *nonce,
335*0afa8e06SEd Maste     uint64_t origsiz, const fido_cred_t *cred)
336*0afa8e06SEd Maste {
337*0afa8e06SEd Maste 	uint8_t aad[4 + sizeof(uint64_t)];
338*0afa8e06SEd Maste 	EVP_CIPHER_CTX *ctx = NULL;
339*0afa8e06SEd Maste 	const EVP_CIPHER *cipher;
340*0afa8e06SEd Maste 	struct blob plaintext;
341*0afa8e06SEd Maste 	uint64_t tmp;
342*0afa8e06SEd Maste 	int ok = -1;
343*0afa8e06SEd Maste 
344*0afa8e06SEd Maste 	memset(&plaintext, 0, sizeof(plaintext));
345*0afa8e06SEd Maste 
346*0afa8e06SEd Maste 	if (nonce->len != 12)
347*0afa8e06SEd Maste 		return -1;
348*0afa8e06SEd Maste 	if (cred == NULL ||
349*0afa8e06SEd Maste 	    fido_cred_largeblob_key_ptr(cred) == NULL ||
350*0afa8e06SEd Maste 	    fido_cred_largeblob_key_len(cred) != 32)
351*0afa8e06SEd Maste 		return -1;
352*0afa8e06SEd Maste 	if (ciphertext->len > UINT_MAX ||
353*0afa8e06SEd Maste 	    ciphertext->len > SIZE_MAX - 16 ||
354*0afa8e06SEd Maste 	    ciphertext->len < 16)
355*0afa8e06SEd Maste 		return -1;
356*0afa8e06SEd Maste 	plaintext.len = ciphertext->len - 16;
357*0afa8e06SEd Maste 	if ((plaintext.ptr = calloc(1, plaintext.len)) == NULL)
358*0afa8e06SEd Maste 		return -1;
359*0afa8e06SEd Maste 	if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
360*0afa8e06SEd Maste 	    (cipher = EVP_aes_256_gcm()) == NULL ||
361*0afa8e06SEd Maste 	    EVP_CipherInit(ctx, cipher, fido_cred_largeblob_key_ptr(cred),
362*0afa8e06SEd Maste 	    nonce->ptr, 0) == 0)
363*0afa8e06SEd Maste 		goto out;
364*0afa8e06SEd Maste 	if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
365*0afa8e06SEd Maste 	    ciphertext->ptr + ciphertext->len - 16) == 0)
366*0afa8e06SEd Maste 		goto out;
367*0afa8e06SEd Maste 	aad[0] = 0x62; /* b */
368*0afa8e06SEd Maste 	aad[1] = 0x6c; /* l */
369*0afa8e06SEd Maste 	aad[2] = 0x6f; /* o */
370*0afa8e06SEd Maste 	aad[3] = 0x62; /* b */
371*0afa8e06SEd Maste 	tmp = htole64(origsiz);
372*0afa8e06SEd Maste 	memcpy(&aad[4], &tmp, sizeof(uint64_t));
373*0afa8e06SEd Maste 	if (EVP_Cipher(ctx, NULL, aad, (u_int)sizeof(aad)) < 0 ||
374*0afa8e06SEd Maste 	    EVP_Cipher(ctx, plaintext.ptr, ciphertext->ptr,
375*0afa8e06SEd Maste 	    (u_int)plaintext.len) < 0 ||
376*0afa8e06SEd Maste 	    EVP_Cipher(ctx, NULL, NULL, 0) < 0)
377*0afa8e06SEd Maste 		goto out;
378*0afa8e06SEd Maste 	if (decompress(&plaintext, origsiz) < 0)
379*0afa8e06SEd Maste 		goto out;
380*0afa8e06SEd Maste 
381*0afa8e06SEd Maste 	ok = 0;
382*0afa8e06SEd Maste out:
383*0afa8e06SEd Maste 	freezero(plaintext.ptr, plaintext.len);
384*0afa8e06SEd Maste 
385*0afa8e06SEd Maste 	if (ctx != NULL)
386*0afa8e06SEd Maste 		EVP_CIPHER_CTX_free(ctx);
387*0afa8e06SEd Maste 
388*0afa8e06SEd Maste 	return ok;
389*0afa8e06SEd Maste }
390*0afa8e06SEd Maste 
391*0afa8e06SEd Maste static const fido_cred_t *
392*0afa8e06SEd Maste try_rp(const fido_credman_rk_t *rk, const struct blob *ciphertext,
393*0afa8e06SEd Maste     const struct blob *nonce, uint64_t origsiz)
394*0afa8e06SEd Maste {
395*0afa8e06SEd Maste 	const fido_cred_t *cred;
396*0afa8e06SEd Maste 
397*0afa8e06SEd Maste 	for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
398*0afa8e06SEd Maste 		if ((cred = fido_credman_rk(rk, i)) != NULL &&
399*0afa8e06SEd Maste 		    decode(ciphertext, nonce, origsiz, cred) == 0)
400*0afa8e06SEd Maste 			return cred;
401*0afa8e06SEd Maste 
402*0afa8e06SEd Maste 	return NULL;
403*0afa8e06SEd Maste }
404*0afa8e06SEd Maste 
405*0afa8e06SEd Maste static int
406*0afa8e06SEd Maste decode_cbor_blob(struct blob *out, const cbor_item_t *item)
407*0afa8e06SEd Maste {
408*0afa8e06SEd Maste 	if (out->ptr != NULL ||
409*0afa8e06SEd Maste 	    cbor_isa_bytestring(item) == false ||
410*0afa8e06SEd Maste 	    cbor_bytestring_is_definite(item) == false)
411*0afa8e06SEd Maste 		return -1;
412*0afa8e06SEd Maste 	out->len = cbor_bytestring_length(item);
413*0afa8e06SEd Maste 	if ((out->ptr = malloc(out->len)) == NULL)
414*0afa8e06SEd Maste 		return -1;
415*0afa8e06SEd Maste 	memcpy(out->ptr, cbor_bytestring_handle(item), out->len);
416*0afa8e06SEd Maste 
417*0afa8e06SEd Maste 	return 0;
418*0afa8e06SEd Maste }
419*0afa8e06SEd Maste 
420*0afa8e06SEd Maste static int
421*0afa8e06SEd Maste decode_blob_entry(const cbor_item_t *item, struct blob *ciphertext,
422*0afa8e06SEd Maste     struct blob *nonce, uint64_t *origsiz)
423*0afa8e06SEd Maste {
424*0afa8e06SEd Maste 	struct cbor_pair *v;
425*0afa8e06SEd Maste 
426*0afa8e06SEd Maste 	if (item == NULL)
427*0afa8e06SEd Maste 		return -1;
428*0afa8e06SEd Maste 	if (cbor_isa_map(item) == false ||
429*0afa8e06SEd Maste 	    cbor_map_is_definite(item) == false ||
430*0afa8e06SEd Maste 	    (v = cbor_map_handle(item)) == NULL)
431*0afa8e06SEd Maste 		return -1;
432*0afa8e06SEd Maste 	if (cbor_map_size(item) > UINT8_MAX)
433*0afa8e06SEd Maste 		return -1;
434*0afa8e06SEd Maste 
435*0afa8e06SEd Maste 	for (size_t i = 0; i < cbor_map_size(item); i++) {
436*0afa8e06SEd Maste 		if (cbor_isa_uint(v[i].key) == false ||
437*0afa8e06SEd Maste 		    cbor_int_get_width(v[i].key) != CBOR_INT_8)
438*0afa8e06SEd Maste 			continue; /* ignore */
439*0afa8e06SEd Maste 		switch (cbor_get_uint8(v[i].key)) {
440*0afa8e06SEd Maste 		case 1: /* ciphertext */
441*0afa8e06SEd Maste 			if (decode_cbor_blob(ciphertext, v[i].value) < 0)
442*0afa8e06SEd Maste 				return -1;
443*0afa8e06SEd Maste 			break;
444*0afa8e06SEd Maste 		case 2: /* nonce */
445*0afa8e06SEd Maste 			if (decode_cbor_blob(nonce, v[i].value) < 0)
446*0afa8e06SEd Maste 				return -1;
447*0afa8e06SEd Maste 			break;
448*0afa8e06SEd Maste 		case 3: /* origSize */
449*0afa8e06SEd Maste 			if (*origsiz != 0 ||
450*0afa8e06SEd Maste 			    cbor_isa_uint(v[i].value) == false ||
451*0afa8e06SEd Maste 			    (*origsiz = cbor_get_int(v[i].value)) > SIZE_MAX)
452*0afa8e06SEd Maste 				return -1;
453*0afa8e06SEd Maste 		}
454*0afa8e06SEd Maste 	}
455*0afa8e06SEd Maste 	if (ciphertext->ptr == NULL || nonce->ptr == NULL || *origsiz == 0)
456*0afa8e06SEd Maste 		return -1;
457*0afa8e06SEd Maste 
458*0afa8e06SEd Maste 	return 0;
459*0afa8e06SEd Maste }
460*0afa8e06SEd Maste 
461*0afa8e06SEd Maste static void
462*0afa8e06SEd Maste print_blob_entry(size_t idx, const cbor_item_t *item, const struct rkmap *map)
463*0afa8e06SEd Maste {
464*0afa8e06SEd Maste 	struct blob ciphertext, nonce;
465*0afa8e06SEd Maste 	const fido_cred_t *cred = NULL;
466*0afa8e06SEd Maste 	const char *rp_id = NULL;
467*0afa8e06SEd Maste 	char *cred_id = NULL;
468*0afa8e06SEd Maste 	uint64_t origsiz = 0;
469*0afa8e06SEd Maste 
470*0afa8e06SEd Maste 	memset(&ciphertext, 0, sizeof(ciphertext));
471*0afa8e06SEd Maste 	memset(&nonce, 0, sizeof(nonce));
472*0afa8e06SEd Maste 
473*0afa8e06SEd Maste 	if (decode_blob_entry(item, &ciphertext, &nonce, &origsiz) < 0) {
474*0afa8e06SEd Maste 		printf("%02zu: <skipped: bad cbor>\n", idx);
475*0afa8e06SEd Maste 		goto out;
476*0afa8e06SEd Maste 	}
477*0afa8e06SEd Maste 	for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) {
478*0afa8e06SEd Maste 		if ((cred = try_rp(map->rk[i], &ciphertext, &nonce,
479*0afa8e06SEd Maste 		    origsiz)) != NULL) {
480*0afa8e06SEd Maste 			rp_id = fido_credman_rp_id(map->rp, i);
481*0afa8e06SEd Maste 			break;
482*0afa8e06SEd Maste 		}
483*0afa8e06SEd Maste 	}
484*0afa8e06SEd Maste 	if (cred == NULL) {
485*0afa8e06SEd Maste 		if ((cred_id = strdup("<unknown>")) == NULL) {
486*0afa8e06SEd Maste 			printf("%02zu: <skipped: strdup failed>\n", idx);
487*0afa8e06SEd Maste 			goto out;
488*0afa8e06SEd Maste 		}
489*0afa8e06SEd Maste 	} else {
490*0afa8e06SEd Maste 		if (base64_encode(fido_cred_id_ptr(cred),
491*0afa8e06SEd Maste 		    fido_cred_id_len(cred), &cred_id) < 0) {
492*0afa8e06SEd Maste 			printf("%02zu: <skipped: base64_encode failed>\n", idx);
493*0afa8e06SEd Maste 			goto out;
494*0afa8e06SEd Maste 		}
495*0afa8e06SEd Maste 	}
496*0afa8e06SEd Maste 	if (rp_id == NULL)
497*0afa8e06SEd Maste 		rp_id = "<unknown>";
498*0afa8e06SEd Maste 
499*0afa8e06SEd Maste 	printf("%02zu: %4zu %4zu %s %s\n", idx, ciphertext.len,
500*0afa8e06SEd Maste 	    (size_t)origsiz, cred_id, rp_id);
501*0afa8e06SEd Maste out:
502*0afa8e06SEd Maste 	free(ciphertext.ptr);
503*0afa8e06SEd Maste 	free(nonce.ptr);
504*0afa8e06SEd Maste 	free(cred_id);
505*0afa8e06SEd Maste }
506*0afa8e06SEd Maste 
507*0afa8e06SEd Maste static cbor_item_t *
508*0afa8e06SEd Maste get_cbor_array(fido_dev_t *dev)
509*0afa8e06SEd Maste {
510*0afa8e06SEd Maste 	struct cbor_load_result cbor_result;
511*0afa8e06SEd Maste 	cbor_item_t *item = NULL;
512*0afa8e06SEd Maste 	u_char *cbor_ptr = NULL;
513*0afa8e06SEd Maste 	size_t cbor_len;
514*0afa8e06SEd Maste 	int r, ok = -1;
515*0afa8e06SEd Maste 
516*0afa8e06SEd Maste 	if ((r = fido_dev_largeblob_get_array(dev, &cbor_ptr,
517*0afa8e06SEd Maste 	    &cbor_len)) != FIDO_OK) {
518*0afa8e06SEd Maste 		warnx("%s: fido_dev_largeblob_get_array: %s", __func__,
519*0afa8e06SEd Maste 		    fido_strerr(r));
520*0afa8e06SEd Maste 		goto out;
521*0afa8e06SEd Maste 	}
522*0afa8e06SEd Maste 	if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
523*0afa8e06SEd Maste 		warnx("%s: cbor_load", __func__);
524*0afa8e06SEd Maste 		goto out;
525*0afa8e06SEd Maste 	}
526*0afa8e06SEd Maste 	if (cbor_result.read != cbor_len) {
527*0afa8e06SEd Maste 		warnx("%s: cbor_result.read (%zu) != cbor_len (%zu)", __func__,
528*0afa8e06SEd Maste 		    cbor_result.read, cbor_len);
529*0afa8e06SEd Maste 		/* continue */
530*0afa8e06SEd Maste 	}
531*0afa8e06SEd Maste 	if (cbor_isa_array(item) == false ||
532*0afa8e06SEd Maste 	    cbor_array_is_definite(item) == false) {
533*0afa8e06SEd Maste 		warnx("%s: cbor type", __func__);
534*0afa8e06SEd Maste 		goto out;
535*0afa8e06SEd Maste 	}
536*0afa8e06SEd Maste 	if (cbor_array_size(item) > UINT8_MAX) {
537*0afa8e06SEd Maste 		warnx("%s: cbor_array_size > UINT8_MAX", __func__);
538*0afa8e06SEd Maste 		goto out;
539*0afa8e06SEd Maste 	}
540*0afa8e06SEd Maste 	if (cbor_array_size(item) == 0) {
541*0afa8e06SEd Maste 		ok = 0; /* nothing to do */
542*0afa8e06SEd Maste 		goto out;
543*0afa8e06SEd Maste 	}
544*0afa8e06SEd Maste 
545*0afa8e06SEd Maste 	printf("total map size: %zu byte%s\n", cbor_len, plural(cbor_len));
546*0afa8e06SEd Maste 
547*0afa8e06SEd Maste 	ok = 0;
548*0afa8e06SEd Maste out:
549*0afa8e06SEd Maste 	if (ok < 0 && item != NULL) {
550*0afa8e06SEd Maste 		cbor_decref(&item);
551*0afa8e06SEd Maste 		item = NULL;
552*0afa8e06SEd Maste 	}
553*0afa8e06SEd Maste 	free(cbor_ptr);
554*0afa8e06SEd Maste 
555*0afa8e06SEd Maste 	return item;
556*0afa8e06SEd Maste }
557*0afa8e06SEd Maste 
558*0afa8e06SEd Maste int
559*0afa8e06SEd Maste blob_list(const char *path)
560*0afa8e06SEd Maste {
561*0afa8e06SEd Maste 	struct rkmap map;
562*0afa8e06SEd Maste 	fido_dev_t *dev = NULL;
563*0afa8e06SEd Maste 	cbor_item_t *item = NULL, **v;
564*0afa8e06SEd Maste 	int ok = 1;
565*0afa8e06SEd Maste 
566*0afa8e06SEd Maste 	memset(&map, 0, sizeof(map));
567*0afa8e06SEd Maste 	dev = open_dev(path);
568*0afa8e06SEd Maste 	if (map_known_rps(dev, path, &map) < 0 ||
569*0afa8e06SEd Maste 	    (item = get_cbor_array(dev)) == NULL)
570*0afa8e06SEd Maste 		goto out;
571*0afa8e06SEd Maste 	if (cbor_array_size(item) == 0) {
572*0afa8e06SEd Maste 		ok = 0; /* nothing to do */
573*0afa8e06SEd Maste 		goto out;
574*0afa8e06SEd Maste 	}
575*0afa8e06SEd Maste 	if ((v = cbor_array_handle(item)) == NULL) {
576*0afa8e06SEd Maste 		warnx("%s: cbor_array_handle", __func__);
577*0afa8e06SEd Maste 		goto out;
578*0afa8e06SEd Maste 	}
579*0afa8e06SEd Maste 	for (size_t i = 0; i < cbor_array_size(item); i++)
580*0afa8e06SEd Maste 		print_blob_entry(i, v[i], &map);
581*0afa8e06SEd Maste 
582*0afa8e06SEd Maste 	ok = 0; /* success */
583*0afa8e06SEd Maste out:
584*0afa8e06SEd Maste 	free_rkmap(&map);
585*0afa8e06SEd Maste 
586*0afa8e06SEd Maste 	if (item != NULL)
587*0afa8e06SEd Maste 		cbor_decref(&item);
588*0afa8e06SEd Maste 
589*0afa8e06SEd Maste 	fido_dev_close(dev);
590*0afa8e06SEd Maste 	fido_dev_free(&dev);
591*0afa8e06SEd Maste 
592*0afa8e06SEd Maste 	exit(ok);
593*0afa8e06SEd Maste }
594