bip32: reject master key with non-zero child/fingerprint
What changed, and why it matters
This commit tightens validation of Bitcoin wallet master keys. Under the BIP32 standard, a master key must have zero values for its 'child number' and 'parent fingerprint' fields. The change makes Electrum reject extended keys that violate this rule, preventing malformed or intentionally crafted master keys from being accepted. This is a defensive correctness fix rather than an active exploit patch.
Review whether other BIP32 deserialization paths or wallet import flows bypass from_xkey() and may need the same validation. Consider whether invalid master keys already accepted in existing wallets need migration handling.
Security signals we found
Input validation hardening for cryptographic key deserialization
Enforcement of BIP32 standard constraints on master key serialization
Use of known BIP32 test vectors (including test vector 5) for negative test cases
Potential defense against key confusion or fingerprint/child manipulation attacks
Evidence from the diff
The patch adds a validation check in electrum/bip32.py’s from_xkey(): if depth == 0, both child_number and fingerprint must be 4 zero bytes. If not, it raises BitcoinException. Tests are added using BIP32 test vector 5 and related vectors that have depth 0 with non-zero fingerprint or child index. This enforces BIP32 serialization rules for master keys.
Changed components
electrum/bip32.pyBIP32Node.from_xkey()tests/test_bitcoin.pyInspect captured patch +16 / −0
### electrum/bip32.py
@@ -154,6 +154,9 @@ def from_xkey(
xtype = headers_inv[header]
if not allow_custom_headers and xtype != "standard":
raise ValueError(f"only standard xpub/xprv allowed. found custom xtype={xtype}")
+ if depth == 0 and (child_number != bytes(4) or fingerprint != bytes(4)):
+ raise BitcoinException('Invalid extended key: a depth-0 (master) key must have '
+ 'zero child number and zero parent fingerprint')
if is_private:
if xkey[13 + 32] != 0:
raise BitcoinException('Invalid extended private key: '
### tests/test_bitcoin.py
@@ -776,6 +776,19 @@ def test_bip32_from_xkey_private_key_bad_prefix(self):
with self.assertRaises(BitcoinException):
BIP32Node.from_xkey(invalid)
+ def test_bip32_from_xkey_depth0_with_nonzero_child_or_fingerprint(self):
+ # BIP32 requires a depth-0 (master) key to have child number 0 and parent fingerprint 0.
+ for invalid in (
+ # BIP32 test vector 5: zero depth with non-zero parent fingerprint
+ "xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv",
+ "xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ",
+ # BIP32 test vector: zero depth with non-zero index
+ "xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN",
+ "xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8",
+ ):
+ with self.assertRaises(BitcoinException):
+ BIP32Node.from_xkey(invalid)
+
def test_is_bip32_derivation(self):
self.assertTrue(is_bip32_derivation("m/0'/1"))
self.assertTrue(is_bip32_derivation("m/0'/0'"))Why this scored 47/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.