xref: /netbsd-src/external/bsd/libfido2/dist/examples/select.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
11fc1e710Schristos /*
2*2d40c451Schristos  * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
31fc1e710Schristos  * Use of this source code is governed by a BSD-style
41fc1e710Schristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
61fc1e710Schristos  */
71fc1e710Schristos 
81fc1e710Schristos #include <errno.h>
995dbdf32Schristos #include <fido.h>
101fc1e710Schristos #include <stdio.h>
111fc1e710Schristos #include <stdlib.h>
121fc1e710Schristos #include <time.h>
131fc1e710Schristos 
141fc1e710Schristos #include "../openbsd-compat/openbsd-compat.h"
151fc1e710Schristos 
161fc1e710Schristos #define FIDO_POLL_MS	50
171fc1e710Schristos 
181fc1e710Schristos #if defined(_MSC_VER)
191fc1e710Schristos static int
nanosleep(const struct timespec * rqtp,struct timespec * rmtp)201fc1e710Schristos nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
211fc1e710Schristos {
221fc1e710Schristos 	if (rmtp != NULL) {
231fc1e710Schristos 		errno = EINVAL;
241fc1e710Schristos 		return (-1);
251fc1e710Schristos 	}
261fc1e710Schristos 
27*2d40c451Schristos 	Sleep((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(rqtp->tv_nsec / 1000000));
281fc1e710Schristos 
291fc1e710Schristos 	return (0);
301fc1e710Schristos }
311fc1e710Schristos #endif
321fc1e710Schristos 
331fc1e710Schristos static fido_dev_t *
open_dev(const fido_dev_info_t * di)341fc1e710Schristos open_dev(const fido_dev_info_t *di)
351fc1e710Schristos {
361fc1e710Schristos 	fido_dev_t	*dev;
371fc1e710Schristos 	int		 r;
381fc1e710Schristos 
391fc1e710Schristos 	if ((dev = fido_dev_new()) == NULL) {
401fc1e710Schristos 		warnx("%s: fido_dev_new", __func__);
411fc1e710Schristos 		return (NULL);
421fc1e710Schristos 	}
431fc1e710Schristos 
441fc1e710Schristos 	if ((r = fido_dev_open(dev, fido_dev_info_path(di))) != FIDO_OK) {
451fc1e710Schristos 		warnx("%s: fido_dev_open %s: %s", __func__,
461fc1e710Schristos 		    fido_dev_info_path(di), fido_strerr(r));
471fc1e710Schristos 		fido_dev_free(&dev);
481fc1e710Schristos 		return (NULL);
491fc1e710Schristos 	}
501fc1e710Schristos 
511fc1e710Schristos 	printf("%s (0x%04x:0x%04x) is %s\n", fido_dev_info_path(di),
521fc1e710Schristos 	    fido_dev_info_vendor(di), fido_dev_info_product(di),
531fc1e710Schristos 	    fido_dev_is_fido2(dev) ? "fido2" : "u2f");
541fc1e710Schristos 
551fc1e710Schristos 	return (dev);
561fc1e710Schristos }
571fc1e710Schristos 
581fc1e710Schristos static int
select_dev(const fido_dev_info_t * devlist,size_t ndevs,fido_dev_t ** dev,size_t * idx,int secs)591fc1e710Schristos select_dev(const fido_dev_info_t *devlist, size_t ndevs, fido_dev_t **dev,
601fc1e710Schristos     size_t *idx, int secs)
611fc1e710Schristos {
621fc1e710Schristos 	const fido_dev_info_t	 *di;
631fc1e710Schristos 	fido_dev_t		**devtab;
641fc1e710Schristos 	struct timespec		  ts_start;
651fc1e710Schristos 	struct timespec		  ts_now;
661fc1e710Schristos 	struct timespec		  ts_delta;
671fc1e710Schristos 	struct timespec		  ts_pause;
681fc1e710Schristos 	size_t			  nopen = 0;
691fc1e710Schristos 	int			  touched;
701fc1e710Schristos 	int			  r;
711fc1e710Schristos 	long			  ms_remain;
721fc1e710Schristos 
731fc1e710Schristos 	*dev = NULL;
741fc1e710Schristos 	*idx = 0;
751fc1e710Schristos 
761fc1e710Schristos 	printf("%u authenticator(s) detected\n", (unsigned)ndevs);
771fc1e710Schristos 
781fc1e710Schristos 	if (ndevs == 0)
791fc1e710Schristos 		return (0); /* nothing to do */
801fc1e710Schristos 
811fc1e710Schristos 	if ((devtab = calloc(ndevs, sizeof(*devtab))) == NULL) {
821fc1e710Schristos 		warn("%s: calloc", __func__);
831fc1e710Schristos 		return (-1);
841fc1e710Schristos 	}
851fc1e710Schristos 
861fc1e710Schristos 	for (size_t i = 0; i < ndevs; i++) {
871fc1e710Schristos 		di = fido_dev_info_ptr(devlist, i);
881fc1e710Schristos 		if ((devtab[i] = open_dev(di)) != NULL) {
891fc1e710Schristos 			*idx = i;
901fc1e710Schristos 			nopen++;
911fc1e710Schristos 		}
921fc1e710Schristos 	}
931fc1e710Schristos 
941fc1e710Schristos 	printf("%u authenticator(s) opened\n", (unsigned)nopen);
951fc1e710Schristos 
961fc1e710Schristos 	if (nopen < 2) {
971fc1e710Schristos 		if (nopen == 1)
981fc1e710Schristos 			*dev = devtab[*idx]; /* single candidate */
991fc1e710Schristos 		r = 0;
1001fc1e710Schristos 		goto out;
1011fc1e710Schristos 	}
1021fc1e710Schristos 
1031fc1e710Schristos 	for (size_t i = 0; i < ndevs; i++) {
1041fc1e710Schristos 		di = fido_dev_info_ptr(devlist, i);
1051fc1e710Schristos 		if (devtab[i] == NULL)
1061fc1e710Schristos 			continue; /* failed to open */
1071fc1e710Schristos 		if ((r = fido_dev_get_touch_begin(devtab[i])) != FIDO_OK) {
1081fc1e710Schristos 			warnx("%s: fido_dev_get_touch_begin %s: %s", __func__,
1091fc1e710Schristos 			    fido_dev_info_path(di), fido_strerr(r));
1101fc1e710Schristos 			r = -1;
1111fc1e710Schristos 			goto out;
1121fc1e710Schristos 		}
1131fc1e710Schristos 	}
1141fc1e710Schristos 
1151fc1e710Schristos 	if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
1161fc1e710Schristos 		warn("%s: clock_gettime", __func__);
1171fc1e710Schristos 		r = -1;
1181fc1e710Schristos 		goto out;
1191fc1e710Schristos 	}
1201fc1e710Schristos 
1211fc1e710Schristos 	ts_pause.tv_sec = 0;
1221fc1e710Schristos 	ts_pause.tv_nsec = 200000000; /* 200ms */
1231fc1e710Schristos 
1241fc1e710Schristos 	do {
1251fc1e710Schristos 		nanosleep(&ts_pause, NULL);
1261fc1e710Schristos 
1271fc1e710Schristos 		for (size_t i = 0; i < ndevs; i++) {
1281fc1e710Schristos 			di = fido_dev_info_ptr(devlist, i);
1291fc1e710Schristos 			if (devtab[i] == NULL) {
1301fc1e710Schristos 				/* failed to open or discarded */
1311fc1e710Schristos 				continue;
1321fc1e710Schristos 			}
1331fc1e710Schristos 			if ((r = fido_dev_get_touch_status(devtab[i], &touched,
1341fc1e710Schristos 			    FIDO_POLL_MS)) != FIDO_OK) {
1351fc1e710Schristos 				warnx("%s: fido_dev_get_touch_status %s: %s",
1361fc1e710Schristos 				    __func__, fido_dev_info_path(di),
1371fc1e710Schristos 				    fido_strerr(r));
1381fc1e710Schristos 				fido_dev_close(devtab[i]);
1391fc1e710Schristos 				fido_dev_free(&devtab[i]);
1401fc1e710Schristos 				continue; /* discard */
1411fc1e710Schristos 			}
1421fc1e710Schristos 			if (touched) {
1431fc1e710Schristos 				*dev = devtab[i];
1441fc1e710Schristos 				*idx = i;
1451fc1e710Schristos 				r = 0;
1461fc1e710Schristos 				goto out;
1471fc1e710Schristos 			}
1481fc1e710Schristos 		}
1491fc1e710Schristos 
1501fc1e710Schristos 		if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
1511fc1e710Schristos 			warn("%s: clock_gettime", __func__);
1521fc1e710Schristos 			r = -1;
1531fc1e710Schristos 			goto out;
1541fc1e710Schristos 		}
1551fc1e710Schristos 
1561fc1e710Schristos 		timespecsub(&ts_now, &ts_start, &ts_delta);
1571fc1e710Schristos 		ms_remain = (secs * 1000) - ((long)ts_delta.tv_sec * 1000) +
1581fc1e710Schristos 		    ((long)ts_delta.tv_nsec / 1000000);
1591fc1e710Schristos 	} while (ms_remain > FIDO_POLL_MS);
1601fc1e710Schristos 
1611fc1e710Schristos 	printf("timeout after %d seconds\n", secs);
1621fc1e710Schristos 	r = -1;
1631fc1e710Schristos out:
1641fc1e710Schristos 	if (r != 0) {
1651fc1e710Schristos 		*dev = NULL;
1661fc1e710Schristos 		*idx = 0;
1671fc1e710Schristos 	}
1681fc1e710Schristos 
1691fc1e710Schristos 	for (size_t i = 0; i < ndevs; i++) {
1701fc1e710Schristos 		if (devtab[i] && devtab[i] != *dev) {
1711fc1e710Schristos 			fido_dev_cancel(devtab[i]);
1721fc1e710Schristos 			fido_dev_close(devtab[i]);
1731fc1e710Schristos 			fido_dev_free(&devtab[i]);
1741fc1e710Schristos 		}
1751fc1e710Schristos 	}
1761fc1e710Schristos 
1771fc1e710Schristos 	free(devtab);
1781fc1e710Schristos 
1791fc1e710Schristos 	return (r);
1801fc1e710Schristos }
1811fc1e710Schristos 
1821fc1e710Schristos int
main(void)1831fc1e710Schristos main(void)
1841fc1e710Schristos {
1851fc1e710Schristos 	const fido_dev_info_t	*di;
1861fc1e710Schristos 	fido_dev_info_t		*devlist;
1871fc1e710Schristos 	fido_dev_t		*dev;
1881fc1e710Schristos 	size_t			 idx;
1891fc1e710Schristos 	size_t			 ndevs;
1901fc1e710Schristos 	int			 r;
1911fc1e710Schristos 
1921fc1e710Schristos 	fido_init(0);
1931fc1e710Schristos 
1941fc1e710Schristos 	if ((devlist = fido_dev_info_new(64)) == NULL)
1951fc1e710Schristos 		errx(1, "fido_dev_info_new");
1961fc1e710Schristos 
1971fc1e710Schristos 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
1981fc1e710Schristos 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
1991fc1e710Schristos 	if (select_dev(devlist, ndevs, &dev, &idx, 15) != 0)
2001fc1e710Schristos 		errx(1, "select_dev");
2011fc1e710Schristos 	if (dev == NULL)
2021fc1e710Schristos 		errx(1, "no authenticator found");
2031fc1e710Schristos 
2041fc1e710Schristos 	di = fido_dev_info_ptr(devlist, idx);
2051fc1e710Schristos 	printf("%s: %s by %s (PIN %sset)\n", fido_dev_info_path(di),
2061fc1e710Schristos 	    fido_dev_info_product_string(di),
2071fc1e710Schristos 	    fido_dev_info_manufacturer_string(di),
2081fc1e710Schristos 	    fido_dev_has_pin(dev) ? "" : "un");
2091fc1e710Schristos 
2101fc1e710Schristos 	fido_dev_close(dev);
2111fc1e710Schristos 	fido_dev_free(&dev);
2121fc1e710Schristos 	fido_dev_info_free(&devlist, ndevs);
2131fc1e710Schristos 
2141fc1e710Schristos 	exit(0);
2151fc1e710Schristos }
216