2 * Copyright (c) 2019 Yubico AB. 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.
17 #include <devpropdef.h>
24 #if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 6
25 WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO,
26 PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE,
27 DWORD, PDWORD, DWORD);
30 #if defined(__MINGW32__)
31 DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97,
32 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);
40 size_t report_out_len;
41 unsigned char report[1 + CTAP_MAX_REPORT_LEN];
47 PHIDP_PREPARSED_DATA data = NULL;
51 if (HidD_GetPreparsedData(dev, &data) == false) {
52 fido_log_debug("%s: HidD_GetPreparsedData", __func__);
56 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
57 fido_log_debug("%s: HidP_GetCaps", __func__);
61 fido = (uint16_t)caps.UsagePage == 0xf1d0;
64 HidD_FreePreparsedData(data);
70 get_report_len(HANDLE dev, int dir, size_t *report_len)
72 PHIDP_PREPARSED_DATA data = NULL;
77 if (HidD_GetPreparsedData(dev, &data) == false) {
78 fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir);
82 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
83 fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir);
88 v = caps.InputReportByteLength;
90 v = caps.OutputReportByteLength;
92 if ((*report_len = (size_t)v) == 0) {
93 fido_log_debug("%s: report_len == 0", __func__);
100 HidD_FreePreparsedData(data);
106 get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
108 HIDD_ATTRIBUTES attr;
110 attr.Size = sizeof(attr);
112 if (HidD_GetAttributes(dev, &attr) == false) {
113 fido_log_debug("%s: HidD_GetAttributes", __func__);
117 *vendor_id = (int16_t)attr.VendorID;
118 *product_id = (int16_t)attr.ProductID;
124 get_str(HANDLE dev, char **manufacturer, char **product)
130 *manufacturer = NULL;
133 if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
134 fido_log_debug("%s: HidD_GetManufacturerString", __func__);
138 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
139 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
140 fido_log_debug("%s: WideCharToMultiByte", __func__);
144 if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) {
145 fido_log_debug("%s: malloc", __func__);
149 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
150 *manufacturer, utf8_len, NULL, NULL) != utf8_len) {
151 fido_log_debug("%s: WideCharToMultiByte", __func__);
155 if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
156 fido_log_debug("%s: HidD_GetProductString", __func__);
160 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
161 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
162 fido_log_debug("%s: WideCharToMultiByte", __func__);
166 if ((*product = malloc((size_t)utf8_len)) == NULL) {
167 fido_log_debug("%s: malloc", __func__);
171 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
172 *product, utf8_len, NULL, NULL) != utf8_len) {
173 fido_log_debug("%s: WideCharToMultiByte", __func__);
182 *manufacturer = NULL;
190 get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata)
192 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
197 * "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail
198 * with a NULL DeviceInterfaceDetailData pointer, a
199 * DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize
200 * variable. In response to such a call, this function returns the
201 * required buffer size at RequiredSize and fails with GetLastError
202 * returning ERROR_INSUFFICIENT_BUFFER."
204 if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len,
205 NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
206 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
211 if ((ifdetail = malloc(len)) == NULL) {
212 fido_log_debug("%s: malloc", __func__);
216 ifdetail->cbSize = sizeof(*ifdetail);
218 if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len,
219 NULL, NULL) == false) {
220 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
225 if ((path = strdup(ifdetail->DevicePath)) == NULL) {
226 fido_log_debug("%s: strdup", __func__);
238 hid_ok(HDEVINFO devinfo, DWORD idx)
240 SP_DEVINFO_DATA devinfo_data;
241 wchar_t *parent = NULL;
242 DWORD parent_type = DEVPROP_TYPE_STRING;
246 memset(&devinfo_data, 0, sizeof(devinfo_data));
247 devinfo_data.cbSize = sizeof(devinfo_data);
249 if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) {
250 fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__);
254 if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
255 &DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false ||
256 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
257 fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__);
261 if ((parent = malloc(len)) == NULL) {
262 fido_log_debug("%s: malloc", __func__);
266 if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
267 &DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL,
269 fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__);
273 ok = wcsncmp(parent, L"USB\\", 4) == 0;
282 copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx,
283 SP_DEVICE_INTERFACE_DATA *ifdata)
285 HANDLE dev = INVALID_HANDLE_VALUE;
288 memset(di, 0, sizeof(*di));
290 if ((di->path = get_path(devinfo, ifdata)) == NULL) {
291 fido_log_debug("%s: get_path", __func__);
295 fido_log_debug("%s: path=%s", __func__, di->path);
298 if (hid_ok(devinfo, idx) == false) {
299 fido_log_debug("%s: hid_ok", __func__);
304 dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
305 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
306 if (dev == INVALID_HANDLE_VALUE) {
307 fido_log_debug("%s: CreateFileA", __func__);
311 if (is_fido(dev) == false) {
312 fido_log_debug("%s: is_fido", __func__);
316 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 ||
317 get_str(dev, &di->manufacturer, &di->product) < 0) {
318 fido_log_debug("%s: get_int/get_str", __func__);
324 if (dev != INVALID_HANDLE_VALUE)
329 free(di->manufacturer);
331 explicit_bzero(di, sizeof(*di));
338 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
340 GUID hid_guid = GUID_DEVINTERFACE_HID;
341 HDEVINFO devinfo = INVALID_HANDLE_VALUE;
342 SP_DEVICE_INTERFACE_DATA ifdata;
344 int r = FIDO_ERR_INTERNAL;
349 return (FIDO_OK); /* nothing to do */
351 return (FIDO_ERR_INVALID_ARGUMENT);
353 if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
354 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) {
355 fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
359 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
361 for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid,
362 idx, &ifdata) == true; idx++) {
363 if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) {
364 devlist[*olen].io = (fido_dev_io_t) {
370 if (++(*olen) == ilen)
377 if (devinfo != INVALID_HANDLE_VALUE)
378 SetupDiDestroyDeviceInfoList(devinfo);
384 fido_hid_open(const char *path)
388 if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
391 ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
392 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
393 FILE_FLAG_OVERLAPPED, NULL);
395 if (ctx->dev == INVALID_HANDLE_VALUE) {
400 if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE,
402 fido_log_debug("%s: CreateEventA", __func__);
407 if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 ||
408 get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) {
409 fido_log_debug("%s: get_report_len", __func__);
418 fido_hid_close(void *handle)
420 struct hid_win *ctx = handle;
422 if (ctx->overlap.hEvent != NULL) {
423 if (ctx->report_pending) {
424 fido_log_debug("%s: report_pending", __func__);
425 if (CancelIoEx(ctx->dev, &ctx->overlap) == 0)
426 fido_log_debug("%s CancelIoEx: 0x%lx",
427 __func__, GetLastError());
429 CloseHandle(ctx->overlap.hEvent);
432 explicit_bzero(ctx->report, sizeof(ctx->report));
433 CloseHandle(ctx->dev);
438 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
443 return (FIDO_ERR_INTERNAL);
447 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
449 struct hid_win *ctx = handle;
452 if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) {
453 fido_log_debug("%s: len %zu", __func__, len);
457 if (ctx->report_pending == 0) {
458 memset(&ctx->report, 0, sizeof(ctx->report));
459 ResetEvent(ctx->overlap.hEvent);
460 if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n,
461 &ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) {
463 fido_log_debug("%s: ReadFile", __func__);
466 ctx->report_pending = 1;
469 if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent,
470 (DWORD)ms) != WAIT_OBJECT_0)
473 ctx->report_pending = 0;
475 if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) {
476 fido_log_debug("%s: GetOverlappedResult", __func__);
481 fido_log_debug("%s: expected %zu, got %zu", __func__,
486 memcpy(buf, ctx->report + 1, len);
487 explicit_bzero(ctx->report, sizeof(ctx->report));
493 fido_hid_write(void *handle, const unsigned char *buf, size_t len)
495 struct hid_win *ctx = handle;
499 memset(&overlap, 0, sizeof(overlap));
501 if (len != ctx->report_out_len) {
502 fido_log_debug("%s: len %zu", __func__, len);
506 if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 &&
507 GetLastError() != ERROR_IO_PENDING) {
508 fido_log_debug("%s: WriteFile", __func__);
512 if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) {
513 fido_log_debug("%s: GetOverlappedResult", __func__);
518 fido_log_debug("%s: expected %zu, got %zu", __func__, len,
527 fido_hid_report_in_len(void *handle)
529 struct hid_win *ctx = handle;
531 return (ctx->report_in_len - 1);
535 fido_hid_report_out_len(void *handle)
537 struct hid_win *ctx = handle;
539 return (ctx->report_out_len - 1);