xref: /netbsd-src/external/bsd/libfido2/dist/src/hid_hidapi.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
1ba9bdd8bSchristos /*
2ba9bdd8bSchristos  * Copyright (c) 2019 Google LLC. All rights reserved.
3ba9bdd8bSchristos  * Use of this source code is governed by a BSD-style
4ba9bdd8bSchristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
6ba9bdd8bSchristos  */
7ba9bdd8bSchristos 
81fc1e710Schristos #ifdef __linux__
91fc1e710Schristos #include <sys/ioctl.h>
101fc1e710Schristos #include <linux/hidraw.h>
111fc1e710Schristos #include <linux/input.h>
121fc1e710Schristos #include <fcntl.h>
131fc1e710Schristos #endif
14ba9bdd8bSchristos 
1595dbdf32Schristos #include <errno.h>
161fc1e710Schristos #include <hidapi.h>
17ba9bdd8bSchristos #include <wchar.h>
18ba9bdd8bSchristos 
19ba9bdd8bSchristos #include "fido.h"
20ba9bdd8bSchristos 
211fc1e710Schristos struct hid_hidapi {
221fc1e710Schristos 	void *handle;
231fc1e710Schristos 	size_t report_in_len;
241fc1e710Schristos 	size_t report_out_len;
251fc1e710Schristos };
261fc1e710Schristos 
27ba9bdd8bSchristos static size_t
fido_wcslen(const wchar_t * wcs)28ba9bdd8bSchristos fido_wcslen(const wchar_t *wcs)
29ba9bdd8bSchristos {
30ba9bdd8bSchristos 	size_t l = 0;
31ba9bdd8bSchristos 	while (*wcs++ != L'\0')
32ba9bdd8bSchristos 		l++;
33ba9bdd8bSchristos 	return l;
34ba9bdd8bSchristos }
35ba9bdd8bSchristos 
36ba9bdd8bSchristos static char *
wcs_to_cs(const wchar_t * wcs)37ba9bdd8bSchristos wcs_to_cs(const wchar_t *wcs)
38ba9bdd8bSchristos {
39ba9bdd8bSchristos 	char *cs;
40ba9bdd8bSchristos 	size_t i;
41ba9bdd8bSchristos 
42ba9bdd8bSchristos 	if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL)
43ba9bdd8bSchristos 		return NULL;
44ba9bdd8bSchristos 
45ba9bdd8bSchristos 	for (i = 0; i < fido_wcslen(wcs); i++) {
46ba9bdd8bSchristos 		if (wcs[i] >= 128) {
47ba9bdd8bSchristos 			/* give up on parsing non-ASCII text */
48ba9bdd8bSchristos 			free(cs);
49ba9bdd8bSchristos 			return strdup("hidapi device");
50ba9bdd8bSchristos 		}
51ba9bdd8bSchristos 		cs[i] = (char)wcs[i];
52ba9bdd8bSchristos 	}
53ba9bdd8bSchristos 
54ba9bdd8bSchristos 	return cs;
55ba9bdd8bSchristos }
56ba9bdd8bSchristos 
57ba9bdd8bSchristos static int
copy_info(fido_dev_info_t * di,const struct hid_device_info * d)58ba9bdd8bSchristos copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
59ba9bdd8bSchristos {
60ba9bdd8bSchristos 	memset(di, 0, sizeof(*di));
61ba9bdd8bSchristos 
62ba9bdd8bSchristos 	if (d->path != NULL)
63ba9bdd8bSchristos 		di->path = strdup(d->path);
64ba9bdd8bSchristos 	else
65ba9bdd8bSchristos 		di->path = strdup("");
66ba9bdd8bSchristos 
67ba9bdd8bSchristos 	if (d->manufacturer_string != NULL)
68ba9bdd8bSchristos 		di->manufacturer = wcs_to_cs(d->manufacturer_string);
69ba9bdd8bSchristos 	else
70ba9bdd8bSchristos 		di->manufacturer = strdup("");
71ba9bdd8bSchristos 
72ba9bdd8bSchristos 	if (d->product_string != NULL)
73ba9bdd8bSchristos 		di->product = wcs_to_cs(d->product_string);
74ba9bdd8bSchristos 	else
75ba9bdd8bSchristos 		di->product = strdup("");
76ba9bdd8bSchristos 
77ba9bdd8bSchristos 	if (di->path == NULL ||
78ba9bdd8bSchristos 	    di->manufacturer == NULL ||
79ba9bdd8bSchristos 	    di->product == NULL) {
80ba9bdd8bSchristos 		free(di->path);
81ba9bdd8bSchristos 		free(di->manufacturer);
82ba9bdd8bSchristos 		free(di->product);
831fc1e710Schristos 		explicit_bzero(di, sizeof(*di));
84ba9bdd8bSchristos 		return -1;
85ba9bdd8bSchristos 	}
86ba9bdd8bSchristos 
871fc1e710Schristos 	di->product_id = (int16_t)d->product_id;
881fc1e710Schristos 	di->vendor_id = (int16_t)d->vendor_id;
89ba9bdd8bSchristos 	di->io = (fido_dev_io_t) {
90ba9bdd8bSchristos 		&fido_hid_open,
91ba9bdd8bSchristos 		&fido_hid_close,
92ba9bdd8bSchristos 		&fido_hid_read,
93ba9bdd8bSchristos 		&fido_hid_write,
94ba9bdd8bSchristos 	};
95ba9bdd8bSchristos 
96ba9bdd8bSchristos 	return 0;
97ba9bdd8bSchristos }
98ba9bdd8bSchristos 
991fc1e710Schristos #ifdef __linux__
1001fc1e710Schristos static int
get_report_descriptor(const char * path,struct hidraw_report_descriptor * hrd)1011fc1e710Schristos get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
1021fc1e710Schristos {
1031fc1e710Schristos 	int fd;
1041fc1e710Schristos 	int s = -1;
1051fc1e710Schristos 	int ok = -1;
1061fc1e710Schristos 
10795dbdf32Schristos 	if ((fd = fido_hid_unix_open(path)) == -1) {
10895dbdf32Schristos 		fido_log_debug("%s: fido_hid_unix_open", __func__);
1091fc1e710Schristos 		return -1;
1101fc1e710Schristos 	}
1111fc1e710Schristos 
11295dbdf32Schristos 	if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 ||
1131fc1e710Schristos 	    (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
11495dbdf32Schristos 		fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
1151fc1e710Schristos 		goto fail;
1161fc1e710Schristos 	}
1171fc1e710Schristos 
1181fc1e710Schristos 	hrd->size = (unsigned)s;
1191fc1e710Schristos 
12095dbdf32Schristos 	if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) {
12195dbdf32Schristos 		fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
1221fc1e710Schristos 		goto fail;
1231fc1e710Schristos 	}
1241fc1e710Schristos 
1251fc1e710Schristos 	ok = 0;
1261fc1e710Schristos fail:
1271fc1e710Schristos 	if (fd != -1)
1281fc1e710Schristos 		close(fd);
1291fc1e710Schristos 
1301fc1e710Schristos 	return ok;
1311fc1e710Schristos }
1321fc1e710Schristos 
1331fc1e710Schristos static bool
is_fido(const struct hid_device_info * hdi)1341fc1e710Schristos is_fido(const struct hid_device_info *hdi)
1351fc1e710Schristos {
1361fc1e710Schristos 	uint32_t usage_page = 0;
137*2d40c451Schristos 	struct hidraw_report_descriptor *hrd;
1381fc1e710Schristos 
139*2d40c451Schristos 	if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
140*2d40c451Schristos 	    get_report_descriptor(hdi->path, hrd) < 0 ||
141*2d40c451Schristos 	    fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
142*2d40c451Schristos 		usage_page = 0;
1431fc1e710Schristos 
144*2d40c451Schristos 	free(hrd);
1451fc1e710Schristos 
1461fc1e710Schristos 	return usage_page == 0xf1d0;
1471fc1e710Schristos }
1481fc1e710Schristos #elif defined(_WIN32) || defined(__APPLE__)
1491fc1e710Schristos static bool
is_fido(const struct hid_device_info * hdi)1501fc1e710Schristos is_fido(const struct hid_device_info *hdi)
1511fc1e710Schristos {
1521fc1e710Schristos 	return hdi->usage_page == 0xf1d0;
1531fc1e710Schristos }
1541fc1e710Schristos #else
1551fc1e710Schristos static bool
is_fido(const struct hid_device_info * hdi)1561fc1e710Schristos is_fido(const struct hid_device_info *hdi)
1571fc1e710Schristos {
1581fc1e710Schristos 	(void)hdi;
1591fc1e710Schristos 	fido_log_debug("%s: assuming FIDO HID", __func__);
1601fc1e710Schristos 	return true;
1611fc1e710Schristos }
1621fc1e710Schristos #endif
1631fc1e710Schristos 
164ba9bdd8bSchristos void *
fido_hid_open(const char * path)165ba9bdd8bSchristos fido_hid_open(const char *path)
166ba9bdd8bSchristos {
1671fc1e710Schristos 	struct hid_hidapi *ctx;
1681fc1e710Schristos 
1691fc1e710Schristos 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
1701fc1e710Schristos 		return (NULL);
1711fc1e710Schristos 	}
1721fc1e710Schristos 
1731fc1e710Schristos 	if ((ctx->handle = hid_open_path(path)) == NULL) {
1741fc1e710Schristos 		free(ctx);
1751fc1e710Schristos 		return (NULL);
1761fc1e710Schristos 	}
1771fc1e710Schristos 
1781fc1e710Schristos 	ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN;
1791fc1e710Schristos 
1801fc1e710Schristos 	return ctx;
181ba9bdd8bSchristos }
182ba9bdd8bSchristos 
183ba9bdd8bSchristos void
fido_hid_close(void * handle)1841fc1e710Schristos fido_hid_close(void *handle)
185ba9bdd8bSchristos {
1861fc1e710Schristos 	struct hid_hidapi *ctx = handle;
1871fc1e710Schristos 
1881fc1e710Schristos 	hid_close(ctx->handle);
1891fc1e710Schristos 	free(ctx);
190ba9bdd8bSchristos }
191ba9bdd8bSchristos 
192ba9bdd8bSchristos int
fido_hid_set_sigmask(void * handle,const fido_sigset_t * sigmask)19395dbdf32Schristos fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
19495dbdf32Schristos {
19595dbdf32Schristos 	(void)handle;
19695dbdf32Schristos 	(void)sigmask;
19795dbdf32Schristos 
19895dbdf32Schristos 	return (FIDO_ERR_INTERNAL);
19995dbdf32Schristos }
20095dbdf32Schristos 
20195dbdf32Schristos int
fido_hid_read(void * handle,unsigned char * buf,size_t len,int ms)2021fc1e710Schristos fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
203ba9bdd8bSchristos {
2041fc1e710Schristos 	struct hid_hidapi *ctx = handle;
2051fc1e710Schristos 
2061fc1e710Schristos 	if (len != ctx->report_in_len) {
2071fc1e710Schristos 		fido_log_debug("%s: len %zu", __func__, len);
2081fc1e710Schristos 		return -1;
2091fc1e710Schristos 	}
2101fc1e710Schristos 
2111fc1e710Schristos 	return hid_read_timeout(ctx->handle, buf, len, ms);
212ba9bdd8bSchristos }
213ba9bdd8bSchristos 
214ba9bdd8bSchristos int
fido_hid_write(void * handle,const unsigned char * buf,size_t len)2151fc1e710Schristos fido_hid_write(void *handle, const unsigned char *buf, size_t len)
216ba9bdd8bSchristos {
2171fc1e710Schristos 	struct hid_hidapi *ctx = handle;
2181fc1e710Schristos 
2191fc1e710Schristos 	if (len != ctx->report_out_len + 1) {
2201fc1e710Schristos 		fido_log_debug("%s: len %zu", __func__, len);
2211fc1e710Schristos 		return -1;
2221fc1e710Schristos 	}
2231fc1e710Schristos 
2241fc1e710Schristos 	return hid_write(ctx->handle, buf, len);
225ba9bdd8bSchristos }
226ba9bdd8bSchristos 
227ba9bdd8bSchristos int
fido_hid_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)228ba9bdd8bSchristos fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
229ba9bdd8bSchristos {
230ba9bdd8bSchristos 	struct hid_device_info *hdi;
231ba9bdd8bSchristos 
232ba9bdd8bSchristos 	*olen = 0;
233ba9bdd8bSchristos 
234ba9bdd8bSchristos 	if (ilen == 0)
235ba9bdd8bSchristos 		return FIDO_OK; /* nothing to do */
236ba9bdd8bSchristos 	if (devlist == NULL)
237ba9bdd8bSchristos 		return FIDO_ERR_INVALID_ARGUMENT;
238ba9bdd8bSchristos 	if ((hdi = hid_enumerate(0, 0)) == NULL)
239ba9bdd8bSchristos 		return FIDO_OK; /* nothing to do */
240ba9bdd8bSchristos 
241ba9bdd8bSchristos 	for (struct hid_device_info *d = hdi; d != NULL; d = d->next) {
2421fc1e710Schristos 		if (is_fido(d) == false)
243ba9bdd8bSchristos 			continue;
244ba9bdd8bSchristos 		if (copy_info(&devlist[*olen], d) == 0) {
245ba9bdd8bSchristos 			if (++(*olen) == ilen)
246ba9bdd8bSchristos 				break;
247ba9bdd8bSchristos 		}
248ba9bdd8bSchristos 	}
249ba9bdd8bSchristos 
250ba9bdd8bSchristos 	hid_free_enumeration(hdi);
251ba9bdd8bSchristos 
252ba9bdd8bSchristos 	return FIDO_OK;
253ba9bdd8bSchristos }
2541fc1e710Schristos 
2551fc1e710Schristos size_t
fido_hid_report_in_len(void * handle)2561fc1e710Schristos fido_hid_report_in_len(void *handle)
2571fc1e710Schristos {
2581fc1e710Schristos 	struct hid_hidapi *ctx = handle;
2591fc1e710Schristos 
2601fc1e710Schristos 	return (ctx->report_in_len);
2611fc1e710Schristos }
2621fc1e710Schristos 
2631fc1e710Schristos size_t
fido_hid_report_out_len(void * handle)2641fc1e710Schristos fido_hid_report_out_len(void *handle)
2651fc1e710Schristos {
2661fc1e710Schristos 	struct hid_hidapi *ctx = handle;
2671fc1e710Schristos 
2681fc1e710Schristos 	return (ctx->report_out_len);
2691fc1e710Schristos }
270