CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
ccid.cpp
Go to the documentation of this file.
1
7
11#include "cdc_log.h"
12#include <string.h>
13#include <stdio.h>
14
15static const char *TAG = "CCID";
16
20const uint8_t CCID_DESCRIPTOR[] = {
21 0x36, // bLength: 54 bytes
22 0x21, // bDescriptorType: Functional Descriptor
23 0x10, 0x01, // bcdCCID: CCID version 1.10
24 0x00, // bMaxSlotIndex: 1 slot (index 0)
25 0x07, // bVoltageSupport: 5V, 3V, 1.8V
26 0x02, 0x00, 0x00, 0x00, // dwProtocols: T=1 only
27 0xA0, 0x0F, 0x00, 0x00, // dwDefaultClock: 4000 kHz
28 0xA0, 0x0F, 0x00, 0x00, // dwMaximumClock: 4000 kHz
29 0x00, // bNumClockSupported
30 0xB0, 0x04, 0x00, 0x00, // dwDataRate: 1200 bps
31 0xB0, 0x04, 0x00, 0x00, // dwMaxDataRate: 1200 bps
32 0x00, // bNumDataRatesSupported
33 0xFE, 0x00, 0x00, 0x00, // dwMaxIFSD: 254 bytes
34 0x00, 0x00, 0x00, 0x00, // dwSynchProtocols: none
35 0x00, 0x00, 0x00, 0x00, // dwMechanical: none
36 // dwFeatures:
37 // - Auto ICC clock frequency change
38 // - Auto baud rate change
39 // - Auto parameter negotiation
40 // - Short and Extended APDU level exchange
41 0x42, 0x08, 0x04, 0x00,
42 0x00, 0x08, 0x00, 0x00, // dwMaxCCIDMessageLength: 2048
43 0xFF, // bClassGetResponse: echo
44 0xFF, // bClassEnvelope: echo
45 0x00, 0x00, // wLcdLayout: none
46 0x00, // bPINSupport: none
47 0x01 // bMaxCCIDBusySlots: 1
48};
49
50const size_t CCID_DESCRIPTOR_LEN = sizeof(CCID_DESCRIPTOR);
51
57static const uint8_t ATR[] = {
58 0x3B, // TS: Direct convention
59 0xDA, // T0: Y1=D (TA1,TC1,TD1 present), K=10 historical bytes
60 0x18, // TA1: FI=1, DI=8
61 0xFF, // TC1: N=255 (guard time)
62 0x81, // TD1: Y2=8 (TD2 present), T=1
63 0xB1, // TD2: Y3=B (TA3,TB3,TD3 present), T=1
64 0xFE, // TA3: IFSC=254
65 0x75, // TB3: BWI=7, CWI=5
66 0x1F, // TD3: Y4=1 (TA4 present), T=15
67 0x03, // TA4: Class indicator
68 // 10 Historical bytes (OpenPGP format)
69 0x00, 0x31, 0xF5, 0x73, 0xC0, 0x01, 0x60, 0x00, 0x90, 0x00,
70 0x1C // TCK: checksum
71};
72
73static bool initialized = false;
74static uint8_t current_slot = 0;
75static uint8_t current_seq = 0;
76
81extern "C" void ccid_driver_link_anchor(void);
82
83bool ccid_init(void) {
85
86 if (!openpgp_init()) {
87 LOG_E(TAG, "Failed to initialize OpenPGP");
88 return false;
89 }
90
91 initialized = true;
92 LOG_I(TAG, "CCID initialized");
93 return true;
94}
95
101const uint8_t* ccid_get_atr(size_t *len) {
102 if (len) {
103 *len = sizeof(ATR);
104 }
105 return ATR;
106}
107
113 return initialized;
114}
115
126static void ccid_build_header(uint8_t *resp, uint8_t msg_type, uint32_t data_len,
127 uint8_t slot, uint8_t seq, uint8_t status, uint8_t error) {
128 resp[0] = msg_type;
129 resp[1] = data_len & 0xFF;
130 resp[2] = (data_len >> 8) & 0xFF;
131 resp[3] = (data_len >> 16) & 0xFF;
132 resp[4] = (data_len >> 24) & 0xFF;
133 resp[5] = slot;
134 resp[6] = seq;
135 resp[7] = status;
136 resp[8] = error;
137 resp[9] = 0; // Chain parameter
138}
139
148int ccid_process_message(const uint8_t *msg, size_t msg_len,
149 uint8_t *resp, size_t resp_max) {
150 if (!msg || msg_len < CCID_HEADER_SIZE || !resp || resp_max < CCID_HEADER_SIZE) {
151 LOG_E(TAG, "Invalid parameters: msg_len=%zu resp_max=%zu", msg_len, resp_max);
152 return -1;
153 }
154
155 const ccid_header_t *hdr = (const ccid_header_t *)msg;
156 current_slot = hdr->bSlot;
157 current_seq = hdr->bSeq;
158
159 uint8_t status = CCID_ICC_PRESENT_ACTIVE;
160 uint8_t error = 0;
161 int result_len = 0;
162
163 switch (hdr->bMessageType) {
165 size_t atr_len;
166 const uint8_t *atr = ccid_get_atr(&atr_len);
168 current_slot, current_seq, status, error);
169 memcpy(resp + CCID_HEADER_SIZE, atr, atr_len);
170 result_len = CCID_HEADER_SIZE + atr_len;
171 break;
172 }
173
177 current_slot, current_seq, status, error);
178 result_len = CCID_HEADER_SIZE;
179 break;
180 }
181
184 current_slot, current_seq, status, error);
185 result_len = CCID_HEADER_SIZE;
186 break;
187 }
188
190 uint32_t apdu_len = hdr->dwLength;
191
192 // Subtractive comparison prevents integer overflow when dwLength is
193 // attacker-controlled.
194 if (msg_len < CCID_HEADER_SIZE ||
195 apdu_len > (uint32_t)(msg_len - CCID_HEADER_SIZE) ||
196 apdu_len > (uint32_t)(resp_max - CCID_HEADER_SIZE)) {
197 LOG_E(TAG, "XFR length invalid (apdu=%u msg=%zu rmax=%zu)",
198 apdu_len, msg_len, resp_max);
200 status = CCID_CMD_STATUS_FAILED;
202 current_slot, current_seq, status, error);
203 result_len = CCID_HEADER_SIZE;
204 break;
205 }
206
207 const uint8_t *apdu_data = msg + CCID_HEADER_SIZE;
208 uint8_t *resp_data = resp + CCID_HEADER_SIZE;
209 size_t resp_data_max = resp_max - CCID_HEADER_SIZE;
210
211 int resp_len = openpgp_process_apdu(apdu_data, apdu_len,
212 resp_data, resp_data_max);
213
214 if (resp_len < 0) {
215 error = CCID_ERROR_HW_ERROR;
216 status = CCID_CMD_STATUS_FAILED;
218 current_slot, current_seq, status, error);
219 result_len = CCID_HEADER_SIZE;
220 } else {
222 current_slot, current_seq, status, error);
223 result_len = CCID_HEADER_SIZE + resp_len;
224 }
225
226 // One-line summary per APDU. INS = byte 1 of the APDU, SW = last two response bytes.
227 uint16_t sw = (resp_len >= 2)
228 ? ((uint16_t)resp_data[resp_len - 2] << 8) | resp_data[resp_len - 1]
229 : 0;
230 LOG_I(TAG, "XFR seq=%u INS=0x%02X in=%uB out=%dB sw=%04X",
231 hdr->bSeq, apdu_len >= 2 ? apdu_data[1] : 0,
232 apdu_len, resp_len, sw);
233 break;
234 }
235
237 uint8_t params[] = {
238 0x01, // bmFindexDindex
239 0x00, // bmTCCKST1
240 0x00, // bGuardTimeT1
241 0xFE, // bmWaitingIntegersT1 (BWI=15, CWI=14)
242 0x00, // bClockStop
243 0xFE, // bIFSC
244 0x00 // bNadValue
245 };
246 ccid_build_header(resp, CCID_RDR_TO_PC_PARAMETERS, sizeof(params),
247 current_slot, current_seq, status, error);
248 resp[9] = 0x01; // bProtocolNum: T=1
249 memcpy(resp + CCID_HEADER_SIZE, params, sizeof(params));
250 result_len = CCID_HEADER_SIZE + sizeof(params);
251 break;
252 }
253
255 uint8_t params[] = {
256 0x01, 0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00
257 };
258 ccid_build_header(resp, CCID_RDR_TO_PC_PARAMETERS, sizeof(params),
259 current_slot, current_seq, status, error);
260 resp[9] = 0x01; // bProtocolNum: T=1
261 memcpy(resp + CCID_HEADER_SIZE, params, sizeof(params));
262 result_len = CCID_HEADER_SIZE + sizeof(params);
263 break;
264 }
265
266 default:
267 LOG_W(TAG, "Unknown CCID type 0x%02X seq=%u", hdr->bMessageType, hdr->bSeq);
269 status = CCID_CMD_STATUS_FAILED;
271 current_slot, current_seq, status, error);
272 result_len = CCID_HEADER_SIZE;
273 break;
274 }
275
276 return result_len;
277}
static const char * TAG
static const uint8_t ATR[]
ATR (Answer To Reset) for CDC Badge OpenPGP card.
Definition ccid.cpp:57
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
static uint8_t current_slot
Definition ccid.cpp:74
static uint8_t current_seq
Definition ccid.cpp:75
bool ccid_card_present(void)
Returns whether virtual CCID card is available.
Definition ccid.cpp:112
void ccid_driver_link_anchor(void)
Initializes CCID transport and backing OpenPGP applet.
bool ccid_init(void)
Definition ccid.cpp:83
const uint8_t * ccid_get_atr(size_t *len)
Returns pointer and length of ATR bytes.
Definition ccid.cpp:101
static void ccid_build_header(uint8_t *resp, uint8_t msg_type, uint32_t data_len, uint8_t slot, uint8_t seq, uint8_t status, uint8_t error)
Builds a CCID response header in transport byte format.
Definition ccid.cpp:126
#define CCID_PC_TO_RDR_GET_PARAMETERS
Definition ccid.h:32
#define CCID_ERROR_HW_ERROR
Definition ccid.h:57
#define CCID_PC_TO_RDR_XFR_BLOCK
Definition ccid.h:31
#define CCID_PC_TO_RDR_ICC_POWER_ON
Definition ccid.h:28
#define CCID_ERROR_CMD_NOT_SUPPORTED
Definition ccid.h:58
ccid_header_t
Definition ccid.h:71
const uint8_t CCID_DESCRIPTOR[]
CCID functional descriptor (54 bytes) per OpenPGP 3.4.1 profile.
Definition ccid.cpp:20
#define CCID_PC_TO_RDR_ICC_POWER_OFF
Definition ccid.h:29
#define CCID_HEADER_SIZE
Definition ccid.h:62
#define CCID_PC_TO_RDR_GET_SLOT_STATUS
Definition ccid.h:30
#define CCID_RDR_TO_PC_PARAMETERS
Definition ccid.h:40
#define CCID_CMD_STATUS_FAILED
Definition ccid.h:49
#define CCID_PC_TO_RDR_RESET_PARAMETERS
Definition ccid.h:33
const size_t CCID_DESCRIPTOR_LEN
Definition ccid.cpp:50
#define CCID_ICC_PRESENT_INACTIVE
Definition ccid.h:44
#define CCID_RDR_TO_PC_SLOT_STATUS
Definition ccid.h:39
#define CCID_RDR_TO_PC_DATA_BLOCK
Definition ccid.h:38
#define CCID_ICC_PRESENT_ACTIVE
Definition ccid.h:43
#define CCID_ERROR_XFR_OVERRUN
Definition ccid.h:56
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
bool initialized
Definition ctap2.cpp:61
int openpgp_process_apdu(const uint8_t *cmd, size_t cmd_len, uint8_t *resp, size_t resp_max)
Definition openpgp.cpp:2683
bool openpgp_init(void)
Definition openpgp.cpp:863