CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
GpgStorage.cpp
Go to the documentation of this file.
1
8
11#include "cdc_core/Crypto.h"
12#include "cdc_log.h"
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>
18#include <esp_attr.h>
19#include <string.h>
20
21static const char* TAG = "GPGStorage";
22
23// Relative offsets within the mod_gpg R-Memory range. Each offset is the
24// metadata slot for the matching ECC key slot (RMEM slot N pairs with ECC
25// slot N). The range starts at slot 1 (= ECC slot 1 = SIG), which is
26// hardware-only and needs no software metadata, so offset 0 is unused.
28static constexpr uint16_t RMEM_SLOT_DEC_KEY = 1;
29
31static constexpr uint16_t RMEM_SLOT_AES_KEY = 2;
32
34static constexpr uint8_t DEC_KEY_MAGIC[4] = {'E', 'C', 'D', 'H'};
35
37static constexpr uint8_t AES_KEY_MAGIC[4] = {'A', 'E', 'S', '1'};
38
39static constexpr size_t MAGIC_SIZE = 4;
40static constexpr size_t NONCE_SIZE = 12;
41static constexpr size_t TAG_SIZE = 16;
42static constexpr size_t PRIVKEY_SIZE = 32;
43static constexpr size_t AES_MAX_KEY_SIZE = 32;
45static constexpr size_t AES_RECORD_PAYLOAD = 1 + AES_MAX_KEY_SIZE;
47
49static constexpr char HKDF_INFO[] = "GPG-STORAGE-V2";
50
51#ifdef __DOXYGEN__
52namespace cdc::mod_gpg {
53#endif
54
55#pragma pack(push, 1)
57 uint8_t magic[MAGIC_SIZE];
58 uint8_t nonce[NONCE_SIZE];
60 uint8_t tag[TAG_SIZE];
61};
62
69#pragma pack(pop)
70
71#ifdef __DOXYGEN__
72} // namespace cdc::mod_gpg
73#endif
74
75static_assert(sizeof(DecKeyStorage) == DEC_TOTAL_SIZE, "DecKeyStorage size mismatch");
76static_assert(sizeof(AesKeyStorage) == AES_TOTAL_SIZE, "AesKeyStorage size mismatch");
77
78namespace {
79
80template <size_t N>
81inline void secureWipe(uint8_t (&buf)[N]) {
82 mbedtls_platform_zeroize(buf, N);
83}
84
85template <typename T>
86inline void secureWipeObject(T& obj) {
87 mbedtls_platform_zeroize(&obj, sizeof(obj));
88}
89
90} // namespace
91
92static struct {
93 bool ready = false;
94 uint16_t eccStart = 0;
95 uint16_t eccEnd = 0;
96 uint16_t rmemStart = 0;
97 uint16_t rmemEnd = 0;
98 uint8_t sigSlot = 0;
99 uint8_t decSlot = 0;
100 uint8_t autSlot = 0;
101
102 bool sessionActive = false;
103 uint8_t sessionKey[32];
105
109
116static bool pin_to_hash(const char* pin, uint8_t* hash_out) {
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;
121}
122
130static bool derive_storage_key(uint16_t slot_id, const uint8_t* pin_hash, uint8_t* key_out) {
131 if (!key_out) return false;
132
133 uint8_t ikm[16 + 32] = {};
134 size_t ikm_len = 16;
135 auto* se = get_se();
136 if (se) {
137 se->getChipId(ikm, 16);
138 }
139 if (pin_hash) {
140 memcpy(ikm + 16, pin_hash, 32);
141 ikm_len = 16 + 32;
142 }
143
144 uint8_t salt[2];
145 salt[0] = static_cast<uint8_t>((slot_id >> 8) & 0xFF);
146 salt[1] = static_cast<uint8_t>(slot_id & 0xFF);
147
148 const mbedtls_md_info_t* md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
149 if (!md) {
150 mbedtls_platform_zeroize(ikm, sizeof(ikm));
151 return false;
152 }
153
154 int ret = mbedtls_hkdf(
155 md,
156 salt, sizeof(salt),
157 ikm, ikm_len,
158 reinterpret_cast<const uint8_t*>(HKDF_INFO), strlen(HKDF_INFO),
159 key_out, 32
160 );
161
162 mbedtls_platform_zeroize(ikm, sizeof(ikm));
163 return ret == 0;
164}
165
172static void build_aad(uint16_t slot_id, const uint8_t magic[MAGIC_SIZE], uint8_t aad_out[6]) {
173 aad_out[0] = static_cast<uint8_t>((slot_id >> 8) & 0xFF);
174 aad_out[1] = static_cast<uint8_t>(slot_id & 0xFF);
175 memcpy(aad_out + 2, magic, MAGIC_SIZE);
176}
177
183static uint16_t resolve_slot(uint16_t rel_index) {
184 return static_cast<uint16_t>(s_storage.rmemStart + rel_index);
185}
186
187void gpg_storage_set_slot_range(uint16_t eccStart, uint16_t eccEnd) {
188 s_storage.ready = false;
189 s_storage.eccStart = eccStart;
190 s_storage.eccEnd = eccEnd;
191
192 if (eccStart == 0 || eccEnd == 0 || eccStart > eccEnd || (eccEnd - eccStart + 1) < 3) {
193 return;
194 }
195
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);
199 s_storage.ready = true;
200}
201
202void gpg_storage_set_rmem_range(uint16_t rmemStart, uint16_t rmemEnd) {
203 s_storage.rmemStart = rmemStart;
204 s_storage.rmemEnd = rmemEnd;
205}
206
207bool gpg_storage_ready(void) { return s_storage.ready; }
208uint8_t gpg_storage_sig_slot(void) { return s_storage.sigSlot; }
209uint8_t gpg_storage_dec_slot(void) { return s_storage.decSlot; }
210uint8_t gpg_storage_aut_slot(void) { return s_storage.autSlot; }
211
223static bool save_slot_encrypted(uint16_t slot_id,
224 const uint8_t magic[MAGIC_SIZE],
225 const uint8_t* payload, size_t payload_len,
226 uint8_t* record_buf, size_t record_buf_len,
227 const char* pin) {
228 auto* se = get_se();
229 if (!se) return false;
230 if (record_buf_len < MAGIC_SIZE + NONCE_SIZE + payload_len + TAG_SIZE) return false;
231
232 uint8_t pin_hash[32];
233 bool have_pin = (pin && pin[0] != '\0');
234 if (have_pin && !pin_to_hash(pin, pin_hash)) {
235 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
236 return false;
237 }
238
239 uint8_t enc_key[32];
240 bool ok = derive_storage_key(slot_id, have_pin ? pin_hash : nullptr, enc_key);
241 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
242 if (!ok) {
243 secureWipe(enc_key);
244 return false;
245 }
246
247 uint8_t* p_magic = record_buf;
248 uint8_t* p_nonce = record_buf + MAGIC_SIZE;
249 uint8_t* p_ct = record_buf + MAGIC_SIZE + NONCE_SIZE;
250 uint8_t* p_tag = p_ct + payload_len;
251
252 memcpy(p_magic, magic, MAGIC_SIZE);
253 if (!se->getRandomStrict(p_nonce, NONCE_SIZE)) {
254 secureWipe(enc_key);
255 LOG_E(TAG, "Cannot get hardware entropy for nonce on slot %u", slot_id);
256 return false;
257 }
258
259 uint8_t aad[6];
260 build_aad(slot_id, magic, aad);
261
262 bool enc_ok = cdc::core::aesGcm256Seal(
263 enc_key, p_nonce, NONCE_SIZE,
264 aad, sizeof(aad),
265 payload, payload_len,
266 p_ct, p_tag);
267 secureWipe(enc_key);
268 if (!enc_ok) {
269 LOG_E(TAG, "GCM encrypt failed");
270 return false;
271 }
272
273 se->rmemErase(slot_id);
274 size_t total_len = MAGIC_SIZE + NONCE_SIZE + payload_len + TAG_SIZE;
275 if (se->rmemWrite(slot_id, record_buf, static_cast<uint16_t>(total_len)) != cdc::hal::SeResult::OK) {
276 LOG_E(TAG, "rmemWrite slot %u failed", slot_id);
277 return false;
278 }
279 return true;
280}
281
291static bool load_slot_decrypted(uint16_t slot_id,
292 const uint8_t magic[MAGIC_SIZE],
293 uint8_t* payload_out, size_t payload_len,
294 const char* pin) {
295 auto* se = get_se();
296 if (!se) return false;
297
298 uint8_t buf[128];
299 uint16_t buf_len = 0;
300 size_t expected = MAGIC_SIZE + NONCE_SIZE + payload_len + TAG_SIZE;
301 if (expected > sizeof(buf)) return false;
302
303 if (se->rmemRead(slot_id, buf, sizeof(buf), &buf_len) != cdc::hal::SeResult::OK || buf_len < expected) {
304 return false;
305 }
306 if (memcmp(buf, magic, MAGIC_SIZE) != 0) {
307 return false;
308 }
309
310 uint8_t pin_hash[32];
311 bool have_pin = (pin && pin[0] != '\0');
312 if (have_pin && !pin_to_hash(pin, pin_hash)) {
313 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
314 mbedtls_platform_zeroize(buf, sizeof(buf));
315 return false;
316 }
317
318 uint8_t dec_key[32];
319 bool ok = derive_storage_key(slot_id, have_pin ? pin_hash : nullptr, dec_key);
320 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
321 if (!ok) {
322 secureWipe(dec_key);
323 mbedtls_platform_zeroize(buf, sizeof(buf));
324 return false;
325 }
326
327 const uint8_t* p_nonce = buf + MAGIC_SIZE;
328 const uint8_t* p_ct = buf + MAGIC_SIZE + NONCE_SIZE;
329 const uint8_t* p_tag = p_ct + payload_len;
330
331 uint8_t aad[6];
332 build_aad(slot_id, magic, aad);
333
334 bool dec_ok = cdc::core::aesGcm256Open(
335 dec_key, p_nonce, NONCE_SIZE,
336 aad, sizeof(aad),
337 p_ct, payload_len,
338 p_tag, payload_out);
339 secureWipe(dec_key);
340 mbedtls_platform_zeroize(buf, sizeof(buf));
341 if (!dec_ok) {
342 mbedtls_platform_zeroize(payload_out, payload_len);
343 return false;
344 }
345 return true;
346}
347
348bool gpg_storage_save_dec_privkey(const uint8_t* privkey, const char* pin) {
349 if (!privkey) return false;
350 uint16_t slot = resolve_slot(RMEM_SLOT_DEC_KEY);
351 uint8_t record[DEC_TOTAL_SIZE];
352 bool ok = save_slot_encrypted(slot, DEC_KEY_MAGIC, privkey, PRIVKEY_SIZE,
353 record, sizeof(record), pin);
354 secureWipe(record);
355 if (ok) {
356 LOG_I(TAG, "Saved DEC private key (slot %u)", slot);
357 }
358 return ok;
359}
360
361bool gpg_storage_load_dec_privkey(uint8_t* privkey_out, const char* pin) {
362 if (!privkey_out) return false;
363 uint16_t slot = resolve_slot(RMEM_SLOT_DEC_KEY);
364 return load_slot_decrypted(slot, DEC_KEY_MAGIC, privkey_out, PRIVKEY_SIZE, pin);
365}
366
368 auto* se = get_se();
369 if (!se) return false;
370 uint16_t slot = resolve_slot(RMEM_SLOT_DEC_KEY);
371 uint8_t buf[DEC_TOTAL_SIZE];
372 uint16_t buf_len = 0;
373 if (se->rmemRead(slot, buf, sizeof(buf), &buf_len) != cdc::hal::SeResult::OK || buf_len < MAGIC_SIZE) {
374 return false;
375 }
376 return memcmp(buf, DEC_KEY_MAGIC, MAGIC_SIZE) == 0;
377}
378
380 auto* se = get_se();
381 if (!se) return false;
382 uint16_t slot = resolve_slot(RMEM_SLOT_DEC_KEY);
383 return se->rmemErase(slot) == cdc::hal::SeResult::OK;
384}
385
386bool gpg_storage_save_aes_key(const uint8_t* key, size_t key_len, const char* pin) {
387 if (!key) return false;
388 if (key_len != 16 && key_len != 32) return false;
389 uint16_t slot = resolve_slot(RMEM_SLOT_AES_KEY);
390
391 uint8_t payload[AES_RECORD_PAYLOAD];
392 payload[0] = static_cast<uint8_t>(key_len);
393 memcpy(payload + 1, key, key_len);
394 if (key_len < AES_MAX_KEY_SIZE) {
395 memset(payload + 1 + key_len, 0, AES_MAX_KEY_SIZE - key_len);
396 }
397
398 uint8_t record[AES_TOTAL_SIZE];
399 bool ok = save_slot_encrypted(slot, AES_KEY_MAGIC, payload, sizeof(payload),
400 record, sizeof(record), pin);
401 secureWipe(payload);
402 secureWipe(record);
403 if (ok) {
404 LOG_I(TAG, "Saved AES key (slot %u, %zu bytes)", slot, key_len);
405 }
406 return ok;
407}
408
409bool gpg_storage_load_aes_key(uint8_t* key_out, size_t* key_len_out, const char* pin) {
410 if (!key_out || !key_len_out) return false;
411 uint16_t slot = resolve_slot(RMEM_SLOT_AES_KEY);
412
413 uint8_t payload[AES_RECORD_PAYLOAD];
414 if (!load_slot_decrypted(slot, AES_KEY_MAGIC, payload, sizeof(payload), pin)) {
415 return false;
416 }
417 size_t len = payload[0];
418 if (len != 16 && len != 32) {
419 secureWipe(payload);
420 return false;
421 }
422 memcpy(key_out, payload + 1, len);
423 *key_len_out = len;
424 secureWipe(payload);
425 return true;
426}
427
429 auto* se = get_se();
430 if (!se) return false;
431 uint16_t slot = resolve_slot(RMEM_SLOT_AES_KEY);
432 uint8_t buf[MAGIC_SIZE];
433 uint16_t buf_len = 0;
434 if (se->rmemRead(slot, buf, sizeof(buf), &buf_len) != cdc::hal::SeResult::OK || buf_len < MAGIC_SIZE) {
435 return false;
436 }
437 return memcmp(buf, AES_KEY_MAGIC, MAGIC_SIZE) == 0;
438}
439
441 auto* se = get_se();
442 if (!se) return false;
443 uint16_t slot = resolve_slot(RMEM_SLOT_AES_KEY);
444 return se->rmemErase(slot) == cdc::hal::SeResult::OK;
445}
446
448static constexpr uint8_t RSA_KEY_MAGIC[MAGIC_SIZE] = {'R', 'S', 'A', '1'};
449
450static constexpr uint16_t RSA_SLOTS_PER_ROLE = 2;
451static constexpr uint8_t RSA_ROLE_COUNT = 3;
452static constexpr size_t RSA_REC_HEADER = 2; // u16 LE record-body length prefix
453static constexpr size_t RSA_ENVELOPE = MAGIC_SIZE + NONCE_SIZE + TAG_SIZE;
455
456static uint16_t s_rsa_rmem_start = 0;
457static uint16_t s_rsa_rmem_end = 0;
458
459// Single record scratch shared by the (single-threaded) RSA save/load path.
460EXT_RAM_BSS_ATTR static uint8_t s_rsa_record[RSA_REC_HEADER + GPG_RSA_BLOB_MAX + RSA_ENVELOPE];
461
462void gpg_storage_set_rsa_slot_range(uint16_t start, uint16_t end) {
463 s_rsa_rmem_start = start;
464 s_rsa_rmem_end = end;
465}
466
474static bool rsa_role_slots(uint8_t role, uint16_t* slot0, uint16_t* slot1) {
475 if (role >= RSA_ROLE_COUNT || s_rsa_rmem_start == 0) return false;
476 uint16_t base = static_cast<uint16_t>(s_rsa_rmem_start + role * RSA_SLOTS_PER_ROLE);
477 if (base + 1 > s_rsa_rmem_end) return false;
478 *slot0 = base;
479 *slot1 = static_cast<uint16_t>(base + 1);
480 return true;
481}
482
483bool gpg_storage_save_rsa_key(uint8_t role, const uint8_t* blob, size_t blob_len, const char* pin) {
484 if (!blob || blob_len == 0 || blob_len > GPG_RSA_BLOB_MAX) return false;
485 uint16_t slot0 = 0, slot1 = 0;
486 if (!rsa_role_slots(role, &slot0, &slot1)) return false;
487 auto* se = get_se();
488 if (!se) return false;
489
490 uint8_t pin_hash[32];
491 bool have_pin = (pin && pin[0] != '\0');
492 if (have_pin && !pin_to_hash(pin, pin_hash)) {
493 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
494 return false;
495 }
496 uint8_t enc_key[32];
497 bool ok = derive_storage_key(slot0, have_pin ? pin_hash : nullptr, enc_key);
498 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
499 if (!ok) {
500 secureWipe(enc_key);
501 return false;
502 }
503
504 const size_t rec_body = RSA_ENVELOPE + blob_len; // magic + nonce + ct + tag
505 const size_t total = RSA_REC_HEADER + rec_body;
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);
508 uint8_t* p_magic = s_rsa_record + RSA_REC_HEADER;
509 uint8_t* p_nonce = p_magic + MAGIC_SIZE;
510 uint8_t* p_ct = p_nonce + NONCE_SIZE;
511 uint8_t* p_tag = p_ct + blob_len;
512 memcpy(p_magic, RSA_KEY_MAGIC, MAGIC_SIZE);
513 if (!se->getRandomStrict(p_nonce, NONCE_SIZE)) {
514 secureWipe(enc_key);
515 return false;
516 }
517 uint8_t aad[6];
518 build_aad(slot0, RSA_KEY_MAGIC, aad);
519 bool enc_ok = cdc::core::aesGcm256Seal(enc_key, p_nonce, NONCE_SIZE, aad, sizeof(aad),
520 blob, blob_len, p_ct, p_tag);
521 secureWipe(enc_key);
522 if (!enc_ok) {
523 mbedtls_platform_zeroize(s_rsa_record, total);
524 return false;
525 }
526
527 const size_t c0 = (total > RSA_SLOT_CAP) ? RSA_SLOT_CAP : total;
528 se->rmemErase(slot0);
529 se->rmemErase(slot1);
530 bool wrote = se->rmemWrite(slot0, s_rsa_record, static_cast<uint16_t>(c0)) == cdc::hal::SeResult::OK;
531 if (wrote && total > c0) {
532 wrote = se->rmemWrite(slot1, s_rsa_record + c0,
533 static_cast<uint16_t>(total - c0)) == cdc::hal::SeResult::OK;
534 }
535 mbedtls_platform_zeroize(s_rsa_record, total);
536 if (!wrote) {
537 LOG_E(TAG, "RSA key write failed (role %u)", role);
538 return false;
539 }
540 LOG_I(TAG, "Saved RSA key (role %u, %zu bytes, slots %u/%u)", role, blob_len, slot0, slot1);
541 return true;
542}
543
544bool gpg_storage_load_rsa_key(uint8_t role, uint8_t* blob_out, size_t blob_cap,
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;
548 if (!rsa_role_slots(role, &slot0, &slot1)) return false;
549 auto* se = get_se();
550 if (!se) return false;
551
552 uint16_t l0 = 0;
553 if (se->rmemRead(slot0, s_rsa_record, sizeof(s_rsa_record), &l0) != cdc::hal::SeResult::OK ||
555 return false;
556 }
557 const size_t rec_body = static_cast<size_t>(s_rsa_record[0]) |
558 (static_cast<size_t>(s_rsa_record[1]) << 8);
559 const size_t total = RSA_REC_HEADER + rec_body;
560 if (rec_body < RSA_ENVELOPE || total > sizeof(s_rsa_record)) {
561 return false;
562 }
563 size_t have = l0;
564 if (total > l0) {
565 uint16_t l1 = 0;
566 if (se->rmemRead(slot1, s_rsa_record + l0,
567 static_cast<uint16_t>(sizeof(s_rsa_record) - l0), &l1) != cdc::hal::SeResult::OK) {
568 return false;
569 }
570 have = static_cast<size_t>(l0) + l1;
571 }
572 if (have < total) return false;
574 return false;
575 }
576 const size_t blob_len = rec_body - RSA_ENVELOPE;
577 if (blob_len == 0 || blob_len > GPG_RSA_BLOB_MAX || blob_len > blob_cap) {
578 return false;
579 }
580
581 uint8_t pin_hash[32];
582 bool have_pin = (pin && pin[0] != '\0');
583 if (have_pin && !pin_to_hash(pin, pin_hash)) {
584 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
585 mbedtls_platform_zeroize(s_rsa_record, total);
586 return false;
587 }
588 uint8_t dec_key[32];
589 bool ok = derive_storage_key(slot0, have_pin ? pin_hash : nullptr, dec_key);
590 mbedtls_platform_zeroize(pin_hash, sizeof(pin_hash));
591 if (!ok) {
592 secureWipe(dec_key);
593 mbedtls_platform_zeroize(s_rsa_record, total);
594 return false;
595 }
596
597 const uint8_t* p_nonce = s_rsa_record + RSA_REC_HEADER + MAGIC_SIZE;
598 const uint8_t* p_ct = p_nonce + NONCE_SIZE;
599 const uint8_t* p_tag = p_ct + blob_len;
600 uint8_t aad[6];
601 build_aad(slot0, RSA_KEY_MAGIC, aad);
602 bool dec_ok = cdc::core::aesGcm256Open(dec_key, p_nonce, NONCE_SIZE, aad, sizeof(aad),
603 p_ct, blob_len, p_tag, blob_out);
604 secureWipe(dec_key);
605 mbedtls_platform_zeroize(s_rsa_record, total);
606 if (!dec_ok) {
607 mbedtls_platform_zeroize(blob_out, blob_len);
608 return false;
609 }
610 *blob_len_out = blob_len;
611 return true;
612}
613
614bool gpg_storage_has_rsa_key(uint8_t role) {
615 uint16_t slot0 = 0, slot1 = 0;
616 if (!rsa_role_slots(role, &slot0, &slot1)) return false;
617 auto* se = get_se();
618 if (!se) return false;
619 uint8_t buf[RSA_REC_HEADER + MAGIC_SIZE];
620 uint16_t buf_len = 0;
621 if (se->rmemRead(slot0, buf, sizeof(buf), &buf_len) != cdc::hal::SeResult::OK ||
622 buf_len < sizeof(buf)) {
623 return false;
624 }
625 return memcmp(buf + RSA_REC_HEADER, RSA_KEY_MAGIC, MAGIC_SIZE) == 0;
626}
627
628bool gpg_storage_delete_rsa_key(uint8_t role) {
629 uint16_t slot0 = 0, slot1 = 0;
630 if (!rsa_role_slots(role, &slot0, &slot1)) return false;
631 auto* se = get_se();
632 if (!se) return false;
633 bool a = se->rmemErase(slot0) == cdc::hal::SeResult::OK;
634 bool b = se->rmemErase(slot1) == cdc::hal::SeResult::OK;
635 return a && b;
636}
637
638void gpg_storage_set_session_pin(const char* pin) {
639 if (!pin) {
641 return;
642 }
643 uint8_t hash[32];
644 if (pin_to_hash(pin, hash)) {
645 memcpy(s_storage.sessionKey, hash, sizeof(hash));
646 s_storage.sessionActive = true;
647 }
648 mbedtls_platform_zeroize(hash, sizeof(hash));
649}
650
651bool gpg_storage_get_session_key(uint8_t* key_out) {
652 if (!s_storage.sessionActive || !key_out) {
653 return false;
654 }
655 memcpy(key_out, s_storage.sessionKey, 32);
656 return true;
657}
658
660 mbedtls_platform_zeroize(s_storage.sessionKey, sizeof(s_storage.sessionKey));
661 s_storage.sessionActive = false;
662}
static const char * TAG
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).
Definition GpgStorage.h:64
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
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.
Definition Crypto.h:48
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.
Definition Crypto.h:79
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]