Skip to content

Commit df23172

Browse files
committed
Reject altered leading zero bytes in BTC addresses
1 parent 22786cc commit df23172

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

‎src/validators/crypto_addresses/btc_address.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@
99

1010

1111
def _decode_base58(addr: str):
12-
"""Decode base58."""
12+
"""Decode base58, preserving each leading one as a zero byte."""
1313
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
14-
return sum((58**enm) * alphabet.index(idx) for enm, idx in enumerate(addr[::-1]))
14+
decoded = sum((58**enm) * alphabet.index(idx) for enm, idx in enumerate(addr[::-1]))
15+
zero_bytes = len(addr) - len(addr.lstrip("1"))
16+
return b"\0" * zero_bytes + decoded.to_bytes((decoded.bit_length() + 7) // 8, "big")
1517

1618

1719
def _validate_old_btc_address(addr: str):
1820
"""Validate P2PKH and P2SH type address."""
1921
if len(addr) not in range(25, 35):
2022
return False
21-
decoded_bytes = _decode_base58(addr).to_bytes(25, "big")
23+
decoded_bytes = _decode_base58(addr)
24+
if len(decoded_bytes) != 25:
25+
return False
2226
header, checksum = decoded_bytes[:-4], decoded_bytes[-4:]
2327
return checksum == sha256(sha256(header).digest()).digest()[:4]
2428

‎tests/crypto_addresses/test_btc_address.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
[
1313
# P2PKH (Pay-to-PubkeyHash) type
1414
"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
15+
# P2PKH with zero bytes in the payload, encoded as leading ones
16+
"1111111111111111111114oLvT2",
17+
# Legacy testnet P2PKH
18+
"mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn",
1519
# P2SH (Pay to script hash) type
1620
"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy",
1721
# Bech32/segwit type
@@ -41,3 +45,18 @@ def test_returns_true_on_valid_btc_address(value: str):
4145
def test_returns_failed_validation_on_invalid_btc_address(value: str):
4246
"""Test returns failed validation on invalid btc address."""
4347
assert isinstance(btc_address(value), ValidationError)
48+
49+
50+
@pytest.mark.parametrize(
51+
"value",
52+
[
53+
# Removing the version byte's leading one must not be silently padded.
54+
"BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
55+
# Both remain within the existing character-count bounds.
56+
"111111111111111111114oLvT2",
57+
"11111111111111111111114oLvT2",
58+
],
59+
)
60+
def test_rejects_changed_leading_zero_bytes(value: str):
61+
"""Leading ones encode bytes and cannot be added or removed."""
62+
assert isinstance(btc_address(value), ValidationError)

0 commit comments

Comments
 (0)