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