Skip to content

Commit 17a4550

Browse files
committed
keystore: copy instead of returning static pointer
Currently the seed is in RAM in plaintext after unlock. We want to encrypt it instead, using the secure chip. This refactors the _get_seed and _get_bip39_seed functions to not simply return the static pointer, but a copy, allowing us to perform decryption later once the seeds are encrypted in RAM.
1 parent 6f94c6f commit 17a4550

File tree

1 file changed

+46
-36
lines changed

1 file changed

+46
-36
lines changed

src/keystore.c

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ static bool _is_unlocked_device = false;
3939
// Must be defined if is_unlocked is true. Length of the seed store in `_retained_seed`. See also:
4040
// `_validate_seed_length()`.
4141
static size_t _seed_length = 0;
42-
// Must be defined if is_unlocked is true. ONLY ACCESS THIS WITH _get_seed()
42+
// Must be defined if is_unlocked is true. ONLY ACCESS THIS WITH keystore_copy_seed().
4343
static uint8_t _retained_seed[KEYSTORE_MAX_SEED_LENGTH] = {0};
4444

4545
// Change this ONLY via keystore_unlock_bip39().
4646
static bool _is_unlocked_bip39 = false;
47-
// Must be defined if _is_unlocked is true. ONLY ACCESS THIS WITH _get_bip39_seed().
47+
// Must be defined if _is_unlocked is true. ONLY ACCESS THIS WITH _copy_bip39_seed().
4848
static uint8_t _retained_bip39_seed[64] = {0};
4949

5050
#ifdef TESTING
@@ -70,40 +70,36 @@ static bool _validate_seed_length(size_t seed_len)
7070
return seed_len == 16 || seed_len == 24 || seed_len == 32;
7171
}
7272

