1- // Copyright (c) 2017 Pieter Wuille
1+ // Copyright (c) 2017, 2021 Pieter Wuille
22// Distributed under the MIT software license, see the accompanying
33// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
77
88#include < assert.h>
99
10+ namespace bech32
11+ {
12+
1013namespace
1114{
1215
@@ -27,6 +30,12 @@ const int8_t CHARSET_REV[128] = {
2730 1 , 0 , 3 , 16 , 11 , 28 , 12 , 14 , 6 , 4 , 2 , -1 , -1 , -1 , -1 , -1
2831};
2932
33+ /* Determine the final constant to use for the specified encoding. */
34+ uint32_t EncodingConstant (Encoding encoding) {
35+ assert (encoding == Encoding::BECH32 || encoding == Encoding::BECH32M);
36+ return encoding == Encoding::BECH32 ? 1 : 0x2bc830a3 ;
37+ }
38+
3039/* * This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
3140 * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
3241 * bits correspond to earlier values. */
@@ -111,21 +120,24 @@ data ExpandHRP(const std::string& hrp)
111120}
112121
113122/* * Verify a checksum. */
114- bool VerifyChecksum (const std::string& hrp, const data& values)
123+ Encoding VerifyChecksum (const std::string& hrp, const data& values)
115124{
116125 // PolyMod computes what value to xor into the final values to make the checksum 0. However,
117126 // if we required that the checksum was 0, it would be the case that appending a 0 to a valid
118127 // list of values would result in a new valid list. For that reason, Bech32 requires the
119- // resulting checksum to be 1 instead.
120- return PolyMod (Cat (ExpandHRP (hrp), values)) == 1 ;
128+ // resulting checksum to be 1 instead. In Bech32m, this constant was amended.
129+ const uint32_t check = PolyMod (Cat (ExpandHRP (hrp), values));
130+ if (check == EncodingConstant (Encoding::BECH32)) return Encoding::BECH32;
131+ if (check == EncodingConstant (Encoding::BECH32M)) return Encoding::BECH32M;
132+ return Encoding::INVALID;
121133}
122134
123135/* * Create a checksum. */
124- data CreateChecksum (const std::string& hrp, const data& values)
136+ data CreateChecksum (Encoding encoding, const std::string& hrp, const data& values)
125137{
126138 data enc = Cat (ExpandHRP (hrp), values);
127139 enc.resize (enc.size () + 6 ); // Append 6 zeroes
128- uint32_t mod = PolyMod (enc) ^ 1 ; // Determine what to XOR into those 6 zeroes.
140+ uint32_t mod = PolyMod (enc) ^ EncodingConstant (encoding) ; // Determine what to XOR into those 6 zeroes.
129141 data ret (6 );
130142 for (size_t i = 0 ; i < 6 ; ++i) {
131143 // Convert the 5-bit groups in mod to checksum values.
@@ -136,16 +148,13 @@ data CreateChecksum(const std::string& hrp, const data& values)
136148
137149} // namespace
138150
139- namespace bech32
140- {
141-
142- /* * Encode a Bech32 string. */
143- std::string Encode (const std::string& hrp, const data& values) {
151+ /* * Encode a Bech32 or Bech32m string. */
152+ std::string Encode (Encoding encoding, const std::string& hrp, const data& values) {
144153 // First ensure that the HRP is all lowercase. BIP-173 requires an encoder
145154 // to return a lowercase Bech32 string, but if given an uppercase HRP, the
146155 // result will always be invalid.
147156 for (const char & c : hrp) assert (c < ' A' || c > ' Z' );
148- data checksum = CreateChecksum (hrp, values);
157+ data checksum = CreateChecksum (encoding, hrp, values);
149158 data combined = Cat (values, checksum);
150159 std::string ret = hrp + ' 1' ;
151160 ret.reserve (ret.size () + combined.size ());
@@ -155,8 +164,8 @@ std::string Encode(const std::string& hrp, const data& values) {
155164 return ret;
156165}
157166
158- /* * Decode a Bech32 string. */
159- std::pair<std::string, data> Decode (const std::string& str) {
167+ /* * Decode a Bech32 or Bech32m string. */
168+ DecodeResult Decode (const std::string& str) {
160169 bool lower = false , upper = false ;
161170 for (size_t i = 0 ; i < str.size (); ++i) {
162171 unsigned char c = str[i];
@@ -183,10 +192,9 @@ std::pair<std::string, data> Decode(const std::string& str) {
183192 for (size_t i = 0 ; i < pos; ++i) {
184193 hrp += LowerCase (str[i]);
185194 }
186- if (!VerifyChecksum (hrp, values)) {
187- return {};
188- }
189- return {hrp, data (values.begin (), values.end () - 6 )};
195+ Encoding result = VerifyChecksum (hrp, values);
196+ if (result == Encoding::INVALID) return {};
197+ return {result, std::move (hrp), data (values.begin (), values.end () - 6 )};
190198}
191199
192200} // namespace bech32
0 commit comments