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 <hidapi.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <wchar.h> 18 19 #include "fido.h" 20 21 struct hid_hidapi { 22 void *handle; 23 size_t report_in_len; 24 size_t report_out_len; 25 }; 26 27 static size_t 28 fido_wcslen(const wchar_t *wcs) 29 { 30 size_t l = 0; 31 while (*wcs++ != L'\0') 32 l++; 33 return l; 34 } 35 36 static char * 37 wcs_to_cs(const wchar_t *wcs) 38 { 39 char *cs; 40 size_t i; 41 42 if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) 43 return NULL; 44 45 for (i = 0; i < fido_wcslen(wcs); i++) { 46 if (wcs[i] >= 128) { 47 /* give up on parsing non-ASCII text */ 48 free(cs); 49 return strdup("hidapi device"); 50 } 51 cs[i] = (char)wcs[i]; 52 } 53 54 return cs; 55 } 56 57 static int 58 copy_info(fido_dev_info_t *di, const struct hid_device_info *d) 59 { 60 memset(di, 0, sizeof(*di)); 61 62 if (d->path != NULL) 63 di->path = strdup(d->path); 64 else 65 di->path = strdup(""); 66 67 if (d->manufacturer_string != NULL) 68 di->manufacturer = wcs_to_cs(d->manufacturer_string); 69 else 70 di->manufacturer = strdup(""); 71 72 if (d->product_string != NULL) 73 di->product = wcs_to_cs(d->product_string); 74 else 75 di->product = strdup(""); 76 77 if (di->path == NULL || 78 di->manufacturer == NULL || 79 di->product == NULL) { 80 free(di->path); 81 free(di->manufacturer); 82 free(di->product); 83 explicit_bzero(di, sizeof(*di)); 84 return -1; 85 } 86 87 di->product_id = (int16_t)d->product_id; 88 di->vendor_id = (int16_t)d->vendor_id; 89 di->io = (fido_dev_io_t) { 90 &fido_hid_open, 91 &fido_hid_close, 92 &fido_hid_read, 93 &fido_hid_write, 94 }; 95 96 return 0; 97 } 98 99 #ifdef __linux__ 100 static int 101 get_key_len(uint8_t tag, uint8_t *key, size_t *key_len) 102 { 103 *key = tag & 0xfc; 104 if ((*key & 0xf0) == 0xf0) { 105 fido_log_debug("%s: *key=0x%02x", __func__, *key); 106 return -1; 107 } 108 109 *key_len = tag & 0x3; 110 if (*key_len == 3) { 111 *key_len = 4; 112 } 113 114 return 0; 115 } 116 117 static int 118 get_key_val(const void *body, size_t key_len, uint32_t *val) 119 { 120 const uint8_t *ptr = body; 121 122 switch (key_len) { 123 case 0: 124 *val = 0; 125 break; 126 case 1: 127 *val = ptr[0]; 128 break; 129 case 2: 130 *val = (uint32_t)((ptr[1] << 8) | ptr[0]); 131 break; 132 default: 133 fido_log_debug("%s: key_len=%zu", __func__, key_len); 134 return -1; 135 } 136 137 return 0; 138 } 139 140 static int 141 get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page, 142 uint32_t *usage) 143 { 144 const uint8_t *ptr = hrd->value; 145 size_t len = hrd->size; 146 147 while (len > 0) { 148 const uint8_t tag = ptr[0]; 149 150 ptr++; 151 len--; 152 153 uint8_t key; 154 size_t key_len; 155 uint32_t key_val; 156 157 if (get_key_len(tag, &key, &key_len) < 0 || key_len > len || 158 get_key_val(ptr, key_len, &key_val) < 0) { 159 return -1; 160 } 161 162 if (key == 0x4) { 163 *usage_page = key_val; 164 } else if (key == 0x8) { 165 *usage = key_val; 166 } 167 168 ptr += key_len; 169 len -= key_len; 170 } 171 172 return 0; 173 } 174 175 static int 176 get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) 177 { 178 int fd; 179 int s = -1; 180 int ok = -1; 181 182 if ((fd = open(path, O_RDONLY)) < 0) { 183 fido_log_debug("%s: open", __func__); 184 return -1; 185 } 186 187 if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 || 188 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { 189 fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__); 190 goto fail; 191 } 192 193 hrd->size = (unsigned)s; 194 195 if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) { 196 fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__); 197 goto fail; 198 } 199 200 ok = 0; 201 fail: 202 if (fd != -1) 203 close(fd); 204 205 return ok; 206 } 207 208 static bool 209 is_fido(const struct hid_device_info *hdi) 210 { 211 uint32_t usage = 0; 212 uint32_t usage_page = 0; 213 struct hidraw_report_descriptor hrd; 214 215 memset(&hrd, 0, sizeof(hrd)); 216 217 if (get_report_descriptor(hdi->path, &hrd) < 0 || 218 get_usage_info(&hrd, &usage_page, &usage) < 0) { 219 return false; 220 } 221 222 return usage_page == 0xf1d0; 223 } 224 #elif defined(_WIN32) || defined(__APPLE__) 225 static bool 226 is_fido(const struct hid_device_info *hdi) 227 { 228 return hdi->usage_page == 0xf1d0; 229 } 230 #else 231 static bool 232 is_fido(const struct hid_device_info *hdi) 233 { 234 (void)hdi; 235 fido_log_debug("%s: assuming FIDO HID", __func__); 236 return true; 237 } 238 #endif 239 240 void * 241 fido_hid_open(const char *path) 242 { 243 struct hid_hidapi *ctx; 244 245 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 246 return (NULL); 247 } 248 249 if ((ctx->handle = hid_open_path(path)) == NULL) { 250 free(ctx); 251 return (NULL); 252 } 253 254 ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN; 255 256 return ctx; 257 } 258 259 void 260 fido_hid_close(void *handle) 261 { 262 struct hid_hidapi *ctx = handle; 263 264 hid_close(ctx->handle); 265 free(ctx); 266 } 267 268 int 269 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 270 { 271 struct hid_hidapi *ctx = handle; 272 273 if (len != ctx->report_in_len) { 274 fido_log_debug("%s: len %zu", __func__, len); 275 return -1; 276 } 277 278 return hid_read_timeout(ctx->handle, buf, len, ms); 279 } 280 281 int 282 fido_hid_write(void *handle, const unsigned char *buf, size_t len) 283 { 284 struct hid_hidapi *ctx = handle; 285 286 if (len != ctx->report_out_len + 1) { 287 fido_log_debug("%s: len %zu", __func__, len); 288 return -1; 289 } 290 291 return hid_write(ctx->handle, buf, len); 292 } 293 294 int 295 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 296 { 297 struct hid_device_info *hdi; 298 299 *olen = 0; 300 301 if (ilen == 0) 302 return FIDO_OK; /* nothing to do */ 303 if (devlist == NULL) 304 return FIDO_ERR_INVALID_ARGUMENT; 305 if ((hdi = hid_enumerate(0, 0)) == NULL) 306 return FIDO_OK; /* nothing to do */ 307 308 for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { 309 if (is_fido(d) == false) 310 continue; 311 if (copy_info(&devlist[*olen], d) == 0) { 312 if (++(*olen) == ilen) 313 break; 314 } 315 } 316 317 hid_free_enumeration(hdi); 318 319 return FIDO_OK; 320 } 321 322 size_t 323 fido_hid_report_in_len(void *handle) 324 { 325 struct hid_hidapi *ctx = handle; 326 327 return (ctx->report_in_len); 328 } 329 330 size_t 331 fido_hid_report_out_len(void *handle) 332 { 333 struct hid_hidapi *ctx = handle; 334 335 return (ctx->report_out_len); 336 } 337