CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
CommandRegistry.cpp
Go to the documentation of this file.
1
5
10#include "cdc_log.h"
11#include <cstdio>
12#include <cstring>
13#include <cctype>
14
15static const char* TAG = "CMDREGS";
16
17namespace cdc::serial {
18
22static constexpr size_t MAX_COMMANDS = 64;
23
25public:
31 void setAuthProvider(bool (*authCheck)()) override {
32 authCheck_ = authCheck;
33 }
34
40 bool registerCommand(const Command& cmd) override {
41 if (count_ >= MAX_COMMANDS) {
42 LOG_W(TAG, "Command limit reached");
43 return false;
44 }
45
46 // Check for duplicate
47 for (size_t i = 0; i < count_; i++) {
48 if (strcasecmp(commands_[i].name, cmd.name) == 0) {
49 LOG_W(TAG, "Command '%s' already registered", cmd.name);
50 return false;
51 }
52 }
53
54 commands_[count_++] = cmd;
55 LOG_I(TAG, "Registered command: %s (%s)", cmd.name, cmd.moduleName);
56 return true;
57 }
58
64 void unregisterModule(const char* moduleName) override {
65 if (!moduleName) return;
66
67 size_t writeIdx = 0;
68 for (size_t readIdx = 0; readIdx < count_; readIdx++) {
69 if (commands_[readIdx].moduleName &&
70 strcmp(commands_[readIdx].moduleName, moduleName) == 0) {
71 // Skip this command (remove it)
72 continue;
73 }
74 if (writeIdx != readIdx) {
75 commands_[writeIdx] = commands_[readIdx];
76 }
77 writeIdx++;
78 }
79 count_ = writeIdx;
80 }
81
87 void setLineInterceptor(LineInterceptor interceptor) override {
88 lineInterceptor_ = interceptor;
89 }
90
91 void setByteInterceptor(ByteInterceptor interceptor) override {
92 byteInterceptor_ = interceptor;
93 }
94
96 return byteInterceptor_;
97 }
98
104 bool processCommand(const char* line) override {
105 if (!line || !*line) return false;
106
107 // Check line interceptor first (multiline input modes)
108 if (lineInterceptor_ && lineInterceptor_(line)) {
109 return true;
110 }
111
112 // Find command name (first word)
113 char cmdBuf[64];
114 size_t cmdLen = 0;
115 while (*line && !isspace(*line) && cmdLen < sizeof(cmdBuf) - 1) {
116 cmdBuf[cmdLen++] = *line++;
117 }
118 cmdBuf[cmdLen] = '\0';
119
120 // Skip whitespace to get to arguments
121 while (*line && isspace(*line)) line++;
122
123#if FEATURE_SECURE_SERIAL
124 // Check if PIN is blocked (lockout or retries exhausted)
125 // When blocked, only PING is allowed (to check device is alive)
127 if (pm.isBadgeBlocked()) {
128 if (strcasecmp(cmdBuf, "PING") != 0) {
129 if (pm.isLockoutActive()) {
130 uint32_t remainingSec = pm.getLockoutRemainingMs() / 1000;
131 Console::printf("ERROR: PIN locked. Wait %lu seconds.\r\n",
132 (unsigned long)remainingSec);
133 } else {
134 Console::printf("ERROR: PIN permanently locked.\r\n");
135 }
136 return true; // Command blocked
137 }
138 // PING is allowed even when blocked
139 } else {
140 // When secure serial is enabled, block ALL commands except PING and AUTH
141 // when not authenticated
142 bool isAllowedWithoutAuth = (strcasecmp(cmdBuf, "PING") == 0 ||
143 strcasecmp(cmdBuf, "AUTH") == 0);
144 if (!isAllowedWithoutAuth && authCheck_ && !authCheck_()) {
145 Console::printf("ERROR: Not authenticated. Use AUTH <pin> to login.\r\n");
146 return true; // Command blocked
147 }
148 }
149#endif
150
151 // Find and execute command
152 for (size_t i = 0; i < count_; i++) {
153 if (strcasecmp(commands_[i].name, cmdBuf) == 0) {
154 // Per-command auth check (for commands that require auth even when
155 // FEATURE_SECURE_SERIAL is disabled)
156 if (commands_[i].requiresAuth && authCheck_ && !authCheck_()) {
157 Console::printf("ERROR: Authentication required. Use AUTH <pin> first.\r\n");
158 return true; // Command found but not executed
159 }
160 if (commands_[i].handler) {
161 commands_[i].handler(line);
162 }
163 // Signal successful command execution for timer reset
164 if (onCommandExecuted_) {
165 onCommandExecuted_();
166 }
167 return true;
168 }
169 }
170
171 Console::printf("ERROR: Unknown command '%s'\r\n", cmdBuf);
172 Console::printf("Type 'HELP' for available commands.\r\n");
173 return false;
174 }
175
180 void showHelp() override {
181 Console::printf("=== Available Commands ===\r\n");
182
183 // Group by module
184 const char* currentModule = nullptr;
185
186 for (size_t i = 0; i < count_; i++) {
187 const char* module = commands_[i].moduleName ? commands_[i].moduleName : "system";
188
189 if (!currentModule || strcmp(currentModule, module) != 0) {
190 Console::printf("\r\n[%s]\r\n", module);
191 currentModule = module;
192 }
193
194 Console::printf(" %-20s %s\r\n",
195 commands_[i].name,
196 commands_[i].help ? commands_[i].help : "");
197
198 if (!commands_[i].subCommands) continue;
199 for (const SubCommand* e = commands_[i].subCommands; e->name; ++e) {
200 char head[kSubCommandHeadBufSize];
201 if (e->args && *e->args) {
202 std::snprintf(head, sizeof(head), "%s %s", e->name, e->args);
203 } else {
204 std::snprintf(head, sizeof(head), "%s", e->name);
205 }
206 Console::printf(" %-22s %s\r\n",
207 head, e->help ? e->help : "");
208 }
209 }
210
211 Console::printf("\r\n");
213 }
214
219 size_t getCommandCount() const override {
220 return count_;
221 }
222
228 void setOnCommandExecuted(void (*callback)()) override {
229 onCommandExecuted_ = callback;
230 }
231
232private:
233 Command commands_[MAX_COMMANDS] = {};
234 size_t count_ = 0;
235 bool (*authCheck_)() = nullptr;
236 void (*onCommandExecuted_)() = nullptr;
237 LineInterceptor lineInterceptor_ = nullptr;
238 ByteInterceptor byteInterceptor_ = nullptr;
239};
240
246 static CommandRegistry* g_commandRegistry = new CommandRegistry();
247 return *g_commandRegistry;
248}
249
250} // namespace cdc::serial
static const char * TAG
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
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
static PinManager & instance()
Returns singleton PIN manager instance.
ByteInterceptor getByteInterceptor() const override
bool processCommand(const char *line) override
Parses and executes one command line.
void showHelp() override
Prints grouped help for all registered commands.
void setLineInterceptor(LineInterceptor interceptor) override
Sets optional line interceptor for multiline modes.
void setOnCommandExecuted(void(*callback)()) override
Sets callback fired after successful command execution.
void unregisterModule(const char *moduleName) override
Unregisters all commands belonging to one module.
void setAuthProvider(bool(*authCheck)()) override
Sets external authentication status provider.
bool registerCommand(const Command &cmd) override
Registers a command in the dispatch table.
void setByteInterceptor(ByteInterceptor interceptor) override
size_t getCommandCount() const override
Returns count of currently registered commands.
static void flush()
Flushes pending console output.
Definition Console.cpp:82
static void printf(const char *format,...) __attribute__((format(printf
Prints formatted text to console.
Definition Console.cpp:30
void(*)(uint8_t byte) ByteInterceptor
bool(*)(const char *line) LineInterceptor
constexpr size_t kSubCommandHeadBufSize
Buffer size for the "<name> <args>" column built during HELP rendering.
ICommandRegistry & getCommandRegistry()
Returns singleton command-registry interface.
static constexpr size_t MAX_COMMANDS
Maximum number of commands that can be registered.
const char * moduleName
Module that registered the command (used for HELP grouping).
const char * name
Top-level command (e.g. "TOTP" or "PING").
const char * name
Sub-command keyword, e.g. "LIST". nullptr terminates the array.