CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
CapabilityChecker.cpp
Go to the documentation of this file.
5
6#include <cctype>
7
8namespace cdc::plugin_manager {
9
10namespace {
11
12// ESP-IDF NVS namespace identifier is bounded to 15 chars + NUL.
13constexpr size_t NVS_NAMESPACE_MAX_LEN = 15;
14
15// rmem slot name fits the on-chip 16-byte name field minus trailing NUL.
16constexpr size_t RMEM_NAME_MAX_LEN = HOST_RMEM_NAME_MAX;
17
18// Plugin linear memory and runtime structures live in PSRAM (Fast Interpreter).
19// This range-checks the manifest's linear_memory_kb; the operand+frame stack is
20// a fixed 64 KB allocated in Plugin.cpp.
21constexpr uint32_t LINEAR_MEMORY_MIN_KB = 16;
22constexpr uint32_t LINEAR_MEMORY_MAX_KB = 4096;
23
24// 8-4-4-4-12 lowercase hex with dashes, e.g. "0000180a-0000-1000-8000-00805f9b34fb".
25bool isValidUuid128(const std::string& s)
26{
27 static constexpr size_t kLen = 36;
28 static constexpr size_t kDashPos[] = {8, 13, 18, 23};
29 if (s.size() != kLen) return false;
30 for (size_t pos : kDashPos) if (s[pos] != '-') return false;
31 for (size_t i = 0; i < kLen; ++i) {
32 if (i == 8 || i == 13 || i == 18 || i == 23) continue;
33 char c = s[i];
34 bool digit = (c >= '0' && c <= '9');
35 bool lower = (c >= 'a' && c <= 'f');
36 if (!digit && !lower) return false;
37 }
38 return true;
39}
40
41} // namespace
42
44{
48 "plugin needs API " + m.host_api_level_min +
49 ", firmware provides " + std::string(HOST_API_LEVEL_STR) };
50 }
51
52 if (m.linear_memory_kb < LINEAR_MEMORY_MIN_KB ||
53 m.linear_memory_kb > LINEAR_MEMORY_MAX_KB) {
55 "linear_memory_kb out of [" +
56 std::to_string(LINEAR_MEMORY_MIN_KB) + ", " +
57 std::to_string(LINEAR_MEMORY_MAX_KB) + "]" };
58 }
59
60 for (const std::string& name : m.capabilities.rmem) {
61 if (name.empty() || name.size() > RMEM_NAME_MAX_LEN) {
63 "rmem name '" + name + "' must be 1-" +
64 std::to_string(RMEM_NAME_MAX_LEN) + " chars" };
65 }
66 }
67
68 for (const std::string& name : m.capabilities.ecc) {
69 if (name.empty() || name.size() > HOST_ECC_NAME_MAX) {
71 "ecc name '" + name + "' must be 1-" +
72 std::to_string(HOST_ECC_NAME_MAX) + " chars" };
73 }
74 }
75
76 for (uint8_t pin : m.capabilities.gpio_pins) {
77 if (gpio_policy::isBlocked(pin)) {
79 "GPIO " + std::to_string(pin) + " is reserved by firmware hardware" };
80 }
81 if (!gpio_policy::isAllowed(pin)) {
83 "GPIO " + std::to_string(pin) + " not on plugin whitelist" };
84 }
85 }
86
87 for (uint8_t pin : m.capabilities.pwm_pins) {
88 if (gpio_policy::isBlocked(pin)) {
90 "PWM pin " + std::to_string(pin) + " is reserved by firmware hardware" };
91 }
92 if (!gpio_policy::isAllowed(pin)) {
94 "PWM pin " + std::to_string(pin) + " not on plugin whitelist" };
95 }
96 }
97
98 for (uint8_t pin : m.capabilities.adc_pins) {
99 if (gpio_policy::isBlocked(pin)) {
101 "ADC pin " + std::to_string(pin) + " is reserved by firmware hardware" };
102 }
103 if (!gpio_policy::isAllowed(pin)) {
105 "ADC pin " + std::to_string(pin) + " not on plugin whitelist" };
106 }
107 }
108
109 for (uint8_t bus : m.capabilities.i2c_bus) {
110 // Bus 0 is the internal charger + IO expander bus. Plugins must never
111 // touch it - that bus controls power and IO expansion.
112 if (bus == 0) {
114 "I2C bus 0 is reserved for internal hardware" };
115 }
116 }
117
118 if (!m.capabilities.nvs_namespace.empty()) {
119 const auto& ns = m.capabilities.nvs_namespace;
120 if (ns.size() > NVS_NAMESPACE_MAX_LEN) {
122 "nvs_namespace exceeds " +
123 std::to_string(NVS_NAMESPACE_MAX_LEN) + " chars" };
124 }
125 const bool prefixOk = ns.rfind("plg_", 0) == 0 ||
126 ns.rfind("plugin_", 0) == 0;
127 if (!prefixOk) {
129 "nvs_namespace must start with 'plg_' or 'plugin_'" };
130 }
131 for (char c : ns) {
132 bool ok = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_';
133 if (!ok) {
135 "nvs_namespace must be [a-z0-9_]" };
136 }
137 }
138 } else if (!m.capabilities.rmem.empty()) {
140 "nvs_namespace required when persistent state used" };
141 }
142
143 for (const auto& uuid : m.capabilities.ble_service_uuids) {
144 if (!isValidUuid128(uuid)) {
146 "ble_service_uuid '" + uuid +
147 "' not a 128-bit lowercase UUID" };
148 }
149 }
150
153 "provides exceeds " + std::to_string(EXT_FEATURE_MAX_PER_PLUGIN) +
154 " entries" };
155 }
156 for (const std::string& name : m.capabilities.provides) {
157 if (!isValidExtFeatureName(name.c_str())) {
159 "provides entry '" + name + "' must be [a-z][a-z0-9_]*, 1-" +
160 std::to_string(EXT_FEATURE_NAME_MAX - 1) + " chars" };
161 }
162 }
163
164 return { CapabilityResult::Ok, {} };
165}
166
167} // namespace cdc::plugin_manager
Load-time validation of plugin capabilities + manifest sanity.
Validation for external-feature names declared under the manifest provides capability and passed to t...
Single source of truth for plugin-accessible GPIO pins.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
static CapabilityCheckResult validate(const PluginManifest &manifest)
#define HOST_ECC_NAME_MAX
Definition host_api.h:281
#define HOST_RMEM_NAME_MAX
Definition host_api.h:249
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_API_LEVEL_MAJOR
Definition host_api.h:28
#define HOST_API_LEVEL_STR
Definition host_api.h:30
#define HOST_API_LEVEL_MINOR
Definition host_api.h:29
constexpr size_t EXT_FEATURE_MAX_PER_PLUGIN
Maximum number of provides entries a single plugin manifest may declare.
constexpr size_t EXT_FEATURE_NAME_MAX
Maximum external-feature name length including the trailing NUL.
bool isValidExtFeatureName(const char *name)
True if name is a well-formed external-feature name.
std::vector< std::string > ble_service_uuids
std::vector< std::string > provides