pin: disallow pin update on initialized units in debug mode
What changed, and why it matters
This commit removes a special debug-only exception that previously allowed the PIN server's public key to be changed on a Jade hardware wallet that already had a wallet set up. In normal operation, changing this public key on an initialized device is blocked to prevent an attacker from redirecting the device to a malicious PIN server and potentially unlocking or controlling the wallet. The debug exception created a security gap: if a debug-mode device (or an attacker able to trigger debug mode) was initialized, they could swap the PIN server key. The fix closes that gap by applying the same restriction regardless of debug mode. A legitimate developer who needs to change the key can still use debug_reset first.
Treat this as a security hardening fix and include it in the next firmware release. Ensure debug builds used in production-like environments (e.g., QA, refurbished devices, developer editions) are updated, since the old debug exception could have allowed pinserver key substitution on initialized units. Review whether any prior firmware versions shipped with debug mode enabled or accessible, and consider whether additional guidance is needed for users of debug firmware.
Security signals we found
Removal of debug-mode bypass for a security-critical authorization check
Protection against pinserver public key substitution on initialized devices
Defense-in-depth for wallet PIN/server trust binding
Commit explicitly frames change as security-relevant: 'disallow pin update on initialized units in debug mode'
Evidence from the diff
The change removes the #ifndef CONFIG_DEBUG_MODE / #endif guard around the check that blocks updating the pinserver public key when keychain_has_pin() returns true. The condition is also reordered from keychain_has_pin() && pubkey_changed to pubkey_changed && keychain_has_pin(), which is functionally equivalent but short-circuits on the cheaper comparison first. The effect is that the Cannot update initialized unit error now applies in debug builds as well as release builds. The commit message notes debug_reset can be used as a workaround, implying the debug path remains available but requires explicit reset rather than silently bypassing the initialized-unit protection.
Changed components
main/process/update_pinserver.cJade PIN server update flowDebug build configuration (CONFIG_DEBUG_MODE)keychain_has_pin() state checkInspect captured patch +1 / −3
### main/process/update_pinserver.c
@@ -128,16 +128,14 @@ int update_pinserver(const CborValue* const params, const char** errmsg)
const uint8_t* const new_pubkey = pubkey ? pubkey : (reset_details ? server_public_key_start : old_pubkey);
const bool pubkey_changed = memcmp(old_pubkey, new_pubkey, EC_PUBLIC_KEY_LEN) != 0;
-#ifndef CONFIG_DEBUG_MODE
// Check that we are not trying to update the pinserver pubkey on a Jade unit
// that already has a wallet set up/persisted in flash.
// NOTE: we do allow an update of just the url/certs, as this may be a url change
// that still connects to the same backend pinserver instance.
- if (keychain_has_pin() && pubkey_changed) {
+ if (pubkey_changed && keychain_has_pin()) {
*errmsg = "Cannot update initialized unit";
goto cleanup;
}
-#endif // CONFIG_DEBUG_MODE
const storage_pin_privkey_action_t privkey_action
= pubkey_changed ? STORAGE_PIN_ERASE_PRIVKEY : STORAGE_PIN_KEEP_PRIVKEY;Why this scored 59/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.