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 "fido.h" 8 #include "packed.h" 9 10 PACKED_TYPE(frame_t, 11 struct frame { 12 uint32_t cid; /* channel id */ 13 union { 14 uint8_t type; 15 struct { 16 uint8_t cmd; 17 uint8_t bcnth; 18 uint8_t bcntl; 19 uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN]; 20 } init; 21 struct { 22 uint8_t seq; 23 uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN]; 24 } cont; 25 } body; 26 }) 27 28 #ifndef MIN 29 #define MIN(x, y) ((x) > (y) ? (y) : (x)) 30 #endif 31 32 static int 33 tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms) 34 { 35 struct timespec ts; 36 int n; 37 38 if (fido_time_now(&ts) != 0) 39 return (-1); 40 41 n = d->io.write(d->io_handle, pkt, len); 42 43 if (fido_time_delta(&ts, ms) != 0) 44 return (-1); 45 46 return (n); 47 } 48 49 static int 50 tx_empty(fido_dev_t *d, uint8_t cmd, int *ms) 51 { 52 struct frame *fp; 53 unsigned char pkt[sizeof(*fp) + 1]; 54 const size_t len = d->tx_len + 1; 55 int n; 56 57 memset(&pkt, 0, sizeof(pkt)); 58 fp = (struct frame *)(pkt + 1); 59 fp->cid = d->cid; 60 fp->body.init.cmd = CTAP_FRAME_INIT | cmd; 61 62 if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 || 63 (size_t)n != len) 64 return (-1); 65 66 return (0); 67 } 68 69 static size_t 70 tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms) 71 { 72 struct frame *fp; 73 unsigned char pkt[sizeof(*fp) + 1]; 74 const size_t len = d->tx_len + 1; 75 int n; 76 77 if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data)) 78 return (0); 79 80 memset(&pkt, 0, sizeof(pkt)); 81 fp = (struct frame *)(pkt + 1); 82 fp->cid = d->cid; 83 fp->body.init.cmd = CTAP_FRAME_INIT | cmd; 84 fp->body.init.bcnth = (count >> 8) & 0xff; 85 fp->body.init.bcntl = count & 0xff; 86 count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN); 87 memcpy(&fp->body.init.data, buf, count); 88 89 if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 || 90 (size_t)n != len) 91 return (0); 92 93 return (count); 94 } 95 96 static size_t 97 tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms) 98 { 99 struct frame *fp; 100 unsigned char pkt[sizeof(*fp) + 1]; 101 const size_t len = d->tx_len + 1; 102 int n; 103 104 if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data)) 105 return (0); 106 107 memset(&pkt, 0, sizeof(pkt)); 108 fp = (struct frame *)(pkt + 1); 109 fp->cid = d->cid; 110 fp->body.cont.seq = seq; 111 count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN); 112 memcpy(&fp->body.cont.data, buf, count); 113 114 if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 || 115 (size_t)n != len) 116 return (0); 117 118 return (count); 119 } 120 121 static int 122 tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms) 123 { 124 size_t n, sent; 125 126 if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) { 127 fido_log_debug("%s: tx_preamble", __func__); 128 return (-1); 129 } 130 131 for (uint8_t seq = 0; sent < count; sent += n) { 132 if (seq & 0x80) { 133 fido_log_debug("%s: seq & 0x80", __func__); 134 return (-1); 135 } 136 if ((n = tx_frame(d, seq++, buf + sent, count - sent, 137 ms)) == 0) { 138 fido_log_debug("%s: tx_frame", __func__); 139 return (-1); 140 } 141 } 142 143 return (0); 144 } 145 146 static int 147 transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms) 148 { 149 struct timespec ts; 150 int n; 151 152 if (fido_time_now(&ts) != 0) 153 return (-1); 154 155 n = d->transport.tx(d, cmd, buf, count); 156 157 if (fido_time_delta(&ts, ms) != 0) 158 return (-1); 159 160 return (n); 161 } 162 163 int 164 fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms) 165 { 166 fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd); 167 fido_log_xxd(buf, count, "%s", __func__); 168 169 if (d->transport.tx != NULL) 170 return (transport_tx(d, cmd, buf, count, ms)); 171 if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) { 172 fido_log_debug("%s: invalid argument", __func__); 173 return (-1); 174 } 175 176 return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms)); 177 } 178 179 static int 180 rx_frame(fido_dev_t *d, struct frame *fp, int *ms) 181 { 182 struct timespec ts; 183 int n; 184 185 memset(fp, 0, sizeof(*fp)); 186 187 if (fido_time_now(&ts) != 0) 188 return (-1); 189 190 if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle, 191 (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len) 192 return (-1); 193 194 return (fido_time_delta(&ts, ms)); 195 } 196 197 static int 198 rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms) 199 { 200 do { 201 if (rx_frame(d, fp, ms) < 0) 202 return (-1); 203 #ifdef FIDO_FUZZ 204 fp->cid = d->cid; 205 #endif 206 } while (fp->cid != d->cid || (fp->cid == d->cid && 207 fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE))); 208 209 if (d->rx_len > sizeof(*fp)) 210 return (-1); 211 212 fido_log_xxd(fp, d->rx_len, "%s", __func__); 213 #ifdef FIDO_FUZZ 214 fp->body.init.cmd = (CTAP_FRAME_INIT | cmd); 215 #endif 216 217 if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) { 218 fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)", 219 __func__, fp->cid, d->cid, fp->body.init.cmd, cmd); 220 return (-1); 221 } 222 223 return (0); 224 } 225 226 static int 227 rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms) 228 { 229 struct frame f; 230 size_t r, payload_len, init_data_len, cont_data_len; 231 232 if (d->rx_len <= CTAP_INIT_HEADER_LEN || 233 d->rx_len <= CTAP_CONT_HEADER_LEN) 234 return (-1); 235 236 init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN; 237 cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN; 238 239 if (init_data_len > sizeof(f.body.init.data) || 240 cont_data_len > sizeof(f.body.cont.data)) 241 return (-1); 242 243 if (rx_preamble(d, cmd, &f, ms) < 0) { 244 fido_log_debug("%s: rx_preamble", __func__); 245 return (-1); 246 } 247 248 payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl); 249 fido_log_debug("%s: payload_len=%zu", __func__, payload_len); 250 251 if (count < payload_len) { 252 fido_log_debug("%s: count < payload_len", __func__); 253 return (-1); 254 } 255 256 if (payload_len < init_data_len) { 257 memcpy(buf, f.body.init.data, payload_len); 258 return ((int)payload_len); 259 } 260 261 memcpy(buf, f.body.init.data, init_data_len); 262 r = init_data_len; 263 264 for (int seq = 0; r < payload_len; seq++) { 265 if (rx_frame(d, &f, ms) < 0) { 266 fido_log_debug("%s: rx_frame", __func__); 267 return (-1); 268 } 269 270 fido_log_xxd(&f, d->rx_len, "%s", __func__); 271 #ifdef FIDO_FUZZ 272 f.cid = d->cid; 273 f.body.cont.seq = (uint8_t)seq; 274 #endif 275 276 if (f.cid != d->cid || f.body.cont.seq != seq) { 277 fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)", 278 __func__, f.cid, d->cid, f.body.cont.seq, seq); 279 return (-1); 280 } 281 282 if (payload_len - r > cont_data_len) { 283 memcpy(buf + r, f.body.cont.data, cont_data_len); 284 r += cont_data_len; 285 } else { 286 memcpy(buf + r, f.body.cont.data, payload_len - r); 287 r += payload_len - r; /* break */ 288 } 289 } 290 291 return ((int)r); 292 } 293 294 static int 295 transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms) 296 { 297 struct timespec ts; 298 int n; 299 300 if (fido_time_now(&ts) != 0) 301 return (-1); 302 303 n = d->transport.rx(d, cmd, buf, count, *ms); 304 305 if (fido_time_delta(&ts, ms) != 0) 306 return (-1); 307 308 return (n); 309 } 310 311 int 312 fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms) 313 { 314 int n; 315 316 fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d, 317 cmd, *ms); 318 319 if (d->transport.rx != NULL) 320 return (transport_rx(d, cmd, buf, count, ms)); 321 if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) { 322 fido_log_debug("%s: invalid argument", __func__); 323 return (-1); 324 } 325 if ((n = rx(d, cmd, buf, count, ms)) >= 0) 326 fido_log_xxd(buf, (size_t)n, "%s", __func__); 327 328 return (n); 329 } 330 331 int 332 fido_rx_cbor_status(fido_dev_t *d, int *ms) 333 { 334 unsigned char reply[FIDO_MAXMSG]; 335 int reply_len; 336 337 if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply), 338 ms)) < 0 || (size_t)reply_len < 1) { 339 fido_log_debug("%s: fido_rx", __func__); 340 return (FIDO_ERR_RX); 341 } 342 343 return (reply[0]); 344 } 345