25#include <freertos/FreeRTOS.h>
26#include <freertos/semphr.h>
32static const char*
TAG =
"FIDO2_UI";
37 {
"mod_fido2.title",
"WebAuthn"},
38 {
"mod_fido2.details",
"Details"},
39 {
"mod_fido2.fido2_key",
"FIDO2 Key"},
40 {
"mod_fido2.sign_in_to",
"Sign in to"},
41 {
"mod_fido2.register_key",
"Register Key"},
42 {
"mod_fido2.sign_in",
"Sign In"},
43 {
"mod_fido2.use_device",
"Use this device?"},
44 {
"mod_fido2.no_entries",
"No entries"},
45 {
"mod_fido2.overwrite_key",
"OVERWRITE KEY!"},
46 {
"mod_fido2.overwrite_warning",
"Overwrite existing key?"},
102 if (!a && !b)
return 0;
105 return strcasecmp(a, b);
136 "%.45s", info.
rp_id);
147 for (uint8_t display = 0; display < count; display++) {
150 s_listItems[display].userData =
reinterpret_cast<void*
>(
static_cast<uintptr_t
>(display));
168 if (display_index >= count)
return;
170 uint8_t store_index =
s_sortMap[display_index];
176 const char* key_type = (strncmp(info.
rp_id,
"ssh:", 4) == 0) ?
"SSH" :
"WebAuthn";
180 uint8_t pubkey[64] = {};
185 strlcpy(fingerprint,
"(no key)",
sizeof(fingerprint));
188 static char detail_text[384];
190 snprintf(detail_text,
sizeof(detail_text),
191 "Relying Party:\n%s\n\n"
192 "Type: %s Algo: %s\n"
197 "Fingerprint:\n%s\n\n"
206 ui::tr(
"core.hint_back"));
221 if (display_index >= count)
return;
223 uint8_t store_index =
s_sortMap[display_index];
264 items[2] = {
ui::tr(
"core.cancel"), []() {}};
266 const uint8_t store_idx =
s_sortMap[sel];
268 const char* title =
s_labels[store_idx];
270 strncpy(s_contextTitle, info.
rp_id,
sizeof(s_contextTitle) - 1);
271 s_contextTitle[
sizeof(s_contextTitle) - 1] =
'\0';
272 title = s_contextTitle;
274 showContextMenu(title, items, 3);
313 display->backlightOff();
453 return ui::tr(
"mod_fido2.title");
473 LOG_I(
TAG,
"User presence: action=%s, rp='%s', promptActive=%d",
479 (strcmp(
rp_id,
".dummy") == 0 || strcmp(
rp_id,
"make.me.blink") == 0)) {
487 LOG_W(
TAG,
"Presence request supersedes active prompt -> cancel old");
510 bool backlightWasOn = display && display->isBacklightOn();
511 if (display && !backlightWasOn) {
512 display->backlightOn();
515 const char* confirm_msg =
ui::tr(
"mod_fido2.overwrite_warning");
522 const TickType_t poll = pdMS_TO_TICKS(100);
523 TickType_t remaining = pdMS_TO_TICKS(30000);
524 bool approved =
false;
525 bool timedOut =
true;
526 while (remaining > 0) {
527 TickType_t wait = remaining < poll ? remaining : poll;
537 LOG_W(
TAG,
"Overwrite confirm timeout");
541 if (display && !backlightWasOn) {
542 display->backlightOff();
544 LOG_I(
TAG,
"Overwrite denied/timeout - aborting registration");
547 LOG_I(
TAG,
"Overwrite acknowledged, proceeding to user-presence prompt");
558 LOG_W(
TAG,
"Drained stale semaphore");
567 LOG_W(
TAG,
"Could not acquire ViewStack lock for FIDO2 prompt");
584 display->backlightOn();
588 const char* headline =
nullptr;
590 headline =
ui::tr(
"mod_fido2.use_device");
592 headline =
ui::tr(
"mod_fido2.register_key");
594 headline =
ui::tr(
"mod_fido2.overwrite_key");
596 headline =
ui::tr(
"mod_fido2.sign_in");
599 static char prompt_text[260];
601 snprintf(prompt_text,
sizeof(prompt_text),
604 ui::tr(
"core.hint_approve_deny"));
606 snprintf(prompt_text,
sizeof(prompt_text),
607 "!!! %s !!!\n\n%s\n\n%s\n\n%s",
610 ui::tr(
"mod_fido2.overwrite_warning"),
611 ui::tr(
"core.hint_approve_deny"));
613 snprintf(prompt_text,
sizeof(prompt_text),
617 ui::tr(
"core.hint_approve_deny"));
620 const char* title =
ui::tr(
"mod_fido2.title");
621 LOG_I(
TAG,
"Prompt title='%s', text='%.50s...'", title ? title :
"(null)", prompt_text);
627 stack.resetInactivityTimer();
633 const TickType_t up_poll = pdMS_TO_TICKS(100);
634 TickType_t up_remaining = pdMS_TO_TICKS(30000);
635 bool up_done =
false;
636 bool up_cancelled =
false;
637 while (up_remaining > 0) {
638 TickType_t wait = up_remaining < up_poll ? up_remaining : up_poll;
648 up_remaining -= wait;
654 LOG_W(
TAG,
"Semaphore signalled with PENDING result -> deny");
661 LOG_W(
TAG,
"User presence cancelled by CTAP host");
663 LOG_W(
TAG,
"User presence timeout");
670 display->backlightOff();
681 LOG_W(
TAG,
"Prompt aborted externally");
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,...)
#define LOG_I(tag, fmt,...)
RAII wrapper for a FreeRTOS semaphore / mutex.
static constexpr uint8_t BADGE_PIN_MAX
bool verifyBadgePin(const char *pin)
Verifies badge PIN, updates retries, and handles lockout transitions.
static constexpr uint8_t BADGE_PIN_MIN
static PinManager & instance()
Returns singleton PIN manager instance.
static I18n & instance()
Singleton accessor.
void registerEnglishTable(const I18nEntry *entries, std::size_t count)
Append English entries to the lookup table.
static ViewStack & instance()
Returns singleton view-stack instance.
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...
bool ctap2_is_cancelled(void)
Returns true if the current CTAP2 operation has been cancelled.
uint32_t ctaphid_get_current_cid(void)
Returns the channel identifier of the currently processed request.
#define CTAPHID_STATUS_UPNEEDED
void ctaphid_send_keepalive(uint32_t cid, uint8_t status)
Sends a CTAPHID KEEPALIVE packet immediately over USB.
#define CDC_CURVE_ED25519
#define FIDO2_MAX_CREDENTIALS
void fido2_set_pin_verified(bool verified)
Stores whether PIN verification was completed via ClientPIN.
#define FIDO2_RP_ID_MAX_LEN
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.
fido2_user_presence_result_t
bool fido2_is_pin_verified(void)
Returns current PIN-verified state.
uint8_t fido2_get_credential_count(void)
Returns number of stored credentials.
@ FIDO2_ACTION_AUTHENTICATE
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
static ui::InfoView * s_detailView
static void onPinFailure(bool lockedOut)
PIN failure callback handling lockout vs retry messaging.
cdc::ui::IView * fido2_ui_get_list_view()
Returns FIDO2 credential list view.
static void onOverwriteConfirm(void *)
static ui::ListItem s_listItems[FIDO2_MAX_CREDENTIALS]
static bool onPinVerify(const char *pin)
PIN verification callback for locked-screen approval flow.
static void ensurePromptMutex()
static ui::IView * s_promptReturnView
static void onPinCancel()
PIN cancel callback denying user presence.
static SemaphoreHandle_t s_promptSem
User-presence prompt state shared across callback and UI flow.
static volatile fido2_user_presence_result_t s_promptResult
static uint8_t s_listCount
static volatile bool s_overwriteApproved
static void onListSelect(uint16_t index, void *userData)
List selection callback opening credential detail view.
static char s_promptRpId[FIDO2_RP_ID_MAX_LEN]
static void handleDelete(uint16_t display_index)
Deletes selected credential and refreshes list.
static bool s_promptBacklightWasOn
bool fido2_ui_abort_prompt()
Forcibly denies any in-flight user-presence prompt.
static SemaphoreHandle_t s_overwriteSem
Pre-confirm modal state for overwrite warning.
void fido2_ui_init()
Initializes FIDO2 UI resources and list views.
static void rebuildList()
Rebuilds credential list view from current storage contents.
static void showDetail(uint16_t display_index)
Shows detailed view for selected credential.
constexpr ui::I18nEntry kStrings[]
static void promptComplete(fido2_user_presence_result_t result)
Completes user-presence prompt flow with result handling.
static void onPromptApprove(void *userData)
Prompt approve callback; optionally triggers PIN entry on lock screen.
static void onOverwriteCancel(void *)
static uint8_t s_promptReturnDepth
static ui::ListView * s_listView
FIDO2 UI view and list state.
static int strcasecmp_safe(const char *a, const char *b)
Null-safe ASCII case-insensitive comparison.
static void onListMenu(uint16_t index, void *userData)
List menu callback opening context actions for selected credential.
static SemaphoreHandle_t s_promptMutex
static volatile bool s_promptActive
static void registerStrings()
static void onPromptDeny(void *userData)
Prompt deny callback.
static fido2_action_t s_promptAction
static uint8_t s_sortMap[FIDO2_MAX_CREDENTIALS]
static char s_labels[FIDO2_MAX_CREDENTIALS][100]
static ui::InfoView * s_promptView
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.
static bool s_promptWasLocked
static void onPinSuccess()
PIN success callback approving user presence.
const char * fido2_ui_get_label()
Returns localized module label for menus.
static void restoreView()
Restores view stack to pre-prompt depth.
const char * tr(const char *key)
Look up a translation by string key.
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.