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