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