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