10static const char*
TAG =
"GPG_RECV";
15constexpr const char* kNamespace =
"gpg_recv";
16constexpr const char* kKeyPrefix =
"pk_";
17constexpr size_t kNvsKeyLen = 11;
19bool hasKeyPrefix(
const char* k) {
20 return k && std::strncmp(k, kKeyPrefix, 3) == 0;
25 static GpgRecvStore inst;
29void GpgRecvStore::deriveKeyName(
const uint8_t fp_v4[20],
char out[16]) {
30 std::snprintf(out, 16,
"%s%02x%02x%02x%02x",
31 kKeyPrefix, fp_v4[0], fp_v4[1], fp_v4[2], fp_v4[3]);
34bool GpgRecvStore::readByName(
const char* nvs_key, gpg_recv_key_t* out) {
35 if (!nvs_key || !out)
return false;
37 if (!nvs)
return false;
38 size_t expected =
sizeof(gpg_recv_key_t);
39 if (nvs_get_blob(nvs, nvs_key, out, &expected) != ESP_OK)
return false;
40 return expected ==
sizeof(gpg_recv_key_t);
43bool GpgRecvStore::writeByName(
const char* nvs_key,
const gpg_recv_key_t& key) {
44 if (!nvs_key)
return false;
46 if (!nvs)
return false;
47 if (nvs_set_blob(nvs, nvs_key, &key,
sizeof(key)) != ESP_OK)
return false;
48 return nvs.commit() == ESP_OK;
58 bool exists = readByName(
name, &probe);
63 return writeByName(
name, key);
67 nvs_iterator_t it =
nullptr;
68 if (nvs_entry_find(
"nvs", kNamespace, NVS_TYPE_BLOB, &it) != ESP_OK || !it) {
72 while (it !=
nullptr) {
73 nvs_entry_info_t info = {};
74 nvs_entry_info(it, &info);
75 if (hasKeyPrefix(info.key)) ++n;
77 if (nvs_entry_next(&it) != ESP_OK)
break;
79 nvs_release_iterator(it);
84 if (!out || max == 0)
return 0;
86 nvs_iterator_t it =
nullptr;
87 if (nvs_entry_find(
"nvs", kNamespace, NVS_TYPE_BLOB, &it) != ESP_OK || !it) {
92 while (it !=
nullptr && n < max) {
93 nvs_entry_info_t info = {};
94 nvs_entry_info(it, &info);
95 if (hasKeyPrefix(info.key)) {
97 if (readByName(info.key, &k)) {
98 std::snprintf(out[n].nvs_key,
sizeof(out[n].nvs_key),
"%s", info.key);
104 if (nvs_entry_next(&it) != ESP_OK)
break;
106 nvs_release_iterator(it);
108 std::sort(out, out + n,
115bool GpgRecvStore::resolveKeyName(uint8_t index,
char out[16]) {
117 if (!buf)
return false;
119 if (index >= n)
return false;
120 std::snprintf(out, 16,
"%s", buf[index].nvs_key);
125 if (!out)
return false;
127 if (!resolveKeyName(index,
name))
return false;
128 return readByName(
name, out);
133 if (!resolveKeyName(index,
name))
return false;
136 if (!nvs)
return false;
137 if (nvs_erase_key(nvs,
name) != ESP_OK)
return false;
138 return nvs.
commit() == ESP_OK;
142 const uint8_t* sig, uint8_t sig_len,
150 if (!resolveKeyName(index,
name))
return false;
153 if (!readByName(
name, &key))
return false;
160 return writeByName(
name, key);
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
Shared RAII wrappers for firmware resources.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
RAII wrapper for an NVS handle.
esp_err_t commit() noexcept
Commit pending writes. Caller checks the return value if needed.
bool addKey(const gpg_recv_key_t &key)
Persist a new key. Replaces an existing entry if the fingerprint matches.
bool deleteKey(uint8_t index)
Remove one key by sorted index. No-op if index is out of range.
bool setSignature(uint8_t index, const uint8_t *sig, uint8_t sig_len, uint8_t flags)
Attach a cross-signature and flag bits to an existing entry.
bool getKey(uint8_t index, gpg_recv_key_t *out)
Load one key by sorted index (0..count()-1).
static GpgRecvStore & instance()
uint8_t listIndex(gpg_recv_index_entry_t *out, uint8_t max)
Build the sorted index (oldest first).
uint8_t count()
Number of stored keys.
static constexpr uint8_t kMaxKeys
Hard ceiling. Past this addKey rejects further inserts.
PsramUniquePtr< T > psramAlloc(std::size_t count) noexcept
Allocate count elements of T in PSRAM (8-bit capable region).
Sort entry used to expose a stable ordered index over NVS keys.
One GPG public key received from another badge.
uint8_t fingerprint_v4[20]