xref: /freebsd/contrib/libfido2/src/io.c (revision f540a43052c12c76d3453ead881248d5467a1ab0)
10afa8e06SEd Maste /*
20afa8e06SEd Maste  * Copyright (c) 2018 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
50afa8e06SEd Maste  */
60afa8e06SEd Maste 
70afa8e06SEd Maste #include "fido.h"
80afa8e06SEd Maste #include "packed.h"
90afa8e06SEd Maste 
100afa8e06SEd Maste PACKED_TYPE(frame_t,
110afa8e06SEd Maste struct frame {
120afa8e06SEd Maste 	uint32_t cid; /* channel id */
130afa8e06SEd Maste 	union {
140afa8e06SEd Maste 		uint8_t type;
150afa8e06SEd Maste 		struct {
160afa8e06SEd Maste 			uint8_t cmd;
170afa8e06SEd Maste 			uint8_t bcnth;
180afa8e06SEd Maste 			uint8_t bcntl;
190afa8e06SEd Maste 			uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
200afa8e06SEd Maste 		} init;
210afa8e06SEd Maste 		struct {
220afa8e06SEd Maste 			uint8_t seq;
230afa8e06SEd Maste 			uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
240afa8e06SEd Maste 		} cont;
250afa8e06SEd Maste 	} body;
260afa8e06SEd Maste })
270afa8e06SEd Maste 
280afa8e06SEd Maste #ifndef MIN
290afa8e06SEd Maste #define MIN(x, y) ((x) > (y) ? (y) : (x))
300afa8e06SEd Maste #endif
310afa8e06SEd Maste 
320afa8e06SEd Maste static int
33*f540a430SEd Maste tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
34*f540a430SEd Maste {
35*f540a430SEd Maste 	struct timespec ts;
36*f540a430SEd Maste 	int n;
37*f540a430SEd Maste 
38*f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
39*f540a430SEd Maste 		return (-1);
40*f540a430SEd Maste 
41*f540a430SEd Maste 	n = d->io.write(d->io_handle, pkt, len);
42*f540a430SEd Maste 
43*f540a430SEd Maste 	if (fido_time_delta(&ts, ms) != 0)
44*f540a430SEd Maste 		return (-1);
45*f540a430SEd Maste 
46*f540a430SEd Maste 	return (n);
47*f540a430SEd Maste }
48*f540a430SEd Maste 
49*f540a430SEd Maste static int
50*f540a430SEd Maste tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
510afa8e06SEd Maste {
520afa8e06SEd Maste 	struct frame	*fp;
530afa8e06SEd Maste 	unsigned char	 pkt[sizeof(*fp) + 1];
540afa8e06SEd Maste 	const size_t	 len = d->tx_len + 1;
550afa8e06SEd Maste 	int		 n;
560afa8e06SEd Maste 
570afa8e06SEd Maste 	memset(&pkt, 0, sizeof(pkt));
580afa8e06SEd Maste 	fp = (struct frame *)(pkt + 1);
590afa8e06SEd Maste 	fp->cid = d->cid;
600afa8e06SEd Maste 	fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
610afa8e06SEd Maste 
62*f540a430SEd Maste 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
63*f540a430SEd Maste 	    (size_t)n != len)
640afa8e06SEd Maste 		return (-1);
650afa8e06SEd Maste 
660afa8e06SEd Maste 	return (0);
670afa8e06SEd Maste }
680afa8e06SEd Maste 
690afa8e06SEd Maste static size_t
70*f540a430SEd Maste tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
710afa8e06SEd Maste {
720afa8e06SEd Maste 	struct frame	*fp;
730afa8e06SEd Maste 	unsigned char	 pkt[sizeof(*fp) + 1];
740afa8e06SEd Maste 	const size_t	 len = d->tx_len + 1;
750afa8e06SEd Maste 	int		 n;
760afa8e06SEd Maste 
770afa8e06SEd Maste 	if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
780afa8e06SEd Maste 		return (0);
790afa8e06SEd Maste 
800afa8e06SEd Maste 	memset(&pkt, 0, sizeof(pkt));
810afa8e06SEd Maste 	fp = (struct frame *)(pkt + 1);
820afa8e06SEd Maste 	fp->cid = d->cid;
830afa8e06SEd Maste 	fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
840afa8e06SEd Maste 	fp->body.init.bcnth = (count >> 8) & 0xff;
850afa8e06SEd Maste 	fp->body.init.bcntl = count & 0xff;
860afa8e06SEd Maste 	count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
870afa8e06SEd Maste 	memcpy(&fp->body.init.data, buf, count);
880afa8e06SEd Maste 
89*f540a430SEd Maste 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
90*f540a430SEd Maste 	    (size_t)n != len)
910afa8e06SEd Maste 		return (0);
920afa8e06SEd Maste 
930afa8e06SEd Maste 	return (count);
940afa8e06SEd Maste }
950afa8e06SEd Maste 
960afa8e06SEd Maste static size_t
97*f540a430SEd Maste tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
980afa8e06SEd Maste {
990afa8e06SEd Maste 	struct frame	*fp;
1000afa8e06SEd Maste 	unsigned char	 pkt[sizeof(*fp) + 1];
1010afa8e06SEd Maste 	const size_t	 len = d->tx_len + 1;
1020afa8e06SEd Maste 	int		 n;
1030afa8e06SEd Maste 
1040afa8e06SEd Maste 	if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
1050afa8e06SEd Maste 		return (0);
1060afa8e06SEd Maste 
1070afa8e06SEd Maste 	memset(&pkt, 0, sizeof(pkt));
1080afa8e06SEd Maste 	fp = (struct frame *)(pkt + 1);
1090afa8e06SEd Maste 	fp->cid = d->cid;
1100afa8e06SEd Maste 	fp->body.cont.seq = seq;
1110afa8e06SEd Maste 	count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
1120afa8e06SEd Maste 	memcpy(&fp->body.cont.data, buf, count);
1130afa8e06SEd Maste 
114*f540a430SEd Maste 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
115*f540a430SEd Maste 	    (size_t)n != len)
1160afa8e06SEd Maste 		return (0);
1170afa8e06SEd Maste 
1180afa8e06SEd Maste 	return (count);
1190afa8e06SEd Maste }
1200afa8e06SEd Maste 
1210afa8e06SEd Maste static int
122*f540a430SEd Maste tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
1230afa8e06SEd Maste {
1240afa8e06SEd Maste 	size_t n, sent;
1250afa8e06SEd Maste 
126*f540a430SEd Maste 	if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
1270afa8e06SEd Maste 		fido_log_debug("%s: tx_preamble", __func__);
1280afa8e06SEd Maste 		return (-1);
1290afa8e06SEd Maste 	}
1300afa8e06SEd Maste 
1310afa8e06SEd Maste 	for (uint8_t seq = 0; sent < count; sent += n) {
1320afa8e06SEd Maste 		if (seq & 0x80) {
1330afa8e06SEd Maste 			fido_log_debug("%s: seq & 0x80", __func__);
1340afa8e06SEd Maste 			return (-1);
1350afa8e06SEd Maste 		}
136*f540a430SEd Maste 		if ((n = tx_frame(d, seq++, buf + sent, count - sent,
137*f540a430SEd Maste 		    ms)) == 0) {
1380afa8e06SEd Maste 			fido_log_debug("%s: tx_frame", __func__);
1390afa8e06SEd Maste 			return (-1);
1400afa8e06SEd Maste 		}
1410afa8e06SEd Maste 	}
1420afa8e06SEd Maste 
1430afa8e06SEd Maste 	return (0);
1440afa8e06SEd Maste }
1450afa8e06SEd Maste 
146*f540a430SEd Maste static int
147*f540a430SEd Maste transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
148*f540a430SEd Maste {
149*f540a430SEd Maste 	struct timespec ts;
150*f540a430SEd Maste 	int n;
151*f540a430SEd Maste 
152*f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
153*f540a430SEd Maste 		return (-1);
154*f540a430SEd Maste 
155*f540a430SEd Maste 	n = d->transport.tx(d, cmd, buf, count);
156*f540a430SEd Maste 
157*f540a430SEd Maste 	if (fido_time_delta(&ts, ms) != 0)
158*f540a430SEd Maste 		return (-1);
159*f540a430SEd Maste 
160*f540a430SEd Maste 	return (n);
161*f540a430SEd Maste }
162*f540a430SEd Maste 
1630afa8e06SEd Maste int
164*f540a430SEd Maste fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
1650afa8e06SEd Maste {
1660afa8e06SEd Maste 	fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
1670afa8e06SEd Maste 	fido_log_xxd(buf, count, "%s", __func__);
1680afa8e06SEd Maste 
1690afa8e06SEd Maste 	if (d->transport.tx != NULL)
170*f540a430SEd Maste 		return (transport_tx(d, cmd, buf, count, ms));
1710afa8e06SEd Maste 	if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
1720afa8e06SEd Maste 		fido_log_debug("%s: invalid argument", __func__);
1730afa8e06SEd Maste 		return (-1);
1740afa8e06SEd Maste 	}
1750afa8e06SEd Maste 
176*f540a430SEd Maste 	return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
1770afa8e06SEd Maste }
1780afa8e06SEd Maste 
1790afa8e06SEd Maste static int
180*f540a430SEd Maste rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
1810afa8e06SEd Maste {
182*f540a430SEd Maste 	struct timespec ts;
1830afa8e06SEd Maste 	int n;
1840afa8e06SEd Maste 
1850afa8e06SEd Maste 	memset(fp, 0, sizeof(*fp));
1860afa8e06SEd Maste 
187*f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
1880afa8e06SEd Maste 		return (-1);
1890afa8e06SEd Maste 
190*f540a430SEd Maste 	if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
191*f540a430SEd Maste 	    (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
192*f540a430SEd Maste 		return (-1);
193*f540a430SEd Maste 
194*f540a430SEd Maste 	return (fido_time_delta(&ts, ms));
1950afa8e06SEd Maste }
1960afa8e06SEd Maste 
1970afa8e06SEd Maste static int
198*f540a430SEd Maste rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
1990afa8e06SEd Maste {
2000afa8e06SEd Maste 	do {
2010afa8e06SEd Maste 		if (rx_frame(d, fp, ms) < 0)
2020afa8e06SEd Maste 			return (-1);
2030afa8e06SEd Maste #ifdef FIDO_FUZZ
2040afa8e06SEd Maste 		fp->cid = d->cid;
2050afa8e06SEd Maste #endif
2060afa8e06SEd Maste 	} while (fp->cid != d->cid || (fp->cid == d->cid &&
2070afa8e06SEd Maste 	    fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
2080afa8e06SEd Maste 
2090afa8e06SEd Maste 	if (d->rx_len > sizeof(*fp))
2100afa8e06SEd Maste 		return (-1);
2110afa8e06SEd Maste 
2120afa8e06SEd Maste 	fido_log_xxd(fp, d->rx_len, "%s", __func__);
2130afa8e06SEd Maste #ifdef FIDO_FUZZ
2140afa8e06SEd Maste 	fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
2150afa8e06SEd Maste #endif
2160afa8e06SEd Maste 
2170afa8e06SEd Maste 	if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
2180afa8e06SEd Maste 		fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
2190afa8e06SEd Maste 		    __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
2200afa8e06SEd Maste 		return (-1);
2210afa8e06SEd Maste 	}
2220afa8e06SEd Maste 
2230afa8e06SEd Maste 	return (0);
2240afa8e06SEd Maste }
2250afa8e06SEd Maste 
2260afa8e06SEd Maste static int
227*f540a430SEd Maste rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
2280afa8e06SEd Maste {
2290afa8e06SEd Maste 	struct frame f;
2300afa8e06SEd Maste 	size_t r, payload_len, init_data_len, cont_data_len;
2310afa8e06SEd Maste 
2320afa8e06SEd Maste 	if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
2330afa8e06SEd Maste 	    d->rx_len <= CTAP_CONT_HEADER_LEN)
2340afa8e06SEd Maste 		return (-1);
2350afa8e06SEd Maste 
2360afa8e06SEd Maste 	init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
2370afa8e06SEd Maste 	cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
2380afa8e06SEd Maste 
2390afa8e06SEd Maste 	if (init_data_len > sizeof(f.body.init.data) ||
2400afa8e06SEd Maste 	    cont_data_len > sizeof(f.body.cont.data))
2410afa8e06SEd Maste 		return (-1);
2420afa8e06SEd Maste 
2430afa8e06SEd Maste 	if (rx_preamble(d, cmd, &f, ms) < 0) {
2440afa8e06SEd Maste 		fido_log_debug("%s: rx_preamble", __func__);
2450afa8e06SEd Maste 		return (-1);
2460afa8e06SEd Maste 	}
2470afa8e06SEd Maste 
2480afa8e06SEd Maste 	payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
2490afa8e06SEd Maste 	fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
2500afa8e06SEd Maste 
2510afa8e06SEd Maste 	if (count < payload_len) {
2520afa8e06SEd Maste 		fido_log_debug("%s: count < payload_len", __func__);
2530afa8e06SEd Maste 		return (-1);
2540afa8e06SEd Maste 	}
2550afa8e06SEd Maste 
2560afa8e06SEd Maste 	if (payload_len < init_data_len) {
2570afa8e06SEd Maste 		memcpy(buf, f.body.init.data, payload_len);
2580afa8e06SEd Maste 		return ((int)payload_len);
2590afa8e06SEd Maste 	}
2600afa8e06SEd Maste 
2610afa8e06SEd Maste 	memcpy(buf, f.body.init.data, init_data_len);
2620afa8e06SEd Maste 	r = init_data_len;
2630afa8e06SEd Maste 
2640afa8e06SEd Maste 	for (int seq = 0; r < payload_len; seq++) {
2650afa8e06SEd Maste 		if (rx_frame(d, &f, ms) < 0) {
2660afa8e06SEd Maste 			fido_log_debug("%s: rx_frame", __func__);
2670afa8e06SEd Maste 			return (-1);
2680afa8e06SEd Maste 		}
2690afa8e06SEd Maste 
2700afa8e06SEd Maste 		fido_log_xxd(&f, d->rx_len, "%s", __func__);
2710afa8e06SEd Maste #ifdef FIDO_FUZZ
2720afa8e06SEd Maste 		f.cid = d->cid;
2730afa8e06SEd Maste 		f.body.cont.seq = (uint8_t)seq;
2740afa8e06SEd Maste #endif
2750afa8e06SEd Maste 
2760afa8e06SEd Maste 		if (f.cid != d->cid || f.body.cont.seq != seq) {
2770afa8e06SEd Maste 			fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
2780afa8e06SEd Maste 			    __func__, f.cid, d->cid, f.body.cont.seq, seq);
2790afa8e06SEd Maste 			return (-1);
2800afa8e06SEd Maste 		}
2810afa8e06SEd Maste 
2820afa8e06SEd Maste 		if (payload_len - r > cont_data_len) {
2830afa8e06SEd Maste 			memcpy(buf + r, f.body.cont.data, cont_data_len);
2840afa8e06SEd Maste 			r += cont_data_len;
2850afa8e06SEd Maste 		} else {
2860afa8e06SEd Maste 			memcpy(buf + r, f.body.cont.data, payload_len - r);
2870afa8e06SEd Maste 			r += payload_len - r; /* break */
2880afa8e06SEd Maste 		}
2890afa8e06SEd Maste 	}
2900afa8e06SEd Maste 
2910afa8e06SEd Maste 	return ((int)r);
2920afa8e06SEd Maste }
2930afa8e06SEd Maste 
294*f540a430SEd Maste static int
295*f540a430SEd Maste transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
296*f540a430SEd Maste {
297*f540a430SEd Maste 	struct timespec ts;
298*f540a430SEd Maste 	int n;
299*f540a430SEd Maste 
300*f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
301*f540a430SEd Maste 		return (-1);
302*f540a430SEd Maste 
303*f540a430SEd Maste 	n = d->transport.rx(d, cmd, buf, count, *ms);
304*f540a430SEd Maste 
305*f540a430SEd Maste 	if (fido_time_delta(&ts, ms) != 0)
306*f540a430SEd Maste 		return (-1);
307*f540a430SEd Maste 
308*f540a430SEd Maste 	return (n);
309*f540a430SEd Maste }
310*f540a430SEd Maste 
3110afa8e06SEd Maste int
312*f540a430SEd Maste fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
3130afa8e06SEd Maste {
3140afa8e06SEd Maste 	int n;
3150afa8e06SEd Maste 
3160afa8e06SEd Maste 	fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
317*f540a430SEd Maste 	    cmd, *ms);
3180afa8e06SEd Maste 
3190afa8e06SEd Maste 	if (d->transport.rx != NULL)
320*f540a430SEd Maste 		return (transport_rx(d, cmd, buf, count, ms));
3210afa8e06SEd Maste 	if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
3220afa8e06SEd Maste 		fido_log_debug("%s: invalid argument", __func__);
3230afa8e06SEd Maste 		return (-1);
3240afa8e06SEd Maste 	}
3250afa8e06SEd Maste 	if ((n = rx(d, cmd, buf, count, ms)) >= 0)
3260afa8e06SEd Maste 		fido_log_xxd(buf, (size_t)n, "%s", __func__);
3270afa8e06SEd Maste 
3280afa8e06SEd Maste 	return (n);
3290afa8e06SEd Maste }
3300afa8e06SEd Maste 
3310afa8e06SEd Maste int
332*f540a430SEd Maste fido_rx_cbor_status(fido_dev_t *d, int *ms)
3330afa8e06SEd Maste {
3340afa8e06SEd Maste 	unsigned char	reply[FIDO_MAXMSG];
3350afa8e06SEd Maste 	int		reply_len;
3360afa8e06SEd Maste 
3370afa8e06SEd Maste 	if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply),
3380afa8e06SEd Maste 	    ms)) < 0 || (size_t)reply_len < 1) {
3390afa8e06SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
3400afa8e06SEd Maste 		return (FIDO_ERR_RX);
3410afa8e06SEd Maste 	}
3420afa8e06SEd Maste 
3430afa8e06SEd Maste 	return (reply[0]);
3440afa8e06SEd Maste }
345