summaryrefslogtreecommitdiff
path: root/src/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/io.c')
-rw-r--r--src/io.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/io.c b/src/io.c
new file mode 100644
index 0000000..aa88720
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,256 @@
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 <stdint.h>
8#include <stdio.h>
9#include <string.h>
10
11#include "fido.h"
12#include "packed.h"
13
14PACKED_TYPE(frame_t,
15struct frame {
16 uint32_t cid; /* channel id */
17 union {
18 uint8_t type;
19 struct {
20 uint8_t cmd;
21 uint8_t bcnth;
22 uint8_t bcntl;
23 uint8_t data[CTAP_RPT_SIZE - 7];
24 } init;
25 struct {
26 uint8_t seq;
27 uint8_t data[CTAP_RPT_SIZE - 5];
28 } cont;
29 } body;
30})
31
32#ifndef MIN
33#define MIN(x, y) ((x) > (y) ? (y) : (x))
34#endif
35
36static size_t
37tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
38{
39 struct frame *fp;
40 unsigned char pkt[sizeof(*fp) + 1];
41 int n;
42
43 if (d->io.write == NULL || (cmd & 0x80) == 0)
44 return (0);
45
46 memset(&pkt, 0, sizeof(pkt));
47 fp = (struct frame *)(pkt + 1);
48 fp->cid = d->cid;
49 fp->body.init.cmd = 0x80 | cmd;
50 fp->body.init.bcnth = (count >> 8) & 0xff;
51 fp->body.init.bcntl = count & 0xff;
52 count = MIN(count, sizeof(fp->body.init.data));
53 if (count)
54 memcpy(&fp->body.init.data, buf, count);
55
56 n = d->io.write(d->io_handle, pkt, sizeof(pkt));
57 if (n < 0 || (size_t)n != sizeof(pkt))
58 return (0);
59
60 return (count);
61}
62
63static size_t
64tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count)
65{
66 struct frame *fp;
67 unsigned char pkt[sizeof(*fp) + 1];
68 int n;
69
70 if (d->io.write == NULL || seq < 0 || seq > UINT8_MAX)
71 return (0);
72
73 memset(&pkt, 0, sizeof(pkt));
74 fp = (struct frame *)(pkt + 1);
75 fp->cid = d->cid;
76 fp->body.cont.seq = (uint8_t)seq;
77 count = MIN(count, sizeof(fp->body.cont.data));
78 memcpy(&fp->body.cont.data, buf, count);
79
80 n = d->io.write(d->io_handle, pkt, sizeof(pkt));
81 if (n < 0 || (size_t)n != sizeof(pkt))
82 return (0);
83
84 return (count);
85}
86
87int
88fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
89{
90 int seq = 0;
91 size_t sent;
92
93 fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
94 (void *)d, cmd, buf, count);
95 fido_log_xxd(buf, count);
96
97 if (d->io_handle == NULL || count > UINT16_MAX) {
98 fido_log_debug("%s: invalid argument (%p, %zu)", __func__,
99 d->io_handle, count);
100 return (-1);
101 }
102
103 if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
104 fido_log_debug("%s: tx_preamble", __func__);
105 return (-1);
106 }
107
108 while (sent < count) {
109 if (seq & 0x80) {
110 fido_log_debug("%s: seq & 0x80", __func__);
111 return (-1);
112 }
113 const uint8_t *p = (const uint8_t *)buf + sent;
114 size_t n = tx_frame(d, seq++, p, count - sent);
115 if (n == 0) {
116 fido_log_debug("%s: tx_frame", __func__);
117 return (-1);
118 }
119 sent += n;
120 }
121
122 return (0);
123}
124
125static int
126rx_frame(fido_dev_t *d, struct frame *fp, int ms)
127{
128 int n;
129
130 if (d->io.read == NULL)
131 return (-1);
132
133 n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms);
134 if (n < 0 || (size_t)n != sizeof(*fp))
135 return (-1);
136
137 return (0);
138}
139
140static int
141rx_preamble(fido_dev_t *d, struct frame *fp, int ms)
142{
143 do {
144 if (rx_frame(d, fp, ms) < 0)
145 return (-1);
146#ifdef FIDO_FUZZ
147 fp->cid = d->cid;
148#endif
149 } while (fp->cid == d->cid &&
150 fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
151
152 return (0);
153}
154
155int
156fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
157{
158 struct frame f;
159 uint16_t r;
160 uint16_t flen;
161 int seq;
162
163 if (d->io_handle == NULL || (cmd & 0x80) == 0) {
164 fido_log_debug("%s: invalid argument (%p, 0x%02x)", __func__,
165 d->io_handle, cmd);
166 return (-1);
167 }
168
169 if (rx_preamble(d, &f, ms) < 0) {
170 fido_log_debug("%s: rx_preamble", __func__);
171 return (-1);
172 }
173
174 fido_log_debug("%s: initiation frame at %p, len %zu", __func__,
175 (void *)&f, sizeof(f));
176 fido_log_xxd(&f, sizeof(f));
177
178#ifdef FIDO_FUZZ
179 f.cid = d->cid;
180 f.body.init.cmd = cmd;
181#endif
182
183 if (f.cid != d->cid || f.body.init.cmd != cmd) {
184 fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
185 __func__, f.cid, d->cid, f.body.init.cmd, cmd);
186 return (-1);
187 }
188
189 flen = (f.body.init.bcnth << 8) | f.body.init.bcntl;
190 if (count < (size_t)flen) {
191 fido_log_debug("%s: count < flen (%zu, %zu)", __func__, count,
192 (size_t)flen);
193 return (-1);
194 }
195 if (flen < sizeof(f.body.init.data)) {
196 memcpy(buf, f.body.init.data, flen);
197 return (flen);
198 }
199
200 memcpy(buf, f.body.init.data, sizeof(f.body.init.data));
201 r = sizeof(f.body.init.data);
202 seq = 0;
203
204 while ((size_t)r < flen) {
205 if (rx_frame(d, &f, ms) < 0) {
206 fido_log_debug("%s: rx_frame", __func__);
207 return (-1);
208 }
209
210 fido_log_debug("%s: continuation frame at %p, len %zu",
211 __func__, (void *)&f, sizeof(f));
212 fido_log_xxd(&f, sizeof(f));
213
214#ifdef FIDO_FUZZ
215 f.cid = d->cid;
216 f.body.cont.seq = seq;
217#endif
218
219 if (f.cid != d->cid || f.body.cont.seq != seq++) {
220 fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
221 __func__, f.cid, d->cid, f.body.cont.seq, seq);
222 return (-1);
223 }
224
225 uint8_t *p = (uint8_t *)buf + r;
226
227 if ((size_t)(flen - r) > sizeof(f.body.cont.data)) {
228 memcpy(p, f.body.cont.data, sizeof(f.body.cont.data));
229 r += sizeof(f.body.cont.data);
230 } else {
231 memcpy(p, f.body.cont.data, flen - r);
232 r += (flen - r); /* break */
233 }
234 }
235
236 fido_log_debug("%s: payload at %p, len %zu", __func__, buf, (size_t)r);
237 fido_log_xxd(buf, r);
238
239 return (r);
240}
241
242int
243fido_rx_cbor_status(fido_dev_t *d, int ms)
244{
245 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
246 unsigned char reply[2048];
247 int reply_len;
248
249 if ((reply_len = fido_rx(d, cmd, &reply, sizeof(reply), ms)) < 0 ||
250 (size_t)reply_len < 1) {
251 fido_log_debug("%s: fido_rx", __func__);
252 return (FIDO_ERR_RX);
253 }
254
255 return (reply[0]);
256}