xref: /freebsd/contrib/libfido2/src/hid.c (revision 4e8d558c)
1 /*
2  * Copyright (c) 2018 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 "fido.h"
8 
9 static int
10 get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
11 {
12 	*key = tag & 0xfc;
13 	if ((*key & 0xf0) == 0xf0) {
14 		fido_log_debug("%s: *key=0x%02x", __func__, *key);
15 		return (-1);
16 	}
17 
18 	*key_len = tag & 0x3;
19 	if (*key_len == 3) {
20 		*key_len = 4;
21 	}
22 
23 	return (0);
24 }
25 
26 static int
27 get_key_val(const void *body, size_t key_len, uint32_t *val)
28 {
29 	const uint8_t *ptr = body;
30 
31 	switch (key_len) {
32 	case 0:
33 		*val = 0;
34 		break;
35 	case 1:
36 		*val = ptr[0];
37 		break;
38 	case 2:
39 		*val = (uint32_t)((ptr[1] << 8) | ptr[0]);
40 		break;
41 	default:
42 		fido_log_debug("%s: key_len=%zu", __func__, key_len);
43 		return (-1);
44 	}
45 
46 	return (0);
47 }
48 
49 int
50 fido_hid_get_usage(const uint8_t *report_ptr, size_t report_len,
51     uint32_t *usage_page)
52 {
53 	const uint8_t	*ptr = report_ptr;
54 	size_t		 len = report_len;
55 
56 	while (len > 0) {
57 		const uint8_t tag = ptr[0];
58 		ptr++;
59 		len--;
60 
61 		uint8_t  key;
62 		size_t   key_len;
63 		uint32_t key_val;
64 
65 		if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
66 		    get_key_val(ptr, key_len, &key_val) < 0) {
67 			return (-1);
68 		}
69 
70 		if (key == 0x4) {
71 			*usage_page = key_val;
72 		}
73 
74 		ptr += key_len;
75 		len -= key_len;
76 	}
77 
78 	return (0);
79 }
80 
81 int
82 fido_hid_get_report_len(const uint8_t *report_ptr, size_t report_len,
83     size_t *report_in_len, size_t *report_out_len)
84 {
85 	const uint8_t	*ptr = report_ptr;
86 	size_t		 len = report_len;
87 	uint32_t	 report_size = 0;
88 
89 	while (len > 0) {
90 		const uint8_t tag = ptr[0];
91 		ptr++;
92 		len--;
93 
94 		uint8_t  key;
95 		size_t   key_len;
96 		uint32_t key_val;
97 
98 		if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
99 		    get_key_val(ptr, key_len, &key_val) < 0) {
100 			return (-1);
101 		}
102 
103 		if (key == 0x94) {
104 			report_size = key_val;
105 		} else if (key == 0x80) {
106 			*report_in_len = (size_t)report_size;
107 		} else if (key == 0x90) {
108 			*report_out_len = (size_t)report_size;
109 		}
110 
111 		ptr += key_len;
112 		len -= key_len;
113 	}
114 
115 	return (0);
116 }
117 
118 fido_dev_info_t *
119 fido_dev_info_new(size_t n)
120 {
121 	return (calloc(n, sizeof(fido_dev_info_t)));
122 }
123 
124 static void
125 fido_dev_info_reset(fido_dev_info_t *di)
126 {
127 	free(di->path);
128 	free(di->manufacturer);
129 	free(di->product);
130 	memset(di, 0, sizeof(*di));
131 }
132 
133 void
134 fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
135 {
136 	fido_dev_info_t *devlist;
137 
138 	if (devlist_p == NULL || (devlist = *devlist_p) == NULL)
139 		return;
140 
141 	for (size_t i = 0; i < n; i++)
142 		fido_dev_info_reset(&devlist[i]);
143 
144 	free(devlist);
145 
146 	*devlist_p = NULL;
147 }
148 
149 const fido_dev_info_t *
150 fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i)
151 {
152 	return (&devlist[i]);
153 }
154 
155 int
156 fido_dev_info_set(fido_dev_info_t *devlist, size_t i,
157     const char *path, const char *manufacturer, const char *product,
158     const fido_dev_io_t *io, const fido_dev_transport_t *transport)
159 {
160 	char *path_copy = NULL, *manu_copy = NULL, *prod_copy = NULL;
161 	int r;
162 
163 	if (path == NULL || manufacturer == NULL || product == NULL ||
164 	    io == NULL) {
165 		r = FIDO_ERR_INVALID_ARGUMENT;
166 		goto out;
167 	}
168 
169 	if ((path_copy = strdup(path)) == NULL ||
170 	    (manu_copy = strdup(manufacturer)) == NULL ||
171 	    (prod_copy = strdup(product)) == NULL) {
172 		r = FIDO_ERR_INTERNAL;
173 		goto out;
174 	}
175 
176 	fido_dev_info_reset(&devlist[i]);
177 	devlist[i].path = path_copy;
178 	devlist[i].manufacturer = manu_copy;
179 	devlist[i].product = prod_copy;
180 	devlist[i].io = *io;
181 	if (transport)
182 		devlist[i].transport = *transport;
183 	r = FIDO_OK;
184 out:
185 	if (r != FIDO_OK) {
186 		free(prod_copy);
187 		free(manu_copy);
188 		free(path_copy);
189 	}
190 	return (r);
191 }
192 
193 const char *
194 fido_dev_info_path(const fido_dev_info_t *di)
195 {
196 	return (di->path);
197 }
198 
199 int16_t
200 fido_dev_info_vendor(const fido_dev_info_t *di)
201 {
202 	return (di->vendor_id);
203 }
204 
205 int16_t
206 fido_dev_info_product(const fido_dev_info_t *di)
207 {
208 	return (di->product_id);
209 }
210 
211 const char *
212 fido_dev_info_manufacturer_string(const fido_dev_info_t *di)
213 {
214 	return (di->manufacturer);
215 }
216 
217 const char *
218 fido_dev_info_product_string(const fido_dev_info_t *di)
219 {
220 	return (di->product);
221 }
222