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 the
141 // auth-free set (liveness, login, and the read-only version/help
142 // commands) when not authenticated.
143 bool isAllowedWithoutAuth = (strcasecmp(cmdBuf, "PING") == 0 ||
144 strcasecmp(cmdBuf, "AUTH") == 0 ||
145 strcasecmp(cmdBuf, "VERSION") == 0 ||
146 strcasecmp(cmdBuf, "HELP") == 0);
147 if (!isAllowedWithoutAuth && authCheck_ && !authCheck_()) {
148 Console::printf("ERROR: Not authenticated. Use AUTH <pin> to login.\r\n");
149 return true; // Command blocked
150 }
151 }
152#endif
153
154 // Find and execute command
155 for (size_t i = 0; i < count_; i++) {
156 if (strcasecmp(commands_[i].name, cmdBuf) == 0) {
157 // Per-command auth check (for commands that require auth even when
158 // FEATURE_SECURE_SERIAL is disabled)
159 if (commands_[i].requiresAuth && authCheck_ && !authCheck_()) {
160 Console::printf("ERROR: Authentication required. Use AUTH <pin> first.\r\n");
161 return true; // Command found but not executed
162 }
163 if (commands_[i].handler) {
164 commands_[i].handler(line);
165 }
166 // Signal successful command execution for timer reset
167 if (onCommandExecuted_) {
168 onCommandExecuted_();
169 }
170 return true;
171 }
172 }
173
174 Console::printf("ERROR: Unknown command '%s'\r\n", cmdBuf);
175 Console::printf("Type 'HELP' for available commands.\r\n");
176 return false;
177 }
178
183 void showHelp() override {
184 Console::printf("=== Available Commands ===\r\n");
185
186 // Group by module
187 const char* currentModule = nullptr;
188
189 for (size_t i = 0; i < count_; i++) {
190 const char* module = commands_[i].moduleName ? commands_[i].moduleName : "system";
191
192 if (!currentModule || strcmp(currentModule, module) != 0) {
193 Console::printf("\r\n[%s]\r\n", module);
194 currentModule = module;
195 }
196
197 Console::printf(" %-20s %s\r\n",
198 commands_[i].name,
199 commands_[i].help ? commands_[i].help : "");
200
201 if (!commands_[i].subCommands) continue;
202 for (const SubCommand* e = commands_[i].subCommands; e->name; ++e) {
203 char head[kSubCommandHeadBufSize];
204 if (e->args && *e->args) {
205 std::snprintf(head, sizeof(head), "%s %s", e->name, e->args);
206 } else {
207 std::snprintf(head, sizeof(head), "%s", e->name);
208 }
209 Console::printf(" %-22s %s\r\n",
210 head, e->help ? e->help : "");
211 }
212 }
213
214 Console::printf("\r\n");
216 }
217
222 size_t getCommandCount() const override {
223 return count_;
224 }
225
231 void setOnCommandExecuted(void (*callback)()) override {
232 onCommandExecuted_ = callback;
233 }
234
235private:
236 Command commands_[MAX_COMMANDS] = {};
237 size_t count_ = 0;
238 bool (*authCheck_)() = nullptr;
239 void (*onCommandExecuted_)() = nullptr;
240 LineInterceptor lineInterceptor_ = nullptr;
241 ByteInterceptor byteInterceptor_ = nullptr;
242};
243
249 static CommandRegistry* g_commandRegistry = new CommandRegistry();
250 return *g_commandRegistry;
251}
252
253} // 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:104
static void printf(const char *format,...) __attribute__((format(printf
Prints formatted text to console.
Definition Console.cpp:32
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.