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