CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
ctaphid.cpp
Go to the documentation of this file.
1
5
6#include "mod_fido2/ctaphid.h"
7#include "mod_fido2/ctap2.h"
8#include "mod_fido2/u2f.h"
9#include "mod_fido2/Fido2Ui.h"
10#include "cdc_hal/IDisplay.h"
11#include "cdc_log.h"
13#include <esp_attr.h>
14
15namespace cdc::mod_fido2 {
16 // USB transport callback implemented in Fido2Module.cpp.
17 bool fido2_usb_write(const uint8_t* buffer);
18}
20#include <string.h>
21#include <freertos/FreeRTOS.h>
22#include <freertos/task.h>
23#include <freertos/semphr.h>
24
25static const char* TAG = "CTAPHID";
26
28
29#ifndef CTAPHID_DEBUG_PACKETS
30#define CTAPHID_DEBUG_PACKETS DEBUG_MODE // Controlled by feature_flags.h
31#endif
32#define CTAPHID_MSG_TIMEOUT_MS 500 // Message assembly timeout
33#define CTAPHID_MAX_CHANNELS 8 // Max concurrent channels
34#define CTAPHID_RESPONSE_QUEUE_SIZE 8 // Response packet queue
35
37
39#define CTAPHID_RATE_LIMIT_WINDOW_MS 1000
40#define CTAPHID_RATE_LIMIT_MAX_CMDS 200
41
45
46static struct {
48 uint32_t next_cid; // Next channel ID to allocate
49 ctaphid_channel_t channels[CTAPHID_MAX_CHANNELS];
50 uint8_t init_buffer[64]; // Small buffer for INIT command only
51 SemaphoreHandle_t mutex;
52 uint32_t current_cid; // CID of active transaction
53 uint8_t response_rr_index; // Round-robin cursor over channels
54 // Rate limiting state
55 uint32_t rate_window_start; // Start of current rate window (ms)
56 uint8_t rate_cmd_count; // Commands in current window
57 // Command counters
58 uint32_t cbor_cmd_count; // CTAPHID_CBOR commands
59 uint32_t msg_cmd_count; // CTAPHID_MSG (U2F) commands
60} g_ctaphid = {};
61
67static ctaphid_channel_t *find_channel(uint32_t cid) {
68 if (cid == 0) return NULL;
69 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
70 if (g_ctaphid.channels[i].cid == cid) {
71 return &g_ctaphid.channels[i];
72 }
73 }
74 return NULL;
75}
76
87static void init_channel_slot(int index, uint32_t cid) {
88 ctaphid_channel_t *ch = &g_ctaphid.channels[index];
89 ch->cid = cid;
90 ch->active = false;
91 ch->buffer = s_msg_buffers[index];
92 ch->buffer_size = CTAPHID_MAX_MSG_SIZE;
93 ch->response_buffer = s_response_buffers[index];
94 ch->response_buffer_size = CTAPHID_MAX_MSG_SIZE;
95 ch->response_len = 0;
96 ch->response_offset = 0;
97 ch->response_cmd = 0;
98 ch->response_pending = false;
99}
100
101static ctaphid_channel_t *alloc_channel(uint32_t cid) {
102 // First check if already exists
103 ctaphid_channel_t *ch = find_channel(cid);
104 if (ch) return ch;
105
106 // Find free slot
107 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
108 if (g_ctaphid.channels[i].cid == 0) {
109 init_channel_slot(i, cid);
110 return &g_ctaphid.channels[i];
111 }
112 }
113
114 // All channels full - evict oldest inactive channel
115 int oldest_idx = -1;
116 uint32_t oldest_time = UINT32_MAX;
117 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
118 if (!g_ctaphid.channels[i].active &&
119 !g_ctaphid.channels[i].response_pending &&
120 g_ctaphid.channels[i].last_activity < oldest_time) {
121 oldest_time = g_ctaphid.channels[i].last_activity;
122 oldest_idx = i;
123 }
124 }
125
126 if (oldest_idx >= 0) {
127 LOG_W(TAG, "Evicting oldest channel 0x%08lX", g_ctaphid.channels[oldest_idx].cid);
128 init_channel_slot(oldest_idx, cid);
129 return &g_ctaphid.channels[oldest_idx];
130 }
131
132 return NULL; // All channels actively in use
133}
134
139static uint32_t allocate_cid(void) {
140 // Skip 0 (sentinel for unused slot) and the broadcast CID.
141 if (g_ctaphid.next_cid == 0 || g_ctaphid.next_cid == CTAPHID_BROADCAST_CID) {
142 g_ctaphid.next_cid = 1;
143 }
144 uint32_t cid = g_ctaphid.next_cid++;
145 if (g_ctaphid.next_cid == 0 || g_ctaphid.next_cid == CTAPHID_BROADCAST_CID) {
146 g_ctaphid.next_cid = 1;
147 }
148 return cid;
149}
150
160static void build_init_packet(uint8_t *packet, uint32_t cid, uint8_t cmd,
161 uint16_t bcnt, const uint8_t *data, uint16_t data_len) {
162 memset(packet, 0, CTAPHID_PACKET_SIZE);
163
164 // Header
165 packet[0] = (cid >> 24) & 0xFF;
166 packet[1] = (cid >> 16) & 0xFF;
167 packet[2] = (cid >> 8) & 0xFF;
168 packet[3] = cid & 0xFF;
169 packet[4] = cmd | 0x80; // Init packet has bit 7 set
170 packet[5] = (bcnt >> 8) & 0xFF;
171 packet[6] = bcnt & 0xFF;
172
173 // Data
174 if (data && data_len > 0) {
175 uint16_t copy = (data_len > CTAPHID_INIT_DATA) ? CTAPHID_INIT_DATA : data_len;
176 memcpy(packet + 7, data, copy);
177 }
178}
179
188static void build_cont_packet(uint8_t *packet, uint32_t cid, uint8_t seq,
189 const uint8_t *data, uint16_t data_len) {
190 memset(packet, 0, CTAPHID_PACKET_SIZE);
191
192 // Header
193 packet[0] = (cid >> 24) & 0xFF;
194 packet[1] = (cid >> 16) & 0xFF;
195 packet[2] = (cid >> 8) & 0xFF;
196 packet[3] = cid & 0xFF;
197 packet[4] = seq & 0x7F; // Continuation packet has bit 7 clear
198
199 // Data
200 if (data && data_len > 0) {
201 uint16_t copy = (data_len > CTAPHID_CONT_DATA) ? CTAPHID_CONT_DATA : data_len;
202 memcpy(packet + 5, data, copy);
203 }
204}
205
213static void prepare_response(uint32_t cid, uint8_t cmd, const uint8_t *data, uint16_t len) {
214 ctaphid_channel_t *ch = find_channel(cid);
215 if (!ch) {
216 LOG_W(TAG, "prepare_response: no channel for CID 0x%08lX", cid);
217 return;
218 }
219 ch->response_cid = cid;
220 ch->response_cmd = cmd;
221 if (data && len > 0) {
222 if (len > ch->response_buffer_size) {
223 LOG_E(TAG, "Response too large: %u > %u", len, ch->response_buffer_size);
224 len = ch->response_buffer_size;
225 }
226 memcpy(ch->response_buffer, data, len);
227 }
228 ch->response_len = len;
229 ch->response_offset = 0;
230 ch->response_pending = true;
231 if (cmd == CTAPHID_CBOR && len > 0) {
232 LOG_I(TAG, "Prepared CBOR response len=%u status=0x%02X", len, data[0]);
233 }
234}
235
244static void prepare_response_on(ctaphid_channel_t *ch, uint32_t send_cid, uint8_t cmd,
245 const uint8_t *data, uint16_t len) {
246 if (!ch) return;
247 ch->response_cid = send_cid;
248 ch->response_cmd = cmd;
249 if (data && len > 0) {
250 if (len > ch->response_buffer_size) {
251 LOG_E(TAG, "Response too large: %u > %u", len, ch->response_buffer_size);
252 len = ch->response_buffer_size;
253 }
254 memcpy(ch->response_buffer, data, len);
255 }
256 ch->response_len = len;
257 ch->response_offset = 0;
258 ch->response_pending = true;
259}
260
267static void handle_init(uint32_t cid, const uint8_t *data, uint16_t len) {
268 if (len < 8) {
270 return;
271 }
272
273 // A new channel is a clean slate: any pending cancel from a previous
274 // transaction must not abort this INIT response or the channel's first
275 // CBOR exchange.
277
278 // Allocate new channel
279 uint32_t new_cid = (cid == CTAPHID_BROADCAST_CID) ? allocate_cid() : cid;
280
281 ctaphid_channel_t *ch = alloc_channel(new_cid);
282 if (!ch) {
284 return;
285 }
286
287 // Build INIT response (17 bytes)
288 uint8_t response[17];
289 memcpy(response, data, 8); // Echo nonce
290 response[8] = (new_cid >> 24) & 0xFF;
291 response[9] = (new_cid >> 16) & 0xFF;
292 response[10] = (new_cid >> 8) & 0xFF;
293 response[11] = new_cid & 0xFF;
294 response[12] = 2; // Protocol version
295 response[13] = 0; // Device version major
296 response[14] = 1; // Device version minor
297 response[15] = 0; // Device version build
298 response[16] = CTAPHID_CAP_WINK | CTAPHID_CAP_CBOR;
299
300 prepare_response_on(ch, cid, CTAPHID_INIT, response, 17);
301 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "INIT: allocated CID 0x%08lX", new_cid);
302}
303
310static void handle_ping(uint32_t cid, const uint8_t *data, uint16_t len) {
311 // Echo back the data
312 prepare_response(cid, CTAPHID_PING, data, len);
313 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "PING: echoing %d bytes", len);
314}
315
320static void handle_wink(uint32_t cid) {
321 prepare_response(cid, CTAPHID_WINK, NULL, 0);
323 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "WINK");
324}
325
330static void handle_cancel(uint32_t cid) {
331 ctap2_cancel();
333 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "CANCEL");
334}
335
342static void handle_cbor(uint32_t cid, const uint8_t *data, uint16_t len) {
343 if (len < 1) {
345 return;
346 }
347
348 ctaphid_channel_t *ch = find_channel(cid);
349 if (!ch) {
351 return;
352 }
353
354 g_ctaphid.current_cid = cid;
355
356 // Write the CTAP2 response directly into the per-channel response buffer.
357 uint16_t response_len = ch->response_buffer_size;
358 uint8_t status = ctap2_process_command(data, len,
359 ch->response_buffer,
360 &response_len);
361
362 ch->response_cid = cid;
363 ch->response_cmd = CTAPHID_CBOR;
364 ch->response_len = response_len;
365 ch->response_offset = 0;
366 ch->response_pending = true;
367 if (response_len > 0) {
368 LOG_I(TAG, "Prepared CBOR response len=%u status=0x%02X",
369 response_len, ch->response_buffer[0]);
370 }
371 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "CBOR: cmd=0x%02X status=0x%02X len=%d", data[0], status, response_len);
372}
373
378static void process_complete_message(ctaphid_channel_t *ch) {
379 uint32_t cid = ch->cid;
380 uint8_t cmd = ch->cmd;
381 uint8_t *data = ch->buffer;
382 uint16_t len = ch->bcnt;
383
384 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "Processing cmd=0x%02X len=%d", cmd, len);
385
386 switch (cmd) {
387 case CTAPHID_INIT:
388 handle_init(cid, data, len);
389 break;
390 case CTAPHID_PING:
391 handle_ping(cid, data, len);
392 break;
393 case CTAPHID_WINK:
394 handle_wink(cid);
395 break;
396 case CTAPHID_CANCEL:
397 handle_cancel(cid);
398 break;
399 case CTAPHID_CBOR:
400 g_ctaphid.cbor_cmd_count++;
401 handle_cbor(cid, data, len);
402 break;
403 case CTAPHID_MSG:
404 // U2F/CTAP1 - process via U2F handler
405 {
406 g_ctaphid.msg_cmd_count++;
407 g_ctaphid.current_cid = cid;
408 // Use static PSRAM buffer for U2F response
409 EXT_RAM_BSS_ATTR static uint8_t u2f_response[512];
410 uint16_t u2f_response_len = u2f_process_apdu(data, len, u2f_response, sizeof(u2f_response));
411 prepare_response(cid, CTAPHID_MSG, u2f_response, u2f_response_len);
412 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "U2F: response len=%d", u2f_response_len);
413 }
414 break;
415 default:
416 if (cmd >= CTAPHID_VENDOR_FIRST && cmd <= CTAPHID_VENDOR_LAST) {
417 // Vendor command not supported
419 } else {
421 }
422 break;
423 }
424
425 // Clear channel state
426 ch->active = false;
427 ch->offset = 0;
428}
429
434bool ctaphid_init(void) {
435 LOG_I(TAG, "Initializing...");
436
437 memset(&g_ctaphid, 0, sizeof(g_ctaphid));
438 g_ctaphid.next_cid = 1;
439
440 g_ctaphid.mutex = xSemaphoreCreateMutex();
441 if (!g_ctaphid.mutex) {
442 LOG_E(TAG, "Mutex creation failed");
443 return false;
444 }
445
446 g_ctaphid.initialized = true;
447 LOG_I(TAG, "Initialized");
448 return true;
449}
450
456void ctaphid_get_cmd_counts(uint32_t *cbor_count, uint32_t *msg_count) {
457 if (cbor_count) *cbor_count = g_ctaphid.cbor_cmd_count;
458 if (msg_count) *msg_count = g_ctaphid.msg_cmd_count;
459}
460
465 g_ctaphid.cbor_cmd_count = 0;
466 g_ctaphid.msg_cmd_count = 0;
467}
468
474bool ctaphid_process_packet(const uint8_t *packet) {
475 if (!g_ctaphid.initialized || !packet) return false;
476
477 xSemaphoreTake(g_ctaphid.mutex, portMAX_DELAY);
478
479 // Rate limiting check
480 uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS;
481 if (now - g_ctaphid.rate_window_start >= CTAPHID_RATE_LIMIT_WINDOW_MS) {
482 // New window
483 g_ctaphid.rate_window_start = now;
484 g_ctaphid.rate_cmd_count = 1;
485 } else {
486 g_ctaphid.rate_cmd_count++;
487 if (g_ctaphid.rate_cmd_count > CTAPHID_RATE_LIMIT_MAX_CMDS) {
488 LOG_W(TAG, "Rate limit exceeded (%d cmd/s)", g_ctaphid.rate_cmd_count);
489 // Don't send error - just drop silently to avoid amplification
490 xSemaphoreGive(g_ctaphid.mutex);
491 return true;
492 }
493 }
494
495 // Parse header
496 uint32_t cid = ((uint32_t)packet[0] << 24) | ((uint32_t)packet[1] << 16) |
497 ((uint32_t)packet[2] << 8) | packet[3];
498 bool is_init = (packet[4] & 0x80) != 0;
499
500 if (is_init) {
501 // Initialization packet
502 uint8_t cmd = packet[4] & 0x7F;
503 uint16_t bcnt = ((uint16_t)packet[5] << 8) | packet[6];
504
505 if (CTAPHID_DEBUG_PACKETS) LOG_D(TAG, "Init packet: CID=0x%08lX CMD=0x%02X BCNT=%d", cid, cmd, bcnt);
506
507 // Validate bcnt to prevent buffer overflow
508 if (bcnt > CTAPHID_MAX_MSG_SIZE) {
509 LOG_W(TAG, "bcnt exceeds max: %u > %u", bcnt, CTAPHID_MAX_MSG_SIZE);
511 xSemaphoreGive(g_ctaphid.mutex);
512 return true;
513 }
514
515 // INIT command is special - handle on any CID
516 if (cmd == CTAPHID_INIT) {
517 ctaphid_channel_t temp = {};
518 temp.cid = cid;
519 temp.cmd = cmd;
520 temp.bcnt = bcnt;
521 temp.buffer = g_ctaphid.init_buffer;
522 temp.buffer_size = CTAPHID_INIT_DATA;
523 temp.last_activity = now;
524 memcpy(g_ctaphid.init_buffer, packet + 7, CTAPHID_INIT_DATA);
526 xSemaphoreGive(g_ctaphid.mutex);
527 return true;
528 }
529
530 // Find or create channel
531 ctaphid_channel_t *ch = find_channel(cid);
532 if (!ch) {
533 LOG_W(TAG, "Unknown CID 0x%08lX", cid);
535 xSemaphoreGive(g_ctaphid.mutex);
536 return true;
537 }
538
539 // Check if another transaction is active on this channel
540 if (ch->active) {
541 LOG_W(TAG, "Channel busy");
543 xSemaphoreGive(g_ctaphid.mutex);
544 return true;
545 }
546
547 // Start new transaction
548 ch->cmd = cmd;
549 ch->bcnt = bcnt;
550 ch->seq = 0;
551 ch->offset = 0;
552 ch->active = true;
553 ch->last_activity = xTaskGetTickCount() * portTICK_PERIOD_MS;
554
555 // Copy data
556 uint16_t copy = (bcnt > CTAPHID_INIT_DATA) ? CTAPHID_INIT_DATA : bcnt;
557 memcpy(ch->buffer, packet + 7, copy);
558 ch->offset = copy;
559
560 // If message complete, process it
561 if (ch->offset >= ch->bcnt) {
563 }
564 } else {
565 // Continuation packet
566 uint8_t seq = packet[4] & 0x7F;
567
568 ctaphid_channel_t *ch = find_channel(cid);
569 if (!ch || !ch->active) {
570 LOG_W(TAG, "Continuation for inactive channel");
572 xSemaphoreGive(g_ctaphid.mutex);
573 return true;
574 }
575
576 // Check sequence
577 if (seq != ch->seq) {
578 LOG_W(TAG, "Invalid sequence: got %d, expected %d", seq, ch->seq);
579 ch->active = false;
581 xSemaphoreGive(g_ctaphid.mutex);
582 return true;
583 }
584
585 // Copy data
586 uint16_t remaining = ch->bcnt - ch->offset;
587 uint16_t copy = (remaining > CTAPHID_CONT_DATA) ? CTAPHID_CONT_DATA : remaining;
588 memcpy(ch->buffer + ch->offset, packet + 5, copy);
589 ch->offset += copy;
590 ch->seq++;
591 ch->last_activity = xTaskGetTickCount() * portTICK_PERIOD_MS;
592
593 // If message complete, process it
594 if (ch->offset >= ch->bcnt) {
596 }
597 }
598
599 xSemaphoreGive(g_ctaphid.mutex);
600 return true;
601}
602
608 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
609 if (g_ctaphid.channels[i].response_pending) return true;
610 }
611 return false;
612}
613
618static ctaphid_channel_t *pick_next_response_channel(void) {
619 for (int n = 0; n < CTAPHID_MAX_CHANNELS; n++) {
620 int idx = (g_ctaphid.response_rr_index + n) % CTAPHID_MAX_CHANNELS;
621 if (g_ctaphid.channels[idx].response_pending) {
622 g_ctaphid.response_rr_index = (idx + 1) % CTAPHID_MAX_CHANNELS;
623 return &g_ctaphid.channels[idx];
624 }
625 }
626 return NULL;
627}
628
634bool ctaphid_get_response_packet(uint8_t *packet) {
635 if (!packet) return false;
636
637 xSemaphoreTake(g_ctaphid.mutex, portMAX_DELAY);
638
640 ctaphid_channel_t *ch = pick_next_response_channel();
641 if (!ch) {
642 xSemaphoreGive(g_ctaphid.mutex);
643 return false;
644 }
645
646 if (cancelled) {
647 LOG_W(TAG, "Send aborted: CTAP2 cancelled by host (cid=0x%08lX offset=%u/%u)",
648 ch->response_cid, ch->response_offset, ch->response_len);
649 ch->response_pending = false;
650 ch->response_offset = 0;
651 ch->response_len = 0;
652 xSemaphoreGive(g_ctaphid.mutex);
653 return false;
654 }
655
656 uint16_t remaining = ch->response_len - ch->response_offset;
657
658 if (ch->response_offset == 0) {
659 // Send init packet
660 build_init_packet(packet, ch->response_cid, ch->response_cmd,
661 ch->response_len,
662 ch->response_buffer, remaining);
663 uint16_t sent = (remaining > CTAPHID_INIT_DATA) ? CTAPHID_INIT_DATA : remaining;
664 ch->response_offset = sent;
665 } else {
666 // Send continuation packet
667 uint8_t seq = (ch->response_offset - CTAPHID_INIT_DATA) / CTAPHID_CONT_DATA;
668 build_cont_packet(packet, ch->response_cid, seq,
669 ch->response_buffer + ch->response_offset,
670 remaining);
671 uint16_t sent = (remaining > CTAPHID_CONT_DATA) ? CTAPHID_CONT_DATA : remaining;
672 ch->response_offset += sent;
673 }
674
675 // Check if response complete
676 if (ch->response_offset >= ch->response_len) {
677 ch->response_pending = false;
678 }
679
680 xSemaphoreGive(g_ctaphid.mutex);
681 return true;
682}
683
689void ctaphid_send_keepalive(uint32_t cid, uint8_t status) {
690 uint8_t packet[CTAPHID_PACKET_SIZE];
691 uint8_t data = status;
692 build_init_packet(packet, cid, CTAPHID_KEEPALIVE, 1, &data, 1);
693
694 // Send directly via USB
695 fido2_usb_write(packet);
696}
697
703void ctaphid_send_error(uint32_t cid, uint8_t error) {
704 uint8_t data = error;
705 ctaphid_channel_t *ch = find_channel(cid);
706 if (!ch && cid != 0 && cid != CTAPHID_BROADCAST_CID) {
707 ch = alloc_channel(cid);
708 }
709 if (!ch) {
710 // Fall back to any free slot for unknown / reserved CIDs so the host
711 // still receives the framing-level error packet.
712 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
713 if (!g_ctaphid.channels[i].response_pending) {
714 ch = &g_ctaphid.channels[i];
715 break;
716 }
717 }
718 }
719 if (!ch) {
720 LOG_W(TAG, "send_error: no channel slot available for CID 0x%08lX", cid);
721 return;
722 }
723 prepare_response_on(ch, cid, CTAPHID_ERROR, &data, 1);
724 LOG_W(TAG, "Sending error 0x%02X to CID 0x%08lX", error, cid);
725}
726
731 if (!g_ctaphid.initialized) return;
732
733 uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS;
734
735 xSemaphoreTake(g_ctaphid.mutex, portMAX_DELAY);
736
737 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
738 ctaphid_channel_t *ch = &g_ctaphid.channels[i];
739 if (ch->active && (now - ch->last_activity) > CTAPHID_MSG_TIMEOUT_MS) {
740 LOG_W(TAG, "Channel 0x%08lX timed out", ch->cid);
742 ch->active = false;
743 }
744 }
745
746 xSemaphoreGive(g_ctaphid.mutex);
747}
748
754 return g_ctaphid.current_cid;
755}
756
761bool ctaphid_is_busy(void) {
762 for (int i = 0; i < CTAPHID_MAX_CHANNELS; i++) {
763 if (g_ctaphid.channels[i].active) return true;
764 }
765 return false;
766}
static const char * TAG
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_D(tag, fmt,...)
Definition cdc_log.h:148
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
bool cancelled
Definition ctap2.cpp:63
bool initialized
Definition ctap2.cpp:61
void ctap2_cancel(void)
Marks current CTAP2 operation as cancelled.
Definition ctap2.cpp:3624
uint8_t ctap2_process_command(const uint8_t *cmd, uint16_t cmd_len, uint8_t *response, uint16_t *response_len)
Dispatches one CTAP2 command and writes response payload.
Definition ctap2.cpp:3529
void ctap2_clear_cancel(void)
Clears the cancel flag. Called when a new CTAPHID channel is opened so a cancel from a previous chann...
Definition ctap2.cpp:3634
bool ctap2_is_cancelled(void)
Returns true if the current CTAP2 operation has been cancelled.
Definition ctap2.cpp:3641
#define CTAPHID_RATE_LIMIT_MAX_CMDS
Definition ctaphid.cpp:40
uint32_t msg_cmd_count
Definition ctaphid.cpp:59
void ctaphid_get_cmd_counts(uint32_t *cbor_count, uint32_t *msg_count)
Returns cumulative counters for CTAPHID CBOR and MSG commands.
Definition ctaphid.cpp:456
static void handle_cancel(uint32_t cid)
Handles CTAPHID CANCEL requests and aborts active CTAP2 work.
Definition ctaphid.cpp:330
bool ctaphid_init(void)
Initializes CTAPHID transport state and synchronization primitives.
Definition ctaphid.cpp:434
#define CTAPHID_MAX_CHANNELS
Definition ctaphid.cpp:33
void ctaphid_check_timeout(void)
Expires active channels whose message assembly timeout elapsed.
Definition ctaphid.cpp:730
uint8_t init_buffer[64]
Definition ctaphid.cpp:50
static void handle_cbor(uint32_t cid, const uint8_t *data, uint16_t len)
Handles CTAPHID CBOR requests by dispatching to the CTAP2 command processor.
Definition ctaphid.cpp:342
static void init_channel_slot(int index, uint32_t cid)
Allocates or reuses a channel slot for the provided channel identifier.
Definition ctaphid.cpp:87
static void prepare_response(uint32_t cid, uint8_t cmd, const uint8_t *data, uint16_t len)
Stores a command response so it can be packetized and read out later.
Definition ctaphid.cpp:213
bool ctaphid_is_busy(void)
Reports whether any CTAPHID channel currently has an active transaction.
Definition ctaphid.cpp:761
uint32_t rate_window_start
Definition ctaphid.cpp:55
#define CTAPHID_RATE_LIMIT_WINDOW_MS
Runtime state and channel bookkeeping for CTAPHID transport.
Definition ctaphid.cpp:39
uint32_t ctaphid_get_current_cid(void)
Returns the channel identifier of the currently processed request.
Definition ctaphid.cpp:753
#define CTAPHID_MSG_TIMEOUT_MS
Definition ctaphid.cpp:32
uint32_t current_cid
Definition ctaphid.cpp:52
static ctaphid_channel_t * pick_next_response_channel(void)
Selects the next channel with a pending response in a round-robin fashion.
Definition ctaphid.cpp:618
uint8_t rate_cmd_count
Definition ctaphid.cpp:56
uint32_t cbor_cmd_count
Definition ctaphid.cpp:58
static ctaphid_channel_t * find_channel(uint32_t cid)
Returns the channel record for a given channel identifier.
Definition ctaphid.cpp:67
bool ctaphid_has_response(void)
Indicates whether any channel has a response queued for host retrieval.
Definition ctaphid.cpp:607
static void build_init_packet(uint8_t *packet, uint32_t cid, uint8_t cmd, uint16_t bcnt, const uint8_t *data, uint16_t data_len)
Builds a CTAPHID initialization packet.
Definition ctaphid.cpp:160
static uint8_t s_response_buffers[8][CTAPHID_MAX_MSG_SIZE]
Definition ctaphid.cpp:44
static struct @105157365137133325115210077320112075163264255254 g_ctaphid
static void prepare_response_on(ctaphid_channel_t *ch, uint32_t send_cid, uint8_t cmd, const uint8_t *data, uint16_t len)
Stores a response on the given channel slot but addressed to a different CID.
Definition ctaphid.cpp:244
uint32_t next_cid
Definition ctaphid.cpp:48
static void process_complete_message(ctaphid_channel_t *ch)
Dispatches a fully assembled channel message to its command handler.
Definition ctaphid.cpp:378
ctaphid_channel_t channels[8]
Definition ctaphid.cpp:49
static void handle_ping(uint32_t cid, const uint8_t *data, uint16_t len)
Handles CTAPHID PING by echoing the request payload.
Definition ctaphid.cpp:310
static void handle_wink(uint32_t cid)
Handles CTAPHID WINK requests.
Definition ctaphid.cpp:320
static void build_cont_packet(uint8_t *packet, uint32_t cid, uint8_t seq, const uint8_t *data, uint16_t data_len)
Builds a CTAPHID continuation packet.
Definition ctaphid.cpp:188
void ctaphid_reset_cmd_counts(void)
Resets CTAPHID command counters.
Definition ctaphid.cpp:464
#define CTAPHID_DEBUG_PACKETS
Enable verbose packet-level debug logging when feature flags allow it.
Definition ctaphid.cpp:30
bool ctaphid_get_response_packet(uint8_t *packet)
Retrieves the next response HID packet from a per-channel response queue.
Definition ctaphid.cpp:634
static ctaphid_channel_t * alloc_channel(uint32_t cid)
Definition ctaphid.cpp:101
void ctaphid_send_error(uint32_t cid, uint8_t error)
Queues a CTAPHID ERROR response for the given channel.
Definition ctaphid.cpp:703
static void handle_init(uint32_t cid, const uint8_t *data, uint16_t len)
Handles CTAPHID INIT and returns negotiated channel/capability data.
Definition ctaphid.cpp:267
bool ctaphid_process_packet(const uint8_t *packet)
Processes one incoming 64-byte CTAPHID packet.
Definition ctaphid.cpp:474
static uint32_t allocate_cid(void)
Allocates the next non-broadcast CTAPHID channel identifier.
Definition ctaphid.cpp:139
static uint8_t s_msg_buffers[8][CTAPHID_MAX_MSG_SIZE]
Message and response buffers located in PSRAM to save internal RAM.
Definition ctaphid.cpp:43
void ctaphid_send_keepalive(uint32_t cid, uint8_t status)
Sends a CTAPHID KEEPALIVE packet immediately over USB.
Definition ctaphid.cpp:689
uint8_t response_rr_index
Definition ctaphid.cpp:53
bool fido2_usb_write(const uint8_t *buffer)
Sends one CTAPHID packet over USB HID.
SemaphoreHandle_t mutex
Definition ctaphid.cpp:51
#define CTAPHID_ERR_INVALID_CHANNEL
Definition ctaphid.h:39
#define CTAPHID_ERR_CHANNEL_BUSY
Definition ctaphid.h:37
#define CTAPHID_INIT_DATA
Definition ctaphid.h:14
#define CTAPHID_ERR_MSG_TIMEOUT
Definition ctaphid.h:36
#define CTAPHID_CAP_CBOR
Definition ctaphid.h:52
#define CTAPHID_CANCEL
Definition ctaphid.h:27
#define CTAPHID_PING
Definition ctaphid.h:21
#define CTAPHID_VENDOR_LAST
Definition ctaphid.h:44
#define CTAPHID_ERROR
Definition ctaphid.h:29
#define CTAPHID_PACKET_SIZE
Definition ctaphid.h:13
#define CTAPHID_ERR_INVALID_CMD
Definition ctaphid.h:32
#define CTAPHID_ERR_INVALID_SEQ
Definition ctaphid.h:35
#define CTAPHID_ERR_INVALID_LEN
Definition ctaphid.h:34
#define CTAPHID_WINK
Definition ctaphid.h:25
#define CTAPHID_ERR_OTHER
Definition ctaphid.h:40
#define CTAPHID_KEEPALIVE
Definition ctaphid.h:28
#define CTAPHID_MSG
Definition ctaphid.h:22
#define CTAPHID_CONT_DATA
Definition ctaphid.h:15
#define CTAPHID_BROADCAST_CID
Definition ctaphid.h:18
#define CTAPHID_INIT
Definition ctaphid.h:24
#define CTAPHID_MAX_MSG_SIZE
Definition ctaphid.h:16
#define CTAPHID_CAP_WINK
Definition ctaphid.h:51
#define CTAPHID_CBOR
Definition ctaphid.h:26
#define CTAPHID_VENDOR_FIRST
Definition ctaphid.h:43
void winkBacklight(uint8_t count=2, uint16_t period_ms=150)
Blink the backlight as a visual "look at me" signal.
bool fido2_ui_abort_prompt()
Forcibly denies any in-flight user-presence prompt.
Definition Fido2Ui.cpp:677
bool fido2_usb_write(const uint8_t *buffer)
Sends one CTAPHID packet over USB HID.
uint16_t u2f_process_apdu(const uint8_t *apdu, uint16_t apdu_len, uint8_t *response, uint16_t response_max)
Parses U2F APDU and dispatches to instruction handlers.
Definition u2f.cpp:738