pin: erase the pin privatekey when changing/erasing pin settings
What changed, and why it matters
This commit changes how Blockstream Jade handles a private key used for PIN server authentication. Previously, the device kept the same private key even when the user changed or removed their PIN server settings. Now it deletes that key whenever the PIN server settings change, so a fresh key is generated the next time it is needed. This reduces the risk that an old or compromised key could be reused across different PIN server configurations.
Treat as a security hardening improvement. Review whether the PIN private key is otherwise generated deterministically or randomly, and confirm that first-use re-creation logic exists and is invoked correctly. No urgent patch is required beyond applying the commit.
Security signals we found
Private key lifecycle tied to configuration change
Removal of standalone erase function in favor of inline erasure
Defensive key rotation on pinserver change/erase
No explicit vulnerability or CVE referenced in commit
Evidence from the diff
The patch removes the standalone storage_erase_pin_privatekey() function and instead erases PIN_PRIVATEKEY_FIELD directly inside storage_set_pinserver_details() and storage_erase_pinserver_details(). The commit message states this ensures a new private key is created on demand whenever the pinserver is changed or erased. The change is defensive: it prevents key reuse across pinserver configurations, which could matter if the key is tied to a specific server or if an attacker can influence pinserver settings. The diff itself does not show a clear active vulnerability, only a hardening measure.
Changed components
main/storage.cmain/storage.hPIN server private key storagePIN server configuration managementInspect captured patch +2 / −3
diff --git a/main/storage.c b/main/storage.c
index a181b15..ae22af0 100644
--- a/main/storage.c
+++ b/main/storage.c
@@ -441,8 +441,6 @@ bool storage_set_pin_privatekey(const uint8_t* privatekey, const size_t key_len)
return store_blob(DEFAULT_NAMESPACE, PIN_PRIVATEKEY_FIELD, privatekey, key_len);
}
-bool storage_erase_pin_privatekey(void) { return erase_key(DEFAULT_NAMESPACE, PIN_PRIVATEKEY_FIELD); }
-
bool storage_set_encrypted_blob(const uint8_t* encrypted, const size_t encrypted_len)
{
JADE_ASSERT(encrypted);
@@ -539,6 +537,7 @@ bool storage_set_pinserver_details(const char* urlA, const char* urlB, const uin
STORAGE_OPEN(handle, DEFAULT_NAMESPACE, NVS_READWRITE);
STORAGE_SET_STRING(handle, USER_PINSERVER_URL_A, urlA);
STORAGE_SET_STRING(handle, USER_PINSERVER_URL_B, urlB);
+ STORAGE_ERASE(handle, PIN_PRIVATEKEY_FIELD); // Re-create on first use later
// Pubkey is optional (as just server public address may change)
if (pubkey && pubkey_len > 0) {
@@ -572,6 +571,7 @@ bool storage_erase_pinserver_details(void)
STORAGE_ERASE(handle, USER_PINSERVER_URL_A);
STORAGE_ERASE(handle, USER_PINSERVER_URL_B);
STORAGE_ERASE(handle, USER_PINSERVER_PUBKEY);
+ STORAGE_ERASE(handle, PIN_PRIVATEKEY_FIELD); // Re-create on first use later
STORAGE_COMMIT(handle);
STORAGE_CLOSE(handle);
return true;
diff --git a/main/storage.h b/main/storage.h
index 1f3aa7b..2e3fbdc 100644
--- a/main/storage.h
+++ b/main/storage.h
@@ -41,7 +41,6 @@ bool storage_key_name_valid(const char* name);
bool storage_set_pin_privatekey(const uint8_t* privatekey, size_t key_len);
bool storage_get_pin_privatekey(uint8_t* privatekey, size_t key_len);
-bool storage_erase_pin_privatekey(void);
bool storage_set_encrypted_blob(const uint8_t* encrypted, size_t encrypted_len);
bool storage_get_encrypted_blob(uint8_t* encrypted, size_t encrypted_len, size_t* written);
Why this scored 42/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.