CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
ccid_driver.cpp
Go to the documentation of this file.
3#include "cdc_log.h"
4
5extern "C" {
6#include "tusb.h"
7#include "device/usbd_pvt.h"
8}
9
10#include <esp_attr.h>
11#include <string.h>
12#include <stdio.h>
13
14static const char* TAG = "CCID";
15
16#define CCID_LOG(tag, fmt, ...) LOG_I(tag, fmt, ##__VA_ARGS__)
17#define CCID_LOG_E(tag, fmt, ...) LOG_E(tag, fmt, ##__VA_ARGS__)
18#define CCID_LOG_W(tag, fmt, ...) LOG_W(tag, fmt, ##__VA_ARGS__)
19
20#define CCID_RX_BUF_SIZE (CCID_MAX_MSG_SIZE + CCID_HEADER_SIZE)
21#define CCID_TX_BUF_SIZE (CCID_MAX_MSG_SIZE + CCID_HEADER_SIZE)
22
23// Must live in internal SRAM: ESP32-S3 USB OTG DMA cannot access PSRAM.
26
27static struct {
29 uint8_t itf_num;
30 uint8_t ep_in;
31 uint8_t ep_out;
32 uint8_t rhport;
33
34 uint16_t rx_len;
36
37 uint32_t rx_count;
38 uint32_t tx_count;
39 uint32_t error_count;
41
42static void ccid_driver_init(void) {
43 CCID_LOG(TAG, "CCID driver init");
44 memset(&ccid_state, 0, sizeof(ccid_state));
45}
46
47static void ccid_driver_reset(uint8_t rhport) {
48 (void)rhport;
49 ccid_state.rx_len = 0;
50 ccid_state.rx_pending = false;
51 ccid_state.initialized = false;
52 ccid_state.rx_count = 0;
53 ccid_state.tx_count = 0;
54 ccid_state.error_count = 0;
55}
56
57static uint16_t ccid_driver_open(uint8_t rhport, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
58 if (desc_itf->bInterfaceClass != TUSB_CLASS_SMART_CARD) {
59 return 0;
60 }
61
62 ccid_state.itf_num = desc_itf->bInterfaceNumber;
63 ccid_state.rhport = rhport;
64
65 uint16_t drv_len = sizeof(tusb_desc_interface_t);
66 uint8_t const* p_desc = (uint8_t const*)desc_itf + drv_len;
67
68 while (drv_len < max_len) {
69 uint8_t desc_len = p_desc[0];
70 uint8_t desc_type = p_desc[1];
71 if (desc_type == 0x21) {
72 drv_len += desc_len;
73 p_desc += desc_len;
74 } else {
75 break;
76 }
77 }
78
79 uint8_t ep_count = 0;
80 while (drv_len < max_len && ep_count < desc_itf->bNumEndpoints) {
81 uint8_t desc_len = p_desc[0];
82 uint8_t desc_type = p_desc[1];
83
84 if (desc_type == TUSB_DESC_ENDPOINT) {
85 tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*)p_desc;
86 uint8_t ep_addr = ep_desc->bEndpointAddress;
87
88 if (usbd_edpt_open(rhport, ep_desc)) {
89 if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
90 ccid_state.ep_in = ep_addr;
91 } else {
92 ccid_state.ep_out = ep_addr;
93 }
94 ep_count++;
95 }
96 }
97 drv_len += desc_len;
98 p_desc += desc_len;
99 }
100
101 if (ccid_state.ep_out) {
102 bool ok = usbd_edpt_xfer(rhport, ccid_state.ep_out, ccid_rx_buf, sizeof(ccid_rx_buf));
103 ccid_state.rx_pending = ok;
104 }
105
106 ccid_state.initialized = true;
107 return drv_len;
108}
109
110static bool ccid_driver_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
111 if (stage != CONTROL_STAGE_SETUP) return true;
112
113 if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
114 request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE &&
115 request->wIndex == ccid_state.itf_num) {
116
117 // CCID 1.10 §5.3.1 — CCID_ABORT.
118 // Host clears a stalled XFR_BLOCK; we just acknowledge the status
119 // stage, drop any pending response remainder, and let the bulk EP
120 // reset naturally on the next request.
121 if (request->bRequest == 0x01) {
122 ccid_state.rx_pending = false;
123 ccid_state.rx_len = 0;
124 return tud_control_status(rhport, request);
125 }
126
127 // CCID 1.10 §5.3.2 — GET_CLOCK_FREQUENCIES.
128 // Returns the supported clock frequencies as little-endian uint32
129 // values in kHz. We advertise 4000 kHz to match dwDefaultClock /
130 // dwMaximumClock from the functional descriptor.
131 if (request->bRequest == 0x02) {
132 static const uint8_t clocks[] = {0xA0, 0x0F, 0x00, 0x00}; // 4000 kHz
133 return tud_control_xfer(rhport, request,
134 const_cast<uint8_t*>(clocks),
135 sizeof(clocks));
136 }
137
138 // CCID 1.10 §5.3.3 — GET_DATA_RATES.
139 // Returns the supported asynchronous data rates as little-endian
140 // uint32 values in bits/s. We mirror dwDataRate / dwMaxDataRate
141 // (1200 bps) so libccid keeps using our descriptor defaults.
142 if (request->bRequest == 0x03) {
143 static const uint8_t rates[] = {0xB0, 0x04, 0x00, 0x00}; // 1200 bps
144 return tud_control_xfer(rhport, request,
145 const_cast<uint8_t*>(rates),
146 sizeof(rates));
147 }
148 return false;
149 }
150
151 return false;
152}
153
154static bool ccid_driver_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
155 if (result != XFER_RESULT_SUCCESS) {
156 ccid_state.error_count++;
157 return true;
158 }
159
160 if (ep_addr == ccid_state.ep_out) {
161 ccid_state.rx_count++;
162 ccid_state.rx_len = xferred_bytes;
163 ccid_state.rx_pending = false;
164
165 if (xferred_bytes >= CCID_HEADER_SIZE) {
166 uint32_t data_len = (uint32_t)ccid_rx_buf[1] |
167 ((uint32_t)ccid_rx_buf[2] << 8) |
168 ((uint32_t)ccid_rx_buf[3] << 16) |
169 ((uint32_t)ccid_rx_buf[4] << 24);
170
171 // Reject host-controlled lengths that would overflow the rx buffer.
172 // Subtractive form avoids integer wraparound on attacker-chosen sizes.
173 if (data_len > sizeof(ccid_rx_buf) - CCID_HEADER_SIZE) {
174 ccid_state.error_count++;
175 } else {
176 uint32_t total_len = CCID_HEADER_SIZE + data_len;
177
178 if ((uint32_t)ccid_state.rx_len >= total_len) {
179 int resp_len = ccid_process_message(ccid_rx_buf, total_len,
180 ccid_tx_buf, sizeof(ccid_tx_buf));
181 if (resp_len > 0) {
182 ccid_state.tx_count++;
183 bool ok = usbd_edpt_xfer(rhport, ccid_state.ep_in, ccid_tx_buf, resp_len);
184 if (!ok) {
185 ccid_state.error_count++;
186 }
187 } else if (resp_len < 0) {
188 ccid_state.error_count++;
189 }
190 }
191 }
192 }
193
194 bool rx_ok = usbd_edpt_xfer(rhport, ccid_state.ep_out, ccid_rx_buf, sizeof(ccid_rx_buf));
195 if (!rx_ok) {
196 ccid_state.error_count++;
197 }
198 ccid_state.rx_pending = rx_ok;
199 return true;
200 }
201
202 if (ep_addr == ccid_state.ep_in) {
203 return true;
204 }
205
206 return true;
207}
208
209static usbd_class_driver_t const ccid_driver = {
210 .name = "CCID",
211 .init = ccid_driver_init,
212 .deinit = NULL,
213 .reset = ccid_driver_reset,
214 .open = ccid_driver_open,
215 .control_xfer_cb = ccid_driver_control_xfer_cb,
216 .xfer_cb = ccid_driver_xfer_cb,
217 .xfer_isr = NULL,
218 .sof = NULL
219};
220
221// Symbol referenced from ccid.cpp so the linker pulls this translation
222// unit into the final image. Without an external reference, the only
223// strong symbol here would be usbd_app_driver_get_cb — which tinyusb
224// already provides as a weak default, leading the linker to drop our
225// strong override and silently disable the entire CCID class driver.
226extern "C" void ccid_driver_link_anchor(void) {}
227
228extern "C" usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) {
229 *driver_count = 1;
230 return &ccid_driver;
231}
static const char * TAG
int ccid_process_message(const uint8_t *msg, size_t msg_len, uint8_t *resp, size_t resp_max)
Processes one incoming CCID message and writes corresponding response.
Definition ccid.cpp:148
#define CCID_HEADER_SIZE
Definition ccid.h:62
usbd_class_driver_t const * usbd_app_driver_get_cb(uint8_t *driver_count)
static struct @003011067126132137154336156300372031204254255025 ccid_state
static void ccid_driver_init(void)
static usbd_class_driver_t const ccid_driver
uint8_t ep_in
uint32_t tx_count
uint16_t rx_len
static uint16_t ccid_driver_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
static void ccid_driver_reset(uint8_t rhport)
uint8_t itf_num
static bool ccid_driver_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
static uint8_t ccid_rx_buf[(2048+10)]
#define CCID_LOG(tag, fmt,...)
void ccid_driver_link_anchor(void)
Initializes CCID transport and backing OpenPGP applet.
#define CCID_TX_BUF_SIZE
static bool ccid_driver_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
uint8_t ep_out
bool rx_pending
uint8_t rhport
uint32_t error_count
uint32_t rx_count
#define CCID_RX_BUF_SIZE
static uint8_t ccid_tx_buf[(2048+10)]
CDC Log: logging over TinyUSB CDC and UART.
bool initialized
Definition ctap2.cpp:61