CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
BackupMenuUi.cpp
Go to the documentation of this file.
1
10
11#include "AppUiInternal.h"
13
17#include "serial_cmd/Console.h"
18
19#include <mbedtls/platform_util.h>
20
21#include <cstdio>
22#include <cstring>
23
24namespace cdc::ui {
25
26namespace {
27
29constexpr uint16_t PASSPHRASE_MAX = 64;
30
34static ListView* s_backupMenu = nullptr;
35static ListItem s_backupItems[3];
36static PasswordT9View s_passInput;
37static char s_firstPass[PASSPHRASE_MAX + 1] = {0};
38static char s_summaryText[InfoView::MAX_TEXT_LEN] = {0};
39
40void wipeFirstPass() {
41 mbedtls_platform_zeroize(s_firstPass, sizeof(s_firstPass));
42}
43
44void pushPassphrase(const char* title, T9InputView::SaveCallback onSave) {
45 s_passInput.init(title, nullptr, PASSPHRASE_MAX);
46 s_passInput.setOnSave(onSave);
47 ViewStack::instance().push(&s_passInput);
48}
49
50// --- Export ---------------------------------------------------------------
51
52void onExportConfirm(const char* text) {
53 if (!text || std::strcmp(text, s_firstPass) != 0) {
54 wipeFirstPass();
55 showToastError(ui::tr("core.pins_dont_match"), TOAST_DURATION_MEDIUM_MS);
56 return;
57 }
58
59 showToastTask(ui::tr("core.task_working"), 0);
60 bool ok = os_ui::BackupManager::instance().exportTo(s_firstPass);
61 wipeFirstPass();
63
64 showToast(ok ? ui::tr("core.backup_export_ok") : ui::tr("core.failed"),
66}
67
68void onExportFirst(const char* text) {
69 if (!text || text[0] == '\0') {
70 showToastError(ui::tr("core.backup_pass_empty"), TOAST_DURATION_MEDIUM_MS);
71 return;
72 }
73 strncpy(s_firstPass, text, PASSPHRASE_MAX);
74 s_firstPass[PASSPHRASE_MAX] = '\0';
75 pushPassphrase(ui::tr("core.backup_confirm_passphrase"), onExportConfirm);
76}
77
78void startExport() {
79 wipeFirstPass();
80 pushPassphrase(ui::tr("core.backup_passphrase"), onExportFirst);
81}
82
83// --- Import ---------------------------------------------------------------
84
85void onImportPass(const char* text) {
86 if (!text || text[0] == '\0') {
87 showToastError(ui::tr("core.backup_pass_empty"), TOAST_DURATION_MEDIUM_MS);
88 return;
89 }
90
91 showToastTask(ui::tr("core.task_working"), 0);
92 os_ui::BackupSummary s = os_ui::BackupManager::instance().importFrom(text);
94
95 if (!s.ok) {
96 showToastError(ui::tr("core.backup_import_fail"), TOAST_DURATION_LONG_MS);
97 return;
98 }
99
100 snprintf(s_summaryText, sizeof(s_summaryText),
101 "%s\n\n%s: %u\n%s: %u\n%s: %u\n%s: %u\n%s: %s",
102 ui::tr("core.backup_scope_info"),
103 ui::tr("core.backup_imported"), s.imported,
104 ui::tr("core.backup_failed"), s.failed,
105 ui::tr("core.backup_modules"), s.modules,
106 ui::tr("core.backup_skipped"), s.skipped,
107 ui::tr("core.backup_system"),
108 ui::tr(s.system ? "core.yes" : "core.no"));
109 showInfo(ui::tr("core.backup_summary"), s_summaryText);
110}
111
112void startImport() {
113 if (!os_ui::BackupManager::instance().backupExists()) {
114 showToastInfo(ui::tr("core.backup_none"), TOAST_DURATION_MEDIUM_MS);
115 return;
116 }
117 pushPassphrase(ui::tr("core.backup_passphrase"), onImportPass);
118}
119
120// --- Delete ---------------------------------------------------------------
121
122void onDeleteConfirm(void* /*userData*/) {
124 showToast(ok ? ui::tr("core.deleted") : ui::tr("core.failed"),
126}
127
128void startDelete() {
129 if (!os_ui::BackupManager::instance().backupExists()) {
130 showToastInfo(ui::tr("core.backup_none"), TOAST_DURATION_MEDIUM_MS);
131 return;
132 }
133 showConfirm(ui::tr("core.backup_delete_q"), onDeleteConfirm, nullptr,
135}
136
137// --- Menu -----------------------------------------------------------------
138
139void onBackupMenuSelect(uint16_t index, void* /*userData*/) {
140 switch (index) {
141 case 0: startExport(); break;
142 case 1: startImport(); break;
143 case 2: startDelete(); break;
144 default: break;
145 }
146}
147
148// --- Serial: BACKUP EXPORT/IMPORT/DELETE (AUTH-gated, hardcoded English) ---
149
150using cdc::serial::Console;
151
152void cmdBackupExport(const char* args) {
153 if (!args || !*args) { Console::printf("Usage: BACKUP EXPORT <passphrase>\r\n"); return; }
154 if (os_ui::BackupManager::instance().exportTo(args)) {
155 Console::printf("OK: Backup written\r\n");
156 } else {
157 Console::printf("ERROR: Backup export failed\r\n");
158 }
159}
160
161void cmdBackupImport(const char* args) {
162 if (!args || !*args) { Console::printf("Usage: BACKUP IMPORT <passphrase>\r\n"); return; }
163 if (!os_ui::BackupManager::instance().backupExists()) {
164 Console::printf("ERROR: No backup present\r\n");
165 return;
166 }
167 os_ui::BackupSummary s = os_ui::BackupManager::instance().importFrom(args);
168 if (!s.ok) {
169 Console::printf("ERROR: Import failed (wrong passphrase or corrupt file)\r\n");
170 return;
171 }
172 Console::printf("NOTE: FIDO2/GPG signing keys are NOT restored.\r\n");
173 Console::printf("OK: imported=%u failed=%u modules=%u skipped=%u system=%u\r\n",
174 s.imported, s.failed, s.modules, s.skipped, s.system ? 1 : 0);
175}
176
177void cmdBackupDelete(const char* /*args*/) {
178 if (!os_ui::BackupManager::instance().backupExists()) {
179 Console::printf("OK: No backup present\r\n");
180 return;
181 }
183 ? "OK: Backup deleted\r\n"
184 : "ERROR: Delete failed\r\n");
185}
186
187const cdc::serial::SubCommand kBackupSubs[] = {
188 {"EXPORT", "<passphrase>", "Write encrypted backup to vFAT", cmdBackupExport},
189 {"IMPORT", "<passphrase>", "Restore from the on-device backup", cmdBackupImport},
190 {"DELETE", "", "Delete the on-device backup", cmdBackupDelete},
191 {nullptr, nullptr, nullptr, nullptr},
192};
193
194void cmdBackup(const char* args) {
195 cdc::serial::dispatchSubCommand("BACKUP", args, kBackupSubs);
196}
197
198} // namespace
199
204 if (!s_backupMenu) {
205 s_backupMenu = new ListView();
206 s_backupMenu->setOnSelect(onBackupMenuSelect);
207 }
208 s_backupItems[0] = {ui::tr("core.backup_export"), 0, false, nullptr};
209 s_backupItems[1] = {ui::tr("core.backup_import"), 0, false, nullptr};
210 s_backupItems[2] = {ui::tr("core.backup_delete"), 0, false, nullptr};
211 s_backupMenu->init(ui::tr("core.backup"), s_backupItems, 3);
212 ViewStack::instance().push(s_backupMenu);
213}
214
220 {"BACKUP", "Encrypted backup: EXPORT/IMPORT/DELETE",
221 cmdBackup, "backup", true, kBackupSubs});
222}
223
224} // namespace cdc::ui
bool exportTo(const char *passphrase)
Exports all module sections into one encrypted backup file.
BackupSummary importFrom(const char *passphrase)
Restores from the on-device backup file (best-effort).
bool deleteBackup()
Deletes the on-device backup file.
static BackupManager & instance()
Returns the process-wide singleton.
virtual bool registerCommand(const Command &cmd)=0
static void printf(const char *format,...) __attribute__((format(printf
Prints formatted text to console.
Definition Console.cpp:30
static constexpr uint16_t MAX_TEXT_LEN
Definition InfoView.h:22
T9 input variant for secrets: displays asterisks instead of letters and offers a long-press reveal to...
void(*)(const char *text) SaveCallback
Definition T9InputView.h:29
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:34
void push(IView *view, void *context=nullptr)
ICommandRegistry & getCommandRegistry()
Returns singleton command-registry interface.
void dispatchSubCommand(const char *parent, const char *args, const SubCommand *table)
Routes a sub-command line to its handler.
Definition SubCommand.h:73
Centralized key-code constants for cdc_views.
Definition IModule.h:8
const char * tr(const char *key)
Look up a translation by string key.
Definition I18n.h:208
void showToast(const char *message, uint16_t durationMs=1500)
Shows a plain toast message.
InfoView * showInfo(const char *title, const char *text, const char *hint=nullptr)
Shows a shared info view instance and pushes it onto the view stack.
Definition InfoView.cpp:310
static constexpr uint32_t TOAST_DURATION_LONG_MS
void showToastTask(const char *message, uint16_t durationMs=0)
Shows a task/progress toast message.
static constexpr uint32_t TOAST_DURATION_MEDIUM_MS
void registerBackupSerialCommand()
Registers the AUTH-gated BACKUP serial command.
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.
static constexpr uint32_t TOAST_DURATION_SHORT_MS
void showToastInfo(const char *message, uint16_t durationMs=1500)
Shows an informational toast message.
void showToastError(const char *message, uint16_t durationMs=1500)
Shows an error toast message.
void showBackupMenu()
Shows the Backup submenu (Export / Import / Delete).