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