]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/io.c
Merge bearssl-20220418
[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_empty(fido_dev_t *d, uint8_t cmd)
34 {
35         struct frame    *fp;
36         unsigned char    pkt[sizeof(*fp) + 1];
37         const size_t     len = d->tx_len + 1;
38         int              n;
39
40         memset(&pkt, 0, sizeof(pkt));
41         fp = (struct frame *)(pkt + 1);
42         fp->cid = d->cid;
43         fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
44
45         if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
46             len)) < 0 || (size_t)n != len)
47                 return (-1);
48
49         return (0);
50 }
51
52 static size_t
53 tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
54 {
55         struct frame    *fp;
56         unsigned char    pkt[sizeof(*fp) + 1];
57         const size_t     len = d->tx_len + 1;
58         int              n;
59
60         if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
61                 return (0);
62
63         memset(&pkt, 0, sizeof(pkt));
64         fp = (struct frame *)(pkt + 1);
65         fp->cid = d->cid;
66         fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
67         fp->body.init.bcnth = (count >> 8) & 0xff;
68         fp->body.init.bcntl = count & 0xff;
69         count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
70         memcpy(&fp->body.init.data, buf, count);
71
72         if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
73             len)) < 0 || (size_t)n != len)
74                 return (0);
75
76         return (count);
77 }
78
79 static size_t
80 tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
81 {
82         struct frame    *fp;
83         unsigned char    pkt[sizeof(*fp) + 1];
84         const size_t     len = d->tx_len + 1;
85         int              n;
86
87         if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
88                 return (0);
89
90         memset(&pkt, 0, sizeof(pkt));
91         fp = (struct frame *)(pkt + 1);
92         fp->cid = d->cid;
93         fp->body.cont.seq = seq;
94         count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
95         memcpy(&fp->body.cont.data, buf, count);
96
97         if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
98             len)) < 0 || (size_t)n != len)
99                 return (0);
100
101         return (count);
102 }
103
104 static int
105 tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
106 {
107         size_t n, sent;
108
109         if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
110                 fido_log_debug("%s: tx_preamble", __func__);
111                 return (-1);
112         }
113
114         for (uint8_t seq = 0; sent < count; sent += n) {
115                 if (seq & 0x80) {
116                         fido_log_debug("%s: seq & 0x80", __func__);
117                         return (-1);
118                 }
119                 if ((n = tx_frame(d, seq++, buf + sent, count - sent)) == 0) {
120                         fido_log_debug("%s: tx_frame", __func__);
121                         return (-1);
122                 }
123         }
124
125         return (0);
126 }
127
128 int
129 fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
130 {
131         fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
132         fido_log_xxd(buf, count, "%s", __func__);
133
134         if (d->transport.tx != NULL)
135                 return (d->transport.tx(d, cmd, buf, count));
136         if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
137                 fido_log_debug("%s: invalid argument", __func__);
138                 return (-1);
139         }
140
141         return (count == 0 ? tx_empty(d, cmd) : tx(d, cmd, buf, count));
142 }
143
144 static int
145 rx_frame(fido_dev_t *d, struct frame *fp, int ms)
146 {
147         int n;
148
149         memset(fp, 0, sizeof(*fp));
150
151         if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
152             (unsigned char *)fp, d->rx_len, ms)) < 0 || (size_t)n != d->rx_len)
153                 return (-1);
154
155         return (0);
156 }
157
158 static int
159 rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
160 {
161         do {
162                 if (rx_frame(d, fp, ms) < 0)
163                         return (-1);
164 #ifdef FIDO_FUZZ
165                 fp->cid = d->cid;
166 #endif
167         } while (fp->cid != d->cid || (fp->cid == d->cid &&
168             fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
169
170         if (d->rx_len > sizeof(*fp))
171                 return (-1);
172
173         fido_log_xxd(fp, d->rx_len, "%s", __func__);
174 #ifdef FIDO_FUZZ
175         fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
176 #endif
177
178         if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
179                 fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
180                     __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
181                 return (-1);
182         }
183
184         return (0);
185 }
186
187 static int
188 rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
189 {
190         struct frame f;
191         size_t r, payload_len, init_data_len, cont_data_len;
192
193         if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
194             d->rx_len <= CTAP_CONT_HEADER_LEN)
195                 return (-1);
196
197         init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
198         cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
199
200         if (init_data_len > sizeof(f.body.init.data) ||
201             cont_data_len > sizeof(f.body.cont.data))
202                 return (-1);
203
204         if (rx_preamble(d, cmd, &f, ms) < 0) {
205                 fido_log_debug("%s: rx_preamble", __func__);
206                 return (-1);
207         }
208
209         payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
210         fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
211
212         if (count < payload_len) {
213                 fido_log_debug("%s: count < payload_len", __func__);
214                 return (-1);
215         }
216
217         if (payload_len < init_data_len) {
218                 memcpy(buf, f.body.init.data, payload_len);
219                 return ((int)payload_len);
220         }
221
222         memcpy(buf, f.body.init.data, init_data_len);
223         r = init_data_len;
224
225         for (int seq = 0; r < payload_len; seq++) {
226                 if (rx_frame(d, &f, ms) < 0) {
227                         fido_log_debug("%s: rx_frame", __func__);
228                         return (-1);
229                 }
230
231                 fido_log_xxd(&f, d->rx_len, "%s", __func__);
232 #ifdef FIDO_FUZZ
233                 f.cid = d->cid;
234                 f.body.cont.seq = (uint8_t)seq;
235 #endif
236
237                 if (f.cid != d->cid || f.body.cont.seq != seq) {
238                         fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
239                             __func__, f.cid, d->cid, f.body.cont.seq, seq);
240                         return (-1);
241                 }
242
243                 if (payload_len - r > cont_data_len) {
244                         memcpy(buf + r, f.body.cont.data, cont_data_len);
245                         r += cont_data_len;
246                 } else {
247                         memcpy(buf + r, f.body.cont.data, payload_len - r);
248                         r += payload_len - r; /* break */
249                 }
250         }
251
252         return ((int)r);
253 }
254
255 int
256 fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
257 {
258         int n;
259
260         fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
261             cmd, ms);
262
263         if (d->transport.rx != NULL)
264                 return (d->transport.rx(d, cmd, buf, count, ms));
265         if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
266                 fido_log_debug("%s: invalid argument", __func__);
267                 return (-1);
268         }
269         if ((n = rx(d, cmd, buf, count, ms)) >= 0)
270                 fido_log_xxd(buf, (size_t)n, "%s", __func__);
271
272         return (n);
273 }
274
275 int
276 fido_rx_cbor_status(fido_dev_t *d, int ms)
277 {
278         unsigned char   reply[FIDO_MAXMSG];
279         int             reply_len;
280
281         if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
282             ms)) < 0 || (size_t)reply_len < 1) {
283                 fido_log_debug("%s: fido_rx", __func__);
284                 return (FIDO_ERR_RX);
285         }
286
287         return (reply[0]);
288 }