1 /* 2 * Copyright (c) 2019 Google LLC. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 */ 6 7 #ifdef __linux__ 8 #include <sys/ioctl.h> 9 #include <linux/hidraw.h> 10 #include <linux/input.h> 11 #include <fcntl.h> 12 #endif 13 14 #include <errno.h> 15 #include <hidapi.h> 16 #include <wchar.h> 17 18 #include "fido.h" 19 20 struct hid_hidapi { 21 void *handle; 22 size_t report_in_len; 23 size_t report_out_len; 24 }; 25 26 static size_t 27 fido_wcslen(const wchar_t *wcs) 28 { 29 size_t l = 0; 30 while (*wcs++ != L'\0') 31 l++; 32 return l; 33 } 34 35 static char * 36 wcs_to_cs(const wchar_t *wcs) 37 { 38 char *cs; 39 size_t i; 40 41 if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) 42 return NULL; 43 44 for (i = 0; i < fido_wcslen(wcs); i++) { 45 if (wcs[i] >= 128) { 46 /* give up on parsing non-ASCII text */ 47 free(cs); 48 return strdup("hidapi device"); 49 } 50 cs[i] = (char)wcs[i]; 51 } 52 53 return cs; 54 } 55 56 static int 57 copy_info(fido_dev_info_t *di, const struct hid_device_info *d) 58 { 59 memset(di, 0, sizeof(*di)); 60 61 if (d->path != NULL) 62 di->path = strdup(d->path); 63 else 64 di->path = strdup(""); 65 66 if (d->manufacturer_string != NULL) 67 di->manufacturer = wcs_to_cs(d->manufacturer_string); 68 else 69 di->manufacturer = strdup(""); 70 71 if (d->product_string != NULL) 72 di->product = wcs_to_cs(d->product_string); 73 else 74 di->product = strdup(""); 75 76 if (di->path == NULL || 77 di->manufacturer == NULL || 78 di->product == NULL) { 79 free(di->path); 80 free(di->manufacturer); 81 free(di->product); 82 explicit_bzero(di, sizeof(*di)); 83 return -1; 84 } 85 86 di->product_id = (int16_t)d->product_id; 87 di->vendor_id = (int16_t)d->vendor_id; 88 di->io = (fido_dev_io_t) { 89 &fido_hid_open, 90 &fido_hid_close, 91 &fido_hid_read, 92 &fido_hid_write, 93 }; 94 95 return 0; 96 } 97 98 #ifdef __linux__ 99 static int 100 get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) 101 { 102 int fd; 103 int s = -1; 104 int ok = -1; 105 106 if ((fd = fido_hid_unix_open(path)) == -1) { 107 fido_log_debug("%s: fido_hid_unix_open", __func__); 108 return -1; 109 } 110 111 if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 || 112 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { 113 fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__); 114 goto fail; 115 } 116 117 hrd->size = (unsigned)s; 118 119 if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) { 120 fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__); 121 goto fail; 122 } 123 124 ok = 0; 125 fail: 126 if (fd != -1) 127 close(fd); 128 129 return ok; 130 } 131 132 static bool 133 is_fido(const struct hid_device_info *hdi) 134 { 135 uint32_t usage_page = 0; 136 struct hidraw_report_descriptor hrd; 137 138 memset(&hrd, 0, sizeof(hrd)); 139 140 if (get_report_descriptor(hdi->path, &hrd) < 0 || 141 fido_hid_get_usage(hrd.value, hrd.size, &usage_page) < 0) { 142 return false; 143 } 144 145 return usage_page == 0xf1d0; 146 } 147 #elif defined(_WIN32) || defined(__APPLE__) 148 static bool 149 is_fido(const struct hid_device_info *hdi) 150 { 151 return hdi->usage_page == 0xf1d0; 152 } 153 #else 154 static bool 155 is_fido(const struct hid_device_info *hdi) 156 { 157 (void)hdi; 158 fido_log_debug("%s: assuming FIDO HID", __func__); 159 return true; 160 } 161 #endif 162 163 void * 164 fido_hid_open(const char *path) 165 { 166 struct hid_hidapi *ctx; 167 168 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 169 return (NULL); 170 } 171 172 if ((ctx->handle = hid_open_path(path)) == NULL) { 173 free(ctx); 174 return (NULL); 175 } 176 177 ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN; 178 179 return ctx; 180 } 181 182 void 183 fido_hid_close(void *handle) 184 { 185 struct hid_hidapi *ctx = handle; 186 187 hid_close(ctx->handle); 188 free(ctx); 189 } 190 191 int 192 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) 193 { 194 (void)handle; 195 (void)sigmask; 196 197 return (FIDO_ERR_INTERNAL); 198 } 199 200 int 201 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 202 { 203 struct hid_hidapi *ctx = handle; 204 205 if (len != ctx->report_in_len) { 206 fido_log_debug("%s: len %zu", __func__, len); 207 return -1; 208 } 209 210 return hid_read_timeout(ctx->handle, buf, len, ms); 211 } 212 213 int 214 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 215 { 216 struct hid_hidapi *ctx = handle; 217 218 if (len != ctx->report_out_len + 1) { 219 fido_log_debug("%s: len %zu", __func__, len); 220 return -1; 221 } 222 223 return hid_write(ctx->handle, buf, len); 224 } 225 226 int 227 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 228 { 229 struct hid_device_info *hdi; 230 231 *olen = 0; 232 233 if (ilen == 0) 234 return FIDO_OK; /* nothing to do */ 235 if (devlist == NULL) 236 return FIDO_ERR_INVALID_ARGUMENT; 237 if ((hdi = hid_enumerate(0, 0)) == NULL) 238 return FIDO_OK; /* nothing to do */ 239 240 for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { 241 if (is_fido(d) == false) 242 continue; 243 if (copy_info(&devlist[*olen], d) == 0) { 244 if (++(*olen) == ilen) 245 break; 246 } 247 } 248 249 hid_free_enumeration(hdi); 250 251 return FIDO_OK; 252 } 253 254 size_t 255 fido_hid_report_in_len(void *handle) 256 { 257 struct hid_hidapi *ctx = handle; 258 259 return (ctx->report_in_len); 260 } 261 262 size_t 263 fido_hid_report_out_len(void *handle) 264 { 265 struct hid_hidapi *ctx = handle; 266 267 return (ctx->report_out_len); 268 } 269