xref: /freebsd/contrib/libfido2/src/io.c (revision 2ccfa855)
10afa8e06SEd Maste /*
2*2ccfa855SEd Maste  * Copyright (c) 2018-2022 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.
5*2ccfa855SEd Maste  * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste  */
70afa8e06SEd Maste 
80afa8e06SEd Maste #include "fido.h"
90afa8e06SEd Maste #include "packed.h"
100afa8e06SEd Maste 
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; })110afa8e06SEd Maste PACKED_TYPE(frame_t,
120afa8e06SEd Maste struct frame {
130afa8e06SEd Maste 	uint32_t cid; /* channel id */
140afa8e06SEd Maste 	union {
150afa8e06SEd Maste 		uint8_t type;
160afa8e06SEd Maste 		struct {
170afa8e06SEd Maste 			uint8_t cmd;
180afa8e06SEd Maste 			uint8_t bcnth;
190afa8e06SEd Maste 			uint8_t bcntl;
200afa8e06SEd Maste 			uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
210afa8e06SEd Maste 		} init;
220afa8e06SEd Maste 		struct {
230afa8e06SEd Maste 			uint8_t seq;
240afa8e06SEd Maste 			uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
250afa8e06SEd Maste 		} cont;
260afa8e06SEd Maste 	} body;
270afa8e06SEd Maste })
280afa8e06SEd Maste 
290afa8e06SEd Maste #ifndef MIN
300afa8e06SEd Maste #define MIN(x, y) ((x) > (y) ? (y) : (x))
310afa8e06SEd Maste #endif
320afa8e06SEd Maste 
330afa8e06SEd Maste static int
34f540a430SEd Maste tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
35f540a430SEd Maste {
36f540a430SEd Maste 	struct timespec ts;
37f540a430SEd Maste 	int n;
38f540a430SEd Maste 
39f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
40f540a430SEd Maste 		return (-1);
41f540a430SEd Maste 
42f540a430SEd Maste 	n = d->io.write(d->io_handle, pkt, len);
43f540a430SEd Maste 
44f540a430SEd Maste 	if (fido_time_delta(&ts, ms) != 0)
45f540a430SEd Maste 		return (-1);
46f540a430SEd Maste 
47f540a430SEd Maste 	return (n);
48f540a430SEd Maste }
49f540a430SEd Maste 
50f540a430SEd Maste static int
tx_empty(fido_dev_t * d,uint8_t cmd,int * ms)51f540a430SEd Maste tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
520afa8e06SEd Maste {
530afa8e06SEd Maste 	struct frame	*fp;
540afa8e06SEd Maste 	unsigned char	 pkt[sizeof(*fp) + 1];
550afa8e06SEd Maste 	const size_t	 len = d->tx_len + 1;
560afa8e06SEd Maste 	int		 n;
570afa8e06SEd Maste 
580afa8e06SEd Maste 	memset(&pkt, 0, sizeof(pkt));
590afa8e06SEd Maste 	fp = (struct frame *)(pkt + 1);
600afa8e06SEd Maste 	fp->cid = d->cid;
610afa8e06SEd Maste 	fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
620afa8e06SEd Maste 
63f540a430SEd Maste 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
64f540a430SEd Maste 	    (size_t)n != len)
650afa8e06SEd Maste 		return (-1);
660afa8e06SEd Maste 
670afa8e06SEd Maste 	return (0);
680afa8e06SEd Maste }
690afa8e06SEd Maste 
700afa8e06SEd Maste static size_t
tx_preamble(fido_dev_t * d,uint8_t cmd,const void * buf,size_t count,int * ms)71f540a430SEd Maste tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
720afa8e06SEd Maste {
730afa8e06SEd Maste 	struct frame	*fp;
740afa8e06SEd Maste 	unsigned char	 pkt[sizeof(*fp) + 1];
750afa8e06SEd Maste 	const size_t	 len = d->tx_len + 1;
760afa8e06SEd Maste 	int		 n;
770afa8e06SEd Maste 
780afa8e06SEd Maste 	if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
790afa8e06SEd Maste 		return (0);
800afa8e06SEd Maste 
810afa8e06SEd Maste 	memset(&pkt, 0, sizeof(pkt));
820afa8e06SEd Maste 	fp = (struct frame *)(pkt + 1);
830afa8e06SEd Maste 	fp->cid = d->cid;
840afa8e06SEd Maste 	fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
850afa8e06SEd Maste 	fp->body.init.bcnth = (count >> 8) & 0xff;
860afa8e06SEd Maste 	fp->body.init.bcntl = count & 0xff;
870afa8e06SEd Maste 	count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
880afa8e06SEd Maste 	memcpy(&fp->body.init.data, buf, count);
890afa8e06SEd Maste 
90f540a430SEd Maste 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
91f540a430SEd Maste 	    (size_t)n != len)
920afa8e06SEd Maste 		return (0);
930afa8e06SEd Maste 
940afa8e06SEd Maste 	return (count);
950afa8e06SEd Maste }
960afa8e06SEd Maste 
970afa8e06SEd Maste static size_t
tx_frame(fido_dev_t * d,uint8_t seq,const void * buf,size_t count,int * ms)98f540a430SEd Maste tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
990afa8e06SEd Maste {
1000afa8e06SEd Maste 	struct frame	*fp;
1010afa8e06SEd Maste 	unsigned char	 pkt[sizeof(*fp) + 1];
1020afa8e06SEd Maste 	const size_t	 len = d->tx_len + 1;
1030afa8e06SEd Maste 	int		 n;
1040afa8e06SEd Maste 
1050afa8e06SEd Maste 	if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
1060afa8e06SEd Maste 		return (0);
1070afa8e06SEd Maste 
1080afa8e06SEd Maste 	memset(&pkt, 0, sizeof(pkt));
1090afa8e06SEd Maste 	fp = (struct frame *)(pkt + 1);
1100afa8e06SEd Maste 	fp->cid = d->cid;
1110afa8e06SEd Maste 	fp->body.cont.seq = seq;
1120afa8e06SEd Maste 	count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
1130afa8e06SEd Maste 	memcpy(&fp->body.cont.data, buf, count);
1140afa8e06SEd Maste 
115f540a430SEd Maste 	if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
116f540a430SEd Maste 	    (size_t)n != len)
1170afa8e06SEd Maste 		return (0);
1180afa8e06SEd Maste 
1190afa8e06SEd Maste 	return (count);
1200afa8e06SEd Maste }
1210afa8e06SEd Maste 
1220afa8e06SEd Maste static int
tx(fido_dev_t * d,uint8_t cmd,const unsigned char * buf,size_t count,int * ms)123f540a430SEd Maste tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
1240afa8e06SEd Maste {
1250afa8e06SEd Maste 	size_t n, sent;
1260afa8e06SEd Maste 
127f540a430SEd Maste 	if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
1280afa8e06SEd Maste 		fido_log_debug("%s: tx_preamble", __func__);
1290afa8e06SEd Maste 		return (-1);
1300afa8e06SEd Maste 	}
1310afa8e06SEd Maste 
1320afa8e06SEd Maste 	for (uint8_t seq = 0; sent < count; sent += n) {
1330afa8e06SEd Maste 		if (seq & 0x80) {
1340afa8e06SEd Maste 			fido_log_debug("%s: seq & 0x80", __func__);
1350afa8e06SEd Maste 			return (-1);
1360afa8e06SEd Maste 		}
137f540a430SEd Maste 		if ((n = tx_frame(d, seq++, buf + sent, count - sent,
138f540a430SEd Maste 		    ms)) == 0) {
1390afa8e06SEd Maste 			fido_log_debug("%s: tx_frame", __func__);
1400afa8e06SEd Maste 			return (-1);
1410afa8e06SEd Maste 		}
1420afa8e06SEd Maste 	}
1430afa8e06SEd Maste 
1440afa8e06SEd Maste 	return (0);
1450afa8e06SEd Maste }
1460afa8e06SEd Maste 
147f540a430SEd Maste static int
transport_tx(fido_dev_t * d,uint8_t cmd,const void * buf,size_t count,int * ms)148f540a430SEd Maste transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
149f540a430SEd Maste {
150f540a430SEd Maste 	struct timespec ts;
151f540a430SEd Maste 	int n;
152f540a430SEd Maste 
153f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
154f540a430SEd Maste 		return (-1);
155f540a430SEd Maste 
156f540a430SEd Maste 	n = d->transport.tx(d, cmd, buf, count);
157f540a430SEd Maste 
158f540a430SEd Maste 	if (fido_time_delta(&ts, ms) != 0)
159f540a430SEd Maste 		return (-1);
160f540a430SEd Maste 
161f540a430SEd Maste 	return (n);
162f540a430SEd Maste }
163f540a430SEd Maste 
1640afa8e06SEd Maste int
fido_tx(fido_dev_t * d,uint8_t cmd,const void * buf,size_t count,int * ms)165f540a430SEd Maste fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
1660afa8e06SEd Maste {
1670afa8e06SEd Maste 	fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
1680afa8e06SEd Maste 	fido_log_xxd(buf, count, "%s", __func__);
1690afa8e06SEd Maste 
1700afa8e06SEd Maste 	if (d->transport.tx != NULL)
171f540a430SEd Maste 		return (transport_tx(d, cmd, buf, count, ms));
1720afa8e06SEd Maste 	if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
1730afa8e06SEd Maste 		fido_log_debug("%s: invalid argument", __func__);
1740afa8e06SEd Maste 		return (-1);
1750afa8e06SEd Maste 	}
1760afa8e06SEd Maste 
177f540a430SEd Maste 	return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
1780afa8e06SEd Maste }
1790afa8e06SEd Maste 
1800afa8e06SEd Maste static int
rx_frame(fido_dev_t * d,struct frame * fp,int * ms)181f540a430SEd Maste rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
1820afa8e06SEd Maste {
183f540a430SEd Maste 	struct timespec ts;
1840afa8e06SEd Maste 	int n;
1850afa8e06SEd Maste 
1860afa8e06SEd Maste 	memset(fp, 0, sizeof(*fp));
1870afa8e06SEd Maste 
188f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
1890afa8e06SEd Maste 		return (-1);
1900afa8e06SEd Maste 
191f540a430SEd Maste 	if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
192f540a430SEd Maste 	    (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
193f540a430SEd Maste 		return (-1);
194f540a430SEd Maste 
195f540a430SEd Maste 	return (fido_time_delta(&ts, ms));
1960afa8e06SEd Maste }
1970afa8e06SEd Maste 
1980afa8e06SEd Maste static int
rx_preamble(fido_dev_t * d,uint8_t cmd,struct frame * fp,int * ms)199f540a430SEd Maste rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
2000afa8e06SEd Maste {
2010afa8e06SEd Maste 	do {
2020afa8e06SEd Maste 		if (rx_frame(d, fp, ms) < 0)
2030afa8e06SEd Maste 			return (-1);
2040afa8e06SEd Maste #ifdef FIDO_FUZZ
2050afa8e06SEd Maste 		fp->cid = d->cid;
2060afa8e06SEd Maste #endif
2070afa8e06SEd Maste 	} while (fp->cid != d->cid || (fp->cid == d->cid &&
2080afa8e06SEd Maste 	    fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
2090afa8e06SEd Maste 
2100afa8e06SEd Maste 	if (d->rx_len > sizeof(*fp))
2110afa8e06SEd Maste 		return (-1);
2120afa8e06SEd Maste 
2130afa8e06SEd Maste 	fido_log_xxd(fp, d->rx_len, "%s", __func__);
2140afa8e06SEd Maste #ifdef FIDO_FUZZ
2150afa8e06SEd Maste 	fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
2160afa8e06SEd Maste #endif
2170afa8e06SEd Maste 
2180afa8e06SEd Maste 	if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
2190afa8e06SEd Maste 		fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
2200afa8e06SEd Maste 		    __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
2210afa8e06SEd Maste 		return (-1);
2220afa8e06SEd Maste 	}
2230afa8e06SEd Maste 
2240afa8e06SEd Maste 	return (0);
2250afa8e06SEd Maste }
2260afa8e06SEd Maste 
2270afa8e06SEd Maste static int
rx(fido_dev_t * d,uint8_t cmd,unsigned char * buf,size_t count,int * ms)228f540a430SEd Maste rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
2290afa8e06SEd Maste {
2300afa8e06SEd Maste 	struct frame f;
2310afa8e06SEd Maste 	size_t r, payload_len, init_data_len, cont_data_len;
2320afa8e06SEd Maste 
2330afa8e06SEd Maste 	if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
2340afa8e06SEd Maste 	    d->rx_len <= CTAP_CONT_HEADER_LEN)
2350afa8e06SEd Maste 		return (-1);
2360afa8e06SEd Maste 
2370afa8e06SEd Maste 	init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
2380afa8e06SEd Maste 	cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
2390afa8e06SEd Maste 
2400afa8e06SEd Maste 	if (init_data_len > sizeof(f.body.init.data) ||
2410afa8e06SEd Maste 	    cont_data_len > sizeof(f.body.cont.data))
2420afa8e06SEd Maste 		return (-1);
2430afa8e06SEd Maste 
2440afa8e06SEd Maste 	if (rx_preamble(d, cmd, &f, ms) < 0) {
2450afa8e06SEd Maste 		fido_log_debug("%s: rx_preamble", __func__);
2460afa8e06SEd Maste 		return (-1);
2470afa8e06SEd Maste 	}
2480afa8e06SEd Maste 
2490afa8e06SEd Maste 	payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
2500afa8e06SEd Maste 	fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
2510afa8e06SEd Maste 
2520afa8e06SEd Maste 	if (count < payload_len) {
2530afa8e06SEd Maste 		fido_log_debug("%s: count < payload_len", __func__);
2540afa8e06SEd Maste 		return (-1);
2550afa8e06SEd Maste 	}
2560afa8e06SEd Maste 
2570afa8e06SEd Maste 	if (payload_len < init_data_len) {
2580afa8e06SEd Maste 		memcpy(buf, f.body.init.data, payload_len);
2590afa8e06SEd Maste 		return ((int)payload_len);
2600afa8e06SEd Maste 	}
2610afa8e06SEd Maste 
2620afa8e06SEd Maste 	memcpy(buf, f.body.init.data, init_data_len);
2630afa8e06SEd Maste 	r = init_data_len;
2640afa8e06SEd Maste 
2650afa8e06SEd Maste 	for (int seq = 0; r < payload_len; seq++) {
2660afa8e06SEd Maste 		if (rx_frame(d, &f, ms) < 0) {
2670afa8e06SEd Maste 			fido_log_debug("%s: rx_frame", __func__);
2680afa8e06SEd Maste 			return (-1);
2690afa8e06SEd Maste 		}
2700afa8e06SEd Maste 
2710afa8e06SEd Maste 		fido_log_xxd(&f, d->rx_len, "%s", __func__);
2720afa8e06SEd Maste #ifdef FIDO_FUZZ
2730afa8e06SEd Maste 		f.cid = d->cid;
2740afa8e06SEd Maste 		f.body.cont.seq = (uint8_t)seq;
2750afa8e06SEd Maste #endif
2760afa8e06SEd Maste 
2770afa8e06SEd Maste 		if (f.cid != d->cid || f.body.cont.seq != seq) {
2780afa8e06SEd Maste 			fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
2790afa8e06SEd Maste 			    __func__, f.cid, d->cid, f.body.cont.seq, seq);
2800afa8e06SEd Maste 			return (-1);
2810afa8e06SEd Maste 		}
2820afa8e06SEd Maste 
2830afa8e06SEd Maste 		if (payload_len - r > cont_data_len) {
2840afa8e06SEd Maste 			memcpy(buf + r, f.body.cont.data, cont_data_len);
2850afa8e06SEd Maste 			r += cont_data_len;
2860afa8e06SEd Maste 		} else {
2870afa8e06SEd Maste 			memcpy(buf + r, f.body.cont.data, payload_len - r);
2880afa8e06SEd Maste 			r += payload_len - r; /* break */
2890afa8e06SEd Maste 		}
2900afa8e06SEd Maste 	}
2910afa8e06SEd Maste 
2920afa8e06SEd Maste 	return ((int)r);
2930afa8e06SEd Maste }
2940afa8e06SEd Maste 
295f540a430SEd Maste static int
transport_rx(fido_dev_t * d,uint8_t cmd,void * buf,size_t count,int * ms)296f540a430SEd Maste transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
297f540a430SEd Maste {
298f540a430SEd Maste 	struct timespec ts;
299f540a430SEd Maste 	int n;
300f540a430SEd Maste 
301f540a430SEd Maste 	if (fido_time_now(&ts) != 0)
302f540a430SEd Maste 		return (-1);
303f540a430SEd Maste 
304f540a430SEd Maste 	n = d->transport.rx(d, cmd, buf, count, *ms);
305f540a430SEd Maste 
306f540a430SEd Maste 	if (fido_time_delta(&ts, ms) != 0)
307f540a430SEd Maste 		return (-1);
308f540a430SEd Maste 
309f540a430SEd Maste 	return (n);
310f540a430SEd Maste }
311f540a430SEd Maste 
3120afa8e06SEd Maste int
fido_rx(fido_dev_t * d,uint8_t cmd,void * buf,size_t count,int * ms)313f540a430SEd Maste fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
3140afa8e06SEd Maste {
3150afa8e06SEd Maste 	int n;
3160afa8e06SEd Maste 
3170afa8e06SEd Maste 	fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
318f540a430SEd Maste 	    cmd, *ms);
3190afa8e06SEd Maste 
3200afa8e06SEd Maste 	if (d->transport.rx != NULL)
321f540a430SEd Maste 		return (transport_rx(d, cmd, buf, count, ms));
3220afa8e06SEd Maste 	if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
3230afa8e06SEd Maste 		fido_log_debug("%s: invalid argument", __func__);
3240afa8e06SEd Maste 		return (-1);
3250afa8e06SEd Maste 	}
3260afa8e06SEd Maste 	if ((n = rx(d, cmd, buf, count, ms)) >= 0)
3270afa8e06SEd Maste 		fido_log_xxd(buf, (size_t)n, "%s", __func__);
3280afa8e06SEd Maste 
3290afa8e06SEd Maste 	return (n);
3300afa8e06SEd Maste }
3310afa8e06SEd Maste 
3320afa8e06SEd Maste int
fido_rx_cbor_status(fido_dev_t * d,int * ms)333f540a430SEd Maste fido_rx_cbor_status(fido_dev_t *d, int *ms)
3340afa8e06SEd Maste {
335*2ccfa855SEd Maste 	unsigned char	*msg;
336*2ccfa855SEd Maste 	int		 msglen;
337*2ccfa855SEd Maste 	int		 r;
3380afa8e06SEd Maste 
339*2ccfa855SEd Maste 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
340*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
341*2ccfa855SEd Maste 		goto out;
3420afa8e06SEd Maste 	}
3430afa8e06SEd Maste 
344*2ccfa855SEd Maste 	if ((msglen = fido_rx(d, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0 ||
345*2ccfa855SEd Maste 	    (size_t)msglen < 1) {
346*2ccfa855SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
347*2ccfa855SEd Maste 		r = FIDO_ERR_RX;
348*2ccfa855SEd Maste 		goto out;
349*2ccfa855SEd Maste 	}
350*2ccfa855SEd Maste 
351*2ccfa855SEd Maste 	r = msg[0];
352*2ccfa855SEd Maste out:
353*2ccfa855SEd Maste 	freezero(msg, FIDO_MAXMSG);
354*2ccfa855SEd Maste 
355*2ccfa855SEd Maste 	return (r);
3560afa8e06SEd Maste }
357