mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 12:28:09 +00:00
fix(python): replace base58 implementation with a more correct one
based on https://github.com/keis/base58/blob/master/base58/__init__.py
This commit is contained in:
parent
76490e6e5f
commit
0dff9390db
@ -126,59 +126,48 @@ __b58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|||||||
__b58base = len(__b58chars)
|
__b58base = len(__b58chars)
|
||||||
|
|
||||||
|
|
||||||
|
def b58encode_int(i: int) -> str:
|
||||||
|
"""Encode an integer using Base58"""
|
||||||
|
digits = []
|
||||||
|
while i:
|
||||||
|
i, idx = divmod(i, __b58base)
|
||||||
|
digits.append(__b58chars[idx])
|
||||||
|
return "".join(reversed(digits))
|
||||||
|
|
||||||
|
|
||||||
def b58encode(v: bytes) -> str:
|
def b58encode(v: bytes) -> str:
|
||||||
"""encode v, which is a string of bytes, to base58."""
|
"""encode v, which is a string of bytes, to base58."""
|
||||||
|
origlen = len(v)
|
||||||
|
v = v.lstrip(b"\0")
|
||||||
|
newlen = len(v)
|
||||||
|
|
||||||
long_value = 0
|
acc = int.from_bytes(v, byteorder="big") # first byte is most significant
|
||||||
for c in v:
|
|
||||||
long_value = long_value * 256 + c
|
|
||||||
|
|
||||||
result = ""
|
result = b58encode_int(acc)
|
||||||
while long_value >= __b58base:
|
return __b58chars[0] * (origlen - newlen) + result
|
||||||
div, mod = divmod(long_value, __b58base)
|
|
||||||
result = __b58chars[mod] + result
|
|
||||||
long_value = div
|
|
||||||
result = __b58chars[long_value] + result
|
|
||||||
|
|
||||||
# Bitcoin does a little leading-zero-compression:
|
|
||||||
# leading 0-bytes in the input become leading-1s
|
|
||||||
nPad = 0
|
|
||||||
for c in v:
|
|
||||||
if c == 0:
|
|
||||||
nPad += 1
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
return (__b58chars[0] * nPad) + result
|
def b58decode_int(v: str) -> int:
|
||||||
|
"""Decode a Base58 encoded string as an integer"""
|
||||||
|
decimal = 0
|
||||||
|
try:
|
||||||
|
for char in v:
|
||||||
|
decimal = decimal * __b58base + __b58chars.index(char)
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError(f"Invalid character {char!r}") from None # type: ignore [possibly unbound]
|
||||||
|
return decimal
|
||||||
|
|
||||||
|
|
||||||
def b58decode(v: AnyStr, length: Optional[int] = None) -> bytes:
|
def b58decode(v: AnyStr, length: Optional[int] = None) -> bytes:
|
||||||
"""decode v into a string of len bytes."""
|
"""decode v into a string of len bytes."""
|
||||||
str_v = v.decode() if isinstance(v, bytes) else v
|
v_str = v if isinstance(v, str) else v.decode()
|
||||||
|
origlen = len(v_str)
|
||||||
|
v_str = v_str.lstrip(__b58chars[0])
|
||||||
|
newlen = len(v_str)
|
||||||
|
|
||||||
for c in str_v:
|
acc = b58decode_int(v_str)
|
||||||
if c not in __b58chars:
|
|
||||||
raise ValueError("invalid Base58 string")
|
|
||||||
|
|
||||||
long_value = 0
|
result = acc.to_bytes(origlen - newlen + (acc.bit_length() + 7) // 8, "big")
|
||||||
for (i, c) in enumerate(str_v[::-1]):
|
|
||||||
long_value += __b58chars.find(c) * (__b58base**i)
|
|
||||||
|
|
||||||
result = b""
|
|
||||||
while long_value >= 256:
|
|
||||||
div, mod = divmod(long_value, 256)
|
|
||||||
result = struct.pack("B", mod) + result
|
|
||||||
long_value = div
|
|
||||||
result = struct.pack("B", long_value) + result
|
|
||||||
|
|
||||||
nPad = 0
|
|
||||||
for c in str_v:
|
|
||||||
if c == __b58chars[0]:
|
|
||||||
nPad += 1
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
result = b"\x00" * nPad + result
|
|
||||||
if length is not None and len(result) != length:
|
if length is not None and len(result) != length:
|
||||||
raise ValueError("Result length does not match expected_length")
|
raise ValueError("Result length does not match expected_length")
|
||||||
|
|
||||||
|
@ -49,3 +49,41 @@ VECTORS = ( # descriptor, checksum
|
|||||||
@pytest.mark.parametrize("descriptor, checksum", VECTORS)
|
@pytest.mark.parametrize("descriptor, checksum", VECTORS)
|
||||||
def test_descriptor_checksum(descriptor, checksum):
|
def test_descriptor_checksum(descriptor, checksum):
|
||||||
assert tools.descriptor_checksum(descriptor) == checksum
|
assert tools.descriptor_checksum(descriptor) == checksum
|
||||||
|
|
||||||
|
|
||||||
|
BASE58_VECTORS = ( # data_hex, encoding_b58
|
||||||
|
("", ""),
|
||||||
|
("61", "2g"),
|
||||||
|
("626262", "a3gV"),
|
||||||
|
("636363", "aPEr"),
|
||||||
|
("73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"),
|
||||||
|
(
|
||||||
|
"00eb15231dfceb60925886b67d065299925915aeb172c06647",
|
||||||
|
"1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L",
|
||||||
|
),
|
||||||
|
("516b6fcd0f", "ABnLTmg"),
|
||||||
|
("bf4f89001e670274dd", "3SEo3LWLoPntC"),
|
||||||
|
("572e4794", "3EFU7m"),
|
||||||
|
("ecac89cad93923c02321", "EJDM8drfXA6uyA"),
|
||||||
|
("10c8511e", "Rt5zm"),
|
||||||
|
("00000000000000000000", "1111111111"),
|
||||||
|
("00" * 32, "11111111111111111111111111111111"),
|
||||||
|
(
|
||||||
|
"000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5",
|
||||||
|
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
||||||
|
"1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("data_hex,encoding_b58", BASE58_VECTORS)
|
||||||
|
def test_b58encode(data_hex, encoding_b58):
|
||||||
|
assert tools.b58encode(bytes.fromhex(data_hex)) == encoding_b58
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("data_hex,encoding_b58", BASE58_VECTORS)
|
||||||
|
def test_b58decode(data_hex, encoding_b58):
|
||||||
|
assert tools.b58decode(encoding_b58).hex() == data_hex
|
||||||
|
Loading…
Reference in New Issue
Block a user