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>
21static const char*
TAG =
"GPGStorage";
49static constexpr char HKDF_INFO[] =
"GPG-STORAGE-V2";
75static_assert(
sizeof(DecKeyStorage) ==
DEC_TOTAL_SIZE,
"DecKeyStorage size mismatch");
76static_assert(
sizeof(AesKeyStorage) ==
AES_TOTAL_SIZE,
"AesKeyStorage size mismatch");
81inline void secureWipe(uint8_t (&buf)[N]) {
82 mbedtls_platform_zeroize(buf, N);
86inline void secureWipeObject(T& obj) {
87 mbedtls_platform_zeroize(&obj,
sizeof(obj));
94 uint16_t eccStart = 0;
96 uint16_t rmemStart = 0;
102 bool sessionActive =
false;
103 uint8_t sessionKey[32];
117 if (!hash_out)
return false;
118 const uint8_t* in = pin ?
reinterpret_cast<const uint8_t*
>(pin) :
reinterpret_cast<const uint8_t*
>(
"");
119 size_t in_len = pin ? strlen(pin) : 0;
120 return mbedtls_sha256(in, in_len, hash_out, 0) == 0;
131 if (!key_out)
return false;
133 uint8_t ikm[16 + 32] = {};
137 se->getChipId(ikm, 16);
140 memcpy(ikm + 16, pin_hash, 32);
145 salt[0] =
static_cast<uint8_t
>((slot_id >> 8) & 0xFF);
146 salt[1] =
static_cast<uint8_t
>(slot_id & 0xFF);
148 const mbedtls_md_info_t* md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
150 mbedtls_platform_zeroize(ikm,
sizeof(ikm));
154 int ret = mbedtls_hkdf(
162 mbedtls_platform_zeroize(ikm,
sizeof(ikm));
173 aad_out[0] =
static_cast<uint8_t
>((slot_id >> 8) & 0xFF);
174 aad_out[1] =
static_cast<uint8_t
>(slot_id & 0xFF);
184 return static_cast<uint16_t
>(
s_storage.rmemStart + rel_index);
192 if (eccStart == 0 || eccEnd == 0 || eccStart > eccEnd || (eccEnd - eccStart + 1) < 3) {
196 s_storage.sigSlot =
static_cast<uint8_t
>(eccStart);
197 s_storage.decSlot =
static_cast<uint8_t
>(eccStart + 1);
198 s_storage.autSlot =
static_cast<uint8_t
>(eccStart + 2);
225 const uint8_t* payload,
size_t payload_len,
226 uint8_t* record_buf,
size_t record_buf_len,
229 if (!se)
return false;
232 uint8_t pin_hash[32];
233 bool have_pin = (pin && pin[0] !=
'\0');
235 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
241 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
247 uint8_t* p_magic = record_buf;
250 uint8_t* p_tag = p_ct + payload_len;
253 if (!se->getRandomStrict(p_nonce,
NONCE_SIZE)) {
255 LOG_E(
TAG,
"Cannot get hardware entropy for nonce on slot %u", slot_id);
265 payload, payload_len,
273 se->rmemErase(slot_id);
276 LOG_E(
TAG,
"rmemWrite slot %u failed", slot_id);
293 uint8_t* payload_out,
size_t payload_len,
296 if (!se)
return false;
299 uint16_t buf_len = 0;
301 if (expected >
sizeof(buf))
return false;
310 uint8_t pin_hash[32];
311 bool have_pin = (pin && pin[0] !=
'\0');
313 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
314 mbedtls_platform_zeroize(buf,
sizeof(buf));
320 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
323 mbedtls_platform_zeroize(buf,
sizeof(buf));
329 const uint8_t* p_tag = p_ct + payload_len;
340 mbedtls_platform_zeroize(buf,
sizeof(buf));
342 mbedtls_platform_zeroize(payload_out, payload_len);
349 if (!privkey)
return false;
353 record,
sizeof(record), pin);
356 LOG_I(
TAG,
"Saved DEC private key (slot %u)", slot);
362 if (!privkey_out)
return false;
369 if (!se)
return false;
372 uint16_t buf_len = 0;
381 if (!se)
return false;
387 if (!key)
return false;
388 if (key_len != 16 && key_len != 32)
return false;
392 payload[0] =
static_cast<uint8_t
>(key_len);
393 memcpy(payload + 1, key, key_len);
400 record,
sizeof(record), pin);
404 LOG_I(
TAG,
"Saved AES key (slot %u, %zu bytes)", slot, key_len);
410 if (!key_out || !key_len_out)
return false;
417 size_t len = payload[0];
418 if (len != 16 && len != 32) {
422 memcpy(key_out, payload + 1, len);
430 if (!se)
return false;
433 uint16_t buf_len = 0;
442 if (!se)
return false;
479 *slot1 =
static_cast<uint16_t
>(base + 1);
485 uint16_t slot0 = 0, slot1 = 0;
488 if (!se)
return false;
490 uint8_t pin_hash[32];
491 bool have_pin = (pin && pin[0] !=
'\0');
493 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
498 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
506 s_rsa_record[0] =
static_cast<uint8_t
>(rec_body & 0xFF);
507 s_rsa_record[1] =
static_cast<uint8_t
>((rec_body >> 8) & 0xFF);
511 uint8_t* p_tag = p_ct + blob_len;
513 if (!se->getRandomStrict(p_nonce,
NONCE_SIZE)) {
520 blob, blob_len, p_ct, p_tag);
528 se->rmemErase(slot0);
529 se->rmemErase(slot1);
531 if (wrote && total > c0) {
537 LOG_E(
TAG,
"RSA key write failed (role %u)", role);
540 LOG_I(
TAG,
"Saved RSA key (role %u, %zu bytes, slots %u/%u)", role, blob_len, slot0, slot1);
545 size_t* blob_len_out,
const char* pin) {
546 if (!blob_out || !blob_len_out)
return false;
547 uint16_t slot0 = 0, slot1 = 0;
550 if (!se)
return false;
557 const size_t rec_body =
static_cast<size_t>(
s_rsa_record[0]) |
560 if (rec_body < RSA_ENVELOPE || total >
sizeof(
s_rsa_record)) {
570 have =
static_cast<size_t>(l0) + l1;
572 if (have < total)
return false;
581 uint8_t pin_hash[32];
582 bool have_pin = (pin && pin[0] !=
'\0');
584 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
590 mbedtls_platform_zeroize(pin_hash,
sizeof(pin_hash));
599 const uint8_t* p_tag = p_ct + blob_len;
603 p_ct, blob_len, p_tag, blob_out);
607 mbedtls_platform_zeroize(blob_out, blob_len);
610 *blob_len_out = blob_len;
615 uint16_t slot0 = 0, slot1 = 0;
618 if (!se)
return false;
620 uint16_t buf_len = 0;
622 buf_len <
sizeof(buf)) {
629 uint16_t slot0 = 0, slot1 = 0;
632 if (!se)
return false;
645 memcpy(
s_storage.sessionKey, hash,
sizeof(hash));
648 mbedtls_platform_zeroize(hash,
sizeof(hash));
652 if (!
s_storage.sessionActive || !key_out) {
655 memcpy(key_out,
s_storage.sessionKey, 32);
Shared AES-256-GCM helpers built on mbedTLS.
void gpg_storage_set_rsa_slot_range(uint16_t start, uint16_t end)
Sets the dedicated R-Memory range used for RSA private-key blobs.
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 constexpr uint8_t RSA_KEY_MAGIC[MAGIC_SIZE]
Magic marker for encrypted RSA private-key records.
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.
static constexpr uint8_t RSA_ROLE_COUNT
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
bool gpg_storage_delete_rsa_key(uint8_t role)
Deletes the RSA private-key blob for the role (both slots).
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.
bool gpg_storage_save_rsa_key(uint8_t role, const uint8_t *blob, size_t blob_len, const char *pin)
Saves an encrypted RSA private-key blob for a key role.
static bool rsa_role_slots(uint8_t role, uint16_t *slot0, uint16_t *slot1)
Resolves the two consecutive R-Memory slots for an RSA key role.
void gpg_storage_set_session_pin(const char *pin)
Stores session PIN-derived key after successful PIN verification.
bool gpg_storage_has_rsa_key(uint8_t role)
Returns true if an RSA private-key blob exists for the role.
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).
static constexpr size_t RSA_ENVELOPE
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)
static constexpr size_t RSA_REC_HEADER
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 RSA_SLOT_CAP
static constexpr size_t TAG_SIZE
bool gpg_storage_load_rsa_key(uint8_t role, uint8_t *blob_out, size_t blob_cap, size_t *blob_len_out, const char *pin)
Loads and decrypts the RSA private-key blob for a key role.
bool gpg_storage_has_dec_privkey(void)
Returns true if encrypted DEC private key record exists.
static constexpr size_t MAGIC_SIZE
static constexpr uint16_t RSA_SLOTS_PER_ROLE
static uint16_t s_rsa_rmem_start
static constexpr char HKDF_INFO[]
HKDF info string for storage key derivation.
static uint8_t s_rsa_record[RSA_REC_HEADER+GPG_RSA_BLOB_MAX+RSA_ENVELOPE]
static uint16_t s_rsa_rmem_end
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).
#define GPG_RSA_BLOB_MAX
Maximum serialized RSA private-key blob (RSA-4096 n_bits||e||p||q).
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
static constexpr uint16_t RMEM_SLOT_SIZE
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]