Add test case test_zero_fingerprint_handling
What changed, and why it matters
This commit only adds a new automated test to the project. It does not change any production code, so by itself it cannot introduce a security vulnerability or fix one. The test checks that the software can correctly handle PSBT files where wallet fingerprints are all zeros, which can happen when a wallet is created from an extended public key (XPUB) without a full derivation path. The test verifies the software can match the public keys against the seed and fill in the correct fingerprint.
No action required for security. Review the related production code change that this test presumably accompanies, if any, to assess whether the zero-fingerprint handling logic itself is sound.
Security signals we found
No production code changed
Test-only commit
Tests fallback key-matching behavior for zeroed BIP32 fingerprints
Evidence from the diff
The diff adds a single test method, test_zero_fingerprint_handling, to tests/test_psbt_parser.py. The test iterates over PSBT test inputs, replaces all BIP32 and Taproot derivation fingerprints with 0x00000000, then asserts that PSBTParser.has_matching_input_fingerprint still matches the correct seed and rejects a wrong seed. It also instantiates PSBTParser and asserts that zero fingerprints are replaced with the seed’s actual fingerprint. No library or application code is modified.
Changed components
tests/test_psbt_parser.pyInspect captured patch +68 / −0
diff --git a/tests/test_psbt_parser.py b/tests/test_psbt_parser.py
index 897b753..bdfdf86 100644
--- a/tests/test_psbt_parser.py
+++ b/tests/test_psbt_parser.py
@@ -149,6 +149,74 @@ class TestPSBTParser:
assert PSBTParser.has_matching_input_fingerprint(psbt, PSBTTestData.multisig_key_3)
+ def test_zero_fingerprint_handling(self):
+ """
+ PSBTParser should correctly handle PSBTs with zero fingerprints (created from XPUB-only imports,
+ without derivation path) by matching public keys against the seed and filling in correct fingerprints.
+ """
+ for input in PSBTTestData.ALL_INPUTS:
+ psbt = PSBT.parse(a2b_base64(input))
+
+ # Backup original derivations
+ original_derivations = []
+ original_taproot_derivations = []
+ for inp in psbt.inputs:
+ for pub, derivation in inp.bip32_derivations.items():
+ original_derivations.append((pub, derivation))
+
+ for pub, (leaf_hashes, derivation) in inp.taproot_bip32_derivations.items():
+ original_taproot_derivations.append((pub, leaf_hashes, derivation))
+
+ # Set fingerprints to zero to simulate XPUB-only import
+ from embit.psbt import DerivationPath
+ for inp in psbt.inputs:
+ for pub, derivation in inp.bip32_derivations.items():
+ inp.bip32_derivations[pub] = DerivationPath(
+ fingerprint=b"\x00\x00\x00\x00",
+ derivation=derivation.derivation
+ )
+
+ for pub, (leaf_hashes, derivation) in inp.taproot_bip32_derivations.items():
+ inp.taproot_bip32_derivations[pub] = (leaf_hashes, DerivationPath(
+ fingerprint=b"\x00\x00\x00\x00",
+ derivation=derivation.derivation
+ ))
+
+ # Verify that has_matching_input_fingerprint works with zero fingerprints
+ # This tests the fallback mechanism that tries to derive and match pubkeys
+ assert PSBTParser.has_matching_input_fingerprint(psbt, PSBTTestData.seed, SettingsConstants.REGTEST)
+
+ # Test that it correctly rejects wrong seeds
+ wrong_seed = Seed(["bacon"] * 24)
+ assert PSBTParser.has_matching_input_fingerprint(psbt, wrong_seed, SettingsConstants.REGTEST) == False
+
+ # Test the PSBTParser's ability to fill zero fingerprints during parsing
+ parser = PSBTParser(p=psbt, seed=PSBTTestData.seed, network=SettingsConstants.REGTEST)
+
+ # Verify fingerprints were correctly filled after parsing
+ seed_fingerprint = parser.seed.get_fingerprint(SettingsConstants.REGTEST)
+ fingerprints_filled = False
+
+ for inp in parser.psbt.inputs:
+ for pub, derivation in inp.bip32_derivations.items():
+ if derivation.fingerprint != b"\x00\x00\x00\x00":
+ fingerprints_filled = True
+ # Should match the seed's fingerprint
+ from binascii import hexlify
+ assert hexlify(derivation.fingerprint).decode() == seed_fingerprint
+
+ # Also check Taproot derivations
+ for pub, (leaf_hashes, derivation) in inp.taproot_bip32_derivations.items():
+ if derivation.fingerprint != b"\x00\x00\x00\x00":
+ fingerprints_filled = True
+ # Should match the seed's fingerprint
+ from binascii import hexlify
+ assert hexlify(derivation.fingerprint).decode() == seed_fingerprint
+
+ # Fingerprints should have been filled
+ assert fingerprints_filled > 0
+
+
def test_trim_and_sig_count(self):
"""
PSBTParser should correctly trim a psbt of all unnecessary data and count the number of
Why this scored 12/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.