xref: /openbsd-src/lib/libfido2/src/io.c (revision ab19a69ebe1d1275c01611de862453c36b3d15b9)
1d75efeb7Sdjm /*
2d75efeb7Sdjm  * Copyright (c) 2018 Yubico AB. All rights reserved.
3d75efeb7Sdjm  * Use of this source code is governed by a BSD-style
4d75efeb7Sdjm  * license that can be found in the LICENSE file.
5d75efeb7Sdjm  */
6d75efeb7Sdjm 
7d75efeb7Sdjm #include "fido.h"
8d75efeb7Sdjm #include "packed.h"
9d75efeb7Sdjm 
PACKED_TYPE(frame_t,struct frame{ uint32_t cid; union { uint8_t type; struct { uint8_t cmd; uint8_t bcnth; uint8_t bcntl; uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN]; } init; struct { uint8_t seq; uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN]; } cont; } body; })10d75efeb7Sdjm PACKED_TYPE(frame_t,
11d75efeb7Sdjm struct frame {
12d75efeb7Sdjm 	uint32_t cid; /* channel id */
13d75efeb7Sdjm 	union {
14d75efeb7Sdjm 		uint8_t type;
15d75efeb7Sdjm 		struct {
16d75efeb7Sdjm 			uint8_t cmd;
17d75efeb7Sdjm 			uint8_t bcnth;
18d75efeb7Sdjm 			uint8_t bcntl;
19739189a3Sdjm 			uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
20d75efeb7Sdjm 		} init;
21d75efeb7Sdjm 		struct {
22d75efeb7Sdjm 			uint8_t seq;
23739189a3Sdjm 			uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
24d75efeb7Sdjm 		} cont;
25d75efeb7Sdjm 	} body;
26d75efeb7Sdjm })
27d75efeb7Sdjm 
28d75efeb7Sdjm #ifndef MIN
29d75efeb7Sdjm #define MIN(x, y) ((x) > (y) ? (y) : (x))
30d75efeb7Sdjm #endif
31d75efeb7Sdjm 
3232a20e26Sdjm static int
33*ab19a69eSdjm tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
34*ab19a69eSdjm {
35*ab19a69eSdjm 	struct timespec ts;
36*ab19a69eSdjm 	int n;
37*ab19a69eSdjm 
38*ab19a69eSdjm 	if (fido_time_now(&ts) != 0)
39*ab19a69eSdjm 		return (-1);
40*ab19a69eSdjm 
41*ab19a69eSdjm 	n = d->io.write(d->io_handle, pkt, len);
42*ab19a69eSdjm 
43*ab19a69eSdjm 	if (fido_time_delta(&ts, ms) != 0)
44*ab19a69eSdjm 		return (-1);
45*ab19a69eSdjm 
46*ab19a69eSdjm 	return (n);
47*ab19a69eSdjm }
48*ab19a69eSdjm 
49*ab19a69eSdjm static int
tx_empty(fido_dev_t * d,uint8_t cmd,int * ms)50*ab19a69eSdjm tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
5132a20e26Sdjm {
5232a20e26Sdjm 	struct frame	*fp;
5332a20e26Sdjm 	unsigned char	 pkt[sizeof(*fp) + 1];
54739189a3Sdjm 	const size_t	 len = d->tx_len + 1;
5532a20e26Sdjm 	int		 n;
5632a20e26Sdjm 
5732a20e26Sdjm 	memset(&pkt, 0, sizeof(pkt));
5832a20e26Sdjm 	fp = (struct frame *)(pkt + 1);
5932a20e26Sdjm 	fp->cid = d->cid;
6032a20e26Sdjm 	fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
6132a20e26Sdjm 
62*ab19a69eSdjm 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
63*ab19a69eSdjm 	    (size_t)n != len)
6432a20e26Sdjm 		return (-1);
6532a20e26Sdjm 
6632a20e26Sdjm 	return (0);
6732a20e26Sdjm }
6832a20e26Sdjm 
69d75efeb7Sdjm static size_t
tx_preamble(fido_dev_t * d,uint8_t cmd,const void * buf,size_t count,int * ms)70*ab19a69eSdjm tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
71d75efeb7Sdjm {
72d75efeb7Sdjm 	struct frame	*fp;
73d75efeb7Sdjm 	unsigned char	 pkt[sizeof(*fp) + 1];
74739189a3Sdjm 	const size_t	 len = d->tx_len + 1;
75d75efeb7Sdjm 	int		 n;
76d75efeb7Sdjm 
77739189a3Sdjm 	if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
78739189a3Sdjm 		return (0);
79739189a3Sdjm 
80d75efeb7Sdjm 	memset(&pkt, 0, sizeof(pkt));
81d75efeb7Sdjm 	fp = (struct frame *)(pkt + 1);
82d75efeb7Sdjm 	fp->cid = d->cid;
8332a20e26Sdjm 	fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
84d75efeb7Sdjm 	fp->body.init.bcnth = (count >> 8) & 0xff;
85d75efeb7Sdjm 	fp->body.init.bcntl = count & 0xff;
86739189a3Sdjm 	count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
87d75efeb7Sdjm 	memcpy(&fp->body.init.data, buf, count);
88d75efeb7Sdjm 
89*ab19a69eSdjm 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
90*ab19a69eSdjm 	    (size_t)n != len)
91d75efeb7Sdjm 		return (0);
92d75efeb7Sdjm 
93d75efeb7Sdjm 	return (count);
94d75efeb7Sdjm }
95d75efeb7Sdjm 
96d75efeb7Sdjm static size_t
tx_frame(fido_dev_t * d,uint8_t seq,const void * buf,size_t count,int * ms)97*ab19a69eSdjm tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
98d75efeb7Sdjm {
99d75efeb7Sdjm 	struct frame	*fp;
100d75efeb7Sdjm 	unsigned char	 pkt[sizeof(*fp) + 1];
101739189a3Sdjm 	const size_t	 len = d->tx_len + 1;
102d75efeb7Sdjm 	int		 n;
103d75efeb7Sdjm 
104739189a3Sdjm 	if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
105739189a3Sdjm 		return (0);
106739189a3Sdjm 
107d75efeb7Sdjm 	memset(&pkt, 0, sizeof(pkt));
108d75efeb7Sdjm 	fp = (struct frame *)(pkt + 1);
109d75efeb7Sdjm 	fp->cid = d->cid;
11032a20e26Sdjm 	fp->body.cont.seq = seq;
111739189a3Sdjm 	count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
112d75efeb7Sdjm 	memcpy(&fp->body.cont.data, buf, count);
113d75efeb7Sdjm 
114*ab19a69eSdjm 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
115*ab19a69eSdjm 	    (size_t)n != len)
116d75efeb7Sdjm 		return (0);
117d75efeb7Sdjm 
118d75efeb7Sdjm 	return (count);
119d75efeb7Sdjm }
120d75efeb7Sdjm 
12132a20e26Sdjm static int
tx(fido_dev_t * d,uint8_t cmd,const unsigned char * buf,size_t count,int * ms)122*ab19a69eSdjm tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
123d75efeb7Sdjm {
12432a20e26Sdjm 	size_t n, sent;
125d75efeb7Sdjm 
126*ab19a69eSdjm 	if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
12732a20e26Sdjm 		fido_log_debug("%s: tx_preamble", __func__);
128d75efeb7Sdjm 		return (-1);
129d75efeb7Sdjm 	}
130d75efeb7Sdjm 
13132a20e26Sdjm 	for (uint8_t seq = 0; sent < count; sent += n) {
132d75efeb7Sdjm 		if (seq & 0x80) {
13332a20e26Sdjm 			fido_log_debug("%s: seq & 0x80", __func__);
134d75efeb7Sdjm 			return (-1);
135d75efeb7Sdjm 		}
136*ab19a69eSdjm 		if ((n = tx_frame(d, seq++, buf + sent, count - sent,
137*ab19a69eSdjm 		    ms)) == 0) {
13832a20e26Sdjm 			fido_log_debug("%s: tx_frame", __func__);
139d75efeb7Sdjm 			return (-1);
140d75efeb7Sdjm 		}
141d75efeb7Sdjm 	}
142d75efeb7Sdjm 
143d75efeb7Sdjm 	return (0);
144d75efeb7Sdjm }
145d75efeb7Sdjm 
146*ab19a69eSdjm static int
transport_tx(fido_dev_t * d,uint8_t cmd,const void * buf,size_t count,int * ms)147*ab19a69eSdjm transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
148*ab19a69eSdjm {
149*ab19a69eSdjm 	struct timespec ts;
150*ab19a69eSdjm 	int n;
151*ab19a69eSdjm 
152*ab19a69eSdjm 	if (fido_time_now(&ts) != 0)
153*ab19a69eSdjm 		return (-1);
154*ab19a69eSdjm 
155*ab19a69eSdjm 	n = d->transport.tx(d, cmd, buf, count);
156*ab19a69eSdjm 
157*ab19a69eSdjm 	if (fido_time_delta(&ts, ms) != 0)
158*ab19a69eSdjm 		return (-1);
159*ab19a69eSdjm 
160*ab19a69eSdjm 	return (n);
161*ab19a69eSdjm }
162*ab19a69eSdjm 
16332a20e26Sdjm int
fido_tx(fido_dev_t * d,uint8_t cmd,const void * buf,size_t count,int * ms)164*ab19a69eSdjm fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
16532a20e26Sdjm {
166c4a807edSdjm 	fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
167c4a807edSdjm 	fido_log_xxd(buf, count, "%s", __func__);
16832a20e26Sdjm 
169739189a3Sdjm 	if (d->transport.tx != NULL)
170*ab19a69eSdjm 		return (transport_tx(d, cmd, buf, count, ms));
17132a20e26Sdjm 	if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
17232a20e26Sdjm 		fido_log_debug("%s: invalid argument", __func__);
17332a20e26Sdjm 		return (-1);
17432a20e26Sdjm 	}
17532a20e26Sdjm 
176*ab19a69eSdjm 	return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
17732a20e26Sdjm }
17832a20e26Sdjm 
179d75efeb7Sdjm static int
rx_frame(fido_dev_t * d,struct frame * fp,int * ms)180*ab19a69eSdjm rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
181d75efeb7Sdjm {
182*ab19a69eSdjm 	struct timespec ts;
183d75efeb7Sdjm 	int n;
184d75efeb7Sdjm 
185739189a3Sdjm 	memset(fp, 0, sizeof(*fp));
186739189a3Sdjm 
187*ab19a69eSdjm 	if (fido_time_now(&ts) != 0)
188d75efeb7Sdjm 		return (-1);
189d75efeb7Sdjm 
190*ab19a69eSdjm 	if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
191*ab19a69eSdjm 	    (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
192*ab19a69eSdjm 		return (-1);
193*ab19a69eSdjm 
194*ab19a69eSdjm 	return (fido_time_delta(&ts, ms));
195d75efeb7Sdjm }
196d75efeb7Sdjm 
197d75efeb7Sdjm static int
rx_preamble(fido_dev_t * d,uint8_t cmd,struct frame * fp,int * ms)198*ab19a69eSdjm rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
199d75efeb7Sdjm {
200d75efeb7Sdjm 	do {
201d75efeb7Sdjm 		if (rx_frame(d, fp, ms) < 0)
202d75efeb7Sdjm 			return (-1);
203d75efeb7Sdjm #ifdef FIDO_FUZZ
204d75efeb7Sdjm 		fp->cid = d->cid;
205d75efeb7Sdjm #endif
206c4a807edSdjm 	} while (fp->cid != d->cid || (fp->cid == d->cid &&
207c4a807edSdjm 	    fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
208d75efeb7Sdjm 
209739189a3Sdjm 	if (d->rx_len > sizeof(*fp))
210739189a3Sdjm 		return (-1);
211739189a3Sdjm 
212c4a807edSdjm 	fido_log_xxd(fp, d->rx_len, "%s", __func__);
21332a20e26Sdjm #ifdef FIDO_FUZZ
21432a20e26Sdjm 	fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
21532a20e26Sdjm #endif
21632a20e26Sdjm 
21732a20e26Sdjm 	if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
21832a20e26Sdjm 		fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
21932a20e26Sdjm 		    __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
22032a20e26Sdjm 		return (-1);
22132a20e26Sdjm 	}
22232a20e26Sdjm 
223d75efeb7Sdjm 	return (0);
224d75efeb7Sdjm }
225d75efeb7Sdjm 
22632a20e26Sdjm static int
rx(fido_dev_t * d,uint8_t cmd,unsigned char * buf,size_t count,int * ms)227*ab19a69eSdjm rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
228d75efeb7Sdjm {
229d75efeb7Sdjm 	struct frame f;
230739189a3Sdjm 	size_t r, payload_len, init_data_len, cont_data_len;
231739189a3Sdjm 
232739189a3Sdjm 	if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
233739189a3Sdjm 	    d->rx_len <= CTAP_CONT_HEADER_LEN)
234739189a3Sdjm 		return (-1);
235739189a3Sdjm 
236739189a3Sdjm 	init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
237739189a3Sdjm 	cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
238739189a3Sdjm 
239739189a3Sdjm 	if (init_data_len > sizeof(f.body.init.data) ||
240739189a3Sdjm 	    cont_data_len > sizeof(f.body.cont.data))
241739189a3Sdjm 		return (-1);
242d75efeb7Sdjm 
24332a20e26Sdjm 	if (rx_preamble(d, cmd, &f, ms) < 0) {
24432a20e26Sdjm 		fido_log_debug("%s: rx_preamble", __func__);
245d75efeb7Sdjm 		return (-1);
246d75efeb7Sdjm 	}
247d75efeb7Sdjm 
248739189a3Sdjm 	payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
249739189a3Sdjm 	fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
25032a20e26Sdjm 
251739189a3Sdjm 	if (count < payload_len) {
25232a20e26Sdjm 		fido_log_debug("%s: count < payload_len", __func__);
253d75efeb7Sdjm 		return (-1);
254d75efeb7Sdjm 	}
255d75efeb7Sdjm 
256739189a3Sdjm 	if (payload_len < init_data_len) {
25732a20e26Sdjm 		memcpy(buf, f.body.init.data, payload_len);
258739189a3Sdjm 		return ((int)payload_len);
259d75efeb7Sdjm 	}
260d75efeb7Sdjm 
261739189a3Sdjm 	memcpy(buf, f.body.init.data, init_data_len);
262739189a3Sdjm 	r = init_data_len;
263d75efeb7Sdjm 
264739189a3Sdjm 	for (int seq = 0; r < payload_len; seq++) {
265d75efeb7Sdjm 		if (rx_frame(d, &f, ms) < 0) {
26632a20e26Sdjm 			fido_log_debug("%s: rx_frame", __func__);
267d75efeb7Sdjm 			return (-1);
268d75efeb7Sdjm 		}
269d75efeb7Sdjm 
270c4a807edSdjm 		fido_log_xxd(&f, d->rx_len, "%s", __func__);
271d75efeb7Sdjm #ifdef FIDO_FUZZ
272d75efeb7Sdjm 		f.cid = d->cid;
273739189a3Sdjm 		f.body.cont.seq = (uint8_t)seq;
274d75efeb7Sdjm #endif
275d75efeb7Sdjm 
27632a20e26Sdjm 		if (f.cid != d->cid || f.body.cont.seq != seq) {
27732a20e26Sdjm 			fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
278d75efeb7Sdjm 			    __func__, f.cid, d->cid, f.body.cont.seq, seq);
279d75efeb7Sdjm 			return (-1);
280d75efeb7Sdjm 		}
281d75efeb7Sdjm 
282739189a3Sdjm 		if (payload_len - r > cont_data_len) {
283739189a3Sdjm 			memcpy(buf + r, f.body.cont.data, cont_data_len);
284739189a3Sdjm 			r += cont_data_len;
285d75efeb7Sdjm 		} else {
28632a20e26Sdjm 			memcpy(buf + r, f.body.cont.data, payload_len - r);
287739189a3Sdjm 			r += payload_len - r; /* break */
288d75efeb7Sdjm 		}
289d75efeb7Sdjm 	}
290d75efeb7Sdjm 
291739189a3Sdjm 	return ((int)r);
292d75efeb7Sdjm }
293d75efeb7Sdjm 
294*ab19a69eSdjm static int
transport_rx(fido_dev_t * d,uint8_t cmd,void * buf,size_t count,int * ms)295*ab19a69eSdjm transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
296*ab19a69eSdjm {
297*ab19a69eSdjm 	struct timespec ts;
298*ab19a69eSdjm 	int n;
299*ab19a69eSdjm 
300*ab19a69eSdjm 	if (fido_time_now(&ts) != 0)
301*ab19a69eSdjm 		return (-1);
302*ab19a69eSdjm 
303*ab19a69eSdjm 	n = d->transport.rx(d, cmd, buf, count, *ms);
304*ab19a69eSdjm 
305*ab19a69eSdjm 	if (fido_time_delta(&ts, ms) != 0)
306*ab19a69eSdjm 		return (-1);
307*ab19a69eSdjm 
308*ab19a69eSdjm 	return (n);
309*ab19a69eSdjm }
310*ab19a69eSdjm 
311d75efeb7Sdjm int
fido_rx(fido_dev_t * d,uint8_t cmd,void * buf,size_t count,int * ms)312*ab19a69eSdjm fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
313d75efeb7Sdjm {
31432a20e26Sdjm 	int n;
31532a20e26Sdjm 
316c4a807edSdjm 	fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
317*ab19a69eSdjm 	    cmd, *ms);
31832a20e26Sdjm 
319739189a3Sdjm 	if (d->transport.rx != NULL)
320*ab19a69eSdjm 		return (transport_rx(d, cmd, buf, count, ms));
32132a20e26Sdjm 	if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
32232a20e26Sdjm 		fido_log_debug("%s: invalid argument", __func__);
32332a20e26Sdjm 		return (-1);
32432a20e26Sdjm 	}
325c4a807edSdjm 	if ((n = rx(d, cmd, buf, count, ms)) >= 0)
326c4a807edSdjm 		fido_log_xxd(buf, (size_t)n, "%s", __func__);
32732a20e26Sdjm 
32832a20e26Sdjm 	return (n);
32932a20e26Sdjm }
33032a20e26Sdjm 
33132a20e26Sdjm int
fido_rx_cbor_status(fido_dev_t * d,int * ms)332*ab19a69eSdjm fido_rx_cbor_status(fido_dev_t *d, int *ms)
33332a20e26Sdjm {
33432a20e26Sdjm 	unsigned char	reply[FIDO_MAXMSG];
335d75efeb7Sdjm 	int		reply_len;
336d75efeb7Sdjm 
33732a20e26Sdjm 	if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
33832a20e26Sdjm 	    ms)) < 0 || (size_t)reply_len < 1) {
33932a20e26Sdjm 		fido_log_debug("%s: fido_rx", __func__);
340d75efeb7Sdjm 		return (FIDO_ERR_RX);
341d75efeb7Sdjm 	}
342d75efeb7Sdjm 
343d75efeb7Sdjm 	return (reply[0]);
344d75efeb7Sdjm }
345