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