73-
static const uint8_t* _get_seed(void)
74-
{
75-
if (!_is_unlocked_device) {
76-
return NULL;
77-
}
78-
return _retained_seed;
79-
}
80-
8173
bool keystore_copy_seed(uint8_t* seed_out, size_t* length_out)
8274
{
83-
if (_get_seed() == NULL) {
75+
if (!_is_unlocked_device) {
8476
return false;
8577
}
86-
memcpy(seed_out, _get_seed(), _seed_length);
78+
memcpy(seed_out, _retained_seed, _seed_length);
8779
*length_out = _seed_length;
8880
return true;
8981
}
9082

9183
/**
92-
* @return the pointer ot the static bip39 seed on success. returns NULL if the
93-
* keystore is locked.
84+
* Copies the retained bip39 seed into the given buffer. The caller must
85+
* zero the seed with util_zero once it is no longer needed.
86+
* @param[out] bip39_seed_out The seed bytes copied from the retained bip39 seed.
87+
* The buffer must be 64 bytes long.
88+
* @return true if the bip39 seed is available.
9489
*/
95-
static const uint8_t* _get_bip39_seed(void)
90+
static bool _copy_bip39_seed(uint8_t* bip39_seed_out)
9691
{
9792
if (!_is_unlocked_bip39) {
98-
return NULL;
93+
return false;
9994
}
10095
// sanity check
10196
uint8_t zero[64] = {0};
10297
util_zero(zero, 64);
10398
if (MEMEQ(_retained_bip39_seed, zero, sizeof(_retained_bip39_seed))) {
104-
return NULL;
99+
return false;
105100
}
106-
return _retained_bip39_seed;
101+
memcpy(bip39_seed_out, _retained_bip39_seed, sizeof(_retained_bip39_seed));
102+
return true;
107103
}
108104

109105
/**
@@ -382,13 +378,17 @@ bool keystore_unlock_bip39(const char* mnemonic_passphrase)
382378
if (!_is_unlocked_device) {
383379
return false;
384380
}
385-
const uint8_t* seed = _get_seed();
386-
if (seed == NULL) {
387-
return false;
388-
}
389381
char* mnemonic __attribute__((__cleanup__(_free_string))) = NULL;
390-
if (bip39_mnemonic_from_bytes(NULL, seed, _seed_length, &mnemonic) != WALLY_OK) {
391-
return false;
382+
{ // block so that `seed` is zeroed as soon as possible
383+
uint8_t seed[KEYSTORE_MAX_SEED_LENGTH] = {0};
384+
UTIL_CLEANUP_32(seed);
385+
size_t seed_length = 0;
386+
if (!keystore_copy_seed(seed, &seed_length)) {
387+
return false;
388+
}
389+
if (bip39_mnemonic_from_bytes(NULL, seed, seed_length, &mnemonic) != WALLY_OK) {
390+
return false;
391+
}
392392
}
393393
uint8_t bip39_seed[BIP39_SEED_LEN_512] = {0};
394394
UTIL_CLEANUP_64(bip39_seed);
@@ -421,13 +421,17 @@ bool keystore_get_bip39_mnemonic(char* mnemonic_out, size_t mnemonic_out_size)
421421
if (keystore_is_locked()) {
422422
return false;
423423
}
424-
const uint8_t* seed = _get_seed();
425-
if (seed == NULL) {
426-
return false;
427-
}
428424
char* mnemonic = NULL;
429-
if (bip39_mnemonic_from_bytes(NULL, seed, _seed_length, &mnemonic) != WALLY_OK) {
430-
return false;
425+
{ // block so that `seed` is zeroed as soon as possible
426+
uint8_t seed[KEYSTORE_MAX_SEED_LENGTH] = {0};
427+
UTIL_CLEANUP_32(seed);
428+
size_t seed_length = 0;
429+
if (!keystore_copy_seed(seed, &seed_length)) {
430+
return false;
431+
}
432+
if (bip39_mnemonic_from_bytes(NULL, seed, seed_length, &mnemonic) != WALLY_OK) {
433+
return false;
434+
}
431435
}
432436
int snprintf_result = snprintf(mnemonic_out, mnemonic_out_size, "%s", mnemonic);
433437
util_cleanup_str(&mnemonic);
@@ -445,8 +449,10 @@ static bool _get_xprv(const uint32_t* keypath, const size_t keypath_len, struct
445449
if (keystore_is_locked()) {
446450
return false;
447451
}
448-
const uint8_t* bip39_seed = _get_bip39_seed();
449-
if (bip39_seed == NULL) {
452+
453+
uint8_t bip39_seed[64] = {0};
454+
UTIL_CLEANUP_64(bip39_seed);
455+
if (!_copy_bip39_seed(bip39_seed)) {
450456
return false;
451457
}
452458
struct ext_key xprv_master __attribute__((__cleanup__(keystore_zero_xkey))) = {0};
@@ -456,6 +462,7 @@ static bool _get_xprv(const uint32_t* keypath, const size_t keypath_len, struct
456462
WALLY_OK) {
457463
return false;
458464
}
465+
util_zero(bip39_seed, sizeof(bip39_seed));
459466
if (keypath_len == 0) {
460467
*xprv_out = xprv_master;
461468
} else if (
@@ -621,8 +628,9 @@ bool keystore_get_u2f_seed(uint8_t* seed_out)
621628
if (keystore_is_locked()) {
622629
return false;
623630
}
624-
const uint8_t* bip39_seed = _get_bip39_seed();
625-
if (bip39_seed == NULL) {
631+
uint8_t bip39_seed[64] = {0};
632+
UTIL_CLEANUP_64(bip39_seed);
633+
if (!_copy_bip39_seed(bip39_seed)) {
626634
return false;
627635
}
628636
const uint8_t message[] = "u2f";
@@ -635,8 +643,9 @@ bool keystore_get_u2f_seed(uint8_t* seed_out)
635643

636644
bool keystore_get_ed25519_seed(uint8_t* seed_out)
637645
{
638-
const uint8_t* bip39_seed = _get_bip39_seed();
639-
if (bip39_seed == NULL) {
646+
uint8_t bip39_seed[64] = {0};
647+
UTIL_CLEANUP_64(bip39_seed);
648+
if (!_copy_bip39_seed(bip39_seed)) {
640649
return false;
641650
}
642651

@@ -659,6 +668,7 @@ bool keystore_get_ed25519_seed(uint8_t* seed_out)
659668
uint8_t message[65] = {0};
660669
message[0] = 0x01;
661670
memcpy(&message[1], bip39_seed, 64);
671+
util_zero(bip39_seed, sizeof(bip39_seed));
662672
if (wally_hmac_sha256(key, sizeof(key), message, sizeof(message), &seed_out[64], 32) !=
663673
WALLY_OK) {
664674
util_zero(message, sizeof(message));

0 commit comments

Comments
 (0)