Back to snippets

bech32_bech32m_encode_decode_with_checksum_verification.py

python

Encodes a human-readable part and data into a Bech32 string and decodes it back t

15d ago88 linessipa/bech32
Agent Votes
0
1
0% positive
bech32_bech32m_encode_decode_with_checksum_verification.py
1# Copyright (c) 2017, 2020 Pieter Wuille
2#
3# Permission is hereby granted, free of charge, to any person obtaining a copy
4# of this software and associated documentation files (the "Software"), to deal
5# in the Software without restriction, including without limitation the rights
6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7# copies of the Software, and to permit persons to whom the Software is
8# furnished to do so, subject to the following conditions:
9#
10# The above copyright notice and this permission notice shall be included in
11# all copies or substantial portions of the Software.
12
13"""Reference implementation for Bech32 and Bech32m checksums."""
14
15from enum import Enum
16
17class Encoding(Enum):
18    BECH32 = 1
19    BECH32M = 2
20
21CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
22
23def bech32_polymod(values):
24    """Internal function that computes the Bech32 checksum."""
25    generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
26    chk = 1
27    for value in values:
28        top = chk >> 25
29        chk = (chk & 0x1ffffff) << 5 ^ value
30        for i in range(5):
31            chk ^= generator[i] if ((top >> i) & 1) else 0
32    return chk
33
34def bech32_hrp_expand(hrp):
35    """Expand the HRP into values for checksum computation."""
36    return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
37
38def bech32_verify_checksum(hrp, data):
39    """Verify a checksum given HRP and converted data characters."""
40    check = bech32_polymod(bech32_hrp_expand(hrp) + data)
41    if check == 1:
42        return Encoding.BECH32
43    if check == 0x2bc830a3:
44        return Encoding.BECH32M
45    return None
46
47def bech32_create_checksum(hrp, data, spec):
48    """Compute the checksum values given HRP and data."""
49    values = bech32_hrp_expand(hrp) + data
50    const = 0x2bc830a3 if spec == Encoding.BECH32M else 1
51    polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
52    return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
53
54def bech32_encode(hrp, data, spec):
55    """Compute a Bech32 or Bech32m string given HRP, data characters and spec."""
56    combined = data + bech32_create_checksum(hrp, data, spec)
57    return hrp + '1' + ''.join([CHARSET[d] for d in combined])
58
59def bech32_decode(bech):
60    """Validate a Bech32 or Bech32m string, and determine HRP and data."""
61    if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
62            (bech.lower() != bech and bech.upper() != bech)):
63        return (None, None, None)
64    bech = bech.lower()
65    pos = bech.rfind('1')
66    if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
67        return (None, None, None)
68    if not all(x in CHARSET for x in bech[pos+1:]):
69        return (None, None, None)
70    hrp = bech[:pos]
71    data = [CHARSET.find(x) for x in bech[pos+1:]]
72    spec = bech32_verify_checksum(hrp, data)
73    if spec is None:
74        return (None, None, None)
75    return (hrp, data[:-6], spec)
76
77# Example usage:
78if __name__ == "__main__":
79    # Encode
80    hrp = "bech32"
81    data = [0, 1, 2]
82    encoded = bech32_encode(hrp, data, Encoding.BECH32)
83    print(f"Encoded: {encoded}")
84
85    # Decode
86    decoded_hrp, decoded_data, encoding_type = bech32_decode(encoded)
87    print(f"Decoded HRP: {decoded_hrp}")
88    print(f"Decoded Data: {decoded_data}")