@@ -17,22 +17,7 @@ namespace bech32
17
17
namespace
18
18
{
19
19
20
- typedef std::vector<uint8_t > data;
21
-
22
- /* * The Bech32 and Bech32m character set for encoding. */
23
- const char * CHARSET = " qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
24
-
25
- /* * The Bech32 and Bech32m character set for decoding. */
26
- const int8_t CHARSET_REV[128 ] = {
27
- -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
28
- -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
29
- -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
30
- 15 , -1 , 10 , 17 , 21 , 20 , 26 , 30 , 7 , 5 , -1 , -1 , -1 , -1 , -1 , -1 ,
31
- -1 , 29 , -1 , 24 , 13 , 25 , 9 , 8 , 23 , -1 , 18 , 22 , 31 , 27 , 19 , -1 ,
32
- 1 , 0 , 3 , 16 , 11 , 28 , 12 , 14 , 6 , 4 , 2 , -1 , -1 , -1 , -1 , -1 ,
33
- -1 , 29 , -1 , 24 , 13 , 25 , 9 , 8 , 23 , -1 , 18 , 22 , 31 , 27 , 19 , -1 ,
34
- 1 , 0 , 3 , 16 , 11 , 28 , 12 , 14 , 6 , 4 , 2 , -1 , -1 , -1 , -1 , -1
35
- };
20
+ typedef internal::data data;
36
21
37
22
/* * We work with the finite field GF(1024) defined as a degree 2 extension of the base field GF(32)
38
23
* The defining polynomial of the extension is x^2 + 9x + 23.
@@ -308,21 +293,6 @@ bool CheckCharacters(const std::string& str, std::vector<int>& errors)
308
293
return errors.empty ();
309
294
}
310
295
311
- std::vector<unsigned char > PreparePolynomialCoefficients (const std::string& hrp, const data& values)
312
- {
313
- data ret;
314
- ret.reserve (hrp.size () + 1 + hrp.size () + values.size () + CHECKSUM_SIZE);
315
-
316
- /* * Expand a HRP for use in checksum computation. */
317
- for (size_t i = 0 ; i < hrp.size (); ++i) ret.push_back (hrp[i] >> 5 );
318
- ret.push_back (0 );
319
- for (size_t i = 0 ; i < hrp.size (); ++i) ret.push_back (hrp[i] & 0x1f );
320
-
321
- ret.insert (ret.end (), values.begin (), values.end ());
322
-
323
- return ret;
324
- }
325
-
326
296
/* * Verify a checksum. */
327
297
Encoding VerifyChecksum (const std::string& hrp, const data& values)
328
298
{
@@ -331,7 +301,7 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
331
301
// list of values would result in a new valid list. For that reason, Bech32 requires the
332
302
// resulting checksum to be 1 instead. In Bech32m, this constant was amended. See
333
303
// https://gist.github.com/sipa/14c248c288c3880a3b191f978a34508e for details.
334
- auto enc = PreparePolynomialCoefficients (hrp, values);
304
+ auto enc = internal:: PreparePolynomialCoefficients (hrp, values, 0 );
335
305
const uint32_t check = PolyMod (enc);
336
306
if (check == EncodingConstant (Encoding::BECH32)) return Encoding::BECH32;
337
307
if (check == EncodingConstant (Encoding::BECH32M)) return Encoding::BECH32M;
@@ -341,7 +311,7 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
341
311
/* * Create a checksum. */
342
312
data CreateChecksum (Encoding encoding, const std::string& hrp, const data& values)
343
313
{
344
- auto enc = PreparePolynomialCoefficients (hrp, values);
314
+ auto enc = internal:: PreparePolynomialCoefficients (hrp, values, CHECKSUM_SIZE );
345
315
enc.insert (enc.end (), CHECKSUM_SIZE, 0x00 );
346
316
uint32_t mod = PolyMod (enc) ^ EncodingConstant (encoding); // Determine what to XOR into those 6 zeroes.
347
317
data ret (CHECKSUM_SIZE);
@@ -354,29 +324,61 @@ data CreateChecksum(Encoding encoding, const std::string& hrp, const data& value
354
324
355
325
} // namespace
356
326
357
- /* * Encode a Bech32 or Bech32m string. */
358
- std::string Encode (Encoding encoding, const std::string& hrp, const data& values) {
327
+ namespace internal {
328
+
329
+ /* * The Bech32 and Bech32m character set for encoding. */
330
+ const char * CHARSET = " qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
331
+
332
+ /* * The Bech32 and Bech32m character set for decoding. */
333
+ const int8_t CHARSET_REV[128 ] = {
334
+ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
335
+ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
336
+ -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
337
+ 15 , -1 , 10 , 17 , 21 , 20 , 26 , 30 , 7 , 5 , -1 , -1 , -1 , -1 , -1 , -1 ,
338
+ -1 , 29 , -1 , 24 , 13 , 25 , 9 , 8 , 23 , -1 , 18 , 22 , 31 , 27 , 19 , -1 ,
339
+ 1 , 0 , 3 , 16 , 11 , 28 , 12 , 14 , 6 , 4 , 2 , -1 , -1 , -1 , -1 , -1 ,
340
+ -1 , 29 , -1 , 24 , 13 , 25 , 9 , 8 , 23 , -1 , 18 , 22 , 31 , 27 , 19 , -1 ,
341
+ 1 , 0 , 3 , 16 , 11 , 28 , 12 , 14 , 6 , 4 , 2 , -1 , -1 , -1 , -1 , -1
342
+ };
343
+
344
+ std::vector<unsigned char > PreparePolynomialCoefficients (const std::string& hrp, const data& values, size_t extra)
345
+ {
346
+ data ret;
347
+ ret.reserve (hrp.size () + 1 + hrp.size () + values.size () + extra);
348
+
349
+ /* * Expand a HRP for use in checksum computation. */
350
+ for (size_t i = 0 ; i < hrp.size (); ++i) ret.push_back (hrp[i] >> 5 );
351
+ ret.push_back (0 );
352
+ for (size_t i = 0 ; i < hrp.size (); ++i) ret.push_back (hrp[i] & 0x1f );
353
+
354
+ ret.insert (ret.end (), values.begin (), values.end ());
355
+
356
+ return ret;
357
+ }
358
+
359
+ /* * Encode a hrpstring without concerning ourselves with checksum validity */
360
+ std::string Encode (const std::string& hrp, const data& values, const data& checksum) {
359
361
// First ensure that the HRP is all lowercase. BIP-173 and BIP350 require an encoder
360
362
// to return a lowercase Bech32/Bech32m string, but if given an uppercase HRP, the
361
363
// result will always be invalid.
362
364
for (const char & c : hrp) assert (c < ' A' || c > ' Z' );
363
365
364
366
std::string ret;
365
- ret.reserve (hrp.size () + 1 + values.size () + CHECKSUM_SIZE );
367
+ ret.reserve (hrp.size () + 1 + values.size () + checksum. size () );
366
368
ret += hrp;
367
369
ret += SEPARATOR;
368
370
for (const uint8_t & i : values) ret += CHARSET[i];
369
- for (const uint8_t & i : CreateChecksum (encoding, hrp, values) ) ret += CHARSET[i];
371
+ for (const uint8_t & i : checksum ) ret += CHARSET[i];
370
372
return ret;
371
373
}
372
374
373
- /* * Decode a Bech32 or Bech32m string. */
374
- DecodeResult Decode (const std::string& str, CharLimit limit) {
375
+ /* * Decode a hrpstring without concerning ourselves with checksum validity */
376
+ std::pair<std::string, data> Decode (const std::string& str, CharLimit limit, size_t checksum_size ) {
375
377
std::vector<int > errors;
376
378
if (!CheckCharacters (str, errors)) return {};
377
379
size_t pos = str.rfind (SEPARATOR);
378
380
if (str.size () > limit) return {};
379
- if (pos == str.npos || pos == 0 || pos + CHECKSUM_SIZE >= str.size ()) {
381
+ if (pos == str.npos || pos == 0 || pos + checksum_size >= str.size ()) {
380
382
return {};
381
383
}
382
384
data values (str.size () - 1 - pos);
@@ -394,9 +396,22 @@ DecodeResult Decode(const std::string& str, CharLimit limit) {
394
396
for (size_t i = 0 ; i < pos; ++i) {
395
397
hrp += LowerCase (str[i]);
396
398
}
397
- Encoding result = VerifyChecksum (hrp, values);
399
+ return std::make_pair (hrp, values);
400
+ }
401
+
402
+ } // namespace internal
403
+
404
+ /* * Encode a Bech32 or Bech32m string. */
405
+ std::string Encode (Encoding encoding, const std::string& hrp, const data& values) {
406
+ return internal::Encode (hrp, values, CreateChecksum (encoding, hrp, values));
407
+ }
408
+
409
+ /* * Decode a Bech32 or Bech32m string. */
410
+ DecodeResult Decode (const std::string& str, CharLimit limit) {
411
+ auto res = internal::Decode (str, limit, CHECKSUM_SIZE);
412
+ Encoding result = VerifyChecksum (res.first , res.second );
398
413
if (result == Encoding::INVALID) return {};
399
- return {result, std::move (hrp ), data (values. begin (), values .end () - CHECKSUM_SIZE)};
414
+ return {result, std::move (res. first ), data (res. second . begin (), res. second .end () - CHECKSUM_SIZE)};
400
415
}
401
416
402
417
/* * Find index of an incorrect character in a Bech32 string. */
@@ -432,7 +447,7 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, Ch
432
447
data values (length);
433
448
for (size_t i = pos + 1 ; i < str.size (); ++i) {
434
449
unsigned char c = str[i];
435
- int8_t rev = CHARSET_REV[c];
450
+ int8_t rev = internal:: CHARSET_REV[c];
436
451
if (rev == -1 ) {
437
452
error_locations.push_back (i);
438
453
return std::make_pair (" Invalid Base 32 character" , std::move (error_locations));
@@ -447,7 +462,7 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, Ch
447
462
std::vector<int > possible_errors;
448
463
// Recall that (expanded hrp + values) is interpreted as a list of coefficients of a polynomial
449
464
// over GF(32). PolyMod computes the "remainder" of this polynomial modulo the generator G(x).
450
- auto enc = PreparePolynomialCoefficients (hrp, values);
465
+ auto enc = internal:: PreparePolynomialCoefficients (hrp, values, 0 );
451
466
uint32_t residue = PolyMod (enc) ^ EncodingConstant (encoding);
452
467
453
468
// All valid codewords should be multiples of G(x), so this remainder (after XORing with the encoding
0 commit comments