xref: /freebsd-src/contrib/libfido2/examples/select.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 <errno.h>
8*0afa8e06SEd Maste #include <fido.h>
9*0afa8e06SEd Maste #include <stdio.h>
10*0afa8e06SEd Maste #include <stdlib.h>
11*0afa8e06SEd Maste #include <time.h>
12*0afa8e06SEd Maste 
13*0afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
14*0afa8e06SEd Maste 
15*0afa8e06SEd Maste #define FIDO_POLL_MS	50
16*0afa8e06SEd Maste 
17*0afa8e06SEd Maste #if defined(_MSC_VER)
18*0afa8e06SEd Maste static int
19*0afa8e06SEd Maste nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
20*0afa8e06SEd Maste {
21*0afa8e06SEd Maste 	if (rmtp != NULL) {
22*0afa8e06SEd Maste 		errno = EINVAL;
23*0afa8e06SEd Maste 		return (-1);
24*0afa8e06SEd Maste 	}
25*0afa8e06SEd Maste 
26*0afa8e06SEd Maste 	Sleep(rqtp->tv_nsec / 1000000);
27*0afa8e06SEd Maste 
28*0afa8e06SEd Maste 	return (0);
29*0afa8e06SEd Maste }
30*0afa8e06SEd Maste #endif
31*0afa8e06SEd Maste 
32*0afa8e06SEd Maste static fido_dev_t *
33*0afa8e06SEd Maste open_dev(const fido_dev_info_t *di)
34*0afa8e06SEd Maste {
35*0afa8e06SEd Maste 	fido_dev_t	*dev;
36*0afa8e06SEd Maste 	int		 r;
37*0afa8e06SEd Maste 
38*0afa8e06SEd Maste 	if ((dev = fido_dev_new()) == NULL) {
39*0afa8e06SEd Maste 		warnx("%s: fido_dev_new", __func__);
40*0afa8e06SEd Maste 		return (NULL);
41*0afa8e06SEd Maste 	}
42*0afa8e06SEd Maste 
43*0afa8e06SEd Maste 	if ((r = fido_dev_open(dev, fido_dev_info_path(di))) != FIDO_OK) {
44*0afa8e06SEd Maste 		warnx("%s: fido_dev_open %s: %s", __func__,
45*0afa8e06SEd Maste 		    fido_dev_info_path(di), fido_strerr(r));
46*0afa8e06SEd Maste 		fido_dev_free(&dev);
47*0afa8e06SEd Maste 		return (NULL);
48*0afa8e06SEd Maste 	}
49*0afa8e06SEd Maste 
50*0afa8e06SEd Maste 	printf("%s (0x%04x:0x%04x) is %s\n", fido_dev_info_path(di),
51*0afa8e06SEd Maste 	    fido_dev_info_vendor(di), fido_dev_info_product(di),
52*0afa8e06SEd Maste 	    fido_dev_is_fido2(dev) ? "fido2" : "u2f");
53*0afa8e06SEd Maste 
54*0afa8e06SEd Maste 	return (dev);
55*0afa8e06SEd Maste }
56*0afa8e06SEd Maste 
57*0afa8e06SEd Maste static int
58*0afa8e06SEd Maste select_dev(const fido_dev_info_t *devlist, size_t ndevs, fido_dev_t **dev,
59*0afa8e06SEd Maste     size_t *idx, int secs)
60*0afa8e06SEd Maste {
61*0afa8e06SEd Maste 	const fido_dev_info_t	 *di;
62*0afa8e06SEd Maste 	fido_dev_t		**devtab;
63*0afa8e06SEd Maste 	struct timespec		  ts_start;
64*0afa8e06SEd Maste 	struct timespec		  ts_now;
65*0afa8e06SEd Maste 	struct timespec		  ts_delta;
66*0afa8e06SEd Maste 	struct timespec		  ts_pause;
67*0afa8e06SEd Maste 	size_t			  nopen = 0;
68*0afa8e06SEd Maste 	int			  touched;
69*0afa8e06SEd Maste 	int			  r;
70*0afa8e06SEd Maste 	long			  ms_remain;
71*0afa8e06SEd Maste 
72*0afa8e06SEd Maste 	*dev = NULL;
73*0afa8e06SEd Maste 	*idx = 0;
74*0afa8e06SEd Maste 
75*0afa8e06SEd Maste 	printf("%u authenticator(s) detected\n", (unsigned)ndevs);
76*0afa8e06SEd Maste 
77*0afa8e06SEd Maste 	if (ndevs == 0)
78*0afa8e06SEd Maste 		return (0); /* nothing to do */
79*0afa8e06SEd Maste 
80*0afa8e06SEd Maste 	if ((devtab = calloc(ndevs, sizeof(*devtab))) == NULL) {
81*0afa8e06SEd Maste 		warn("%s: calloc", __func__);
82*0afa8e06SEd Maste 		return (-1);
83*0afa8e06SEd Maste 	}
84*0afa8e06SEd Maste 
85*0afa8e06SEd Maste 	for (size_t i = 0; i < ndevs; i++) {
86*0afa8e06SEd Maste 		di = fido_dev_info_ptr(devlist, i);
87*0afa8e06SEd Maste 		if ((devtab[i] = open_dev(di)) != NULL) {
88*0afa8e06SEd Maste 			*idx = i;
89*0afa8e06SEd Maste 			nopen++;
90*0afa8e06SEd Maste 		}
91*0afa8e06SEd Maste 	}
92*0afa8e06SEd Maste 
93*0afa8e06SEd Maste 	printf("%u authenticator(s) opened\n", (unsigned)nopen);
94*0afa8e06SEd Maste 
95*0afa8e06SEd Maste 	if (nopen < 2) {
96*0afa8e06SEd Maste 		if (nopen == 1)
97*0afa8e06SEd Maste 			*dev = devtab[*idx]; /* single candidate */
98*0afa8e06SEd Maste 		r = 0;
99*0afa8e06SEd Maste 		goto out;
100*0afa8e06SEd Maste 	}
101*0afa8e06SEd Maste 
102*0afa8e06SEd Maste 	for (size_t i = 0; i < ndevs; i++) {
103*0afa8e06SEd Maste 		di = fido_dev_info_ptr(devlist, i);
104*0afa8e06SEd Maste 		if (devtab[i] == NULL)
105*0afa8e06SEd Maste 			continue; /* failed to open */
106*0afa8e06SEd Maste 		if ((r = fido_dev_get_touch_begin(devtab[i])) != FIDO_OK) {
107*0afa8e06SEd Maste 			warnx("%s: fido_dev_get_touch_begin %s: %s", __func__,
108*0afa8e06SEd Maste 			    fido_dev_info_path(di), fido_strerr(r));
109*0afa8e06SEd Maste 			r = -1;
110*0afa8e06SEd Maste 			goto out;
111*0afa8e06SEd Maste 		}
112*0afa8e06SEd Maste 	}
113*0afa8e06SEd Maste 
114*0afa8e06SEd Maste 	if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
115*0afa8e06SEd Maste 		warn("%s: clock_gettime", __func__);
116*0afa8e06SEd Maste 		r = -1;
117*0afa8e06SEd Maste 		goto out;
118*0afa8e06SEd Maste 	}
119*0afa8e06SEd Maste 
120*0afa8e06SEd Maste 	ts_pause.tv_sec = 0;
121*0afa8e06SEd Maste 	ts_pause.tv_nsec = 200000000; /* 200ms */
122*0afa8e06SEd Maste 
123*0afa8e06SEd Maste 	do {
124*0afa8e06SEd Maste 		nanosleep(&ts_pause, NULL);
125*0afa8e06SEd Maste 
126*0afa8e06SEd Maste 		for (size_t i = 0; i < ndevs; i++) {
127*0afa8e06SEd Maste 			di = fido_dev_info_ptr(devlist, i);
128*0afa8e06SEd Maste 			if (devtab[i] == NULL) {
129*0afa8e06SEd Maste 				/* failed to open or discarded */
130*0afa8e06SEd Maste 				continue;
131*0afa8e06SEd Maste 			}
132*0afa8e06SEd Maste 			if ((r = fido_dev_get_touch_status(devtab[i], &touched,
133*0afa8e06SEd Maste 			    FIDO_POLL_MS)) != FIDO_OK) {
134*0afa8e06SEd Maste 				warnx("%s: fido_dev_get_touch_status %s: %s",
135*0afa8e06SEd Maste 				    __func__, fido_dev_info_path(di),
136*0afa8e06SEd Maste 				    fido_strerr(r));
137*0afa8e06SEd Maste 				fido_dev_close(devtab[i]);
138*0afa8e06SEd Maste 				fido_dev_free(&devtab[i]);
139*0afa8e06SEd Maste 				continue; /* discard */
140*0afa8e06SEd Maste 			}
141*0afa8e06SEd Maste 			if (touched) {
142*0afa8e06SEd Maste 				*dev = devtab[i];
143*0afa8e06SEd Maste 				*idx = i;
144*0afa8e06SEd Maste 				r = 0;
145*0afa8e06SEd Maste 				goto out;
146*0afa8e06SEd Maste 			}
147*0afa8e06SEd Maste 		}
148*0afa8e06SEd Maste 
149*0afa8e06SEd Maste 		if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
150*0afa8e06SEd Maste 			warn("%s: clock_gettime", __func__);
151*0afa8e06SEd Maste 			r = -1;
152*0afa8e06SEd Maste 			goto out;
153*0afa8e06SEd Maste 		}
154*0afa8e06SEd Maste 
155*0afa8e06SEd Maste 		timespecsub(&ts_now, &ts_start, &ts_delta);
156*0afa8e06SEd Maste 		ms_remain = (secs * 1000) - ((long)ts_delta.tv_sec * 1000) +
157*0afa8e06SEd Maste 		    ((long)ts_delta.tv_nsec / 1000000);
158*0afa8e06SEd Maste 	} while (ms_remain > FIDO_POLL_MS);
159*0afa8e06SEd Maste 
160*0afa8e06SEd Maste 	printf("timeout after %d seconds\n", secs);
161*0afa8e06SEd Maste 	r = -1;
162*0afa8e06SEd Maste out:
163*0afa8e06SEd Maste 	if (r != 0) {
164*0afa8e06SEd Maste 		*dev = NULL;
165*0afa8e06SEd Maste 		*idx = 0;
166*0afa8e06SEd Maste 	}
167*0afa8e06SEd Maste 
168*0afa8e06SEd Maste 	for (size_t i = 0; i < ndevs; i++) {
169*0afa8e06SEd Maste 		if (devtab[i] && devtab[i] != *dev) {
170*0afa8e06SEd Maste 			fido_dev_cancel(devtab[i]);
171*0afa8e06SEd Maste 			fido_dev_close(devtab[i]);
172*0afa8e06SEd Maste 			fido_dev_free(&devtab[i]);
173*0afa8e06SEd Maste 		}
174*0afa8e06SEd Maste 	}
175*0afa8e06SEd Maste 
176*0afa8e06SEd Maste 	free(devtab);
177*0afa8e06SEd Maste 
178*0afa8e06SEd Maste 	return (r);
179*0afa8e06SEd Maste }
180*0afa8e06SEd Maste 
181*0afa8e06SEd Maste int
182*0afa8e06SEd Maste main(void)
183*0afa8e06SEd Maste {
184*0afa8e06SEd Maste 	const fido_dev_info_t	*di;
185*0afa8e06SEd Maste 	fido_dev_info_t		*devlist;
186*0afa8e06SEd Maste 	fido_dev_t		*dev;
187*0afa8e06SEd Maste 	size_t			 idx;
188*0afa8e06SEd Maste 	size_t			 ndevs;
189*0afa8e06SEd Maste 	int			 r;
190*0afa8e06SEd Maste 
191*0afa8e06SEd Maste 	fido_init(0);
192*0afa8e06SEd Maste 
193*0afa8e06SEd Maste 	if ((devlist = fido_dev_info_new(64)) == NULL)
194*0afa8e06SEd Maste 		errx(1, "fido_dev_info_new");
195*0afa8e06SEd Maste 
196*0afa8e06SEd Maste 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
197*0afa8e06SEd Maste 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
198*0afa8e06SEd Maste 	if (select_dev(devlist, ndevs, &dev, &idx, 15) != 0)
199*0afa8e06SEd Maste 		errx(1, "select_dev");
200*0afa8e06SEd Maste 	if (dev == NULL)
201*0afa8e06SEd Maste 		errx(1, "no authenticator found");
202*0afa8e06SEd Maste 
203*0afa8e06SEd Maste 	di = fido_dev_info_ptr(devlist, idx);
204*0afa8e06SEd Maste 	printf("%s: %s by %s (PIN %sset)\n", fido_dev_info_path(di),
205*0afa8e06SEd Maste 	    fido_dev_info_product_string(di),
206*0afa8e06SEd Maste 	    fido_dev_info_manufacturer_string(di),
207*0afa8e06SEd Maste 	    fido_dev_has_pin(dev) ? "" : "un");
208*0afa8e06SEd Maste 
209*0afa8e06SEd Maste 	fido_dev_close(dev);
210*0afa8e06SEd Maste 	fido_dev_free(&dev);
211*0afa8e06SEd Maste 	fido_dev_info_free(&devlist, ndevs);
212*0afa8e06SEd Maste 
213*0afa8e06SEd Maste 	exit(0);
214*0afa8e06SEd Maste }
215