1/*
2 * Copyright (c) 2019-2022 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 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#undef NDEBUG
9
10#include <assert.h>
11#include <string.h>
12#include <time.h>
13
14#define _FIDO_INTERNAL
15
16#include <fido.h>
17
18#include "../fuzz/wiredata_fido2.h"
19
20#define REPORT_LEN	(64 + 1)
21
22static uint8_t	 ctap_nonce[8];
23static uint8_t	*wiredata_ptr;
24static size_t	 wiredata_len;
25static int	 fake_dev_handle;
26static int	 initialised;
27static long	 interval_ms;
28
29#if defined(_MSC_VER)
30static int
31nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
32{
33	if (rmtp != NULL) {
34		errno = EINVAL;
35		return (-1);
36	}
37
38	Sleep((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(rqtp->tv_nsec / 1000000));
39
40	return (0);
41}
42#endif
43
44static void *
45dummy_open(const char *path)
46{
47	(void)path;
48
49	return (&fake_dev_handle);
50}
51
52static void
53dummy_close(void *handle)
54{
55	assert(handle == &fake_dev_handle);
56}
57
58static int
59dummy_read(void *handle, unsigned char *ptr, size_t len, int ms)
60{
61	struct timespec tv;
62	size_t		n;
63	long		d;
64
65	assert(handle == &fake_dev_handle);
66	assert(ptr != NULL);
67	assert(len == REPORT_LEN - 1);
68
69	if (wiredata_ptr == NULL)
70		return (-1);
71
72	if (!initialised) {
73		assert(wiredata_len >= REPORT_LEN - 1);
74		memcpy(&wiredata_ptr[7], &ctap_nonce, sizeof(ctap_nonce));
75		initialised = 1;
76	}
77
78	if (ms >= 0 && ms < interval_ms)
79		d = ms;
80	else
81		d = interval_ms;
82
83	if (d) {
84		tv.tv_sec = d / 1000;
85		tv.tv_nsec = (d % 1000) * 1000000;
86		if (nanosleep(&tv, NULL) == -1)
87			err(1, "nanosleep");
88	}
89
90	if (d != interval_ms)
91		return (-1); /* timeout */
92
93	if (wiredata_len < len)
94		n = wiredata_len;
95	else
96		n = len;
97
98	memcpy(ptr, wiredata_ptr, n);
99	wiredata_ptr += n;
100	wiredata_len -= n;
101
102	return ((int)n);
103}
104
105static int
106dummy_write(void *handle, const unsigned char *ptr, size_t len)
107{
108	struct timespec tv;
109
110	assert(handle == &fake_dev_handle);
111	assert(ptr != NULL);
112	assert(len == REPORT_LEN);
113
114	if (!initialised)
115		memcpy(&ctap_nonce, &ptr[8], sizeof(ctap_nonce));
116
117	if (interval_ms) {
118		tv.tv_sec = interval_ms / 1000;
119		tv.tv_nsec = (interval_ms % 1000) * 1000000;
120		if (nanosleep(&tv, NULL) == -1)
121			err(1, "nanosleep");
122	}
123
124	return ((int)len);
125}
126
127static uint8_t *
128wiredata_setup(const uint8_t *data, size_t len)
129{
130	const uint8_t ctap_init_data[] = { WIREDATA_CTAP_INIT };
131
132	assert(wiredata_ptr == NULL);
133	assert(SIZE_MAX - len > sizeof(ctap_init_data));
134	assert((wiredata_ptr = malloc(sizeof(ctap_init_data) + len)) != NULL);
135
136#if defined(_MSC_VER)
137#pragma warning(push)
138#pragma warning(disable:6386)
139#endif
140	memcpy(wiredata_ptr, ctap_init_data, sizeof(ctap_init_data));
141#if defined(_MSC_VER)
142#pragma warning(pop)
143#endif
144
145	if (len)
146		memcpy(wiredata_ptr + sizeof(ctap_init_data), data, len);
147
148	wiredata_len = sizeof(ctap_init_data) + len;
149
150	return (wiredata_ptr);
151}
152
153static void
154wiredata_clear(uint8_t **wiredata)
155{
156	free(*wiredata);
157	*wiredata = NULL;
158	wiredata_ptr = NULL;
159	wiredata_len = 0;
160	initialised = 0;
161}
162
163/* gh#56 */
164static void
165open_iff_ok(void)
166{
167	fido_dev_t	*dev = NULL;
168	fido_dev_io_t	 io;
169
170	memset(&io, 0, sizeof(io));
171
172	io.open = dummy_open;
173	io.close = dummy_close;
174	io.read = dummy_read;
175	io.write = dummy_write;
176
177	assert((dev = fido_dev_new()) != NULL);
178	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
179	assert(fido_dev_open(dev, "dummy") == FIDO_ERR_RX);
180	assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT);
181
182	fido_dev_free(&dev);
183}
184
185static void
186reopen(void)
187{
188	const uint8_t	 cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO };
189	uint8_t		*wiredata;
190	fido_dev_t	*dev = NULL;
191	fido_dev_io_t	 io;
192
193	memset(&io, 0, sizeof(io));
194
195	io.open = dummy_open;
196	io.close = dummy_close;
197	io.read = dummy_read;
198	io.write = dummy_write;
199
200	wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data));
201	assert((dev = fido_dev_new()) != NULL);
202	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
203	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
204	assert(fido_dev_close(dev) == FIDO_OK);
205	wiredata_clear(&wiredata);
206
207	wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data));
208	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
209	assert(fido_dev_close(dev) == FIDO_OK);
210	fido_dev_free(&dev);
211	wiredata_clear(&wiredata);
212}
213
214static void
215double_open(void)
216{
217	const uint8_t	 cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO };
218	uint8_t		*wiredata;
219	fido_dev_t	*dev = NULL;
220	fido_dev_io_t	 io;
221
222	memset(&io, 0, sizeof(io));
223
224	io.open = dummy_open;
225	io.close = dummy_close;
226	io.read = dummy_read;
227	io.write = dummy_write;
228
229	wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data));
230	assert((dev = fido_dev_new()) != NULL);
231	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
232	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
233	assert(fido_dev_open(dev, "dummy") == FIDO_ERR_INVALID_ARGUMENT);
234	assert(fido_dev_close(dev) == FIDO_OK);
235	fido_dev_free(&dev);
236	wiredata_clear(&wiredata);
237}
238
239static void
240double_close(void)
241{
242	const uint8_t	 cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO };
243	uint8_t		*wiredata;
244	fido_dev_t	*dev = NULL;
245	fido_dev_io_t	 io;
246
247	memset(&io, 0, sizeof(io));
248
249	io.open = dummy_open;
250	io.close = dummy_close;
251	io.read = dummy_read;
252	io.write = dummy_write;
253
254	wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data));
255	assert((dev = fido_dev_new()) != NULL);
256	assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT);
257	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
258	assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT);
259	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
260	assert(fido_dev_close(dev) == FIDO_OK);
261	assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT);
262	fido_dev_free(&dev);
263	wiredata_clear(&wiredata);
264}
265
266static void
267is_fido2(void)
268{
269	const uint8_t	 cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO };
270	uint8_t		*wiredata;
271	fido_dev_t	*dev = NULL;
272	fido_dev_io_t	 io;
273
274	memset(&io, 0, sizeof(io));
275
276	io.open = dummy_open;
277	io.close = dummy_close;
278	io.read = dummy_read;
279	io.write = dummy_write;
280
281	wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data));
282	assert((dev = fido_dev_new()) != NULL);
283	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
284	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
285	assert(fido_dev_is_fido2(dev) == true);
286	assert(fido_dev_supports_pin(dev) == true);
287	fido_dev_force_u2f(dev);
288	assert(fido_dev_is_fido2(dev) == false);
289	assert(fido_dev_supports_pin(dev) == false);
290	assert(fido_dev_close(dev) == FIDO_OK);
291	wiredata_clear(&wiredata);
292
293	wiredata = wiredata_setup(NULL, 0);
294	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
295	assert(fido_dev_is_fido2(dev) == false);
296	assert(fido_dev_supports_pin(dev) == false);
297	fido_dev_force_fido2(dev);
298	assert(fido_dev_is_fido2(dev) == true);
299	assert(fido_dev_supports_pin(dev) == false);
300	assert(fido_dev_close(dev) == FIDO_OK);
301	fido_dev_free(&dev);
302	wiredata_clear(&wiredata);
303}
304
305static void
306has_pin(void)
307{
308	const uint8_t	 set_pin_data[] = {
309			    WIREDATA_CTAP_CBOR_INFO,
310			    WIREDATA_CTAP_CBOR_AUTHKEY,
311			    WIREDATA_CTAP_CBOR_STATUS,
312			    WIREDATA_CTAP_CBOR_STATUS
313			 };
314	uint8_t		*wiredata;
315	fido_dev_t	*dev = NULL;
316	fido_dev_io_t	 io;
317
318	memset(&io, 0, sizeof(io));
319
320	io.open = dummy_open;
321	io.close = dummy_close;
322	io.read = dummy_read;
323	io.write = dummy_write;
324
325	wiredata = wiredata_setup(set_pin_data, sizeof(set_pin_data));
326	assert((dev = fido_dev_new()) != NULL);
327	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
328	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
329	assert(fido_dev_has_pin(dev) == false);
330	assert(fido_dev_set_pin(dev, "top secret", NULL) == FIDO_OK);
331	assert(fido_dev_has_pin(dev) == true);
332	assert(fido_dev_reset(dev) == FIDO_OK);
333	assert(fido_dev_has_pin(dev) == false);
334	assert(fido_dev_close(dev) == FIDO_OK);
335	fido_dev_free(&dev);
336	wiredata_clear(&wiredata);
337}
338
339static void
340timeout_rx(void)
341{
342	const uint8_t	 timeout_rx_data[] = {
343			    WIREDATA_CTAP_CBOR_INFO,
344			    WIREDATA_CTAP_KEEPALIVE,
345			    WIREDATA_CTAP_KEEPALIVE,
346			    WIREDATA_CTAP_KEEPALIVE,
347			    WIREDATA_CTAP_KEEPALIVE,
348			    WIREDATA_CTAP_KEEPALIVE,
349			    WIREDATA_CTAP_CBOR_STATUS
350			 };
351	uint8_t		*wiredata;
352	fido_dev_t	*dev = NULL;
353	fido_dev_io_t	 io;
354
355	memset(&io, 0, sizeof(io));
356
357	io.open = dummy_open;
358	io.close = dummy_close;
359	io.read = dummy_read;
360	io.write = dummy_write;
361
362	wiredata = wiredata_setup(timeout_rx_data, sizeof(timeout_rx_data));
363	assert((dev = fido_dev_new()) != NULL);
364	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
365	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
366	assert(fido_dev_set_timeout(dev, 3 * 1000) == FIDO_OK);
367	interval_ms = 1000;
368	assert(fido_dev_reset(dev) == FIDO_ERR_RX);
369	assert(fido_dev_close(dev) == FIDO_OK);
370	fido_dev_free(&dev);
371	wiredata_clear(&wiredata);
372	interval_ms = 0;
373}
374
375static void
376timeout_ok(void)
377{
378	const uint8_t	 timeout_ok_data[] = {
379			    WIREDATA_CTAP_CBOR_INFO,
380			    WIREDATA_CTAP_KEEPALIVE,
381			    WIREDATA_CTAP_KEEPALIVE,
382			    WIREDATA_CTAP_KEEPALIVE,
383			    WIREDATA_CTAP_KEEPALIVE,
384			    WIREDATA_CTAP_KEEPALIVE,
385			    WIREDATA_CTAP_CBOR_STATUS
386			 };
387	uint8_t		*wiredata;
388	fido_dev_t	*dev = NULL;
389	fido_dev_io_t	 io;
390
391	memset(&io, 0, sizeof(io));
392
393	io.open = dummy_open;
394	io.close = dummy_close;
395	io.read = dummy_read;
396	io.write = dummy_write;
397
398	wiredata = wiredata_setup(timeout_ok_data, sizeof(timeout_ok_data));
399	assert((dev = fido_dev_new()) != NULL);
400	assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
401	assert(fido_dev_open(dev, "dummy") == FIDO_OK);
402	assert(fido_dev_set_timeout(dev, 30 * 1000) == FIDO_OK);
403	interval_ms = 1000;
404	assert(fido_dev_reset(dev) == FIDO_OK);
405	assert(fido_dev_close(dev) == FIDO_OK);
406	fido_dev_free(&dev);
407	wiredata_clear(&wiredata);
408	interval_ms = 0;
409}
410
411static void
412timeout_misc(void)
413{
414	fido_dev_t *dev;
415
416	assert((dev = fido_dev_new()) != NULL);
417	assert(fido_dev_set_timeout(dev, -2) == FIDO_ERR_INVALID_ARGUMENT);
418	assert(fido_dev_set_timeout(dev, 3 * 1000) == FIDO_OK);
419	assert(fido_dev_set_timeout(dev, -1) == FIDO_OK);
420	fido_dev_free(&dev);
421}
422
423int
424main(void)
425{
426	fido_init(0);
427
428	open_iff_ok();
429	reopen();
430	double_open();
431	double_close();
432	is_fido2();
433	has_pin();
434	timeout_rx();
435	timeout_ok();
436	timeout_misc();
437
438	exit(0);
439}
440