summaryrefslogtreecommitdiff
path: root/src/hid_win.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hid_win.c')
-rw-r--r--src/hid_win.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/hid_win.c b/src/hid_win.c
new file mode 100644
index 0000000..6d93778
--- /dev/null
+++ b/src/hid_win.c
@@ -0,0 +1,324 @@
1/*
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.
5 */
6
7#include <sys/types.h>
8
9#include <fcntl.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14#include <windows.h>
15#include <setupapi.h>
16#include <initguid.h>
17#include <hidclass.h>
18#include <hidsdi.h>
19
20#include "fido.h"
21
22#define REPORT_LEN 65
23
24static bool
25is_fido(HANDLE dev)
26{
27 PHIDP_PREPARSED_DATA data = NULL;
28 HIDP_CAPS caps;
29 uint16_t usage_page = 0;
30
31 if (HidD_GetPreparsedData(dev, &data) == false) {
32 fido_log_debug("%s: HidD_GetPreparsedData", __func__);
33 goto fail;
34 }
35
36 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
37 fido_log_debug("%s: HidP_GetCaps", __func__);
38 goto fail;
39 }
40
41 if (caps.OutputReportByteLength != REPORT_LEN ||
42 caps.InputReportByteLength != REPORT_LEN) {
43 fido_log_debug("%s: unsupported report len", __func__);
44 goto fail;
45 }
46
47 usage_page = caps.UsagePage;
48fail:
49 if (data != NULL)
50 HidD_FreePreparsedData(data);
51
52 return (usage_page == 0xf1d0);
53}
54
55static int
56get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
57{
58 HIDD_ATTRIBUTES attr;
59
60 attr.Size = sizeof(attr);
61
62 if (HidD_GetAttributes(dev, &attr) == false) {
63 fido_log_debug("%s: HidD_GetAttributes", __func__);
64 return (-1);
65 }
66
67 *vendor_id = attr.VendorID;
68 *product_id = attr.ProductID;
69
70 return (0);
71}
72
73static int
74get_str(HANDLE dev, char **manufacturer, char **product)
75{
76 wchar_t buf[512];
77 int utf8_len;
78 int ok = -1;
79
80 *manufacturer = NULL;
81 *product = NULL;
82
83 if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
84 fido_log_debug("%s: HidD_GetManufacturerString", __func__);
85 goto fail;
86 }
87
88 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
89 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
90 fido_log_debug("%s: WideCharToMultiByte", __func__);
91 goto fail;
92 }
93
94 if ((*manufacturer = malloc(utf8_len)) == NULL) {
95 fido_log_debug("%s: malloc", __func__);
96 goto fail;
97 }
98
99 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
100 *manufacturer, utf8_len, NULL, NULL) != utf8_len) {
101 fido_log_debug("%s: WideCharToMultiByte", __func__);
102 goto fail;
103 }
104
105 if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
106 fido_log_debug("%s: HidD_GetProductString", __func__);
107 goto fail;
108 }
109
110 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
111 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
112 fido_log_debug("%s: WideCharToMultiByte", __func__);
113 goto fail;
114 }
115
116 if ((*product = malloc(utf8_len)) == NULL) {
117 fido_log_debug("%s: malloc", __func__);
118 goto fail;
119 }
120
121 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
122 *product, utf8_len, NULL, NULL) != utf8_len) {
123 fido_log_debug("%s: WideCharToMultiByte", __func__);
124 goto fail;
125 }
126
127 ok = 0;
128fail:
129 if (ok < 0) {
130 free(*manufacturer);
131 free(*product);
132 *manufacturer = NULL;
133 *product = NULL;
134 }
135
136 return (ok);
137}
138
139static int
140copy_info(fido_dev_info_t *di, const char *path)
141{
142 HANDLE dev = INVALID_HANDLE_VALUE;
143 int ok = -1;
144
145 memset(di, 0, sizeof(*di));
146
147 dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
148 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
149 if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0)
150 goto fail;
151
152 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 ||
153 get_str(dev, &di->manufacturer, &di->product) < 0)
154 goto fail;
155
156 if ((di->path = strdup(path)) == NULL)
157 goto fail;
158
159 ok = 0;
160fail:
161 if (dev != INVALID_HANDLE_VALUE)
162 CloseHandle(dev);
163
164 if (ok < 0) {
165 free(di->path);
166 free(di->manufacturer);
167 free(di->product);
168 explicit_bzero(di, sizeof(*di));
169 }
170
171 return (ok);
172}
173
174int
175fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
176{
177 GUID hid_guid = GUID_DEVINTERFACE_HID;
178 HDEVINFO devinfo = INVALID_HANDLE_VALUE;
179 SP_DEVICE_INTERFACE_DATA ifdata;
180 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
181 DWORD len = 0;
182 DWORD idx = 0;
183 int r = FIDO_ERR_INTERNAL;
184
185 *olen = 0;
186
187 if (ilen == 0)
188 return (FIDO_OK); /* nothing to do */
189
190 if (devlist == NULL)
191 return (FIDO_ERR_INVALID_ARGUMENT);
192
193 devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
194 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
195 if (devinfo == INVALID_HANDLE_VALUE) {
196 fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
197 goto fail;
198 }
199
200 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
201
202 while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++,
203 &ifdata) == true) {
204 /*
205 * "Get the required buffer size. Call
206 * SetupDiGetDeviceInterfaceDetail with a NULL
207 * DeviceInterfaceDetailData pointer, a
208 * DeviceInterfaceDetailDataSize of zero, and a valid
209 * RequiredSize variable. In response to such a call, this
210 * function returns the required buffer size at RequiredSize
211 * and fails with GetLastError returning
212 * ERROR_INSUFFICIENT_BUFFER."
213 */
214 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0,
215 &len, NULL) != false ||
216 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
217 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
218 __func__);
219 goto fail;
220 }
221
222 if ((ifdetail = malloc(len)) == NULL) {
223 fido_log_debug("%s: malloc", __func__);
224 goto fail;
225 }
226
227 ifdetail->cbSize = sizeof(*ifdetail);
228
229 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail,
230 len, NULL, NULL) == false) {
231 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
232 __func__);
233 goto fail;
234 }
235
236 if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) {
237 if (++(*olen) == ilen)
238 break;
239 }
240
241 free(ifdetail);
242 ifdetail = NULL;
243 }
244
245 r = FIDO_OK;
246fail:
247 if (devinfo != INVALID_HANDLE_VALUE)
248 SetupDiDestroyDeviceInfoList(devinfo);
249
250 free(ifdetail);
251
252 return (r);
253}
254
255void *
256fido_hid_open(const char *path)
257{
258 HANDLE dev;
259
260 dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
261 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
262 FILE_ATTRIBUTE_NORMAL, NULL);
263
264 if (dev == INVALID_HANDLE_VALUE)
265 return (NULL);
266
267 return (dev);
268}
269
270void
271fido_hid_close(void *handle)
272{
273 CloseHandle(handle);
274}
275
276int
277fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
278{
279 DWORD n;
280 int r = -1;
281 uint8_t report[REPORT_LEN];
282
283 (void)ms; /* XXX */
284
285 memset(report, 0, sizeof(report));
286
287 if (len != sizeof(report) - 1) {
288 fido_log_debug("%s: invalid len", __func__);
289 return (-1);
290 }
291
292 if (ReadFile(handle, report, sizeof(report), &n, NULL) == false ||
293 n != sizeof(report)) {
294 fido_log_debug("%s: ReadFile", __func__);
295 goto fail;
296 }
297
298 r = sizeof(report) - 1;
299 memcpy(buf, report + 1, len);
300
301fail:
302 explicit_bzero(report, sizeof(report));
303
304 return (r);
305}
306
307int
308fido_hid_write(void *handle, const unsigned char *buf, size_t len)
309{
310 DWORD n;
311
312 if (len != REPORT_LEN) {
313 fido_log_debug("%s: invalid len", __func__);
314 return (-1);
315 }
316
317 if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false ||
318 n != REPORT_LEN) {
319 fido_log_debug("%s: WriteFile", __func__);
320 return (-1);
321 }
322
323 return (REPORT_LEN);
324}