Coverage Report

Created: 2020-03-07 10:10

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