xref: /freebsd/contrib/libfido2/src/hid_win.c (revision 2ccfa855)
10afa8e06SEd Maste /*
2*2ccfa855SEd Maste  * Copyright (c) 2019-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste  * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste  */
70afa8e06SEd Maste 
80afa8e06SEd Maste #include <sys/types.h>
90afa8e06SEd Maste 
100afa8e06SEd Maste #include <fcntl.h>
110afa8e06SEd Maste #ifdef HAVE_UNISTD_H
120afa8e06SEd Maste #include <unistd.h>
130afa8e06SEd Maste #endif
140afa8e06SEd Maste #include <windows.h>
150afa8e06SEd Maste #include <setupapi.h>
160afa8e06SEd Maste #include <initguid.h>
170afa8e06SEd Maste #include <devpkey.h>
180afa8e06SEd Maste #include <devpropdef.h>
190afa8e06SEd Maste #include <hidclass.h>
200afa8e06SEd Maste #include <hidsdi.h>
210afa8e06SEd Maste #include <wchar.h>
220afa8e06SEd Maste 
230afa8e06SEd Maste #include "fido.h"
240afa8e06SEd Maste 
250afa8e06SEd Maste #if defined(__MINGW32__) &&  __MINGW64_VERSION_MAJOR < 6
260afa8e06SEd Maste WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO,
270afa8e06SEd Maste     PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE,
280afa8e06SEd Maste     DWORD, PDWORD, DWORD);
290afa8e06SEd Maste #endif
300afa8e06SEd Maste 
31*2ccfa855SEd Maste #if defined(__MINGW32__) &&  __MINGW64_VERSION_MAJOR < 8
320afa8e06SEd Maste DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97,
330afa8e06SEd Maste     0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);
340afa8e06SEd Maste #endif
350afa8e06SEd Maste 
360afa8e06SEd Maste struct hid_win {
370afa8e06SEd Maste 	HANDLE		dev;
380afa8e06SEd Maste 	OVERLAPPED	overlap;
390afa8e06SEd Maste 	int		report_pending;
400afa8e06SEd Maste 	size_t		report_in_len;
410afa8e06SEd Maste 	size_t		report_out_len;
420afa8e06SEd Maste 	unsigned char	report[1 + CTAP_MAX_REPORT_LEN];
430afa8e06SEd Maste };
440afa8e06SEd Maste 
450afa8e06SEd Maste static bool
is_fido(HANDLE dev)460afa8e06SEd Maste is_fido(HANDLE dev)
470afa8e06SEd Maste {
480afa8e06SEd Maste 	PHIDP_PREPARSED_DATA	data = NULL;
490afa8e06SEd Maste 	HIDP_CAPS		caps;
500afa8e06SEd Maste 	int			fido = 0;
510afa8e06SEd Maste 
520afa8e06SEd Maste 	if (HidD_GetPreparsedData(dev, &data) == false) {
530afa8e06SEd Maste 		fido_log_debug("%s: HidD_GetPreparsedData", __func__);
540afa8e06SEd Maste 		goto fail;
550afa8e06SEd Maste 	}
560afa8e06SEd Maste 
570afa8e06SEd Maste 	if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
580afa8e06SEd Maste 		fido_log_debug("%s: HidP_GetCaps", __func__);
590afa8e06SEd Maste 		goto fail;
600afa8e06SEd Maste 	}
610afa8e06SEd Maste 
620afa8e06SEd Maste 	fido = (uint16_t)caps.UsagePage == 0xf1d0;
630afa8e06SEd Maste fail:
640afa8e06SEd Maste 	if (data != NULL)
650afa8e06SEd Maste 		HidD_FreePreparsedData(data);
660afa8e06SEd Maste 
670afa8e06SEd Maste 	return (fido);
680afa8e06SEd Maste }
690afa8e06SEd Maste 
700afa8e06SEd Maste static int
get_report_len(HANDLE dev,int dir,size_t * report_len)710afa8e06SEd Maste get_report_len(HANDLE dev, int dir, size_t *report_len)
720afa8e06SEd Maste {
730afa8e06SEd Maste 	PHIDP_PREPARSED_DATA	data = NULL;
740afa8e06SEd Maste 	HIDP_CAPS		caps;
750afa8e06SEd Maste 	USHORT			v;
760afa8e06SEd Maste 	int			ok = -1;
770afa8e06SEd Maste 
780afa8e06SEd Maste 	if (HidD_GetPreparsedData(dev, &data) == false) {
790afa8e06SEd Maste 		fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir);
800afa8e06SEd Maste 		goto fail;
810afa8e06SEd Maste 	}
820afa8e06SEd Maste 
830afa8e06SEd Maste 	if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
840afa8e06SEd Maste 		fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir);
850afa8e06SEd Maste 		goto fail;
860afa8e06SEd Maste 	}
870afa8e06SEd Maste 
880afa8e06SEd Maste 	if (dir == 0)
890afa8e06SEd Maste 		v = caps.InputReportByteLength;
900afa8e06SEd Maste 	else
910afa8e06SEd Maste 		v = caps.OutputReportByteLength;
920afa8e06SEd Maste 
930afa8e06SEd Maste 	if ((*report_len = (size_t)v) == 0) {
940afa8e06SEd Maste 		fido_log_debug("%s: report_len == 0", __func__);
950afa8e06SEd Maste 		goto fail;
960afa8e06SEd Maste 	}
970afa8e06SEd Maste 
980afa8e06SEd Maste 	ok = 0;
990afa8e06SEd Maste fail:
1000afa8e06SEd Maste 	if (data != NULL)
1010afa8e06SEd Maste 		HidD_FreePreparsedData(data);
1020afa8e06SEd Maste 
1030afa8e06SEd Maste 	return (ok);
1040afa8e06SEd Maste }
1050afa8e06SEd Maste 
1060afa8e06SEd Maste static int
get_id(HANDLE dev,int16_t * vendor_id,int16_t * product_id)107f540a430SEd Maste get_id(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
1080afa8e06SEd Maste {
1090afa8e06SEd Maste 	HIDD_ATTRIBUTES attr;
1100afa8e06SEd Maste 
1110afa8e06SEd Maste 	attr.Size = sizeof(attr);
1120afa8e06SEd Maste 
1130afa8e06SEd Maste 	if (HidD_GetAttributes(dev, &attr) == false) {
1140afa8e06SEd Maste 		fido_log_debug("%s: HidD_GetAttributes", __func__);
1150afa8e06SEd Maste 		return (-1);
1160afa8e06SEd Maste 	}
1170afa8e06SEd Maste 
1180afa8e06SEd Maste 	*vendor_id = (int16_t)attr.VendorID;
1190afa8e06SEd Maste 	*product_id = (int16_t)attr.ProductID;
1200afa8e06SEd Maste 
1210afa8e06SEd Maste 	return (0);
1220afa8e06SEd Maste }
1230afa8e06SEd Maste 
1240afa8e06SEd Maste static int
get_manufacturer(HANDLE dev,char ** manufacturer)125f540a430SEd Maste get_manufacturer(HANDLE dev, char **manufacturer)
1260afa8e06SEd Maste {
1270afa8e06SEd Maste 	wchar_t	buf[512];
1280afa8e06SEd Maste 	int	utf8_len;
1290afa8e06SEd Maste 	int	ok = -1;
1300afa8e06SEd Maste 
1310afa8e06SEd Maste 	*manufacturer = NULL;
1320afa8e06SEd Maste 
1330afa8e06SEd Maste 	if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
1340afa8e06SEd Maste 		fido_log_debug("%s: HidD_GetManufacturerString", __func__);
1350afa8e06SEd Maste 		goto fail;
1360afa8e06SEd Maste 	}
1370afa8e06SEd Maste 
1380afa8e06SEd Maste 	if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
1390afa8e06SEd Maste 	    -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
1400afa8e06SEd Maste 		fido_log_debug("%s: WideCharToMultiByte", __func__);
1410afa8e06SEd Maste 		goto fail;
1420afa8e06SEd Maste 	}
1430afa8e06SEd Maste 
1440afa8e06SEd Maste 	if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) {
1450afa8e06SEd Maste 		fido_log_debug("%s: malloc", __func__);
1460afa8e06SEd Maste 		goto fail;
1470afa8e06SEd Maste 	}
1480afa8e06SEd Maste 
1490afa8e06SEd Maste 	if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
1500afa8e06SEd Maste 	    *manufacturer, utf8_len, NULL, NULL) != utf8_len) {
1510afa8e06SEd Maste 		fido_log_debug("%s: WideCharToMultiByte", __func__);
1520afa8e06SEd Maste 		goto fail;
1530afa8e06SEd Maste 	}
1540afa8e06SEd Maste 
155f540a430SEd Maste 	ok = 0;
156f540a430SEd Maste fail:
157f540a430SEd Maste 	if (ok < 0) {
158f540a430SEd Maste 		free(*manufacturer);
159f540a430SEd Maste 		*manufacturer = NULL;
160f540a430SEd Maste 	}
161f540a430SEd Maste 
162f540a430SEd Maste 	return (ok);
163f540a430SEd Maste }
164f540a430SEd Maste 
165f540a430SEd Maste static int
get_product(HANDLE dev,char ** product)166f540a430SEd Maste get_product(HANDLE dev, char **product)
167f540a430SEd Maste {
168f540a430SEd Maste 	wchar_t	buf[512];
169f540a430SEd Maste 	int	utf8_len;
170f540a430SEd Maste 	int	ok = -1;
171f540a430SEd Maste 
172f540a430SEd Maste 	*product = NULL;
173f540a430SEd Maste 
1740afa8e06SEd Maste 	if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
1750afa8e06SEd Maste 		fido_log_debug("%s: HidD_GetProductString", __func__);
1760afa8e06SEd Maste 		goto fail;
1770afa8e06SEd Maste 	}
1780afa8e06SEd Maste 
1790afa8e06SEd Maste 	if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
1800afa8e06SEd Maste 	    -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
1810afa8e06SEd Maste 		fido_log_debug("%s: WideCharToMultiByte", __func__);
1820afa8e06SEd Maste 		goto fail;
1830afa8e06SEd Maste 	}
1840afa8e06SEd Maste 
1850afa8e06SEd Maste 	if ((*product = malloc((size_t)utf8_len)) == NULL) {
1860afa8e06SEd Maste 		fido_log_debug("%s: malloc", __func__);
1870afa8e06SEd Maste 		goto fail;
1880afa8e06SEd Maste 	}
1890afa8e06SEd Maste 
1900afa8e06SEd Maste 	if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
1910afa8e06SEd Maste 	    *product, utf8_len, NULL, NULL) != utf8_len) {
1920afa8e06SEd Maste 		fido_log_debug("%s: WideCharToMultiByte", __func__);
1930afa8e06SEd Maste 		goto fail;
1940afa8e06SEd Maste 	}
1950afa8e06SEd Maste 
1960afa8e06SEd Maste 	ok = 0;
1970afa8e06SEd Maste fail:
1980afa8e06SEd Maste 	if (ok < 0) {
1990afa8e06SEd Maste 		free(*product);
2000afa8e06SEd Maste 		*product = NULL;
2010afa8e06SEd Maste 	}
2020afa8e06SEd Maste 
2030afa8e06SEd Maste 	return (ok);
2040afa8e06SEd Maste }
2050afa8e06SEd Maste 
2060afa8e06SEd Maste static char *
get_path(HDEVINFO devinfo,SP_DEVICE_INTERFACE_DATA * ifdata)2070afa8e06SEd Maste get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata)
2080afa8e06SEd Maste {
2090afa8e06SEd Maste 	SP_DEVICE_INTERFACE_DETAIL_DATA_A	*ifdetail = NULL;
2100afa8e06SEd Maste 	char					*path = NULL;
2110afa8e06SEd Maste 	DWORD					 len = 0;
2120afa8e06SEd Maste 
2130afa8e06SEd Maste 	/*
2140afa8e06SEd Maste 	 * "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail
2150afa8e06SEd Maste 	 * with a NULL DeviceInterfaceDetailData pointer, a
2160afa8e06SEd Maste 	 * DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize
2170afa8e06SEd Maste 	 * variable. In response to such a call, this function returns the
2180afa8e06SEd Maste 	 * required buffer size at RequiredSize and fails with GetLastError
2190afa8e06SEd Maste 	 * returning ERROR_INSUFFICIENT_BUFFER."
2200afa8e06SEd Maste 	 */
2210afa8e06SEd Maste 	if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len,
2220afa8e06SEd Maste 	    NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
2230afa8e06SEd Maste 		fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
2240afa8e06SEd Maste 		    __func__);
2250afa8e06SEd Maste 		goto fail;
2260afa8e06SEd Maste 	}
2270afa8e06SEd Maste 
2280afa8e06SEd Maste 	if ((ifdetail = malloc(len)) == NULL) {
2290afa8e06SEd Maste 		fido_log_debug("%s: malloc", __func__);
2300afa8e06SEd Maste 		goto fail;
2310afa8e06SEd Maste 	}
2320afa8e06SEd Maste 
2330afa8e06SEd Maste 	ifdetail->cbSize = sizeof(*ifdetail);
2340afa8e06SEd Maste 
2350afa8e06SEd Maste 	if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len,
2360afa8e06SEd Maste 	    NULL, NULL) == false) {
2370afa8e06SEd Maste 		fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
2380afa8e06SEd Maste 		    __func__);
2390afa8e06SEd Maste 		goto fail;
2400afa8e06SEd Maste 	}
2410afa8e06SEd Maste 
2420afa8e06SEd Maste 	if ((path = strdup(ifdetail->DevicePath)) == NULL) {
2430afa8e06SEd Maste 		fido_log_debug("%s: strdup", __func__);
2440afa8e06SEd Maste 		goto fail;
2450afa8e06SEd Maste 	}
2460afa8e06SEd Maste 
2470afa8e06SEd Maste fail:
2480afa8e06SEd Maste 	free(ifdetail);
2490afa8e06SEd Maste 
2500afa8e06SEd Maste 	return (path);
2510afa8e06SEd Maste }
2520afa8e06SEd Maste 
2530afa8e06SEd Maste #ifndef FIDO_HID_ANY
2540afa8e06SEd Maste static bool
hid_ok(HDEVINFO devinfo,DWORD idx)2550afa8e06SEd Maste hid_ok(HDEVINFO devinfo, DWORD idx)
2560afa8e06SEd Maste {
2570afa8e06SEd Maste 	SP_DEVINFO_DATA	 devinfo_data;
2580afa8e06SEd Maste 	wchar_t		*parent = NULL;
2590afa8e06SEd Maste 	DWORD		 parent_type = DEVPROP_TYPE_STRING;
2600afa8e06SEd Maste 	DWORD		 len = 0;
2610afa8e06SEd Maste 	bool		 ok = false;
2620afa8e06SEd Maste 
2630afa8e06SEd Maste 	memset(&devinfo_data, 0, sizeof(devinfo_data));
2640afa8e06SEd Maste 	devinfo_data.cbSize = sizeof(devinfo_data);
2650afa8e06SEd Maste 
2660afa8e06SEd Maste 	if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) {
2670afa8e06SEd Maste 		fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__);
2680afa8e06SEd Maste 		goto fail;
2690afa8e06SEd Maste 	}
2700afa8e06SEd Maste 
2710afa8e06SEd Maste 	if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
2720afa8e06SEd Maste 	    &DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false ||
2730afa8e06SEd Maste 	    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
2740afa8e06SEd Maste 		fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__);
2750afa8e06SEd Maste 		goto fail;
2760afa8e06SEd Maste 	}
2770afa8e06SEd Maste 
2780afa8e06SEd Maste 	if ((parent = malloc(len)) == NULL) {
2790afa8e06SEd Maste 		fido_log_debug("%s: malloc", __func__);
2800afa8e06SEd Maste 		goto fail;
2810afa8e06SEd Maste 	}
2820afa8e06SEd Maste 
2830afa8e06SEd Maste 	if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
2840afa8e06SEd Maste 	    &DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL,
2850afa8e06SEd Maste 	    0) == false) {
2860afa8e06SEd Maste 		fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__);
2870afa8e06SEd Maste 		goto fail;
2880afa8e06SEd Maste 	}
2890afa8e06SEd Maste 
2900afa8e06SEd Maste 	ok = wcsncmp(parent, L"USB\\", 4) == 0;
2910afa8e06SEd Maste fail:
2920afa8e06SEd Maste 	free(parent);
2930afa8e06SEd Maste 
2940afa8e06SEd Maste 	return (ok);
2950afa8e06SEd Maste }
2960afa8e06SEd Maste #endif
2970afa8e06SEd Maste 
2980afa8e06SEd Maste static int
copy_info(fido_dev_info_t * di,HDEVINFO devinfo,DWORD idx,SP_DEVICE_INTERFACE_DATA * ifdata)2990afa8e06SEd Maste copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx,
3000afa8e06SEd Maste     SP_DEVICE_INTERFACE_DATA *ifdata)
3010afa8e06SEd Maste {
3020afa8e06SEd Maste 	HANDLE	dev = INVALID_HANDLE_VALUE;
3030afa8e06SEd Maste 	int	ok = -1;
3040afa8e06SEd Maste 
3050afa8e06SEd Maste 	memset(di, 0, sizeof(*di));
3060afa8e06SEd Maste 
3070afa8e06SEd Maste 	if ((di->path = get_path(devinfo, ifdata)) == NULL) {
3080afa8e06SEd Maste 		fido_log_debug("%s: get_path", __func__);
3090afa8e06SEd Maste 		goto fail;
3100afa8e06SEd Maste 	}
3110afa8e06SEd Maste 
3120afa8e06SEd Maste 	fido_log_debug("%s: path=%s", __func__, di->path);
3130afa8e06SEd Maste 
3140afa8e06SEd Maste #ifndef FIDO_HID_ANY
3150afa8e06SEd Maste 	if (hid_ok(devinfo, idx) == false) {
3160afa8e06SEd Maste 		fido_log_debug("%s: hid_ok", __func__);
3170afa8e06SEd Maste 		goto fail;
3180afa8e06SEd Maste 	}
3190afa8e06SEd Maste #endif
3200afa8e06SEd Maste 
3210afa8e06SEd Maste 	dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
3220afa8e06SEd Maste 	    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3230afa8e06SEd Maste 	if (dev == INVALID_HANDLE_VALUE) {
3240afa8e06SEd Maste 		fido_log_debug("%s: CreateFileA", __func__);
3250afa8e06SEd Maste 		goto fail;
3260afa8e06SEd Maste 	}
3270afa8e06SEd Maste 
3280afa8e06SEd Maste 	if (is_fido(dev) == false) {
3290afa8e06SEd Maste 		fido_log_debug("%s: is_fido", __func__);
3300afa8e06SEd Maste 		goto fail;
3310afa8e06SEd Maste 	}
3320afa8e06SEd Maste 
333f540a430SEd Maste 	if (get_id(dev, &di->vendor_id, &di->product_id) < 0) {
334f540a430SEd Maste 		fido_log_debug("%s: get_id", __func__);
335f540a430SEd Maste 		goto fail;
336f540a430SEd Maste 	}
337f540a430SEd Maste 
338f540a430SEd Maste 	if (get_manufacturer(dev, &di->manufacturer) < 0) {
339f540a430SEd Maste 		fido_log_debug("%s: get_manufacturer", __func__);
340f540a430SEd Maste 		di->manufacturer = strdup("");
341f540a430SEd Maste 	}
342f540a430SEd Maste 
343f540a430SEd Maste 	if (get_product(dev, &di->product) < 0) {
344f540a430SEd Maste 		fido_log_debug("%s: get_product", __func__);
345f540a430SEd Maste 		di->product = strdup("");
346f540a430SEd Maste 	}
347f540a430SEd Maste 
348f540a430SEd Maste 	if (di->manufacturer == NULL || di->product == NULL) {
349f540a430SEd Maste 		fido_log_debug("%s: manufacturer/product", __func__);
3500afa8e06SEd Maste 		goto fail;
3510afa8e06SEd Maste 	}
3520afa8e06SEd Maste 
3530afa8e06SEd Maste 	ok = 0;
3540afa8e06SEd Maste fail:
3550afa8e06SEd Maste 	if (dev != INVALID_HANDLE_VALUE)
3560afa8e06SEd Maste 		CloseHandle(dev);
3570afa8e06SEd Maste 
3580afa8e06SEd Maste 	if (ok < 0) {
3590afa8e06SEd Maste 		free(di->path);
3600afa8e06SEd Maste 		free(di->manufacturer);
3610afa8e06SEd Maste 		free(di->product);
3620afa8e06SEd Maste 		explicit_bzero(di, sizeof(*di));
3630afa8e06SEd Maste 	}
3640afa8e06SEd Maste 
3650afa8e06SEd Maste 	return (ok);
3660afa8e06SEd Maste }
3670afa8e06SEd Maste 
3680afa8e06SEd Maste int
fido_hid_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)3690afa8e06SEd Maste fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
3700afa8e06SEd Maste {
3710afa8e06SEd Maste 	GUID				hid_guid = GUID_DEVINTERFACE_HID;
3720afa8e06SEd Maste 	HDEVINFO			devinfo = INVALID_HANDLE_VALUE;
3730afa8e06SEd Maste 	SP_DEVICE_INTERFACE_DATA	ifdata;
3740afa8e06SEd Maste 	DWORD				idx;
3750afa8e06SEd Maste 	int				r = FIDO_ERR_INTERNAL;
3760afa8e06SEd Maste 
3770afa8e06SEd Maste 	*olen = 0;
3780afa8e06SEd Maste 
3790afa8e06SEd Maste 	if (ilen == 0)
3800afa8e06SEd Maste 		return (FIDO_OK); /* nothing to do */
3810afa8e06SEd Maste 	if (devlist == NULL)
3820afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
3830afa8e06SEd Maste 
3840afa8e06SEd Maste 	if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
3850afa8e06SEd Maste 	    DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) {
3860afa8e06SEd Maste 		fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
3870afa8e06SEd Maste 		goto fail;
3880afa8e06SEd Maste 	}
3890afa8e06SEd Maste 
3900afa8e06SEd Maste 	ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3910afa8e06SEd Maste 
3920afa8e06SEd Maste 	for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid,
3930afa8e06SEd Maste 	    idx, &ifdata) == true; idx++) {
3940afa8e06SEd Maste 		if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) {
3950afa8e06SEd Maste 			devlist[*olen].io = (fido_dev_io_t) {
3960afa8e06SEd Maste 				fido_hid_open,
3970afa8e06SEd Maste 				fido_hid_close,
3980afa8e06SEd Maste 				fido_hid_read,
3990afa8e06SEd Maste 				fido_hid_write,
4000afa8e06SEd Maste 			};
4010afa8e06SEd Maste 			if (++(*olen) == ilen)
4020afa8e06SEd Maste 				break;
4030afa8e06SEd Maste 		}
4040afa8e06SEd Maste 	}
4050afa8e06SEd Maste 
4060afa8e06SEd Maste 	r = FIDO_OK;
4070afa8e06SEd Maste fail:
4080afa8e06SEd Maste 	if (devinfo != INVALID_HANDLE_VALUE)
4090afa8e06SEd Maste 		SetupDiDestroyDeviceInfoList(devinfo);
4100afa8e06SEd Maste 
4110afa8e06SEd Maste 	return (r);
4120afa8e06SEd Maste }
4130afa8e06SEd Maste 
4140afa8e06SEd Maste void *
fido_hid_open(const char * path)4150afa8e06SEd Maste fido_hid_open(const char *path)
4160afa8e06SEd Maste {
4170afa8e06SEd Maste 	struct hid_win *ctx;
4180afa8e06SEd Maste 
4190afa8e06SEd Maste 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
4200afa8e06SEd Maste 		return (NULL);
4210afa8e06SEd Maste 
4220afa8e06SEd Maste 	ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
4230afa8e06SEd Maste 	    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
4240afa8e06SEd Maste 	    FILE_FLAG_OVERLAPPED, NULL);
4250afa8e06SEd Maste 
4260afa8e06SEd Maste 	if (ctx->dev == INVALID_HANDLE_VALUE) {
4270afa8e06SEd Maste 		free(ctx);
4280afa8e06SEd Maste 		return (NULL);
4290afa8e06SEd Maste 	}
4300afa8e06SEd Maste 
4310afa8e06SEd Maste 	if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE,
4320afa8e06SEd Maste 	    NULL)) == NULL) {
4330afa8e06SEd Maste 		fido_log_debug("%s: CreateEventA", __func__);
4340afa8e06SEd Maste 		fido_hid_close(ctx);
4350afa8e06SEd Maste 		return (NULL);
4360afa8e06SEd Maste 	}
4370afa8e06SEd Maste 
4380afa8e06SEd Maste 	if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 ||
4390afa8e06SEd Maste 	    get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) {
4400afa8e06SEd Maste 		fido_log_debug("%s: get_report_len", __func__);
4410afa8e06SEd Maste 		fido_hid_close(ctx);
4420afa8e06SEd Maste 		return (NULL);
4430afa8e06SEd Maste 	}
4440afa8e06SEd Maste 
4450afa8e06SEd Maste 	return (ctx);
4460afa8e06SEd Maste }
4470afa8e06SEd Maste 
4480afa8e06SEd Maste void
fido_hid_close(void * handle)4490afa8e06SEd Maste fido_hid_close(void *handle)
4500afa8e06SEd Maste {
4510afa8e06SEd Maste 	struct hid_win *ctx = handle;
4520afa8e06SEd Maste 
4530afa8e06SEd Maste 	if (ctx->overlap.hEvent != NULL) {
4540afa8e06SEd Maste 		if (ctx->report_pending) {
4550afa8e06SEd Maste 			fido_log_debug("%s: report_pending", __func__);
4560afa8e06SEd Maste 			if (CancelIoEx(ctx->dev, &ctx->overlap) == 0)
4570afa8e06SEd Maste 				fido_log_debug("%s CancelIoEx: 0x%lx",
4583e696dfbSEd Maste 				    __func__, (u_long)GetLastError());
4590afa8e06SEd Maste 		}
4600afa8e06SEd Maste 		CloseHandle(ctx->overlap.hEvent);
4610afa8e06SEd Maste 	}
4620afa8e06SEd Maste 
4630afa8e06SEd Maste 	explicit_bzero(ctx->report, sizeof(ctx->report));
4640afa8e06SEd Maste 	CloseHandle(ctx->dev);
4650afa8e06SEd Maste 	free(ctx);
4660afa8e06SEd Maste }
4670afa8e06SEd Maste 
4680afa8e06SEd Maste int
fido_hid_set_sigmask(void * handle,const fido_sigset_t * sigmask)4690afa8e06SEd Maste fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
4700afa8e06SEd Maste {
4710afa8e06SEd Maste 	(void)handle;
4720afa8e06SEd Maste 	(void)sigmask;
4730afa8e06SEd Maste 
4740afa8e06SEd Maste 	return (FIDO_ERR_INTERNAL);
4750afa8e06SEd Maste }
4760afa8e06SEd Maste 
4770afa8e06SEd Maste int
fido_hid_read(void * handle,unsigned char * buf,size_t len,int ms)4780afa8e06SEd Maste fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
4790afa8e06SEd Maste {
4800afa8e06SEd Maste 	struct hid_win	*ctx = handle;
4810afa8e06SEd Maste 	DWORD		 n;
4820afa8e06SEd Maste 
4830afa8e06SEd Maste 	if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) {
4840afa8e06SEd Maste 		fido_log_debug("%s: len %zu", __func__, len);
4850afa8e06SEd Maste 		return (-1);
4860afa8e06SEd Maste 	}
4870afa8e06SEd Maste 
4880afa8e06SEd Maste 	if (ctx->report_pending == 0) {
4890afa8e06SEd Maste 		memset(&ctx->report, 0, sizeof(ctx->report));
4900afa8e06SEd Maste 		ResetEvent(ctx->overlap.hEvent);
4910afa8e06SEd Maste 		if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n,
4920afa8e06SEd Maste 		    &ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) {
4930afa8e06SEd Maste 			CancelIo(ctx->dev);
4940afa8e06SEd Maste 			fido_log_debug("%s: ReadFile", __func__);
4950afa8e06SEd Maste 			return (-1);
4960afa8e06SEd Maste 		}
4970afa8e06SEd Maste 		ctx->report_pending = 1;
4980afa8e06SEd Maste 	}
4990afa8e06SEd Maste 
5000afa8e06SEd Maste 	if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent,
5010afa8e06SEd Maste 	    (DWORD)ms) != WAIT_OBJECT_0)
5020afa8e06SEd Maste 		return (0);
5030afa8e06SEd Maste 
5040afa8e06SEd Maste 	ctx->report_pending = 0;
5050afa8e06SEd Maste 
5060afa8e06SEd Maste 	if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) {
5070afa8e06SEd Maste 		fido_log_debug("%s: GetOverlappedResult", __func__);
5080afa8e06SEd Maste 		return (-1);
5090afa8e06SEd Maste 	}
5100afa8e06SEd Maste 
5110afa8e06SEd Maste 	if (n != len + 1) {
5120afa8e06SEd Maste 		fido_log_debug("%s: expected %zu, got %zu", __func__,
5130afa8e06SEd Maste 		    len + 1, (size_t)n);
5140afa8e06SEd Maste 		return (-1);
5150afa8e06SEd Maste 	}
5160afa8e06SEd Maste 
5170afa8e06SEd Maste 	memcpy(buf, ctx->report + 1, len);
5180afa8e06SEd Maste 	explicit_bzero(ctx->report, sizeof(ctx->report));
5190afa8e06SEd Maste 
5200afa8e06SEd Maste 	return ((int)len);
5210afa8e06SEd Maste }
5220afa8e06SEd Maste 
5230afa8e06SEd Maste int
fido_hid_write(void * handle,const unsigned char * buf,size_t len)5240afa8e06SEd Maste fido_hid_write(void *handle, const unsigned char *buf, size_t len)
5250afa8e06SEd Maste {
5260afa8e06SEd Maste 	struct hid_win	*ctx = handle;
5270afa8e06SEd Maste 	OVERLAPPED	 overlap;
5280afa8e06SEd Maste 	DWORD		 n;
5290afa8e06SEd Maste 
5300afa8e06SEd Maste 	memset(&overlap, 0, sizeof(overlap));
5310afa8e06SEd Maste 
5320afa8e06SEd Maste 	if (len != ctx->report_out_len) {
5330afa8e06SEd Maste 		fido_log_debug("%s: len %zu", __func__, len);
5340afa8e06SEd Maste 		return (-1);
5350afa8e06SEd Maste 	}
5360afa8e06SEd Maste 
5370afa8e06SEd Maste 	if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 &&
5380afa8e06SEd Maste 	    GetLastError() != ERROR_IO_PENDING) {
5390afa8e06SEd Maste 		fido_log_debug("%s: WriteFile", __func__);
5400afa8e06SEd Maste 		return (-1);
5410afa8e06SEd Maste 	}
5420afa8e06SEd Maste 
5430afa8e06SEd Maste 	if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) {
5440afa8e06SEd Maste 		fido_log_debug("%s: GetOverlappedResult", __func__);
5450afa8e06SEd Maste 		return (-1);
5460afa8e06SEd Maste 	}
5470afa8e06SEd Maste 
5480afa8e06SEd Maste 	if (n != len) {
5490afa8e06SEd Maste 		fido_log_debug("%s: expected %zu, got %zu", __func__, len,
5500afa8e06SEd Maste 		    (size_t)n);
5510afa8e06SEd Maste 		return (-1);
5520afa8e06SEd Maste 	}
5530afa8e06SEd Maste 
5540afa8e06SEd Maste 	return ((int)len);
5550afa8e06SEd Maste }
5560afa8e06SEd Maste 
5570afa8e06SEd Maste size_t
fido_hid_report_in_len(void * handle)5580afa8e06SEd Maste fido_hid_report_in_len(void *handle)
5590afa8e06SEd Maste {
5600afa8e06SEd Maste 	struct hid_win *ctx = handle;
5610afa8e06SEd Maste 
5620afa8e06SEd Maste 	return (ctx->report_in_len - 1);
5630afa8e06SEd Maste }
5640afa8e06SEd Maste 
5650afa8e06SEd Maste size_t
fido_hid_report_out_len(void * handle)5660afa8e06SEd Maste fido_hid_report_out_len(void *handle)
5670afa8e06SEd Maste {
5680afa8e06SEd Maste 	struct hid_win *ctx = handle;
5690afa8e06SEd Maste 
5700afa8e06SEd Maste 	return (ctx->report_out_len - 1);
5710afa8e06SEd Maste }
572