xref: /freebsd/contrib/libfido2/src/hid_hidapi.c (revision 2ccfa855)
10afa8e06SEd Maste /*
20afa8e06SEd Maste  * Copyright (c) 2019 Google LLC. 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 #ifdef __linux__
90afa8e06SEd Maste #include <sys/ioctl.h>
100afa8e06SEd Maste #include <linux/hidraw.h>
110afa8e06SEd Maste #include <linux/input.h>
120afa8e06SEd Maste #include <fcntl.h>
130afa8e06SEd Maste #endif
140afa8e06SEd Maste 
150afa8e06SEd Maste #include <errno.h>
160afa8e06SEd Maste #include <hidapi.h>
170afa8e06SEd Maste #include <wchar.h>
180afa8e06SEd Maste 
190afa8e06SEd Maste #include "fido.h"
200afa8e06SEd Maste 
210afa8e06SEd Maste struct hid_hidapi {
220afa8e06SEd Maste 	void *handle;
230afa8e06SEd Maste 	size_t report_in_len;
240afa8e06SEd Maste 	size_t report_out_len;
250afa8e06SEd Maste };
260afa8e06SEd Maste 
270afa8e06SEd Maste static size_t
fido_wcslen(const wchar_t * wcs)280afa8e06SEd Maste fido_wcslen(const wchar_t *wcs)
290afa8e06SEd Maste {
300afa8e06SEd Maste 	size_t l = 0;
310afa8e06SEd Maste 	while (*wcs++ != L'\0')
320afa8e06SEd Maste 		l++;
330afa8e06SEd Maste 	return l;
340afa8e06SEd Maste }
350afa8e06SEd Maste 
360afa8e06SEd Maste static char *
wcs_to_cs(const wchar_t * wcs)370afa8e06SEd Maste wcs_to_cs(const wchar_t *wcs)
380afa8e06SEd Maste {
390afa8e06SEd Maste 	char *cs;
400afa8e06SEd Maste 	size_t i;
410afa8e06SEd Maste 
420afa8e06SEd Maste 	if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL)
430afa8e06SEd Maste 		return NULL;
440afa8e06SEd Maste 
450afa8e06SEd Maste 	for (i = 0; i < fido_wcslen(wcs); i++) {
460afa8e06SEd Maste 		if (wcs[i] >= 128) {
470afa8e06SEd Maste 			/* give up on parsing non-ASCII text */
480afa8e06SEd Maste 			free(cs);
490afa8e06SEd Maste 			return strdup("hidapi device");
500afa8e06SEd Maste 		}
510afa8e06SEd Maste 		cs[i] = (char)wcs[i];
520afa8e06SEd Maste 	}
530afa8e06SEd Maste 
540afa8e06SEd Maste 	return cs;
550afa8e06SEd Maste }
560afa8e06SEd Maste 
570afa8e06SEd Maste static int
copy_info(fido_dev_info_t * di,const struct hid_device_info * d)580afa8e06SEd Maste copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
590afa8e06SEd Maste {
600afa8e06SEd Maste 	memset(di, 0, sizeof(*di));
610afa8e06SEd Maste 
620afa8e06SEd Maste 	if (d->path != NULL)
630afa8e06SEd Maste 		di->path = strdup(d->path);
640afa8e06SEd Maste 	else
650afa8e06SEd Maste 		di->path = strdup("");
660afa8e06SEd Maste 
670afa8e06SEd Maste 	if (d->manufacturer_string != NULL)
680afa8e06SEd Maste 		di->manufacturer = wcs_to_cs(d->manufacturer_string);
690afa8e06SEd Maste 	else
700afa8e06SEd Maste 		di->manufacturer = strdup("");
710afa8e06SEd Maste 
720afa8e06SEd Maste 	if (d->product_string != NULL)
730afa8e06SEd Maste 		di->product = wcs_to_cs(d->product_string);
740afa8e06SEd Maste 	else
750afa8e06SEd Maste 		di->product = strdup("");
760afa8e06SEd Maste 
770afa8e06SEd Maste 	if (di->path == NULL ||
780afa8e06SEd Maste 	    di->manufacturer == NULL ||
790afa8e06SEd Maste 	    di->product == NULL) {
800afa8e06SEd Maste 		free(di->path);
810afa8e06SEd Maste 		free(di->manufacturer);
820afa8e06SEd Maste 		free(di->product);
830afa8e06SEd Maste 		explicit_bzero(di, sizeof(*di));
840afa8e06SEd Maste 		return -1;
850afa8e06SEd Maste 	}
860afa8e06SEd Maste 
870afa8e06SEd Maste 	di->product_id = (int16_t)d->product_id;
880afa8e06SEd Maste 	di->vendor_id = (int16_t)d->vendor_id;
890afa8e06SEd Maste 	di->io = (fido_dev_io_t) {
900afa8e06SEd Maste 		&fido_hid_open,
910afa8e06SEd Maste 		&fido_hid_close,
920afa8e06SEd Maste 		&fido_hid_read,
930afa8e06SEd Maste 		&fido_hid_write,
940afa8e06SEd Maste 	};
950afa8e06SEd Maste 
960afa8e06SEd Maste 	return 0;
970afa8e06SEd Maste }
980afa8e06SEd Maste 
990afa8e06SEd Maste #ifdef __linux__
1000afa8e06SEd Maste static int
get_report_descriptor(const char * path,struct hidraw_report_descriptor * hrd)1010afa8e06SEd Maste get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
1020afa8e06SEd Maste {
1030afa8e06SEd Maste 	int fd;
1040afa8e06SEd Maste 	int s = -1;
1050afa8e06SEd Maste 	int ok = -1;
1060afa8e06SEd Maste 
1070afa8e06SEd Maste 	if ((fd = fido_hid_unix_open(path)) == -1) {
1080afa8e06SEd Maste 		fido_log_debug("%s: fido_hid_unix_open", __func__);
1090afa8e06SEd Maste 		return -1;
1100afa8e06SEd Maste 	}
1110afa8e06SEd Maste 
1120afa8e06SEd Maste 	if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 ||
1130afa8e06SEd Maste 	    (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
1140afa8e06SEd Maste 		fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
1150afa8e06SEd Maste 		goto fail;
1160afa8e06SEd Maste 	}
1170afa8e06SEd Maste 
1180afa8e06SEd Maste 	hrd->size = (unsigned)s;
1190afa8e06SEd Maste 
1200afa8e06SEd Maste 	if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) {
1210afa8e06SEd Maste 		fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
1220afa8e06SEd Maste 		goto fail;
1230afa8e06SEd Maste 	}
1240afa8e06SEd Maste 
1250afa8e06SEd Maste 	ok = 0;
1260afa8e06SEd Maste fail:
1270afa8e06SEd Maste 	if (fd != -1)
1280afa8e06SEd Maste 		close(fd);
1290afa8e06SEd Maste 
1300afa8e06SEd Maste 	return ok;
1310afa8e06SEd Maste }
1320afa8e06SEd Maste 
1330afa8e06SEd Maste static bool
is_fido(const struct hid_device_info * hdi)1340afa8e06SEd Maste is_fido(const struct hid_device_info *hdi)
1350afa8e06SEd Maste {
1360afa8e06SEd Maste 	uint32_t usage_page = 0;
137*2ccfa855SEd Maste 	struct hidraw_report_descriptor *hrd;
1380afa8e06SEd Maste 
139*2ccfa855SEd Maste 	if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
140*2ccfa855SEd Maste 	    get_report_descriptor(hdi->path, hrd) < 0 ||
141*2ccfa855SEd Maste 	    fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
142*2ccfa855SEd Maste 		usage_page = 0;
1430afa8e06SEd Maste 
144*2ccfa855SEd Maste 	free(hrd);
1450afa8e06SEd Maste 
1460afa8e06SEd Maste 	return usage_page == 0xf1d0;
1470afa8e06SEd Maste }
1480afa8e06SEd Maste #elif defined(_WIN32) || defined(__APPLE__)
1490afa8e06SEd Maste static bool
is_fido(const struct hid_device_info * hdi)1500afa8e06SEd Maste is_fido(const struct hid_device_info *hdi)
1510afa8e06SEd Maste {
1520afa8e06SEd Maste 	return hdi->usage_page == 0xf1d0;
1530afa8e06SEd Maste }
1540afa8e06SEd Maste #else
1550afa8e06SEd Maste static bool
is_fido(const struct hid_device_info * hdi)1560afa8e06SEd Maste is_fido(const struct hid_device_info *hdi)
1570afa8e06SEd Maste {
1580afa8e06SEd Maste 	(void)hdi;
1590afa8e06SEd Maste 	fido_log_debug("%s: assuming FIDO HID", __func__);
1600afa8e06SEd Maste 	return true;
1610afa8e06SEd Maste }
1620afa8e06SEd Maste #endif
1630afa8e06SEd Maste 
1640afa8e06SEd Maste void *
fido_hid_open(const char * path)1650afa8e06SEd Maste fido_hid_open(const char *path)
1660afa8e06SEd Maste {
1670afa8e06SEd Maste 	struct hid_hidapi *ctx;
1680afa8e06SEd Maste 
1690afa8e06SEd Maste 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
1700afa8e06SEd Maste 		return (NULL);
1710afa8e06SEd Maste 	}
1720afa8e06SEd Maste 
1730afa8e06SEd Maste 	if ((ctx->handle = hid_open_path(path)) == NULL) {
1740afa8e06SEd Maste 		free(ctx);
1750afa8e06SEd Maste 		return (NULL);
1760afa8e06SEd Maste 	}
1770afa8e06SEd Maste 
1780afa8e06SEd Maste 	ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN;
1790afa8e06SEd Maste 
1800afa8e06SEd Maste 	return ctx;
1810afa8e06SEd Maste }
1820afa8e06SEd Maste 
1830afa8e06SEd Maste void
fido_hid_close(void * handle)1840afa8e06SEd Maste fido_hid_close(void *handle)
1850afa8e06SEd Maste {
1860afa8e06SEd Maste 	struct hid_hidapi *ctx = handle;
1870afa8e06SEd Maste 
1880afa8e06SEd Maste 	hid_close(ctx->handle);
1890afa8e06SEd Maste 	free(ctx);
1900afa8e06SEd Maste }
1910afa8e06SEd Maste 
1920afa8e06SEd Maste int
fido_hid_set_sigmask(void * handle,const fido_sigset_t * sigmask)1930afa8e06SEd Maste fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
1940afa8e06SEd Maste {
1950afa8e06SEd Maste 	(void)handle;
1960afa8e06SEd Maste 	(void)sigmask;
1970afa8e06SEd Maste 
1980afa8e06SEd Maste 	return (FIDO_ERR_INTERNAL);
1990afa8e06SEd Maste }
2000afa8e06SEd Maste 
2010afa8e06SEd Maste int
fido_hid_read(void * handle,unsigned char * buf,size_t len,int ms)2020afa8e06SEd Maste fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
2030afa8e06SEd Maste {
2040afa8e06SEd Maste 	struct hid_hidapi *ctx = handle;
2050afa8e06SEd Maste 
2060afa8e06SEd Maste 	if (len != ctx->report_in_len) {
2070afa8e06SEd Maste 		fido_log_debug("%s: len %zu", __func__, len);
2080afa8e06SEd Maste 		return -1;
2090afa8e06SEd Maste 	}
2100afa8e06SEd Maste 
2110afa8e06SEd Maste 	return hid_read_timeout(ctx->handle, buf, len, ms);
2120afa8e06SEd Maste }
2130afa8e06SEd Maste 
2140afa8e06SEd Maste int
fido_hid_write(void * handle,const unsigned char * buf,size_t len)2150afa8e06SEd Maste fido_hid_write(void *handle, const unsigned char *buf, size_t len)
2160afa8e06SEd Maste {
2170afa8e06SEd Maste 	struct hid_hidapi *ctx = handle;
2180afa8e06SEd Maste 
2190afa8e06SEd Maste 	if (len != ctx->report_out_len + 1) {
2200afa8e06SEd Maste 		fido_log_debug("%s: len %zu", __func__, len);
2210afa8e06SEd Maste 		return -1;
2220afa8e06SEd Maste 	}
2230afa8e06SEd Maste 
2240afa8e06SEd Maste 	return hid_write(ctx->handle, buf, len);
2250afa8e06SEd Maste }
2260afa8e06SEd Maste 
2270afa8e06SEd Maste int
fido_hid_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)2280afa8e06SEd Maste fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
2290afa8e06SEd Maste {
2300afa8e06SEd Maste 	struct hid_device_info *hdi;
2310afa8e06SEd Maste 
2320afa8e06SEd Maste 	*olen = 0;
2330afa8e06SEd Maste 
2340afa8e06SEd Maste 	if (ilen == 0)
2350afa8e06SEd Maste 		return FIDO_OK; /* nothing to do */
2360afa8e06SEd Maste 	if (devlist == NULL)
2370afa8e06SEd Maste 		return FIDO_ERR_INVALID_ARGUMENT;
2380afa8e06SEd Maste 	if ((hdi = hid_enumerate(0, 0)) == NULL)
2390afa8e06SEd Maste 		return FIDO_OK; /* nothing to do */
2400afa8e06SEd Maste 
2410afa8e06SEd Maste 	for (struct hid_device_info *d = hdi; d != NULL; d = d->next) {
2420afa8e06SEd Maste 		if (is_fido(d) == false)
2430afa8e06SEd Maste 			continue;
2440afa8e06SEd Maste 		if (copy_info(&devlist[*olen], d) == 0) {
2450afa8e06SEd Maste 			if (++(*olen) == ilen)
2460afa8e06SEd Maste 				break;
2470afa8e06SEd Maste 		}
2480afa8e06SEd Maste 	}
2490afa8e06SEd Maste 
2500afa8e06SEd Maste 	hid_free_enumeration(hdi);
2510afa8e06SEd Maste 
2520afa8e06SEd Maste 	return FIDO_OK;
2530afa8e06SEd Maste }
2540afa8e06SEd Maste 
2550afa8e06SEd Maste size_t
fido_hid_report_in_len(void * handle)2560afa8e06SEd Maste fido_hid_report_in_len(void *handle)
2570afa8e06SEd Maste {
2580afa8e06SEd Maste 	struct hid_hidapi *ctx = handle;
2590afa8e06SEd Maste 
2600afa8e06SEd Maste 	return (ctx->report_in_len);
2610afa8e06SEd Maste }
2620afa8e06SEd Maste 
2630afa8e06SEd Maste size_t
fido_hid_report_out_len(void * handle)2640afa8e06SEd Maste fido_hid_report_out_len(void *handle)
2650afa8e06SEd Maste {
2660afa8e06SEd Maste 	struct hid_hidapi *ctx = handle;
2670afa8e06SEd Maste 
2680afa8e06SEd Maste 	return (ctx->report_out_len);
2690afa8e06SEd Maste }
270