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