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