xref: /freebsd/contrib/libfido2/examples/select.c (revision 2ccfa855)
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 <errno.h>
90afa8e06SEd Maste #include <fido.h>
100afa8e06SEd Maste #include <stdio.h>
110afa8e06SEd Maste #include <stdlib.h>
120afa8e06SEd Maste #include <time.h>
130afa8e06SEd Maste 
140afa8e06SEd Maste #include "../openbsd-compat/openbsd-compat.h"
150afa8e06SEd Maste 
160afa8e06SEd Maste #define FIDO_POLL_MS	50
170afa8e06SEd Maste 
180afa8e06SEd Maste #if defined(_MSC_VER)
190afa8e06SEd Maste static int
nanosleep(const struct timespec * rqtp,struct timespec * rmtp)200afa8e06SEd Maste nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
210afa8e06SEd Maste {
220afa8e06SEd Maste 	if (rmtp != NULL) {
230afa8e06SEd Maste 		errno = EINVAL;
240afa8e06SEd Maste 		return (-1);
250afa8e06SEd Maste 	}
260afa8e06SEd Maste 
27*2ccfa855SEd Maste 	Sleep((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(rqtp->tv_nsec / 1000000));
280afa8e06SEd Maste 
290afa8e06SEd Maste 	return (0);
300afa8e06SEd Maste }
310afa8e06SEd Maste #endif
320afa8e06SEd Maste 
330afa8e06SEd Maste static fido_dev_t *
open_dev(const fido_dev_info_t * di)340afa8e06SEd Maste open_dev(const fido_dev_info_t *di)
350afa8e06SEd Maste {
360afa8e06SEd Maste 	fido_dev_t	*dev;
370afa8e06SEd Maste 	int		 r;
380afa8e06SEd Maste 
390afa8e06SEd Maste 	if ((dev = fido_dev_new()) == NULL) {
400afa8e06SEd Maste 		warnx("%s: fido_dev_new", __func__);
410afa8e06SEd Maste 		return (NULL);
420afa8e06SEd Maste 	}
430afa8e06SEd Maste 
440afa8e06SEd Maste 	if ((r = fido_dev_open(dev, fido_dev_info_path(di))) != FIDO_OK) {
450afa8e06SEd Maste 		warnx("%s: fido_dev_open %s: %s", __func__,
460afa8e06SEd Maste 		    fido_dev_info_path(di), fido_strerr(r));
470afa8e06SEd Maste 		fido_dev_free(&dev);
480afa8e06SEd Maste 		return (NULL);
490afa8e06SEd Maste 	}
500afa8e06SEd Maste 
510afa8e06SEd Maste 	printf("%s (0x%04x:0x%04x) is %s\n", fido_dev_info_path(di),
520afa8e06SEd Maste 	    fido_dev_info_vendor(di), fido_dev_info_product(di),
530afa8e06SEd Maste 	    fido_dev_is_fido2(dev) ? "fido2" : "u2f");
540afa8e06SEd Maste 
550afa8e06SEd Maste 	return (dev);
560afa8e06SEd Maste }
570afa8e06SEd Maste 
580afa8e06SEd Maste static int
select_dev(const fido_dev_info_t * devlist,size_t ndevs,fido_dev_t ** dev,size_t * idx,int secs)590afa8e06SEd Maste select_dev(const fido_dev_info_t *devlist, size_t ndevs, fido_dev_t **dev,
600afa8e06SEd Maste     size_t *idx, int secs)
610afa8e06SEd Maste {
620afa8e06SEd Maste 	const fido_dev_info_t	 *di;
630afa8e06SEd Maste 	fido_dev_t		**devtab;
640afa8e06SEd Maste 	struct timespec		  ts_start;
650afa8e06SEd Maste 	struct timespec		  ts_now;
660afa8e06SEd Maste 	struct timespec		  ts_delta;
670afa8e06SEd Maste 	struct timespec		  ts_pause;
680afa8e06SEd Maste 	size_t			  nopen = 0;
690afa8e06SEd Maste 	int			  touched;
700afa8e06SEd Maste 	int			  r;
710afa8e06SEd Maste 	long			  ms_remain;
720afa8e06SEd Maste 
730afa8e06SEd Maste 	*dev = NULL;
740afa8e06SEd Maste 	*idx = 0;
750afa8e06SEd Maste 
760afa8e06SEd Maste 	printf("%u authenticator(s) detected\n", (unsigned)ndevs);
770afa8e06SEd Maste 
780afa8e06SEd Maste 	if (ndevs == 0)
790afa8e06SEd Maste 		return (0); /* nothing to do */
800afa8e06SEd Maste 
810afa8e06SEd Maste 	if ((devtab = calloc(ndevs, sizeof(*devtab))) == NULL) {
820afa8e06SEd Maste 		warn("%s: calloc", __func__);
830afa8e06SEd Maste 		return (-1);
840afa8e06SEd Maste 	}
850afa8e06SEd Maste 
860afa8e06SEd Maste 	for (size_t i = 0; i < ndevs; i++) {
870afa8e06SEd Maste 		di = fido_dev_info_ptr(devlist, i);
880afa8e06SEd Maste 		if ((devtab[i] = open_dev(di)) != NULL) {
890afa8e06SEd Maste 			*idx = i;
900afa8e06SEd Maste 			nopen++;
910afa8e06SEd Maste 		}
920afa8e06SEd Maste 	}
930afa8e06SEd Maste 
940afa8e06SEd Maste 	printf("%u authenticator(s) opened\n", (unsigned)nopen);
950afa8e06SEd Maste 
960afa8e06SEd Maste 	if (nopen < 2) {
970afa8e06SEd Maste 		if (nopen == 1)
980afa8e06SEd Maste 			*dev = devtab[*idx]; /* single candidate */
990afa8e06SEd Maste 		r = 0;
1000afa8e06SEd Maste 		goto out;
1010afa8e06SEd Maste 	}
1020afa8e06SEd Maste 
1030afa8e06SEd Maste 	for (size_t i = 0; i < ndevs; i++) {
1040afa8e06SEd Maste 		di = fido_dev_info_ptr(devlist, i);
1050afa8e06SEd Maste 		if (devtab[i] == NULL)
1060afa8e06SEd Maste 			continue; /* failed to open */
1070afa8e06SEd Maste 		if ((r = fido_dev_get_touch_begin(devtab[i])) != FIDO_OK) {
1080afa8e06SEd Maste 			warnx("%s: fido_dev_get_touch_begin %s: %s", __func__,
1090afa8e06SEd Maste 			    fido_dev_info_path(di), fido_strerr(r));
1100afa8e06SEd Maste 			r = -1;
1110afa8e06SEd Maste 			goto out;
1120afa8e06SEd Maste 		}
1130afa8e06SEd Maste 	}
1140afa8e06SEd Maste 
1150afa8e06SEd Maste 	if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
1160afa8e06SEd Maste 		warn("%s: clock_gettime", __func__);
1170afa8e06SEd Maste 		r = -1;
1180afa8e06SEd Maste 		goto out;
1190afa8e06SEd Maste 	}
1200afa8e06SEd Maste 
1210afa8e06SEd Maste 	ts_pause.tv_sec = 0;
1220afa8e06SEd Maste 	ts_pause.tv_nsec = 200000000; /* 200ms */
1230afa8e06SEd Maste 
1240afa8e06SEd Maste 	do {
1250afa8e06SEd Maste 		nanosleep(&ts_pause, NULL);
1260afa8e06SEd Maste 
1270afa8e06SEd Maste 		for (size_t i = 0; i < ndevs; i++) {
1280afa8e06SEd Maste 			di = fido_dev_info_ptr(devlist, i);
1290afa8e06SEd Maste 			if (devtab[i] == NULL) {
1300afa8e06SEd Maste 				/* failed to open or discarded */
1310afa8e06SEd Maste 				continue;
1320afa8e06SEd Maste 			}
1330afa8e06SEd Maste 			if ((r = fido_dev_get_touch_status(devtab[i], &touched,
1340afa8e06SEd Maste 			    FIDO_POLL_MS)) != FIDO_OK) {
1350afa8e06SEd Maste 				warnx("%s: fido_dev_get_touch_status %s: %s",
1360afa8e06SEd Maste 				    __func__, fido_dev_info_path(di),
1370afa8e06SEd Maste 				    fido_strerr(r));
1380afa8e06SEd Maste 				fido_dev_close(devtab[i]);
1390afa8e06SEd Maste 				fido_dev_free(&devtab[i]);
1400afa8e06SEd Maste 				continue; /* discard */
1410afa8e06SEd Maste 			}
1420afa8e06SEd Maste 			if (touched) {
1430afa8e06SEd Maste 				*dev = devtab[i];
1440afa8e06SEd Maste 				*idx = i;
1450afa8e06SEd Maste 				r = 0;
1460afa8e06SEd Maste 				goto out;
1470afa8e06SEd Maste 			}
1480afa8e06SEd Maste 		}
1490afa8e06SEd Maste 
1500afa8e06SEd Maste 		if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
1510afa8e06SEd Maste 			warn("%s: clock_gettime", __func__);
1520afa8e06SEd Maste 			r = -1;
1530afa8e06SEd Maste 			goto out;
1540afa8e06SEd Maste 		}
1550afa8e06SEd Maste 
1560afa8e06SEd Maste 		timespecsub(&ts_now, &ts_start, &ts_delta);
1570afa8e06SEd Maste 		ms_remain = (secs * 1000) - ((long)ts_delta.tv_sec * 1000) +
1580afa8e06SEd Maste 		    ((long)ts_delta.tv_nsec / 1000000);
1590afa8e06SEd Maste 	} while (ms_remain > FIDO_POLL_MS);
1600afa8e06SEd Maste 
1610afa8e06SEd Maste 	printf("timeout after %d seconds\n", secs);
1620afa8e06SEd Maste 	r = -1;
1630afa8e06SEd Maste out:
1640afa8e06SEd Maste 	if (r != 0) {
1650afa8e06SEd Maste 		*dev = NULL;
1660afa8e06SEd Maste 		*idx = 0;
1670afa8e06SEd Maste 	}
1680afa8e06SEd Maste 
1690afa8e06SEd Maste 	for (size_t i = 0; i < ndevs; i++) {
1700afa8e06SEd Maste 		if (devtab[i] && devtab[i] != *dev) {
1710afa8e06SEd Maste 			fido_dev_cancel(devtab[i]);
1720afa8e06SEd Maste 			fido_dev_close(devtab[i]);
1730afa8e06SEd Maste 			fido_dev_free(&devtab[i]);
1740afa8e06SEd Maste 		}
1750afa8e06SEd Maste 	}
1760afa8e06SEd Maste 
1770afa8e06SEd Maste 	free(devtab);
1780afa8e06SEd Maste 
1790afa8e06SEd Maste 	return (r);
1800afa8e06SEd Maste }
1810afa8e06SEd Maste 
1820afa8e06SEd Maste int
main(void)1830afa8e06SEd Maste main(void)
1840afa8e06SEd Maste {
1850afa8e06SEd Maste 	const fido_dev_info_t	*di;
1860afa8e06SEd Maste 	fido_dev_info_t		*devlist;
1870afa8e06SEd Maste 	fido_dev_t		*dev;
1880afa8e06SEd Maste 	size_t			 idx;
1890afa8e06SEd Maste 	size_t			 ndevs;
1900afa8e06SEd Maste 	int			 r;
1910afa8e06SEd Maste 
1920afa8e06SEd Maste 	fido_init(0);
1930afa8e06SEd Maste 
1940afa8e06SEd Maste 	if ((devlist = fido_dev_info_new(64)) == NULL)
1950afa8e06SEd Maste 		errx(1, "fido_dev_info_new");
1960afa8e06SEd Maste 
1970afa8e06SEd Maste 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
1980afa8e06SEd Maste 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
1990afa8e06SEd Maste 	if (select_dev(devlist, ndevs, &dev, &idx, 15) != 0)
2000afa8e06SEd Maste 		errx(1, "select_dev");
2010afa8e06SEd Maste 	if (dev == NULL)
2020afa8e06SEd Maste 		errx(1, "no authenticator found");
2030afa8e06SEd Maste 
2040afa8e06SEd Maste 	di = fido_dev_info_ptr(devlist, idx);
2050afa8e06SEd Maste 	printf("%s: %s by %s (PIN %sset)\n", fido_dev_info_path(di),
2060afa8e06SEd Maste 	    fido_dev_info_product_string(di),
2070afa8e06SEd Maste 	    fido_dev_info_manufacturer_string(di),
2080afa8e06SEd Maste 	    fido_dev_has_pin(dev) ? "" : "un");
2090afa8e06SEd Maste 
2100afa8e06SEd Maste 	fido_dev_close(dev);
2110afa8e06SEd Maste 	fido_dev_free(&dev);
2120afa8e06SEd Maste 	fido_dev_info_free(&devlist, ndevs);
2130afa8e06SEd Maste 
2140afa8e06SEd Maste 	exit(0);
2150afa8e06SEd Maste }
216