1/*
2 * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <fido.h>
9#include <stdbool.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#ifdef HAVE_UNISTD_H
14#include <unistd.h>
15#endif
16
17#include "../openbsd-compat/openbsd-compat.h"
18#include "extern.h"
19
20static void
21format_flags(char *ret, size_t retlen, uint8_t flags)
22{
23	memset(ret, 0, retlen);
24
25	if (flags & FIDO_CAP_WINK) {
26		if (strlcat(ret, "wink,", retlen) >= retlen)
27			goto toolong;
28	} else {
29		if (strlcat(ret, "nowink,", retlen) >= retlen)
30			goto toolong;
31	}
32
33	if (flags & FIDO_CAP_CBOR) {
34		if (strlcat(ret, " cbor,", retlen) >= retlen)
35			goto toolong;
36	} else {
37		if (strlcat(ret, " nocbor,", retlen) >= retlen)
38			goto toolong;
39	}
40
41	if (flags & FIDO_CAP_NMSG) {
42		if (strlcat(ret, " nomsg", retlen) >= retlen)
43			goto toolong;
44	} else {
45		if (strlcat(ret, " msg", retlen) >= retlen)
46			goto toolong;
47	}
48
49	return;
50toolong:
51	strlcpy(ret, "toolong", retlen);
52}
53
54static void
55print_attr(const fido_dev_t *dev)
56{
57	char flags_txt[128];
58
59	printf("proto: 0x%02x\n", fido_dev_protocol(dev));
60	printf("major: 0x%02x\n", fido_dev_major(dev));
61	printf("minor: 0x%02x\n", fido_dev_minor(dev));
62	printf("build: 0x%02x\n", fido_dev_build(dev));
63
64	format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev));
65	printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt);
66}
67
68static void
69print_str_array(const char *label, char * const *sa, size_t len)
70{
71	if (len == 0)
72		return;
73
74	printf("%s strings: ", label);
75
76	for (size_t i = 0; i < len; i++)
77		printf("%s%s", i > 0 ? ", " : "", sa[i]);
78
79	printf("\n");
80}
81
82static void
83print_opt_array(const char *label, char * const *name, const bool *value,
84    size_t len)
85{
86	if (len == 0)
87		return;
88
89	printf("%s: ", label);
90
91	for (size_t i = 0; i < len; i++)
92		printf("%s%s%s", i > 0 ? ", " : "",
93		    value[i] ? "" : "no", name[i]);
94
95	printf("\n");
96}
97
98static void
99print_cert_array(const char *label, char * const *name, const uint64_t *value,
100    size_t len)
101{
102	if (len == 0)
103		return;
104
105	printf("%s: ", label);
106
107	for (size_t i = 0; i < len; i++)
108		printf("%s%s %llu", i > 0 ? ", " : "", name[i],
109		    (unsigned long long)value[i]);
110
111	printf("\n");
112}
113
114static void
115print_algorithms(const fido_cbor_info_t *ci)
116{
117	const char *cose, *type;
118	size_t len;
119
120	if ((len = fido_cbor_info_algorithm_count(ci)) == 0)
121		return;
122
123	printf("algorithms: ");
124
125	for (size_t i = 0; i < len; i++) {
126		cose = type = "unknown";
127		switch (fido_cbor_info_algorithm_cose(ci, i)) {
128		case COSE_ES256:
129			cose = "es256";
130			break;
131		case COSE_ES384:
132			cose = "es384";
133			break;
134		case COSE_RS256:
135			cose = "rs256";
136			break;
137		case COSE_EDDSA:
138			cose = "eddsa";
139			break;
140		}
141		if (fido_cbor_info_algorithm_type(ci, i) != NULL)
142			type = fido_cbor_info_algorithm_type(ci, i);
143		printf("%s%s (%s)", i > 0 ? ", " : "", cose, type);
144	}
145
146	printf("\n");
147}
148
149static void
150print_aaguid(const unsigned char *buf, size_t buflen)
151{
152	printf("aaguid: ");
153
154	while (buflen--)
155		printf("%02x", *buf++);
156
157	printf("\n");
158}
159
160static void
161print_maxmsgsiz(uint64_t maxmsgsiz)
162{
163	printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
164}
165
166static void
167print_maxcredcntlst(uint64_t maxcredcntlst)
168{
169	printf("maxcredcntlst: %d\n", (int)maxcredcntlst);
170}
171
172static void
173print_maxcredidlen(uint64_t maxcredidlen)
174{
175	printf("maxcredlen: %d\n", (int)maxcredidlen);
176}
177
178static void
179print_maxlargeblob(uint64_t maxlargeblob)
180{
181	printf("maxlargeblob: %d\n", (int)maxlargeblob);
182}
183
184static void
185print_maxrpid_minpinlen(uint64_t maxrpid)
186{
187	if (maxrpid > 0)
188		printf("maxrpids in minpinlen: %d\n", (int)maxrpid);
189}
190
191static void
192print_minpinlen(uint64_t minpinlen)
193{
194	if (minpinlen > 0)
195		printf("minpinlen: %d\n", (int)minpinlen);
196}
197
198static void
199print_uv_attempts(uint64_t uv_attempts)
200{
201	if (uv_attempts > 0)
202		printf("platform uv attempt(s): %d\n", (int)uv_attempts);
203}
204
205static void
206print_uv_modality(uint64_t uv_modality)
207{
208	uint64_t mode;
209	bool printed = false;
210
211	if (uv_modality == 0)
212		return;
213
214	printf("uv modality: 0x%x (", (int)uv_modality);
215
216	for (size_t i = 0; i < 64; i++) {
217		mode = 1ULL << i;
218		if ((uv_modality & mode) == 0)
219			continue;
220		if (printed)
221			printf(", ");
222		switch (mode) {
223		case FIDO_UV_MODE_TUP:
224			printf("test of user presence");
225			break;
226		case FIDO_UV_MODE_FP:
227			printf("fingerprint check");
228			break;
229		case FIDO_UV_MODE_PIN:
230			printf("pin check");
231			break;
232		case FIDO_UV_MODE_VOICE:
233			printf("voice recognition");
234			break;
235		case FIDO_UV_MODE_FACE:
236			printf("face recognition");
237			break;
238		case FIDO_UV_MODE_LOCATION:
239			printf("location check");
240			break;
241		case FIDO_UV_MODE_EYE:
242			printf("eyeprint check");
243			break;
244		case FIDO_UV_MODE_DRAWN:
245			printf("drawn pattern check");
246			break;
247		case FIDO_UV_MODE_HAND:
248			printf("handprint verification");
249			break;
250		case FIDO_UV_MODE_NONE:
251			printf("none");
252			break;
253		case FIDO_UV_MODE_ALL:
254			printf("all required");
255			break;
256		case FIDO_UV_MODE_EXT_PIN:
257			printf("external pin");
258			break;
259		case FIDO_UV_MODE_EXT_DRAWN:
260			printf("external drawn pattern check");
261			break;
262		default:
263			printf("unknown 0x%llx", (unsigned long long)mode);
264			break;
265		}
266		printed = true;
267	}
268
269	printf(")\n");
270}
271
272static void
273print_rk_remaining(int64_t rk_remaining)
274{
275	if (rk_remaining != -1)
276		printf("remaining rk(s): %d\n", (int)rk_remaining);
277}
278
279static void
280print_fwversion(uint64_t fwversion)
281{
282	printf("fwversion: 0x%x\n", (int)fwversion);
283}
284
285static void
286print_byte_array(const char *label, const uint8_t *ba, size_t len)
287{
288	if (len == 0)
289		return;
290
291	printf("%s: ", label);
292
293	for (size_t i = 0; i < len; i++)
294		printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
295
296	printf("\n");
297}
298
299int
300token_info(int argc, char **argv, char *path)
301{
302	char			*cred_id = NULL;
303	char			*rp_id = NULL;
304	fido_cbor_info_t	*ci = NULL;
305	fido_dev_t		*dev = NULL;
306	int			 ch;
307	int			 credman = 0;
308	int			 r;
309	int			 retrycnt;
310
311	optind = 1;
312
313	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
314		switch (ch) {
315		case 'c':
316			credman = 1;
317			break;
318		case 'i':
319			cred_id = optarg;
320			break;
321		case 'k':
322			rp_id = optarg;
323			break;
324		default:
325			break; /* ignore */
326		}
327	}
328
329	if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL)))
330		usage();
331
332	dev = open_dev(path);
333
334	if (credman)
335		return (credman_get_metadata(dev, path));
336	if (cred_id && rp_id)
337		return (credman_print_rk(dev, path, rp_id, cred_id));
338	if (cred_id || rp_id)
339		usage();
340
341	print_attr(dev);
342
343	if (fido_dev_is_fido2(dev) == false)
344		goto end;
345	if ((ci = fido_cbor_info_new()) == NULL)
346		errx(1, "fido_cbor_info_new");
347	if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
348		errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
349
350	/* print supported protocol versions */
351	print_str_array("version", fido_cbor_info_versions_ptr(ci),
352	    fido_cbor_info_versions_len(ci));
353
354	/* print supported extensions */
355	print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
356	    fido_cbor_info_extensions_len(ci));
357
358	/* print supported transports */
359	print_str_array("transport", fido_cbor_info_transports_ptr(ci),
360	    fido_cbor_info_transports_len(ci));
361
362	/* print supported algorithms */
363	print_algorithms(ci);
364
365	/* print aaguid */
366	print_aaguid(fido_cbor_info_aaguid_ptr(ci),
367	    fido_cbor_info_aaguid_len(ci));
368
369	/* print supported options */
370	print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
371	    fido_cbor_info_options_value_ptr(ci),
372	    fido_cbor_info_options_len(ci));
373
374	/* print certifications */
375	print_cert_array("certifications", fido_cbor_info_certs_name_ptr(ci),
376	    fido_cbor_info_certs_value_ptr(ci),
377	    fido_cbor_info_certs_len(ci));
378
379	/* print firmware version */
380	print_fwversion(fido_cbor_info_fwversion(ci));
381
382	/* print maximum message size */
383	print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
384
385	/* print maximum number of credentials allowed in credential lists */
386	print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci));
387
388	/* print maximum length of a credential ID */
389	print_maxcredidlen(fido_cbor_info_maxcredidlen(ci));
390
391	/* print maximum length of serialized largeBlob array */
392	print_maxlargeblob(fido_cbor_info_maxlargeblob(ci));
393
394	/* print maximum number of RP IDs in fido_dev_set_pin_minlen_rpid() */
395	print_maxrpid_minpinlen(fido_cbor_info_maxrpid_minpinlen(ci));
396
397	/* print estimated number of resident credentials */
398	print_rk_remaining(fido_cbor_info_rk_remaining(ci));
399
400	/* print minimum pin length */
401	print_minpinlen(fido_cbor_info_minpinlen(ci));
402
403	/* print supported pin protocols */
404	print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
405	    fido_cbor_info_protocols_len(ci));
406
407	if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK)
408		printf("pin retries: undefined\n");
409	else
410		printf("pin retries: %d\n", retrycnt);
411
412	printf("pin change required: %s\n",
413	    fido_cbor_info_new_pin_required(ci) ? "true" : "false");
414
415	if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK)
416		printf("uv retries: undefined\n");
417	else
418		printf("uv retries: %d\n", retrycnt);
419
420	/* print platform uv attempts */
421	print_uv_attempts(fido_cbor_info_uv_attempts(ci));
422
423	/* print supported uv mechanisms */
424	print_uv_modality(fido_cbor_info_uv_modality(ci));
425
426	bio_info(dev);
427
428	fido_cbor_info_free(&ci);
429end:
430	fido_dev_close(dev);
431	fido_dev_free(&dev);
432
433	exit(0);
434}
435
436int
437token_reset(char *path)
438{
439	fido_dev_t *dev = NULL;
440	int r;
441
442	if (path == NULL)
443		usage();
444
445	dev = open_dev(path);
446	if ((r = fido_dev_reset(dev)) != FIDO_OK)
447		errx(1, "fido_dev_reset: %s", fido_strerr(r));
448
449	fido_dev_close(dev);
450	fido_dev_free(&dev);
451
452	exit(0);
453}
454
455int
456token_get(int argc, char **argv, char *path)
457{
458	char	*id = NULL;
459	char	*key = NULL;
460	char	*name = NULL;
461	int	 blob = 0;
462	int	 ch;
463
464	optind = 1;
465
466	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
467		switch (ch) {
468		case 'b':
469			blob = 1;
470			break;
471		case 'i':
472			id = optarg;
473			break;
474		case 'k':
475			key = optarg;
476			break;
477		case 'n':
478			name = optarg;
479			break;
480		default:
481			break; /* ignore */
482		}
483	}
484
485	argc -= optind;
486	argv += optind;
487
488	if (blob == 0 || argc != 2)
489		usage();
490
491	return blob_get(path, key, name, id, argv[0]);
492}
493
494int
495token_set(int argc, char **argv, char *path)
496{
497	char	*id = NULL;
498	char	*key = NULL;
499	char	*len = NULL;
500	char	*display_name = NULL;
501	char	*name = NULL;
502	char	*rpid = NULL;
503	int	 blob = 0;
504	int	 cred = 0;
505	int	 ch;
506	int	 enroll = 0;
507	int	 ea = 0;
508	int	 uv = 0;
509	bool	 force = false;
510
511	optind = 1;
512
513	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
514		switch (ch) {
515		case 'a':
516			ea = 1;
517			break;
518		case 'b':
519			blob = 1;
520			break;
521		case 'c':
522			cred = 1;
523			break;
524		case 'e':
525			enroll = 1;
526			break;
527		case 'f':
528			force = true;
529			break;
530		case 'i':
531			id = optarg;
532			break;
533		case 'k':
534			key = optarg;
535			break;
536		case 'l':
537			len = optarg;
538			break;
539		case 'p':
540			display_name = optarg;
541			break;
542		case 'm':
543			rpid = optarg;
544			break;
545		case 'n':
546			name = optarg;
547			break;
548		case 'u':
549			uv = 1;
550			break;
551		default:
552			break; /* ignore */
553		}
554	}
555
556	argc -= optind;
557	argv += optind;
558
559	if (path == NULL)
560		usage();
561
562	if (blob) {
563		if (argc != 2)
564			usage();
565		return (blob_set(path, key, name, id, argv[0]));
566	}
567
568	if (cred) {
569		if (!id || !key)
570			usage();
571		if (!name && !display_name)
572			usage();
573		return (credman_update_rk(path, key, id, name, display_name));
574	}
575
576	if (enroll) {
577		if (ea || uv)
578			usage();
579		if (id && name)
580			return (bio_set_name(path, id, name));
581		if (!id && !name)
582			return (bio_enroll(path));
583		usage();
584	}
585
586	if (ea) {
587		if (uv)
588			usage();
589		return (config_entattest(path));
590	}
591
592	if (len)
593		return (config_pin_minlen(path, len));
594	if (rpid)
595		return (config_pin_minlen_rpid(path, rpid));
596	if (force)
597		return (config_force_pin_change(path));
598	if (uv)
599		return (config_always_uv(path, 1));
600
601	return (pin_set(path));
602}
603
604int
605token_list(int argc, char **argv, char *path)
606{
607	fido_dev_info_t *devlist;
608	size_t ndevs;
609	const char *rp_id = NULL;
610	int blobs = 0;
611	int enrolls = 0;
612	int keys = 0;
613	int rplist = 0;
614	int ch;
615	int r;
616
617	optind = 1;
618
619	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
620		switch (ch) {
621		case 'b':
622			blobs = 1;
623			break;
624		case 'e':
625			enrolls = 1;
626			break;
627		case 'k':
628			keys = 1;
629			rp_id = optarg;
630			break;
631		case 'r':
632			rplist = 1;
633			break;
634		default:
635			break; /* ignore */
636		}
637	}
638
639	if (blobs || enrolls || keys || rplist) {
640		if (path == NULL)
641			usage();
642		if (blobs)
643			return (blob_list(path));
644		if (enrolls)
645			return (bio_list(path));
646		if (keys)
647			return (credman_list_rk(path, rp_id));
648		if (rplist)
649			return (credman_list_rp(path));
650		/* NOTREACHED */
651	}
652
653	if ((devlist = fido_dev_info_new(64)) == NULL)
654		errx(1, "fido_dev_info_new");
655	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
656		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
657
658	for (size_t i = 0; i < ndevs; i++) {
659		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
660		printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
661		    fido_dev_info_path(di),
662		    (uint16_t)fido_dev_info_vendor(di),
663		    (uint16_t)fido_dev_info_product(di),
664		    fido_dev_info_manufacturer_string(di),
665		    fido_dev_info_product_string(di));
666	}
667
668	fido_dev_info_free(&devlist, ndevs);
669
670	exit(0);
671}
672
673int
674token_delete(int argc, char **argv, char *path)
675{
676	char		*id = NULL;
677	char		*key = NULL;
678	char		*name = NULL;
679	int		 blob = 0;
680	int		 ch;
681	int		 enroll = 0;
682	int		 uv = 0;
683
684	optind = 1;
685
686	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
687		switch (ch) {
688		case 'b':
689			blob = 1;
690			break;
691		case 'e':
692			enroll = 1;
693			break;
694		case 'i':
695			id = optarg;
696			break;
697		case 'k':
698			key = optarg;
699			break;
700		case 'n':
701			name = optarg;
702			break;
703		case 'u':
704			uv = 1;
705			break;
706		default:
707			break; /* ignore */
708		}
709	}
710
711	if (path == NULL)
712		usage();
713
714	if (blob)
715		return (blob_delete(path, key, name, id));
716
717	if (id) {
718		if (uv)
719			usage();
720		if (enroll == 0)
721			return (credman_delete_rk(path, id));
722		return (bio_delete(path, id));
723	}
724
725	if (uv == 0)
726		usage();
727
728	return (config_always_uv(path, 0));
729}
730