13#include <mbedtls/sha256.h>
14#include <mbedtls/hkdf.h>
15#include <mbedtls/md.h>
16#include <mbedtls/platform_util.h>
17#include <esp_random.h>
20static const char*
TAG =
"GPGStorage";
48static constexpr char HKDF_INFO[] =
"GPG-STORAGE-V2";
74static_assert(
sizeof(DecKeyStorage) ==
DEC_TOTAL_SIZE,
"DecKeyStorage size mismatch");
75static_assert(
sizeof(AesKeyStorage) ==
AES_TOTAL_SIZE,
"AesKeyStorage size mismatch");
80inline void secureWipe(uint8_t (&buf)[N]) {
81 mbedtls_platform_zeroize(buf, N);
85inline void secureWipeObject(T& obj) {
86 mbedtls_platform_zeroize(&obj,
sizeof(obj));
93 uint16_t eccStart = 0;
95 uint16_t rmemStart = 0;
101 bool sessionActive =
false;
102 uint8_t sessionKey[32];
116 if (!hash_out)
return false;
117 const uint8_t* in = pin ?
reinterpret_cast<const uint8_t*
>(pin) :
reinterpret_cast<const uint8_t*
>(
"");
118 size_t in_len = pin ? strlen(pin) : 0;
119 return mbedtls_sha256(in, in_len, hash_out, 0) == 0;
130 if (!key_out)
return false;
132 uint8_t ikm[16 + 32] = {};
136 se->getChipId(ikm, 16);
139 memcpy(ikm + 16, pin_hash, 32);
144 salt[0] =
static_cast<uint8_t
>((slot_id >> 8) & 0xFF);
145 salt[1] =
static_cast<uint8_t
>(slot_id & 0xFF);
147 const mbedtls_md_info_t* md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
149 mbedtls_platform_zeroize(ikm,
sizeof(ikm));
153 int ret = mbedtls_hkdf(
161 mbedtls_platform_zeroize(ikm,
sizeof(ikm));
172 aad_out[0] =
static_cast<uint8_t
>((slot_id >> 8) & 0xFF);
173 aad_out[1] =
static_cast<uint8_t
>(slot_id & 0xFF);
183 return static_cast<uint16_t
>(
s_storage.rmemStart + rel_index);
191 if (eccStart == 0 || eccEnd == 0 || eccStart > eccEnd || (eccEnd - eccStart + 1) < 3) {
195 s_storage.sigSlot =
static_cast<uint8_t
>(eccStart);
196 s_storage.decSlot =
static_cast<uint8_t
>(eccStart + 1);
197 s_storage.autSlot =
static_cast<uint8_t
>(eccStart + 2);
224 const uint8_t* payload,
size_t payload_len,
225 uint8_t* record_buf,
size_t record_buf_len,
228 if (!se)
return false;
231 uint8_t pin_hash[32];
232 bool have_pin = (pin && pin[0] !=
'\0');
234 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
240 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
246 uint8_t* p_magic = record_buf;
249 uint8_t* p_tag = p_ct + payload_len;
252 if (!se->getRandomStrict(p_nonce,
NONCE_SIZE)) {
254 LOG_E(
TAG,
"Cannot get hardware entropy for nonce on slot %u", slot_id);
264 payload, payload_len,
272 se->rmemErase(slot_id);
275 LOG_E(
TAG,
"rmemWrite slot %u failed", slot_id);
292 uint8_t* payload_out,
size_t payload_len,
295 if (!se)
return false;
298 uint16_t buf_len = 0;
300 if (expected >
sizeof(buf))
return false;
309 uint8_t pin_hash[32];
310 bool have_pin = (pin && pin[0] !=
'\0');
312 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
313 mbedtls_platform_zeroize(buf,
sizeof(buf));
319 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
322 mbedtls_platform_zeroize(buf,
sizeof(buf));
328 const uint8_t* p_tag = p_ct + payload_len;
339 mbedtls_platform_zeroize(buf,
sizeof(buf));
341 mbedtls_platform_zeroize(payload_out, payload_len);
348 if (!privkey)
return false;
352 record,
sizeof(record), pin);
355 LOG_I(
TAG,
"Saved DEC private key (slot %u)", slot);
361 if (!privkey_out)
return false;
368 if (!se)
return false;
371 uint16_t buf_len = 0;
380 if (!se)
return false;
386 if (!key)
return false;
387 if (key_len != 16 && key_len != 32)
return false;
391 payload[0] =
static_cast<uint8_t
>(key_len);
392 memcpy(payload + 1, key, key_len);
399 record,
sizeof(record), pin);
403 LOG_I(
TAG,
"Saved AES key (slot %u, %zu bytes)", slot, key_len);
409 if (!key_out || !key_len_out)
return false;
416 size_t len = payload[0];
417 if (len != 16 && len != 32) {
421 memcpy(key_out, payload + 1, len);
429 if (!se)
return false;
432 uint16_t buf_len = 0;
441 if (!se)
return false;
453 memcpy(
s_storage.sessionKey, hash,
sizeof(hash));
456 mbedtls_platform_zeroize(hash,
sizeof(hash));
460 if (!
s_storage.sessionActive || !key_out) {
463 memcpy(key_out,
s_storage.sessionKey, 32);
Shared AES-256-GCM helpers built on mbedTLS.
static bool save_slot_encrypted(uint16_t slot_id, const uint8_t magic[MAGIC_SIZE], const uint8_t *payload, size_t payload_len, uint8_t *record_buf, size_t record_buf_len, const char *pin)
Encrypts and writes an arbitrary payload to a slot.
static void build_aad(uint16_t slot_id, const uint8_t magic[MAGIC_SIZE], uint8_t aad_out[6])
Builds the 6-byte AAD for a slot: slot_id (BE) || magic (4).
static bool load_slot_decrypted(uint16_t slot_id, const uint8_t magic[MAGIC_SIZE], uint8_t *payload_out, size_t payload_len, const char *pin)
Reads and decrypts a payload from a slot.
bool gpg_storage_save_aes_key(const uint8_t *key, size_t key_len, const char *pin)
Saves the symmetric AES key for PSO:DECIPHER (DO 0xD5).
static bool pin_to_hash(const char *pin, uint8_t *hash_out)
Computes SHA-256 over a PIN string.
static constexpr size_t DEC_TOTAL_SIZE
void gpg_storage_set_rmem_range(uint16_t rmemStart, uint16_t rmemEnd)
bool gpg_storage_delete_dec_privkey(void)
Deletes DEC private key record.
static cdc::hal::ISecureElement * get_se()
static constexpr size_t NONCE_SIZE
uint8_t gpg_storage_dec_slot(void)
bool gpg_storage_ready(void)
bool gpg_storage_save_dec_privkey(const uint8_t *privkey, const char *pin)
Saves a DEC private key into R-Memory using PIN-bound AES-GCM.
void gpg_storage_set_session_pin(const char *pin)
Stores session PIN-derived key after successful PIN verification.
static constexpr uint16_t RMEM_SLOT_AES_KEY
R-Memory slot offset for the symmetric AES key payload (= ECC slot 3).
bool gpg_storage_load_aes_key(uint8_t *key_out, size_t *key_len_out, const char *pin)
Loads the symmetric AES key from R-Memory.
static bool derive_storage_key(uint16_t slot_id, const uint8_t *pin_hash, uint8_t *key_out)
Derives a 32-byte storage key for a specific slot.
static uint16_t resolve_slot(uint16_t rel_index)
Resolves an absolute R-Memory slot index relative to the module range.
static constexpr uint16_t RMEM_SLOT_DEC_KEY
R-Memory slot offset for the DEC private key payload (= ECC slot 2).
bool gpg_storage_load_dec_privkey(uint8_t *privkey_out, const char *pin)
Loads and decrypts the DEC private key from R-Memory.
uint8_t gpg_storage_aut_slot(void)
static constexpr size_t AES_RECORD_PAYLOAD
void gpg_storage_clear_session(void)
Clears the cached session key.
uint8_t gpg_storage_sig_slot(void)
void gpg_storage_set_slot_range(uint16_t eccStart, uint16_t eccEnd)
static struct @103247112223232226025054031216105275222335301367 s_storage
static constexpr size_t PRIVKEY_SIZE
static constexpr size_t AES_MAX_KEY_SIZE
bool gpg_storage_delete_aes_key(void)
Deletes the symmetric AES key record.
static constexpr size_t TAG_SIZE
bool gpg_storage_has_dec_privkey(void)
Returns true if encrypted DEC private key record exists.
static constexpr size_t MAGIC_SIZE
static constexpr char HKDF_INFO[]
HKDF info string for storage key derivation.
static constexpr uint8_t DEC_KEY_MAGIC[4]
Magic marker for encrypted DEC private key records.
bool gpg_storage_get_session_key(uint8_t *key_out)
Returns current session key if session is active.
bool gpg_storage_has_aes_key(void)
Returns true if a symmetric AES key record exists.
static constexpr size_t AES_TOTAL_SIZE
static constexpr uint8_t AES_KEY_MAGIC[4]
Magic marker for symmetric AES key records (DO 0xD5).
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
bool aesGcm256Seal(const uint8_t key[32], const uint8_t *iv, size_t ivLen, const uint8_t *aad, size_t aadLen, const uint8_t *pt, size_t ptLen, uint8_t *ctOut, uint8_t tagOut[16])
Encrypts pt with AES-256-GCM and produces a 16-byte tag.
bool aesGcm256Open(const uint8_t key[32], const uint8_t *iv, size_t ivLen, const uint8_t *aad, size_t aadLen, const uint8_t *ct, size_t ctLen, const uint8_t tag[16], uint8_t *ptOut)
Authenticates and decrypts ct with AES-256-GCM.
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
uint8_t encrypted[AES_RECORD_PAYLOAD]
uint8_t magic[MAGIC_SIZE]
uint8_t nonce[NONCE_SIZE]
uint8_t magic[MAGIC_SIZE]
uint8_t nonce[NONCE_SIZE]
uint8_t encrypted[PRIVKEY_SIZE]