]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/io.c
zfs: merge openzfs/zfs@95f71c019
[FreeBSD/FreeBSD.git] / contrib / libfido2 / src / io.c
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 #include "packed.h"
9
10 PACKED_TYPE(frame_t,
11 struct frame {
12         uint32_t cid; /* channel id */
13         union {
14                 uint8_t type;
15                 struct {
16                         uint8_t cmd;
17                         uint8_t bcnth;
18                         uint8_t bcntl;
19                         uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
20                 } init;
21                 struct {
22                         uint8_t seq;
23                         uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
24                 } cont;
25         } body;
26 })
27
28 #ifndef MIN
29 #define MIN(x, y) ((x) > (y) ? (y) : (x))
30 #endif
31
32 static int
33 tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
34 {
35         struct timespec ts;
36         int n;
37
38         if (fido_time_now(&ts) != 0)
39                 return (-1);
40
41         n = d->io.write(d->io_handle, pkt, len);
42
43         if (fido_time_delta(&ts, ms) != 0)
44                 return (-1);
45
46         return (n);
47 }
48
49 static int
50 tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
51 {
52         struct frame    *fp;
53         unsigned char    pkt[sizeof(*fp) + 1];
54         const size_t     len = d->tx_len + 1;
55         int              n;
56
57         memset(&pkt, 0, sizeof(pkt));
58         fp = (struct frame *)(pkt + 1);
59         fp->cid = d->cid;
60         fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
61
62         if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
63             (size_t)n != len)
64                 return (-1);
65
66         return (0);
67 }
68
69 static size_t
70 tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
71 {
72         struct frame    *fp;
73         unsigned char    pkt[sizeof(*fp) + 1];
74         const size_t     len = d->tx_len + 1;
75         int              n;
76
77         if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
78                 return (0);
79
80         memset(&pkt, 0, sizeof(pkt));
81         fp = (struct frame *)(pkt + 1);
82         fp->cid = d->cid;
83         fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
84         fp->body.init.bcnth = (count >> 8) & 0xff;
85         fp->body.init.bcntl = count & 0xff;
86         count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
87         memcpy(&fp->body.init.data, buf, count);
88
89         if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
90             (size_t)n != len)
91                 return (0);
92
93         return (count);
94 }
95
96 static size_t
97 tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
98 {
99         struct frame    *fp;
100         unsigned char    pkt[sizeof(*fp) + 1];
101         const size_t     len = d->tx_len + 1;
102         int              n;
103
104         if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
105                 return (0);
106
107         memset(&pkt, 0, sizeof(pkt));
108         fp = (struct frame *)(pkt + 1);
109         fp->cid = d->cid;
110         fp->body.cont.seq = seq;
111         count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
112         memcpy(&fp->body.cont.data, buf, count);
113
114         if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
115             (size_t)n != len)
116                 return (0);
117
118         return (count);
119 }
120
121 static int
122 tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
123 {
124         size_t n, sent;
125
126         if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
127                 fido_log_debug("%s: tx_preamble", __func__);
128                 return (-1);
129         }
130
131         for (uint8_t seq = 0; sent < count; sent += n) {
132                 if (seq & 0x80) {
133                         fido_log_debug("%s: seq & 0x80", __func__);
134                         return (-1);
135                 }
136                 if ((n = tx_frame(d, seq++, buf + sent, count - sent,
137                     ms)) == 0) {
138                         fido_log_debug("%s: tx_frame", __func__);
139                         return (-1);
140                 }
141         }
142
143         return (0);
144 }
145
146 static int
147 transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
148 {
149         struct timespec ts;
150         int n;
151
152         if (fido_time_now(&ts) != 0)
153                 return (-1);
154
155         n = d->transport.tx(d, cmd, buf, count);
156
157         if (fido_time_delta(&ts, ms) != 0)
158                 return (-1);
159
160         return (n);
161 }
162
163 int
164 fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
165 {
166         fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
167         fido_log_xxd(buf, count, "%s", __func__);
168
169         if (d->transport.tx != NULL)
170                 return (transport_tx(d, cmd, buf, count, ms));
171         if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
172                 fido_log_debug("%s: invalid argument", __func__);
173                 return (-1);
174         }
175
176         return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
177 }
178
179 static int
180 rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
181 {
182         struct timespec ts;
183         int n;
184
185         memset(fp, 0, sizeof(*fp));
186
187         if (fido_time_now(&ts) != 0)
188                 return (-1);
189
190         if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
191             (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
192                 return (-1);
193
194         return (fido_time_delta(&ts, ms));
195 }
196
197 static int
198 rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
199 {
200         do {
201                 if (rx_frame(d, fp, ms) < 0)
202                         return (-1);
203 #ifdef FIDO_FUZZ
204                 fp->cid = d->cid;
205 #endif
206         } while (fp->cid != d->cid || (fp->cid == d->cid &&
207             fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
208
209         if (d->rx_len > sizeof(*fp))
210                 return (-1);
211
212         fido_log_xxd(fp, d->rx_len, "%s", __func__);
213 #ifdef FIDO_FUZZ
214         fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
215 #endif
216
217         if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
218                 fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
219                     __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
220                 return (-1);
221         }
222
223         return (0);
224 }
225
226 static int
227 rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
228 {
229         struct frame f;
230         size_t r, payload_len, init_data_len, cont_data_len;
231
232         if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
233             d->rx_len <= CTAP_CONT_HEADER_LEN)
234                 return (-1);
235
236         init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
237         cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
238
239         if (init_data_len > sizeof(f.body.init.data) ||
240             cont_data_len > sizeof(f.body.cont.data))
241                 return (-1);
242
243         if (rx_preamble(d, cmd, &f, ms) < 0) {
244                 fido_log_debug("%s: rx_preamble", __func__);
245                 return (-1);
246         }
247
248         payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
249         fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
250
251         if (count < payload_len) {
252                 fido_log_debug("%s: count < payload_len", __func__);
253                 return (-1);
254         }
255
256         if (payload_len < init_data_len) {
257                 memcpy(buf, f.body.init.data, payload_len);
258                 return ((int)payload_len);
259         }
260
261         memcpy(buf, f.body.init.data, init_data_len);
262         r = init_data_len;
263
264         for (int seq = 0; r < payload_len; seq++) {
265                 if (rx_frame(d, &f, ms) < 0) {
266                         fido_log_debug("%s: rx_frame", __func__);
267                         return (-1);
268                 }
269
270                 fido_log_xxd(&f, d->rx_len, "%s", __func__);
271 #ifdef FIDO_FUZZ
272                 f.cid = d->cid;
273                 f.body.cont.seq = (uint8_t)seq;
274 #endif
275
276                 if (f.cid != d->cid || f.body.cont.seq != seq) {
277                         fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
278                             __func__, f.cid, d->cid, f.body.cont.seq, seq);
279                         return (-1);
280                 }
281
282                 if (payload_len - r > cont_data_len) {
283                         memcpy(buf + r, f.body.cont.data, cont_data_len);
284                         r += cont_data_len;
285                 } else {
286                         memcpy(buf + r, f.body.cont.data, payload_len - r);
287                         r += payload_len - r; /* break */
288                 }
289         }
290
291         return ((int)r);
292 }
293
294 static int
295 transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
296 {
297         struct timespec ts;
298         int n;
299
300         if (fido_time_now(&ts) != 0)
301                 return (-1);
302
303         n = d->transport.rx(d, cmd, buf, count, *ms);
304
305         if (fido_time_delta(&ts, ms) != 0)
306                 return (-1);
307
308         return (n);
309 }
310
311 int
312 fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
313 {
314         int n;
315
316         fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
317             cmd, *ms);
318
319         if (d->transport.rx != NULL)
320                 return (transport_rx(d, cmd, buf, count, ms));
321         if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
322                 fido_log_debug("%s: invalid argument", __func__);
323                 return (-1);
324         }
325         if ((n = rx(d, cmd, buf, count, ms)) >= 0)
326                 fido_log_xxd(buf, (size_t)n, "%s", __func__);
327
328         return (n);
329 }
330
331 int
332 fido_rx_cbor_status(fido_dev_t *d, int *ms)
333 {
334         unsigned char   reply[FIDO_MAXMSG];
335         int             reply_len;
336
337         if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
338             ms)) < 0 || (size_t)reply_len < 1) {
339                 fido_log_debug("%s: fido_rx", __func__);
340                 return (FIDO_ERR_RX);
341         }
342
343         return (reply[0]);
344 }