20 typedef std::vector<uint8_t> data;
23 const char* CHARSET =
"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
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
46 constexpr std::pair<std::array<int16_t, 1023>, std::array<int16_t, 1024>> GenerateGFTables()
51 std::array<int8_t, 31> GF32_EXP{};
52 std::array<int8_t, 32> GF32_LOG{};
66 for (
int i = 1; i < 31; ++i) {
73 if (v & 32) v ^= fmod;
79 std::array<int16_t, 1023> GF1024_EXP{};
80 std::array<int16_t, 1024> GF1024_LOG{};
102 for (
int i = 1; i < 1023; ++i) {
106 int v0n = v1 ? GF32_EXP.at((GF32_LOG.at(v1) + GF32_LOG.at(23)) % 31) : 0;
107 int v1n = (v1 ? GF32_EXP.at((GF32_LOG.at(v1) + GF32_LOG.at(9)) % 31) : 0) ^ v0;
114 return std::make_pair(GF1024_EXP, GF1024_LOG);
117 constexpr
auto tables = GenerateGFTables();
118 constexpr
const std::array<int16_t, 1023>& GF1024_EXP = tables.first;
119 constexpr
const std::array<int16_t, 1024>& GF1024_LOG = tables.second;
122 uint32_t EncodingConstant(
Encoding encoding) {
130 uint32_t
PolyMod(
const data& v)
178 for (
const auto v_i : v) {
194 uint8_t c0 = c >> 25;
197 c = ((c & 0x1ffffff) << 5) ^ v_i;
208 if (c0 & 1) c ^= 0x3b6a57b2;
209 if (c0 & 2) c ^= 0x26508e6d;
210 if (c0 & 4) c ^= 0x1ea119fa;
211 if (c0 & 8) c ^= 0x3d4233dd;
212 if (c0 & 16) c ^= 0x2a1462b3;
240 constexpr std::array<uint32_t, 25> GenerateSyndromeConstants() {
241 std::array<uint32_t, 25> SYNDROME_CONSTS{};
242 for (
int k = 1;
k < 6; ++
k) {
243 for (
int shift = 0; shift < 5; ++shift) {
244 int16_t b = GF1024_LOG.at(
size_t{1} << shift);
245 int16_t c0 = GF1024_EXP.at((997*
k + b) % 1023);
246 int16_t c1 = GF1024_EXP.at((998*
k + b) % 1023);
247 int16_t c2 = GF1024_EXP.at((999*
k + b) % 1023);
248 uint32_t c = c2 << 20 | c1 << 10 | c0;
249 int ind = 5*(
k-1) + shift;
250 SYNDROME_CONSTS[ind] = c;
253 return SYNDROME_CONSTS;
255 constexpr std::array<uint32_t, 25> SYNDROME_CONSTS = GenerateSyndromeConstants();
261 uint32_t Syndrome(
const uint32_t residue) {
264 uint32_t low = residue & 0x1f;
267 uint32_t result = low ^ (low << 10) ^ (low << 20);
274 for (
int i = 0; i < 25; ++i) {
275 result ^= ((residue >> (5+i)) & 1 ? SYNDROME_CONSTS.at(i) : 0);
281 inline unsigned char LowerCase(
unsigned char c)
283 return (c >=
'A' && c <=
'Z') ? (c -
'A') +
'a' : c;
287 bool CheckCharacters(
const std::string& str, std::vector<int>& errors)
289 bool lower =
false, upper =
false;
290 for (
size_t i = 0; i < str.size(); ++i) {
291 unsigned char c{(
unsigned char)(str[i])};
292 if (c >=
'a' && c <=
'z') {
298 }
else if (c >=
'A' && c <=
'Z') {
304 }
else if (c < 33 || c > 126) {
308 return errors.empty();
312 data ExpandHRP(
const std::string& hrp)
315 ret.reserve(hrp.size() + 90);
316 ret.resize(hrp.size() * 2 + 1);
317 for (
size_t i = 0; i < hrp.size(); ++i) {
318 unsigned char c = hrp[i];
320 ret[i + hrp.size() + 1] = c & 0x1f;
327 Encoding VerifyChecksum(
const std::string& hrp,
const data&
values)
341 data CreateChecksum(
Encoding encoding,
const std::string& hrp,
const data&
values)
344 enc.resize(enc.size() + 6);
345 uint32_t mod =
PolyMod(enc) ^ EncodingConstant(encoding);
347 for (
size_t i = 0; i < 6; ++i) {
349 ret[i] = (mod >> (5 * (5 - i))) & 31;
361 for (
const char& c : hrp)
assert(c < 'A' || c >
'Z');
362 data checksum = CreateChecksum(encoding, hrp,
values);
364 std::string
ret = hrp +
'1';
365 ret.reserve(
ret.size() + combined.size());
366 for (
const auto c : combined) {
374 std::vector<int> errors;
375 if (!CheckCharacters(str, errors))
return {};
376 size_t pos = str.rfind(
'1');
377 if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
380 data
values(str.size() - 1 - pos);
381 for (
size_t i = 0; i < str.size() - 1 - pos; ++i) {
382 unsigned char c = str[i + pos + 1];
383 int8_t rev = CHARSET_REV[c];
391 for (
size_t i = 0; i < pos; ++i) {
392 hrp += LowerCase(str[i]);
396 return {result, std::move(hrp), data(
values.begin(),
values.end() - 6)};
400 std::pair<std::string, std::vector<int>>
LocateErrors(
const std::string& str) {
401 std::vector<int> error_locations{};
403 if (str.size() > 90) {
404 error_locations.resize(str.size() - 90);
405 std::iota(error_locations.begin(), error_locations.end(), 90);
406 return std::make_pair(
"Bech32 string too long", std::move(error_locations));
409 if (!CheckCharacters(str, error_locations)){
410 return std::make_pair(
"Invalid character or mixed case", std::move(error_locations));
413 size_t pos = str.rfind(
'1');
414 if (pos == str.npos) {
415 return std::make_pair(
"Missing separator", std::vector<int>{});
417 if (pos == 0 || pos + 7 > str.size()) {
418 error_locations.push_back(pos);
419 return std::make_pair(
"Invalid separator position", std::move(error_locations));
423 for (
size_t i = 0; i < pos; ++i) {
424 hrp += LowerCase(str[i]);
427 size_t length = str.size() - 1 - pos;
429 for (
size_t i = pos + 1; i < str.size(); ++i) {
430 unsigned char c = str[i];
431 int8_t rev = CHARSET_REV[c];
433 error_locations.push_back(i);
434 return std::make_pair(
"Invalid Base 32 character", std::move(error_locations));
436 values[i - pos - 1] = rev;
441 std::optional<Encoding> error_encoding;
443 std::vector<int> possible_errors;
446 uint32_t residue =
PolyMod(
Cat(ExpandHRP(hrp),
values)) ^ EncodingConstant(encoding);
453 uint32_t syn = Syndrome(residue);
456 int s0 = syn & 0x3FF;
457 int s1 = (syn >> 10) & 0x3FF;
461 int l_s0 = GF1024_LOG.at(s0);
462 int l_s1 = GF1024_LOG.at(s1);
463 int l_s2 = GF1024_LOG.at(s2);
469 if (l_s0 != -1 && l_s1 != -1 && l_s2 != -1 && (2 * l_s1 - l_s2 - l_s0 + 2046) % 1023 == 0) {
471 size_t p1 = (l_s1 - l_s0 + 1023) % 1023;
474 int l_e1 = l_s0 + (1023 - 997) * p1;
479 if (p1 < length && !(l_e1 % 33)) {
483 possible_errors.push_back(str.size() - p1 - 1);
488 for (
size_t p1 = 0; p1 < length; ++p1) {
498 int s2_s1p1 = s2 ^ (s1 == 0 ? 0 : GF1024_EXP.at((l_s1 + p1) % 1023));
499 if (s2_s1p1 == 0)
continue;
500 int l_s2_s1p1 = GF1024_LOG.at(s2_s1p1);
504 int s1_s0p1 = s1 ^ (s0 == 0 ? 0 : GF1024_EXP.at((l_s0 + p1) % 1023));
505 if (s1_s0p1 == 0)
continue;
506 int l_s1_s0p1 = GF1024_LOG.at(s1_s0p1);
511 size_t p2 = (l_s2_s1p1 - l_s1_s0p1 + 1023) % 1023;
514 if (p2 >= length || p1 == p2)
continue;
519 int s1_s0p2 = s1 ^ (s0 == 0 ? 0 : GF1024_EXP.at((l_s0 + p2) % 1023));
520 if (s1_s0p2 == 0)
continue;
521 int l_s1_s0p2 = GF1024_LOG.at(s1_s0p2);
524 int inv_p1_p2 = 1023 - GF1024_LOG.at(GF1024_EXP.at(p1) ^ GF1024_EXP.at(p2));
529 int l_e2 = l_s1_s0p1 + inv_p1_p2 + (1023 - 997) * p2;
531 if (l_e2 % 33)
continue;
536 int l_e1 = l_s1_s0p2 + inv_p1_p2 + (1023 - 997) * p1;
538 if (l_e1 % 33)
continue;
543 possible_errors.push_back(str.size() - p1 - 1);
544 possible_errors.push_back(str.size() - p2 - 1);
546 possible_errors.push_back(str.size() - p2 - 1);
547 possible_errors.push_back(str.size() - p1 - 1);
554 return std::make_pair(
"", std::vector<int>{});
557 if (error_locations.empty() || (!possible_errors.empty() && possible_errors.size() < error_locations.size())) {
558 error_locations = std::move(possible_errors);
559 if (!error_locations.empty()) error_encoding = encoding;
562 std::string error_message = error_encoding ==
Encoding::BECH32M ?
"Invalid Bech32m checksum"
564 :
"Invalid checksum";
566 return std::make_pair(error_message, std::move(error_locations));
@ INVALID
Failed decoding.
@ BECH32
Bech32 encoding as defined in BIP173.
@ BECH32M
Bech32m encoding as defined in BIP350.
std::string Encode(Encoding encoding, const std::string &hrp, const data &values)
Encode a Bech32 or Bech32m string.
DecodeResult Decode(const std::string &str)
Decode a Bech32 or Bech32m string.
std::pair< std::string, std::vector< int > > LocateErrors(const std::string &str)
Find index of an incorrect character in a Bech32 string.
static const int64_t values[]
A selection of numbers that do not trigger int64_t overflow when added/subtracted.
void PolyMod(const std::vector< typename F::Elem > &mod, std::vector< typename F::Elem > &val, const F &field)
Compute the remainder of a polynomial division of val by mod, putting the result in mod.
V Cat(V v1, V &&v2)
Concatenate two vectors, moving elements.