1 /*
2 * Copyright (c) 2024 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 #include <stdint.h>
14
15 #define _FIDO_INTERNAL
16 #include <fido.h>
17
18 #include "extern.h"
19 #include "../fuzz/wiredata_fido2.h"
20
21 #define REPORT_LEN (64 + 1)
22
23 static uint8_t ctap_nonce[8];
24 static uint8_t *wiredata_ptr;
25 static size_t wiredata_len;
26 static int fake_dev_handle;
27 static int initialised;
28 static long interval_ms;
29
30 #if defined(_MSC_VER)
31 static int
nanosleep(const struct timespec * rqtp,struct timespec * rmtp)32 nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
33 {
34 if (rmtp != NULL) {
35 errno = EINVAL;
36 return (-1);
37 }
38
39 Sleep((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(rqtp->tv_nsec / 1000000));
40
41 return (0);
42 }
43 #endif
44
45 static void *
dummy_open(const char * path)46 dummy_open(const char *path)
47 {
48 (void)path;
49
50 return (&fake_dev_handle);
51 }
52
53 static void
dummy_close(void * handle)54 dummy_close(void *handle)
55 {
56 assert(handle == &fake_dev_handle);
57 }
58
59 static int
dummy_read(void * handle,unsigned char * ptr,size_t len,int ms)60 dummy_read(void *handle, unsigned char *ptr, size_t len, int ms)
61 {
62 struct timespec tv;
63 size_t n;
64 long d;
65
66 assert(handle == &fake_dev_handle);
67 assert(ptr != NULL);
68 assert(len == REPORT_LEN - 1);
69
70 if (wiredata_ptr == NULL)
71 return (-1);
72
73 if (!initialised) {
74 assert(wiredata_len >= REPORT_LEN - 1);
75 memcpy(&wiredata_ptr[7], &ctap_nonce, sizeof(ctap_nonce));
76 initialised = 1;
77 }
78
79 if (ms >= 0 && ms < interval_ms)
80 d = ms;
81 else
82 d = interval_ms;
83
84 if (d) {
85 tv.tv_sec = d / 1000;
86 tv.tv_nsec = (d % 1000) * 1000000;
87 if (nanosleep(&tv, NULL) == -1)
88 err(1, "nanosleep");
89 }
90
91 if (d != interval_ms)
92 return (-1); /* timeout */
93
94 if (wiredata_len < len)
95 n = wiredata_len;
96 else
97 n = len;
98
99 memcpy(ptr, wiredata_ptr, n);
100 wiredata_ptr += n;
101 wiredata_len -= n;
102
103 return ((int)n);
104 }
105
106 static int
dummy_write(void * handle,const unsigned char * ptr,size_t len)107 dummy_write(void *handle, const unsigned char *ptr, size_t len)
108 {
109 struct timespec tv;
110
111 assert(handle == &fake_dev_handle);
112 assert(ptr != NULL);
113 assert(len == REPORT_LEN);
114
115 if (!initialised)
116 memcpy(&ctap_nonce, &ptr[8], sizeof(ctap_nonce));
117
118 if (interval_ms) {
119 tv.tv_sec = interval_ms / 1000;
120 tv.tv_nsec = (interval_ms % 1000) * 1000000;
121 if (nanosleep(&tv, NULL) == -1)
122 err(1, "nanosleep");
123 }
124
125 return ((int)len);
126 }
127
128 uint8_t *
wiredata_setup(const uint8_t * data,size_t len)129 wiredata_setup(const uint8_t *data, size_t len)
130 {
131 const uint8_t ctap_init_data[] = { WIREDATA_CTAP_INIT };
132
133 assert(wiredata_ptr == NULL);
134 assert(SIZE_MAX - len > sizeof(ctap_init_data));
135 assert((wiredata_ptr = malloc(sizeof(ctap_init_data) + len)) != NULL);
136
137 #if defined(_MSC_VER)
138 #pragma warning(push)
139 #pragma warning(disable:6386)
140 #endif
141 memcpy(wiredata_ptr, ctap_init_data, sizeof(ctap_init_data));
142 #if defined(_MSC_VER)
143 #pragma warning(pop)
144 #endif
145
146 if (len)
147 memcpy(wiredata_ptr + sizeof(ctap_init_data), data, len);
148
149 wiredata_len = sizeof(ctap_init_data) + len;
150
151 return (wiredata_ptr);
152 }
153
154 void
wiredata_clear(uint8_t ** wiredata)155 wiredata_clear(uint8_t **wiredata)
156 {
157 free(*wiredata);
158 *wiredata = NULL;
159 wiredata_ptr = NULL;
160 wiredata_len = 0;
161 initialised = 0;
162 }
163
164 void
setup_dummy_io(fido_dev_t * dev)165 setup_dummy_io(fido_dev_t *dev)
166 {
167 fido_dev_io_t io;
168
169 memset(&io, 0, sizeof(io));
170 io.open = dummy_open;
171 io.close = dummy_close;
172 io.read = dummy_read;
173 io.write = dummy_write;
174
175 assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
176 }
177
178 void
set_read_interval(long ms)179 set_read_interval(long ms)
180 {
181 interval_ms = ms;
182 }
183