xref: /netbsd-src/external/bsd/libfido2/dist/tools/assert_get.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
1ba9bdd8bSchristos /*
2ba9bdd8bSchristos  * Copyright (c) 2018 Yubico AB. All rights reserved.
3ba9bdd8bSchristos  * Use of this source code is governed by a BSD-style
4ba9bdd8bSchristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
6ba9bdd8bSchristos  */
7ba9bdd8bSchristos 
8ba9bdd8bSchristos #include <fido.h>
9ba9bdd8bSchristos #include <stdio.h>
10ba9bdd8bSchristos #include <stdlib.h>
11ba9bdd8bSchristos #include <string.h>
12ba9bdd8bSchristos #ifdef HAVE_UNISTD_H
13ba9bdd8bSchristos #include <unistd.h>
14ba9bdd8bSchristos #endif
15ba9bdd8bSchristos 
16ba9bdd8bSchristos #include "../openbsd-compat/openbsd-compat.h"
17ba9bdd8bSchristos #include "extern.h"
18ba9bdd8bSchristos 
191fc1e710Schristos struct toggle {
201fc1e710Schristos 	fido_opt_t up;
211fc1e710Schristos 	fido_opt_t uv;
221fc1e710Schristos 	fido_opt_t pin;
231fc1e710Schristos };
241fc1e710Schristos 
251fc1e710Schristos static const char *
opt2str(fido_opt_t v)261fc1e710Schristos opt2str(fido_opt_t v)
271fc1e710Schristos {
281fc1e710Schristos 	switch (v) {
291fc1e710Schristos 	case FIDO_OPT_OMIT:
301fc1e710Schristos 		return "omit";
311fc1e710Schristos 	case FIDO_OPT_TRUE:
321fc1e710Schristos 		return "true";
331fc1e710Schristos 	case FIDO_OPT_FALSE:
341fc1e710Schristos 		return "false";
351fc1e710Schristos 	default:
361fc1e710Schristos 		return "unknown";
371fc1e710Schristos 	}
381fc1e710Schristos }
391fc1e710Schristos 
401fc1e710Schristos static void
parse_toggle(const char * str,struct toggle * opt)411fc1e710Schristos parse_toggle(const char *str, struct toggle *opt)
421fc1e710Schristos {
431fc1e710Schristos 	fido_opt_t *k;
441fc1e710Schristos 	fido_opt_t  v;
451fc1e710Schristos 	char *assignment;
461fc1e710Schristos 	char *key;
471fc1e710Schristos 	char *val;
481fc1e710Schristos 
491fc1e710Schristos 	if ((assignment = strdup(str)) == NULL)
501fc1e710Schristos 		err(1, "strdup");
511fc1e710Schristos 	if ((val = strchr(assignment, '=')) == NULL)
521fc1e710Schristos 		errx(1, "invalid assignment '%s'", assignment);
531fc1e710Schristos 
541fc1e710Schristos 	key = assignment;
551fc1e710Schristos 	*val++ = '\0';
561fc1e710Schristos 
571fc1e710Schristos 	if (!strcmp(val, "true"))
581fc1e710Schristos 		v = FIDO_OPT_TRUE;
591fc1e710Schristos 	else if (!strcmp(val, "false"))
601fc1e710Schristos 		v = FIDO_OPT_FALSE;
611fc1e710Schristos 	else
621fc1e710Schristos 		errx(1, "unknown value '%s'", val);
631fc1e710Schristos 
641fc1e710Schristos 	if (!strcmp(key, "up"))
651fc1e710Schristos 		k = &opt->up;
661fc1e710Schristos 	else if (!strcmp(key, "uv"))
671fc1e710Schristos 		k = &opt->uv;
681fc1e710Schristos 	else if (!strcmp(key, "pin"))
691fc1e710Schristos 		k = &opt->pin;
701fc1e710Schristos 	else
711fc1e710Schristos 		errx(1, "unknown key '%s'", key);
721fc1e710Schristos 
731fc1e710Schristos 	free(assignment);
741fc1e710Schristos 
751fc1e710Schristos 	*k = v;
761fc1e710Schristos }
771fc1e710Schristos 
78ba9bdd8bSchristos static fido_assert_t *
prepare_assert(FILE * in_f,int flags,const struct toggle * opt)791fc1e710Schristos prepare_assert(FILE *in_f, int flags, const struct toggle *opt)
80ba9bdd8bSchristos {
81ba9bdd8bSchristos 	fido_assert_t *assert = NULL;
82ba9bdd8bSchristos 	struct blob cdh;
83ba9bdd8bSchristos 	struct blob id;
84ba9bdd8bSchristos 	struct blob hmac_salt;
85ba9bdd8bSchristos 	char *rpid = NULL;
86ba9bdd8bSchristos 	int r;
87ba9bdd8bSchristos 
88ba9bdd8bSchristos 	memset(&cdh, 0, sizeof(cdh));
89ba9bdd8bSchristos 	memset(&id, 0, sizeof(id));
90ba9bdd8bSchristos 	memset(&hmac_salt, 0, sizeof(hmac_salt));
91ba9bdd8bSchristos 
92ba9bdd8bSchristos 	r = base64_read(in_f, &cdh);
93ba9bdd8bSchristos 	r |= string_read(in_f, &rpid);
94ba9bdd8bSchristos 	if ((flags & FLAG_RK) == 0)
95ba9bdd8bSchristos 		r |= base64_read(in_f, &id);
96ba9bdd8bSchristos 	if (flags & FLAG_HMAC)
97ba9bdd8bSchristos 		r |= base64_read(in_f, &hmac_salt);
98ba9bdd8bSchristos 	if (r < 0)
99ba9bdd8bSchristos 		errx(1, "input error");
100ba9bdd8bSchristos 
101ba9bdd8bSchristos 	if (flags & FLAG_DEBUG) {
102ba9bdd8bSchristos 		fprintf(stderr, "client data hash:\n");
103ba9bdd8bSchristos 		xxd(cdh.ptr, cdh.len);
104ba9bdd8bSchristos 		fprintf(stderr, "relying party id: %s\n", rpid);
105ba9bdd8bSchristos 		if ((flags & FLAG_RK) == 0) {
106ba9bdd8bSchristos 			fprintf(stderr, "credential id:\n");
107ba9bdd8bSchristos 			xxd(id.ptr, id.len);
108ba9bdd8bSchristos 		}
1091fc1e710Schristos 		fprintf(stderr, "up=%s\n", opt2str(opt->up));
1101fc1e710Schristos 		fprintf(stderr, "uv=%s\n", opt2str(opt->uv));
1111fc1e710Schristos 		fprintf(stderr, "pin=%s\n", opt2str(opt->pin));
112ba9bdd8bSchristos 	}
113ba9bdd8bSchristos 
114ba9bdd8bSchristos 	if ((assert = fido_assert_new()) == NULL)
115ba9bdd8bSchristos 		errx(1, "fido_assert_new");
116ba9bdd8bSchristos 
117ba9bdd8bSchristos 	if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr,
118ba9bdd8bSchristos 	    cdh.len)) != FIDO_OK ||
119ba9bdd8bSchristos 	    (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK)
120ba9bdd8bSchristos 		errx(1, "fido_assert_set: %s", fido_strerr(r));
1211fc1e710Schristos 	if ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK)
122ba9bdd8bSchristos 		errx(1, "fido_assert_set_up: %s", fido_strerr(r));
1231fc1e710Schristos 	if ((r = fido_assert_set_uv(assert, opt->uv)) != FIDO_OK)
124ba9bdd8bSchristos 		errx(1, "fido_assert_set_uv: %s", fido_strerr(r));
1251fc1e710Schristos 
126ba9bdd8bSchristos 	if (flags & FLAG_HMAC) {
127ba9bdd8bSchristos 		if ((r = fido_assert_set_extensions(assert,
128ba9bdd8bSchristos 		    FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
129ba9bdd8bSchristos 			errx(1, "fido_assert_set_extensions: %s",
130ba9bdd8bSchristos 			    fido_strerr(r));
131ba9bdd8bSchristos 		if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr,
132ba9bdd8bSchristos 		    hmac_salt.len)) != FIDO_OK)
133ba9bdd8bSchristos 			errx(1, "fido_assert_set_hmac_salt: %s",
134ba9bdd8bSchristos 			    fido_strerr(r));
135ba9bdd8bSchristos 	}
13695dbdf32Schristos 	if (flags & FLAG_LARGEBLOB) {
13795dbdf32Schristos 		if ((r = fido_assert_set_extensions(assert,
13895dbdf32Schristos 		    FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK)
13995dbdf32Schristos 			errx(1, "fido_assert_set_extensions: %s", fido_strerr(r));
14095dbdf32Schristos 	}
141ba9bdd8bSchristos 	if ((flags & FLAG_RK) == 0) {
142ba9bdd8bSchristos 		if ((r = fido_assert_allow_cred(assert, id.ptr,
143ba9bdd8bSchristos 		    id.len)) != FIDO_OK)
144ba9bdd8bSchristos 			errx(1, "fido_assert_allow_cred: %s", fido_strerr(r));
145ba9bdd8bSchristos 	}
146ba9bdd8bSchristos 
147ba9bdd8bSchristos 	free(hmac_salt.ptr);
148ba9bdd8bSchristos 	free(cdh.ptr);
149ba9bdd8bSchristos 	free(id.ptr);
150ba9bdd8bSchristos 	free(rpid);
151ba9bdd8bSchristos 
152ba9bdd8bSchristos 	return (assert);
153ba9bdd8bSchristos }
154ba9bdd8bSchristos 
155ba9bdd8bSchristos static void
print_assert(FILE * out_f,const fido_assert_t * assert,size_t idx,int flags)156ba9bdd8bSchristos print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags)
157ba9bdd8bSchristos {
158ba9bdd8bSchristos 	char *cdh = NULL;
159ba9bdd8bSchristos 	char *authdata = NULL;
160ba9bdd8bSchristos 	char *sig = NULL;
161ba9bdd8bSchristos 	char *user_id = NULL;
162ba9bdd8bSchristos 	char *hmac_secret = NULL;
16395dbdf32Schristos 	char *key = NULL;
164ba9bdd8bSchristos 	int r;
165ba9bdd8bSchristos 
166ba9bdd8bSchristos 	r = base64_encode(fido_assert_clientdata_hash_ptr(assert),
167ba9bdd8bSchristos 	    fido_assert_clientdata_hash_len(assert), &cdh);
168ba9bdd8bSchristos 	r |= base64_encode(fido_assert_authdata_ptr(assert, idx),
169ba9bdd8bSchristos 	    fido_assert_authdata_len(assert, 0), &authdata);
170ba9bdd8bSchristos 	r |= base64_encode(fido_assert_sig_ptr(assert, idx),
171ba9bdd8bSchristos 	    fido_assert_sig_len(assert, idx), &sig);
172ba9bdd8bSchristos 	if (flags & FLAG_RK)
173ba9bdd8bSchristos 		r |= base64_encode(fido_assert_user_id_ptr(assert, idx),
174ba9bdd8bSchristos 		    fido_assert_user_id_len(assert, idx), &user_id);
175ba9bdd8bSchristos 	if (flags & FLAG_HMAC)
176ba9bdd8bSchristos 		r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx),
177ba9bdd8bSchristos 		    fido_assert_hmac_secret_len(assert, idx), &hmac_secret);
17895dbdf32Schristos 	if (flags & FLAG_LARGEBLOB)
17995dbdf32Schristos 		r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx),
18095dbdf32Schristos 		    fido_assert_largeblob_key_len(assert, idx), &key);
181ba9bdd8bSchristos 	if (r < 0)
182ba9bdd8bSchristos 		errx(1, "output error");
183ba9bdd8bSchristos 
184ba9bdd8bSchristos 	fprintf(out_f, "%s\n", cdh);
185ba9bdd8bSchristos 	fprintf(out_f, "%s\n", fido_assert_rp_id(assert));
186ba9bdd8bSchristos 	fprintf(out_f, "%s\n", authdata);
187ba9bdd8bSchristos 	fprintf(out_f, "%s\n", sig);
188ba9bdd8bSchristos 	if (flags & FLAG_RK)
189ba9bdd8bSchristos 		fprintf(out_f, "%s\n", user_id);
190ba9bdd8bSchristos 	if (hmac_secret) {
191ba9bdd8bSchristos 		fprintf(out_f, "%s\n", hmac_secret);
192ba9bdd8bSchristos 		explicit_bzero(hmac_secret, strlen(hmac_secret));
193ba9bdd8bSchristos 	}
19495dbdf32Schristos 	if (key) {
19595dbdf32Schristos 		fprintf(out_f, "%s\n", key);
19695dbdf32Schristos 		explicit_bzero(key, strlen(key));
19795dbdf32Schristos 	}
198ba9bdd8bSchristos 
19995dbdf32Schristos 	free(key);
200ba9bdd8bSchristos 	free(hmac_secret);
201ba9bdd8bSchristos 	free(cdh);
202ba9bdd8bSchristos 	free(authdata);
203ba9bdd8bSchristos 	free(sig);
204ba9bdd8bSchristos 	free(user_id);
205ba9bdd8bSchristos }
206ba9bdd8bSchristos 
207ba9bdd8bSchristos int
assert_get(int argc,char ** argv)208ba9bdd8bSchristos assert_get(int argc, char **argv)
209ba9bdd8bSchristos {
210ba9bdd8bSchristos 	fido_dev_t *dev = NULL;
211ba9bdd8bSchristos 	fido_assert_t *assert = NULL;
2121fc1e710Schristos 	struct toggle opt;
213ba9bdd8bSchristos 	char prompt[1024];
214*2d40c451Schristos 	char pin[128];
215ba9bdd8bSchristos 	char *in_path = NULL;
216ba9bdd8bSchristos 	char *out_path = NULL;
217ba9bdd8bSchristos 	FILE *in_f = NULL;
218ba9bdd8bSchristos 	FILE *out_f = NULL;
219ba9bdd8bSchristos 	int flags = 0;
220ba9bdd8bSchristos 	int ch;
221ba9bdd8bSchristos 	int r;
222ba9bdd8bSchristos 
2231fc1e710Schristos 	opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT;
2241fc1e710Schristos 
22595dbdf32Schristos 	while ((ch = getopt(argc, argv, "bdhi:o:prt:uv")) != -1) {
226ba9bdd8bSchristos 		switch (ch) {
22795dbdf32Schristos 		case 'b':
22895dbdf32Schristos 			flags |= FLAG_LARGEBLOB;
22995dbdf32Schristos 			break;
230ba9bdd8bSchristos 		case 'd':
231ba9bdd8bSchristos 			flags |= FLAG_DEBUG;
232ba9bdd8bSchristos 			break;
233ba9bdd8bSchristos 		case 'h':
234ba9bdd8bSchristos 			flags |= FLAG_HMAC;
235ba9bdd8bSchristos 			break;
236ba9bdd8bSchristos 		case 'i':
237ba9bdd8bSchristos 			in_path = optarg;
238ba9bdd8bSchristos 			break;
239ba9bdd8bSchristos 		case 'o':
240ba9bdd8bSchristos 			out_path = optarg;
241ba9bdd8bSchristos 			break;
242ba9bdd8bSchristos 		case 'p':
2431fc1e710Schristos 			opt.up = FIDO_OPT_TRUE;
244ba9bdd8bSchristos 			break;
245ba9bdd8bSchristos 		case 'r':
246ba9bdd8bSchristos 			flags |= FLAG_RK;
247ba9bdd8bSchristos 			break;
2481fc1e710Schristos 		case 't' :
2491fc1e710Schristos 			parse_toggle(optarg, &opt);
2501fc1e710Schristos 			break;
251ba9bdd8bSchristos 		case 'u':
252ba9bdd8bSchristos 			flags |= FLAG_U2F;
253ba9bdd8bSchristos 			break;
254ba9bdd8bSchristos 		case 'v':
2551fc1e710Schristos 			/* -v implies both pin and uv for historical reasons */
2561fc1e710Schristos 			opt.pin = FIDO_OPT_TRUE;
2571fc1e710Schristos 			opt.uv = FIDO_OPT_TRUE;
258ba9bdd8bSchristos 			break;
259ba9bdd8bSchristos 		default:
260ba9bdd8bSchristos 			usage();
261ba9bdd8bSchristos 		}
262ba9bdd8bSchristos 	}
263ba9bdd8bSchristos 
264ba9bdd8bSchristos 	argc -= optind;
265ba9bdd8bSchristos 	argv += optind;
266ba9bdd8bSchristos 
267ba9bdd8bSchristos 	if (argc < 1)
268ba9bdd8bSchristos 		usage();
269ba9bdd8bSchristos 
270ba9bdd8bSchristos 	in_f = open_read(in_path);
271ba9bdd8bSchristos 	out_f = open_write(out_path);
272ba9bdd8bSchristos 
273ba9bdd8bSchristos 	fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
274ba9bdd8bSchristos 
2751fc1e710Schristos 	assert = prepare_assert(in_f, flags, &opt);
276ba9bdd8bSchristos 
277ba9bdd8bSchristos 	dev = open_dev(argv[0]);
278ba9bdd8bSchristos 	if (flags & FLAG_U2F)
279ba9bdd8bSchristos 		fido_dev_force_u2f(dev);
280ba9bdd8bSchristos 
2811fc1e710Schristos 	if (opt.pin == FIDO_OPT_TRUE) {
282ba9bdd8bSchristos 		r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
283ba9bdd8bSchristos 		    argv[0]);
284ba9bdd8bSchristos 		if (r < 0 || (size_t)r >= sizeof(prompt))
285ba9bdd8bSchristos 			errx(1, "snprintf");
286ba9bdd8bSchristos 		if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF))
287ba9bdd8bSchristos 			errx(1, "readpassphrase");
288*2d40c451Schristos 		if (strlen(pin) < 4 || strlen(pin) > 63) {
289*2d40c451Schristos 			explicit_bzero(pin, sizeof(pin));
290*2d40c451Schristos 			errx(1, "invalid PIN length");
291*2d40c451Schristos 		}
292ba9bdd8bSchristos 		r = fido_dev_get_assert(dev, assert, pin);
293ba9bdd8bSchristos 	} else
294ba9bdd8bSchristos 		r = fido_dev_get_assert(dev, assert, NULL);
295ba9bdd8bSchristos 
296ba9bdd8bSchristos 	explicit_bzero(pin, sizeof(pin));
297ba9bdd8bSchristos 
298ba9bdd8bSchristos 	if (r != FIDO_OK)
299ba9bdd8bSchristos 		errx(1, "fido_dev_get_assert: %s", fido_strerr(r));
300ba9bdd8bSchristos 
301ba9bdd8bSchristos 	if (flags & FLAG_RK) {
302ba9bdd8bSchristos 		for (size_t idx = 0; idx < fido_assert_count(assert); idx++)
303ba9bdd8bSchristos 			print_assert(out_f, assert, idx, flags);
304ba9bdd8bSchristos 	} else {
305ba9bdd8bSchristos 		if (fido_assert_count(assert) != 1)
306ba9bdd8bSchristos 			errx(1, "fido_assert_count: %zu",
307ba9bdd8bSchristos 			    fido_assert_count(assert));
308ba9bdd8bSchristos 		print_assert(out_f, assert, 0, flags);
309ba9bdd8bSchristos 	}
310ba9bdd8bSchristos 
311ba9bdd8bSchristos 	fido_dev_close(dev);
312ba9bdd8bSchristos 	fido_dev_free(&dev);
313ba9bdd8bSchristos 	fido_assert_free(&assert);
314ba9bdd8bSchristos 
315ba9bdd8bSchristos 	fclose(in_f);
316ba9bdd8bSchristos 	fclose(out_f);
317ba9bdd8bSchristos 	in_f = NULL;
318ba9bdd8bSchristos 	out_f = NULL;
319ba9bdd8bSchristos 
320ba9bdd8bSchristos 	exit(0);
321ba9bdd8bSchristos }
322