CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
Fido2Ui.cpp
Go to the documentation of this file.
1
5
6#include "mod_fido2/Fido2Ui.h"
7#include "mod_fido2/ctap2.h"
8#include "mod_fido2/ctaphid.h"
9#include "mod_fido2/fido2.h"
12#include "cdc_core/PinManager.h"
13#include "cdc_core/Raii.h"
14#include "cdc_hal/IDisplay.h"
15#include "cdc_log.h"
16#include "cdc_ui/I18n.h"
17#include "cdc_ui/ViewStack.h"
20#include "cdc_views/InfoView.h"
21#include "cdc_views/ListView.h"
23#include "cdc_views/ToastView.h"
24#include <esp_attr.h>
25#include <freertos/FreeRTOS.h>
26#include <freertos/semphr.h>
27#include <algorithm>
28#include <cstdio>
29#include <cstring>
30#include <strings.h>
31
32static const char* TAG = "FIDO2_UI";
33
34namespace cdc::mod_fido2 {
35
36constexpr ui::I18nEntry kStrings[] = {
37 {"mod_fido2.title", "WebAuthn"},
38 {"mod_fido2.fido2_key", "FIDO2 Key"},
39 {"mod_fido2.sign_in_to", "Sign in to"},
40 {"mod_fido2.register_key", "Register Key"},
41 {"mod_fido2.sign_in", "Sign In"},
42 {"mod_fido2.use_device", "Use this device?"},
43 {"mod_fido2.no_entries", "No entries"},
44 {"mod_fido2.overwrite_key", "OVERWRITE KEY!"},
45 {"mod_fido2.overwrite_warning", "Overwrite existing key?"},
46};
47
51
53static ui::ListView* s_listView = nullptr;
54static ui::InfoView* s_detailView = nullptr;
55static ui::InfoView* s_promptView = nullptr;
56static ui::PinEntryView* s_pinEntry = nullptr;
57
59EXT_RAM_BSS_ATTR static char s_labels[FIDO2_MAX_CREDENTIALS][100];
61static uint8_t s_listCount = 0;
62
64static SemaphoreHandle_t s_promptSem = nullptr;
65static SemaphoreHandle_t s_promptMutex = nullptr;
69static uint8_t s_promptReturnDepth = 0;
70static ui::IView* s_promptReturnView = nullptr;
71static bool s_promptWasLocked = false;
72static bool s_promptBacklightWasOn = false;
73static volatile bool s_promptActive = false;
74
75static void ensurePromptMutex() {
76 if (!s_promptMutex) {
77 s_promptMutex = xSemaphoreCreateMutex();
78 }
79}
80
82static SemaphoreHandle_t s_overwriteSem = nullptr;
83static volatile bool s_overwriteApproved = false;
84
85static void onOverwriteConfirm(void* /*ud*/) {
87 if (s_overwriteSem) xSemaphoreGive(s_overwriteSem);
88}
89static void onOverwriteCancel(void* /*ud*/) {
90 s_overwriteApproved = false;
91 if (s_overwriteSem) xSemaphoreGive(s_overwriteSem);
92}
93
100static int strcasecmp_safe(const char* a, const char* b) {
101 if (!a && !b) return 0;
102 if (!a) return -1;
103 if (!b) return 1;
104 return strcasecmp(a, b);
105}
106
110static void rebuildList() {
111 s_listCount = 0;
112 uint8_t count = fido2_get_credential_count();
113 if (count == 0) {
114 // Show "No entries" placeholder
115 s_listItems[0].label = ui::tr("mod_fido2.no_entries");
116 s_listItems[0].userData = nullptr;
117 s_listItems[0].icon = 0;
118 s_listItems[0].iconDisabled = true;
119 if (s_listView) {
120 s_listView->init(ui::tr("mod_fido2.title"), s_listItems, 1);
121 s_listView->setHint(ui::tr("core.hint_back"));
122 }
123 return;
124 }
125
126 for (uint8_t i = 0; i < count && i < FIDO2_MAX_CREDENTIALS; i++) {
127 s_sortMap[i] = i;
128 fido2_credential_info_t info = {};
129 if (fido2_get_credential_info(i, &info)) {
130 if (strlen(info.user_name) > 0) {
131 snprintf(s_labels[i], sizeof(s_labels[i]),
132 "%.45s (%.45s)", info.rp_id, info.user_name);
133 } else {
134 snprintf(s_labels[i], sizeof(s_labels[i]),
135 "%.45s", info.rp_id);
136 }
137 } else {
138 s_labels[i][0] = '\0';
139 }
140 }
141
142 std::sort(s_sortMap, s_sortMap + count, [](uint8_t a, uint8_t b) {
143 return strcasecmp_safe(s_labels[a], s_labels[b]) < 0;
144 });
145
146 for (uint8_t display = 0; display < count; display++) {
147 uint8_t store_idx = s_sortMap[display];
148 s_listItems[display].label = s_labels[store_idx];
149 s_listItems[display].userData = reinterpret_cast<void*>(static_cast<uintptr_t>(display));
150 s_listItems[display].icon = 0;
151 s_listItems[display].iconDisabled = false;
152 s_listCount++;
153 }
154
155 if (s_listView) {
156 s_listView->init(ui::tr("mod_fido2.title"), s_listItems, s_listCount);
157 s_listView->setHint(ui::tr("core.hint_list_menu"));
158 }
159}
160
165static void showDetail(uint16_t display_index) {
166 uint8_t count = fido2_get_credential_count();
167 if (display_index >= count) return;
168
169 uint8_t store_index = s_sortMap[display_index];
170 fido2_credential_info_t info = {};
171 if (!fido2_get_credential_info(store_index, &info)) {
172 return;
173 }
174
175 const char* key_type = (strncmp(info.rp_id, "ssh:", 4) == 0) ? "SSH" : "WebAuthn";
176 const char* algo_name = (info.curve == CDC_CURVE_ED25519) ? "Ed25519" : "P-256";
177
178 char fingerprint[KEY_FINGERPRINT_MAX_LEN] = {};
179 uint8_t pubkey[64] = {};
180 size_t pubkey_len = (info.curve == CDC_CURVE_ED25519) ? 32 : 64;
181 if (fido2_storage_get_pubkey(info.slot, pubkey)) {
182 key_fingerprint_from_pubkey(pubkey, pubkey_len, fingerprint, sizeof(fingerprint));
183 } else {
184 strlcpy(fingerprint, "(no key)", sizeof(fingerprint));
185 }
186
187 static char detail_text[384];
188 uint16_t slot_phys = static_cast<uint16_t>(fido2_storage_ecc_start() + info.slot);
189 snprintf(detail_text, sizeof(detail_text),
190 "Relying Party:\n%s\n\n"
191 "Type: %s Algo: %s\n"
192 "User: %s\n"
193 "Slot: %d\n"
194 "Sign count: %lu\n"
195 "Resident: %s\n\n"
196 "Fingerprint:\n%s\n\n"
197 "%s",
198 info.rp_id,
199 key_type, algo_name,
200 strlen(info.user_name) > 0 ? info.user_name : "(none)",
201 slot_phys,
202 info.sign_count,
203 info.resident_key ? ui::tr("core.yes") : ui::tr("core.no"),
204 fingerprint,
205 ui::tr("core.hint_back"));
206
207 if (!s_detailView) {
209 }
210 s_detailView->init(ui::tr("mod_fido2.fido2_key"), detail_text);
212}
213
218static void handleDelete(uint16_t display_index) {
219 uint8_t count = fido2_get_credential_count();
220 if (display_index >= count) return;
221
222 uint8_t store_index = s_sortMap[display_index];
223 fido2_credential_info_t info = {};
224 if (!fido2_get_credential_info(store_index, &info)) {
225 return;
226 }
227
228 if (fido2_delete_credential(info.slot)) {
229 rebuildList();
230 if (s_listView) {
231 s_listView->init(ui::tr("mod_fido2.title"), s_listItems, s_listCount);
232 s_listView->setHint(ui::tr("core.hint_list_menu"));
233 }
234 }
235}
236
242static void onListSelect(uint16_t index, void* userData) {
243 (void)userData;
244 showDetail(index);
245}
246
252static void onListMenu(uint16_t index, void* userData) {
253 (void)userData;
254 if (s_listCount == 0) return;
255
256 static ui::ContextMenuItem items[3];
257 uint16_t sel = s_listView ? s_listView->getSelection() : index;
258 if (sel >= s_listCount) {
259 sel = 0;
260 }
261 items[0] = {ui::tr("core.details"), []() { showDetail(s_listView ? s_listView->getSelection() : 0); }};
262 items[1] = {ui::tr("core.delete"), []() { handleDelete(s_listView ? s_listView->getSelection() : 0); }};
263 items[2] = {ui::tr("core.cancel"), []() {}};
264 static char s_contextTitle[FIDO2_RP_ID_MAX_LEN] = {};
265 const uint8_t store_idx = s_sortMap[sel];
266 fido2_credential_info_t info = {};
267 const char* title = s_labels[store_idx];
268 if (fido2_get_credential_info(store_idx, &info)) {
269 strncpy(s_contextTitle, info.rp_id, sizeof(s_contextTitle) - 1);
270 s_contextTitle[sizeof(s_contextTitle) - 1] = '\0';
271 title = s_contextTitle;
272 }
273 showContextMenu(title, items, 3);
274}
275
279static void restoreView() {
280 auto& stack = ui::ViewStack::instance();
281 while (stack.depth() > s_promptReturnDepth) {
282 stack.pop();
283 }
284}
285
291 s_promptActive = false;
292
293 if (result == FIDO2_UP_APPROVED) {
294 ui::showToastSuccess(ui::tr("core.ok"), 2000);
295 } else {
296 ui::showToastError(ui::tr("core.failed"), 2000);
297 }
298
299 auto& stack = ui::ViewStack::instance();
300 stack.releaseExclusive(s_promptView);
301
302 restoreView();
303
304 if (result == FIDO2_UP_APPROVED &&
307 rebuildList();
308 }
309
310 auto* display = cdc::hal::getDisplayInstance();
311 if (display && !s_promptBacklightWasOn) {
312 display->backlightOff();
313 }
314
315 stack.render();
316
317 s_promptResult = result;
318 if (s_promptSem) {
319 xSemaphoreGive(s_promptSem);
320 }
321}
322
328static bool onPinVerify(const char* pin) {
330}
331
335static void onPinSuccess() {
336 auto& stack = ui::ViewStack::instance();
337 stack.releaseExclusive(s_pinEntry);
338 stack.acquireExclusive(s_promptView);
340}
341
345static void onPinCancel() {
346 auto& stack = ui::ViewStack::instance();
347 stack.releaseExclusive(s_pinEntry);
348 stack.acquireExclusive(s_promptView);
350}
351
356static void onPinFailure(bool lockedOut) {
357 if (lockedOut) {
358 ui::showToastError(ui::tr("core.too_many_attempts"), 2000);
359 auto& stack = ui::ViewStack::instance();
360 stack.releaseExclusive(s_pinEntry);
361 stack.acquireExclusive(s_promptView);
363 } else {
364 ui::showToastError(ui::tr("core.wrong_pin"), 1000);
365 }
366}
367
372static void onPromptApprove(void* userData) {
373 (void)userData;
374 if (fido2_is_pin_verified()) {
376 return;
377 }
378
380 if (!s_pinEntry) {
382 s_pinEntry->init(ui::tr("core.enter_pin"),
385 s_pinEntry->setOnVerify(onPinVerify);
386 s_pinEntry->setOnSuccess(onPinSuccess);
387 s_pinEntry->setOnCancel(onPinCancel);
388 s_pinEntry->setOnFailure(onPinFailure);
389 s_pinEntry->setShowMessages(false);
390 } else {
391 s_pinEntry->init(ui::tr("core.enter_pin"),
394 s_pinEntry->setShowMessages(false);
395 }
396 {
397 auto& stack = ui::ViewStack::instance();
398 stack.releaseExclusive(s_promptView);
399 stack.acquireExclusive(s_pinEntry);
400 stack.push(s_pinEntry);
401 }
402 return;
403 }
404
406}
407
412static void onPromptDeny(void* userData) {
413 (void)userData;
416}
417
423 if (!s_listView) {
424 s_listView = new ui::ListView();
425 s_listView->setOnSelect(onListSelect);
426 s_listView->setOnMenu(onListMenu);
427 }
428 rebuildList();
429
430 if (!s_promptSem) {
431 s_promptSem = xSemaphoreCreateBinary();
432 }
433}
434
440 if (!s_listView) {
442 }
443 rebuildList();
444 return s_listView;
445}
446
451const char* fido2_ui_get_label() {
452 return ui::tr("mod_fido2.title");
453}
454
463 const char* rp_id,
464 fido2_action_t action,
465 const char* user_name
466) {
467 (void)user_name;
468
469 const char* actionStr = (action == FIDO2_ACTION_SELECT) ? "SELECT" :
470 (action == FIDO2_ACTION_REGISTER) ? "REGISTER" :
471 (action == FIDO2_ACTION_OVERWRITE) ? "OVERWRITE" : "AUTH";
472 LOG_I(TAG, "User presence: action=%s, rp='%s', promptActive=%d",
473 actionStr, rp_id ? rp_id : "(null)", s_promptActive ? 1 : 0);
474
475 // Browser-discovery probes never touch the UI: they are protocol-only
476 // pings to ask "is a device there?" and must not influence presence state.
477 if (action == FIDO2_ACTION_SELECT && rp_id &&
478 (strcmp(rp_id, ".dummy") == 0 || strcmp(rp_id, "make.me.blink") == 0)) {
479 LOG_I(TAG, "Auto-approving browser probe '%s'", rp_id);
480 return FIDO2_UP_APPROVED;
481 }
482
484
485 if (s_promptActive) {
486 LOG_W(TAG, "Presence request supersedes active prompt -> cancel old");
488 }
489
491
492 if (!s_promptSem) {
493 s_promptSem = xSemaphoreCreateBinary();
494 if (!s_promptSem) {
495 return FIDO2_UP_DENIED;
496 }
497 }
498
499 // Capture the pre-prompt backlight state once, before any path (the
500 // overwrite confirm or the prompt itself) forces the backlight on, so the
501 // restore on completion reflects the real prior state, not a forced-on one.
502 {
503 auto* display = cdc::hal::getDisplayInstance();
504 s_promptBacklightWasOn = display && display->isBacklightOn();
505 }
506
507 if (action == FIDO2_ACTION_OVERWRITE) {
508 if (!s_overwriteSem) {
509 s_overwriteSem = xSemaphoreCreateBinary();
510 if (!s_overwriteSem) {
511 return FIDO2_UP_DENIED;
512 }
513 }
514 while (xSemaphoreTake(s_overwriteSem, 0) == pdTRUE) {}
515
516 auto* display = cdc::hal::getDisplayInstance();
517 if (display && !s_promptBacklightWasOn) {
518 display->backlightOn();
519 }
520
521 const char* confirm_msg = ui::tr("mod_fido2.overwrite_warning");
522
523 s_overwriteApproved = false;
526
527 const uint32_t cid = ctaphid_get_current_cid();
528 const TickType_t poll = pdMS_TO_TICKS(100);
529 TickType_t remaining = pdMS_TO_TICKS(30000);
530 bool approved = false;
531 bool timedOut = true;
532 while (remaining > 0) {
533 TickType_t wait = remaining < poll ? remaining : poll;
534 if (xSemaphoreTake(s_overwriteSem, wait) == pdTRUE) {
535 approved = s_overwriteApproved;
536 timedOut = false;
537 break;
538 }
540 remaining -= wait;
541 }
542 if (timedOut) {
543 LOG_W(TAG, "Overwrite confirm timeout");
544 }
545
546 if (!approved) {
547 if (display && !s_promptBacklightWasOn) {
548 display->backlightOff();
549 }
550 LOG_I(TAG, "Overwrite denied/timeout - aborting registration");
551 return FIDO2_UP_DENIED;
552 }
553 LOG_I(TAG, "Overwrite acknowledged, proceeding to user-presence prompt");
554 action = FIDO2_ACTION_REGISTER;
555 }
556
557 strncpy(s_promptRpId, rp_id ? rp_id : "Unknown", sizeof(s_promptRpId) - 1);
558 s_promptRpId[sizeof(s_promptRpId) - 1] = '\0';
559 s_promptAction = action;
561 s_promptActive = true; // Mark prompt as active for race condition detection
562
563 while (xSemaphoreTake(s_promptSem, 0) == pdTRUE) {
564 LOG_W(TAG, "Drained stale semaphore");
565 }
566
567 if (!s_promptView) {
569 }
570
571 auto& stack = ui::ViewStack::instance();
572 if (!stack.acquireExclusive(s_promptView)) {
573 LOG_W(TAG, "Could not acquire ViewStack lock for FIDO2 prompt");
574 s_promptActive = false;
575 return FIDO2_UP_DENIED;
576 }
577 s_promptReturnDepth = stack.depth();
578 s_promptReturnView = stack.current();
580
581 // A WebAuthn request takes priority: dismiss any open modal (e.g. the
582 // lock-screen actions menu), which would otherwise overlay the prompt and
583 // swallow all key input.
584 stack.hideModal();
585
586 auto* display = cdc::hal::getDisplayInstance();
587 if (display && !s_promptBacklightWasOn) {
588 display->backlightOn();
589 }
590
591 const char* headline = nullptr;
592 if (action == FIDO2_ACTION_SELECT) {
593 headline = ui::tr("mod_fido2.use_device");
594 } else if (action == FIDO2_ACTION_REGISTER) {
595 headline = ui::tr("mod_fido2.register_key");
596 } else if (action == FIDO2_ACTION_OVERWRITE) {
597 headline = ui::tr("mod_fido2.overwrite_key");
598 } else {
599 headline = ui::tr("mod_fido2.sign_in");
600 }
601
602 static char prompt_text[260];
603 if (action == FIDO2_ACTION_SELECT) {
604 snprintf(prompt_text, sizeof(prompt_text),
605 "%s\n\n%s",
606 headline,
607 ui::tr("core.hint_approve_deny"));
608 } else if (action == FIDO2_ACTION_OVERWRITE) {
609 snprintf(prompt_text, sizeof(prompt_text),
610 "!!! %s !!!\n\n%s\n\n%s\n\n%s",
611 headline,
613 ui::tr("mod_fido2.overwrite_warning"),
614 ui::tr("core.hint_approve_deny"));
615 } else {
616 snprintf(prompt_text, sizeof(prompt_text),
617 "%s\n\n%s\n\n%s",
618 headline,
620 ui::tr("core.hint_approve_deny"));
621 }
622
623 const char* title = ui::tr("mod_fido2.title");
624 LOG_I(TAG, "Prompt title='%s', text='%.50s...'", title ? title : "(null)", prompt_text);
625
626 s_promptView->init(title, prompt_text);
627 s_promptView->setYesNoCallbacks(onPromptApprove, onPromptDeny, nullptr);
628
629 stack.push(s_promptView);
630 stack.resetInactivityTimer();
631 stack.render();
632
634
635 const uint32_t up_cid = ctaphid_get_current_cid();
636 const TickType_t up_poll = pdMS_TO_TICKS(100);
637 TickType_t up_remaining = pdMS_TO_TICKS(30000);
638 bool up_done = false;
639 bool up_cancelled = false;
640 while (up_remaining > 0) {
641 TickType_t wait = up_remaining < up_poll ? up_remaining : up_poll;
642 if (xSemaphoreTake(s_promptSem, wait) == pdTRUE) {
643 up_done = true;
644 break;
645 }
646 if (ctap2_is_cancelled()) {
647 up_cancelled = true;
648 break;
649 }
651 up_remaining -= wait;
652 }
653 if (up_done) {
655 s_promptActive = false;
656 if (result == FIDO2_UP_PENDING) {
657 LOG_W(TAG, "Semaphore signalled with PENDING result -> deny");
658 result = FIDO2_UP_DENIED;
659 }
660 return result;
661 }
662
663 if (up_cancelled) {
664 LOG_W(TAG, "User presence cancelled by CTAP host");
665 } else {
666 LOG_W(TAG, "User presence timeout");
667 }
668 s_promptActive = false;
669 stack.releaseExclusive(s_promptView);
670 restoreView();
671
672 if (display && !s_promptBacklightWasOn) {
673 display->backlightOff();
674 }
675
676 stack.render();
677 return up_cancelled ? FIDO2_UP_DENIED : FIDO2_UP_TIMEOUT;
678}
679
681 if (!s_promptActive) {
682 return false;
683 }
684 LOG_W(TAG, "Prompt aborted externally");
686 return true;
687}
688
689} // namespace cdc::mod_fido2
static const char * TAG
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
bool key_fingerprint_from_pubkey(const uint8_t *pubkey, size_t pubkey_len, char *buf, size_t len)
Generates human-readable fingerprint from public key bytes.
#define KEY_FINGERPRINT_MAX_LEN
Shared RAII wrappers for firmware resources.
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
RAII wrapper for a FreeRTOS semaphore / mutex.
Definition Raii.h:181
static constexpr uint8_t BADGE_PIN_MAX
Definition PinManager.h:50
bool verifyBadgePin(const char *pin)
Verifies badge PIN, updates retries, and handles lockout transitions.
static constexpr uint8_t BADGE_PIN_MIN
Definition PinManager.h:49
static PinManager & instance()
Returns singleton PIN manager instance.
static I18n & instance()
Singleton accessor.
Definition I18n.cpp:306
void registerEnglishTable(const I18nEntry *entries, std::size_t count)
Append English entries to the lookup table.
Definition I18n.cpp:326
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:53
void push(IView *view, void *context=nullptr)
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:3989
bool ctap2_is_cancelled(void)
Returns true if the current CTAP2 operation has been cancelled.
Definition ctap2.cpp:3996
uint32_t ctaphid_get_current_cid(void)
Returns the channel identifier of the currently processed request.
Definition ctaphid.cpp:753
#define CTAPHID_STATUS_UPNEEDED
Definition ctaphid.h:48
void ctaphid_send_keepalive(uint32_t cid, uint8_t status)
Sends a CTAPHID KEEPALIVE packet immediately over USB.
Definition ctaphid.cpp:689
#define CDC_CURVE_ED25519
Definition fido2.h:23
#define FIDO2_MAX_CREDENTIALS
Definition fido2.h:16
void fido2_set_pin_verified(bool verified)
Stores whether PIN verification was completed via ClientPIN.
Definition fido2.cpp:195
#define FIDO2_RP_ID_MAX_LEN
Definition fido2.h:17
bool fido2_get_credential_info(uint8_t index, fido2_credential_info_t *info)
bool fido2_delete_credential(uint8_t slot)
Deletes credential in given slot.
Definition fido2.cpp:258
fido2_user_presence_result_t
Definition fido2.h:30
@ FIDO2_UP_DENIED
Definition fido2.h:33
@ FIDO2_UP_TIMEOUT
Definition fido2.h:34
@ FIDO2_UP_PENDING
Definition fido2.h:31
@ FIDO2_UP_APPROVED
Definition fido2.h:32
bool fido2_is_pin_verified(void)
Returns current PIN-verified state.
Definition fido2.cpp:206
uint8_t fido2_get_credential_count(void)
Returns number of stored credentials.
Definition fido2.cpp:214
fido2_action_t
Definition fido2.h:37
@ FIDO2_ACTION_SELECT
Definition fido2.h:40
@ FIDO2_ACTION_REGISTER
Definition fido2.h:38
@ FIDO2_ACTION_OVERWRITE
Definition fido2.h:41
@ FIDO2_ACTION_AUTHENTICATE
Definition fido2.h:39
char rp_id[FIDO2_RP_ID_MAX_LEN]
char user_name[FIDO2_USER_NAME_MAX_LEN]
uint8_t fido2_storage_ecc_start(void)
Returns configured ECC start slot.
bool fido2_storage_get_pubkey(uint8_t slot, uint8_t *pubkey)
Reads public key from secure-element slot.
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
static ui::PinEntryView * s_pinEntry
Definition Fido2Ui.cpp:56
static ui::InfoView * s_detailView
Definition Fido2Ui.cpp:54
static void onPinFailure(bool lockedOut)
PIN failure callback handling lockout vs retry messaging.
Definition Fido2Ui.cpp:356
cdc::ui::IView * fido2_ui_get_list_view()
Returns FIDO2 credential list view.
Definition Fido2Ui.cpp:439
static void onOverwriteConfirm(void *)
Definition Fido2Ui.cpp:85
static ui::ListItem s_listItems[FIDO2_MAX_CREDENTIALS]
Definition Fido2Ui.cpp:58
static bool onPinVerify(const char *pin)
PIN verification callback for locked-screen approval flow.
Definition Fido2Ui.cpp:328
static void ensurePromptMutex()
Definition Fido2Ui.cpp:75
static ui::IView * s_promptReturnView
Definition Fido2Ui.cpp:70
static void onPinCancel()
PIN cancel callback denying user presence.
Definition Fido2Ui.cpp:345
static SemaphoreHandle_t s_promptSem
User-presence prompt state shared across callback and UI flow.
Definition Fido2Ui.cpp:64
static volatile fido2_user_presence_result_t s_promptResult
Definition Fido2Ui.cpp:66
static uint8_t s_listCount
Definition Fido2Ui.cpp:61
static volatile bool s_overwriteApproved
Definition Fido2Ui.cpp:83
static void onListSelect(uint16_t index, void *userData)
List selection callback opening credential detail view.
Definition Fido2Ui.cpp:242
static char s_promptRpId[FIDO2_RP_ID_MAX_LEN]
Definition Fido2Ui.cpp:67
static void handleDelete(uint16_t display_index)
Deletes selected credential and refreshes list.
Definition Fido2Ui.cpp:218
static bool s_promptBacklightWasOn
Definition Fido2Ui.cpp:72
bool fido2_ui_abort_prompt()
Forcibly denies any in-flight user-presence prompt.
Definition Fido2Ui.cpp:680
static SemaphoreHandle_t s_overwriteSem
Pre-confirm modal state for overwrite warning.
Definition Fido2Ui.cpp:82
void fido2_ui_init()
Initializes FIDO2 UI resources and list views.
Definition Fido2Ui.cpp:421
static void rebuildList()
Rebuilds credential list view from current storage contents.
Definition Fido2Ui.cpp:110
static void showDetail(uint16_t display_index)
Shows detailed view for selected credential.
Definition Fido2Ui.cpp:165
constexpr ui::I18nEntry kStrings[]
Definition Fido2Ui.cpp:36
static void promptComplete(fido2_user_presence_result_t result)
Completes user-presence prompt flow with result handling.
Definition Fido2Ui.cpp:290
static void onPromptApprove(void *userData)
Prompt approve callback; optionally triggers PIN entry on lock screen.
Definition Fido2Ui.cpp:372
static void onOverwriteCancel(void *)
Definition Fido2Ui.cpp:89
static uint8_t s_promptReturnDepth
Definition Fido2Ui.cpp:69
static ui::ListView * s_listView
FIDO2 UI view and list state.
Definition Fido2Ui.cpp:53
static int strcasecmp_safe(const char *a, const char *b)
Null-safe ASCII case-insensitive comparison.
Definition Fido2Ui.cpp:100
static void onListMenu(uint16_t index, void *userData)
List menu callback opening context actions for selected credential.
Definition Fido2Ui.cpp:252
static SemaphoreHandle_t s_promptMutex
Definition Fido2Ui.cpp:65
static volatile bool s_promptActive
Definition Fido2Ui.cpp:73
static void registerStrings()
Definition Fido2Ui.cpp:48
static void onPromptDeny(void *userData)
Prompt deny callback.
Definition Fido2Ui.cpp:412
static fido2_action_t s_promptAction
Definition Fido2Ui.cpp:68
static uint8_t s_sortMap[FIDO2_MAX_CREDENTIALS]
Definition Fido2Ui.cpp:60
static char s_labels[FIDO2_MAX_CREDENTIALS][100]
Definition Fido2Ui.cpp:59
static ui::InfoView * s_promptView
Definition Fido2Ui.cpp:55
fido2_user_presence_result_t fido2_ui_user_presence_callback(const char *rp_id, fido2_action_t action, const char *user_name)
User-presence callback used by FIDO2 core for approval prompts.
Definition Fido2Ui.cpp:462
static bool s_promptWasLocked
Definition Fido2Ui.cpp:71
static void onPinSuccess()
PIN success callback approving user presence.
Definition Fido2Ui.cpp:335
const char * fido2_ui_get_label()
Returns localized module label for menus.
Definition Fido2Ui.cpp:451
static void restoreView()
Restores view stack to pre-prompt depth.
Definition Fido2Ui.cpp:279
const char * tr(const char *key)
Look up a translation by string key.
Definition I18n.h:209
void showConfirm(const char *message, ConfirmView::ConfirmCallback onConfirm, ConfirmView::CancelCallback onCancel=nullptr, ConfirmView::Icon icon=ConfirmView::Icon::QUESTION, void *userData=nullptr)
Shows a shared modal confirmation dialog instance.
void showToastSuccess(const char *message, uint16_t durationMs=1500)
Shows a success toast message.
void showToastError(const char *message, uint16_t durationMs=1500)
Shows an error toast message.
Single English translation entry.
Definition I18n.h:44