|
| 1 | +// Copyright (c) 2017, 2021 Pieter Wuille |
| 2 | +// Copyright (c) 2021-2022 The Bitcoin Core developers |
| 3 | +// Distributed under the MIT software license, see the accompanying |
| 4 | +// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 5 | + |
| 6 | +#include <bech32.h> |
| 7 | +#include <codex32.h> |
| 8 | +#include <util/vector.h> |
| 9 | + |
| 10 | +#include <array> |
| 11 | +#include <assert.h> |
| 12 | +#include <numeric> |
| 13 | +#include <optional> |
| 14 | + |
| 15 | +namespace codex32 |
| 16 | +{ |
| 17 | + |
| 18 | +namespace |
| 19 | +{ |
| 20 | + |
| 21 | +typedef bech32::internal::data data; |
| 22 | + |
| 23 | +// Build multiplication and logarithm tables for GF(32). |
| 24 | +// |
| 25 | +// We represent GF(32) as an extension of GF(2) by appending a root, alpha, of the |
| 26 | +// polynomial x^5 + x^3 + 1. All elements of GF(32) can be represented as degree-4 |
| 27 | +// polynomials in alpha. So e.g. 1 is represented by 1, alpha by 2, alpha^2 by 4, |
| 28 | +// and so on. |
| 29 | +// |
| 30 | +// alpha is also a generator of the multiplicative group of the field. So every nonzero |
| 31 | +// element in GF(32) can be represented as alpha^i, for some i in {0, 1, ..., 31}. |
| 32 | +// This representation makes multiplication and division very easy, since it is just |
| 33 | +// addition and subtraction in the exponent. |
| 34 | +// |
| 35 | +// These tables allow converting from the normal binary representation of GF(32) elements |
| 36 | +// to the power-of-alpha one. |
| 37 | +constexpr std::pair<std::array<int8_t, 31>, std::array<int8_t, 32>> GenerateGF32Tables() { |
| 38 | + // We use these tables to perform arithmetic in GF(32) below, when constructing the |
| 39 | + // tables for GF(1024). |
| 40 | + std::array<int8_t, 31> GF32_EXP{}; |
| 41 | + std::array<int8_t, 32> GF32_LOG{}; |
| 42 | + |
| 43 | + // fmod encodes the defining polynomial of GF(32) over GF(2), x^5 + x^3 + 1. |
| 44 | + // Because coefficients in GF(2) are binary digits, the coefficients are packed as 101001. |
| 45 | + const int fmod = 41; |
| 46 | + |
| 47 | + // Elements of GF(32) are encoded as vectors of length 5 over GF(2), that is, |
| 48 | + // 5 binary digits. Each element (b_4, b_3, b_2, b_1, b_0) encodes a polynomial |
| 49 | + // b_4*x^4 + b_3*x^3 + b_2*x^2 + b_1*x^1 + b_0 (modulo fmod). |
| 50 | + // For example, 00001 = 1 is the multiplicative identity. |
| 51 | + GF32_EXP[0] = 1; |
| 52 | + GF32_LOG[0] = -1; |
| 53 | + GF32_LOG[1] = 0; |
| 54 | + int v = 1; |
| 55 | + for (int i = 1; i < 31; ++i) { |
| 56 | + // Multiplication by x is the same as shifting left by 1, as |
| 57 | + // every coefficient of the polynomial is moved up one place. |
| 58 | + v = v << 1; |
| 59 | + // If the polynomial now has an x^5 term, we subtract fmod from it |
| 60 | + // to remain working modulo fmod. Subtraction is the same as XOR in characteristic |
| 61 | + // 2 fields. |
| 62 | + if (v & 32) v ^= fmod; |
| 63 | + GF32_EXP[i] = v; |
| 64 | + GF32_LOG[v] = i; |
| 65 | + } |
| 66 | + |
| 67 | + return std::make_pair(GF32_EXP, GF32_LOG); |
| 68 | +} |
| 69 | + |
| 70 | +constexpr auto tables32 = GenerateGF32Tables(); |
| 71 | +constexpr const std::array<int8_t, 31>& GF32_EXP = tables32.first; |
| 72 | +constexpr const std::array<int8_t, 32>& GF32_LOG = tables32.second; |
| 73 | + |
| 74 | +uint8_t gf32_mul(uint8_t x, uint8_t y) { |
| 75 | + if (x == 0 || y == 0) { |
| 76 | + return 0; |
| 77 | + } |
| 78 | + return GF32_EXP[(GF32_LOG[x] + GF32_LOG[y]) % 31]; |
| 79 | +} |
| 80 | + |
| 81 | +// The bech32 string "secretshare32" |
| 82 | +constexpr const std::array<uint8_t, 13> CODEX32_M = { |
| 83 | + 16, 25, 24, 3, 25, 11, 16, 23, 29, 3, 25, 17, 10 |
| 84 | +}; |
| 85 | + |
| 86 | +// The bech32 string "secretshare32ex" |
| 87 | +constexpr const std::array<uint8_t, 15> CODEX32_LONG_M = { |
| 88 | + 16, 25, 24, 3, 25, 11, 16, 23, 29, 3, 25, 17, 10, 25, 6, |
| 89 | +}; |
| 90 | + |
| 91 | +// The generator for the codex32 checksum, not including the leading x^13 term |
| 92 | +constexpr const std::array<uint8_t, 13> CODEX32_GEN = { |
| 93 | + 25, 27, 17, 8, 0, 25, 25, 25, 31, 27, 24, 16, 16, |
| 94 | +}; |
| 95 | + |
| 96 | +// The generator for the long codex32 checksum, not including the leading x^15 term |
| 97 | +constexpr const std::array<uint8_t, 15> CODEX32_LONG_GEN = { |
| 98 | + 15, 10, 25, 26, 9, 25, 21, 6, 23, 21, 6, 5, 22, 4, 23, |
| 99 | +}; |
| 100 | + |
| 101 | +/** This function will compute what 5-bit values to XOR into the last <checksum length> |
| 102 | + * input values, in order to make the checksum 0. These values are returned in an array |
| 103 | + * whose length is implied by the type of the generator polynomial (`CODEX32_GEN` or |
| 104 | + * `CODEX32_LONG_GEN`) that is passed in. The result should be xored with the target |
| 105 | + * residue ("secretshare32" or "secretshare32ex". */ |
| 106 | +template <typename Residue> |
| 107 | +Residue PolyMod(const data& v, const Residue& gen) |
| 108 | +{ |
| 109 | + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), |
| 110 | + // in the same way as in bech32. The block comment in bech32::<anonymous>::PolyMod |
| 111 | + // provides more details. |
| 112 | + // |
| 113 | + // Unlike bech32, the output consists of 13 5-bit values, rather than 6, so they cannot |
| 114 | + // be packed into a uint32_t, or even a uint64_t. |
| 115 | + // |
| 116 | + // Like bech32 we have a generator polynomial which defines the BCH code. For "short" |
| 117 | + // strings, whose data part is 93 characters or less, we use |
| 118 | + // g(x) = x^13 + {25}x^12 + {27}x^11 + {17}x^10 + {8}x^9 + {0}x^8 + {25}x^7 |
| 119 | + // + {25}x^6 + {25}x^5 + {31}x^4 + {27}x^3 + {24}x^2 + {16}x + {16} |
| 120 | + // |
| 121 | + // For long strings, whose data part is more than 93 characters, we use |
| 122 | + // g(x) = x^15 + {15}x^14 + {10}x^13 + {25}x^12 + {26}x^11 + {9}x^10 |
| 123 | + // + {25}x^9 + {21}x^8 + {6}x^7 + {23}x^6 + {21}x^5 + {6}x^4 |
| 124 | + // + {5}x^3 + {22}x^2 + {4}x^1 + {23} |
| 125 | + // |
| 126 | + // In both cases g is chosen in such a way that the resulting code is a BCH code which |
| 127 | + // can detect up to 8 errors in a window of 93 characters. Unlike bech32, no further |
| 128 | + // optimization was done to achieve more detection capability than the design parameters. |
| 129 | + // |
| 130 | + // For information about the {n} encoding of GF32 elements, see the block comment in |
| 131 | + // bech32::<anonymous>::PolyMod. |
| 132 | + Residue res{}; |
| 133 | + res[gen.size() - 1] = 1; |
| 134 | + for (const auto v_i : v) { |
| 135 | + // We want to update `res` to correspond to a polynomial with one extra term. That is, |
| 136 | + // we first multiply it by x and add the next character, which is done by left-shifting |
| 137 | + // the entire array and adding the next character to the open slot. |
| 138 | + // |
| 139 | + // We then reduce it module g, which involves taking the shifted-off character, multiplying |
| 140 | + // it by g, and adding it to the result of the previous step. This makes sense because after |
| 141 | + // multiplying by x, `res` has the same degree as g, so reduction by g simply requires |
| 142 | + // dividing the most significant coefficient of `res` by the most significant coefficient of |
| 143 | + // g (which is 1), then subtracting that multiple of g. |
| 144 | + // |
| 145 | + // Recall that we are working in a characteristic-2 field, so that subtraction is the same |
| 146 | + // thing as addition. |
| 147 | + |
| 148 | + // Multiply by x |
| 149 | + uint8_t shift = res[0]; |
| 150 | + for (size_t i = 1; i < res.size(); ++i) { |
| 151 | + res[i - 1] = res[i]; |
| 152 | + } |
| 153 | + // Add the next value |
| 154 | + res[res.size() - 1] = v_i; |
| 155 | + // Reduce |
| 156 | + if (shift != 0) { |
| 157 | + for(size_t i = 0; i < res.size(); ++i) { |
| 158 | + if (gen[i] != 0) { |
| 159 | + res[i] ^= gf32_mul(gen[i], shift); |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + } |
| 164 | + return res; |
| 165 | +} |
| 166 | + |
| 167 | +/** Verify a checksum. */ |
| 168 | +template <typename Residue> |
| 169 | +bool VerifyChecksum(const std::string& hrp, const data& values, const Residue& gen, const Residue& target) |
| 170 | +{ |
| 171 | + auto enc = bech32::internal::PreparePolynomialCoefficients(hrp, values, 0); |
| 172 | + auto res = PolyMod(enc, gen); |
| 173 | + for (size_t i = 0; i < res.size(); ++i) { |
| 174 | + if (res[i] != target[i]) { |
| 175 | + return 0; |
| 176 | + } |
| 177 | + } |
| 178 | + return 1; |
| 179 | +} |
| 180 | + |
| 181 | +/** Create a checksum. */ |
| 182 | +template <typename Residue> |
| 183 | +data CreateChecksum(const std::string& hrp, const data& values, const Residue& gen, const Residue& target) |
| 184 | +{ |
| 185 | + auto enc = bech32::internal::PreparePolynomialCoefficients(hrp, values, gen.size()); |
| 186 | + enc.insert(enc.end(), gen.size(), 0x00); |
| 187 | + const auto checksum = PolyMod(enc, gen); |
| 188 | + data ret(gen.size()); |
| 189 | + for (size_t i = 0; i < checksum.size(); ++i) { |
| 190 | + ret[i] = checksum[i] ^ target[i]; |
| 191 | + } |
| 192 | + return ret; |
| 193 | +} |
| 194 | + |
| 195 | +} // namespace |
| 196 | + |
| 197 | +/** Encode a codex32 string. */ |
| 198 | +std::string Result::Encode() const { |
| 199 | + assert(IsValid()); |
| 200 | + |
| 201 | + const data checksum = m_data.size() <= 80 |
| 202 | + ? CreateChecksum(m_hrp, m_data, CODEX32_GEN, CODEX32_M) |
| 203 | + : CreateChecksum(m_hrp, m_data, CODEX32_LONG_GEN, CODEX32_LONG_M); |
| 204 | + return bech32::internal::Encode(m_hrp, m_data, checksum); |
| 205 | +} |
| 206 | + |
| 207 | +/** Decode a codex32 string */ |
| 208 | +Result::Result(const std::string& str) { |
| 209 | + m_valid = OK; |
| 210 | + |
| 211 | + auto res = bech32::internal::Decode(str, 127, 6); |
| 212 | + |
| 213 | + if (str.size() > 127) { |
| 214 | + m_valid = INVALID_LENGTH; |
| 215 | + // Early return since if we failed the max size check, Decode did not give us any data. |
| 216 | + return; |
| 217 | + } else if (res.first.empty() && res.second.empty()) { |
| 218 | + m_valid = BECH32_DECODE; |
| 219 | + return; |
| 220 | + } else if (res.first != "ms") { |
| 221 | + m_valid = INVALID_HRP; |
| 222 | + // Early return since if the HRP is wrong, all bets are off and no point continuing |
| 223 | + return; |
| 224 | + } |
| 225 | + m_hrp = std::move(res.first); |
| 226 | + |
| 227 | + if (res.second.size() >= 45 && res.second.size() <= 90) { |
| 228 | + // If, after converting back to base-256, we have 5 or more bits of data |
| 229 | + // remaining, it means that we had an entire character of useless data, |
| 230 | + // which shouldn't have been included. |
| 231 | + if (((res.second.size() - 6 - 13) * 5) % 8 > 4) { |
| 232 | + m_valid = INVALID_LENGTH; |
| 233 | + } else if (VerifyChecksum(m_hrp, res.second, CODEX32_GEN, CODEX32_M)) { |
| 234 | + m_data = data(res.second.begin(), res.second.end() - 13); |
| 235 | + } else { |
| 236 | + m_valid = BAD_CHECKSUM; |
| 237 | + } |
| 238 | + } else if (res.second.size() >= 96 && res.second.size() <= 124) { |
| 239 | + if (((res.second.size() - 6 - 15) * 5) % 8 > 4) { |
| 240 | + m_valid = INVALID_LENGTH; |
| 241 | + } else if (VerifyChecksum(m_hrp, res.second, CODEX32_LONG_GEN, CODEX32_LONG_M)) { |
| 242 | + m_data = data(res.second.begin(), res.second.end() - 15); |
| 243 | + } else { |
| 244 | + m_valid = BAD_CHECKSUM; |
| 245 | + } |
| 246 | + } else { |
| 247 | + m_valid = INVALID_LENGTH; |
| 248 | + } |
| 249 | + |
| 250 | + if (m_valid == OK) { |
| 251 | + auto k = bech32::internal::CHARSET[res.second[0]]; |
| 252 | + if (k < '0' || k == '1' || k > '9') { |
| 253 | + m_valid = INVALID_K; |
| 254 | + } |
| 255 | + if (k == '0' && m_data[5] != 16) { |
| 256 | + // If the threshold is 0, the only allowable share is S |
| 257 | + m_valid = INVALID_SHARE_IDX; |
| 258 | + } |
| 259 | + } |
| 260 | +} |
| 261 | + |
| 262 | +Result::Result(std::string&& hrp, size_t k, const std::string& id, char share_idx, const std::vector<unsigned char>& data) { |
| 263 | + m_valid = OK; |
| 264 | + if (hrp != "ms") { |
| 265 | + m_valid = INVALID_HRP; |
| 266 | + } |
| 267 | + m_hrp = hrp; |
| 268 | + if (k == 1 || k > 9) { |
| 269 | + m_valid = INVALID_K; |
| 270 | + } |
| 271 | + if (id.size() != 4) { |
| 272 | + m_valid = INVALID_ID_LEN; |
| 273 | + } |
| 274 | + int8_t sidx = bech32::internal::CHARSET_REV[(unsigned char) share_idx]; |
| 275 | + if (sidx == -1) { |
| 276 | + m_valid = INVALID_SHARE_IDX; |
| 277 | + } |
| 278 | + if (k == 0 && sidx != 16) { |
| 279 | + // If the threshold is 0, the only allowable share is S |
| 280 | + m_valid = INVALID_SHARE_IDX; |
| 281 | + } |
| 282 | + for (size_t i = 0; i < id.size(); ++i) { |
| 283 | + if (bech32::internal::CHARSET_REV[(unsigned char) id[i]] == -1) { |
| 284 | + m_valid = INVALID_ID_CHAR; |
| 285 | + } |
| 286 | + } |
| 287 | + |
| 288 | + if (m_valid != OK) { |
| 289 | + // early bail before allocating memory |
| 290 | + return; |
| 291 | + } |
| 292 | + |
| 293 | + m_data.reserve(6 + ((data.size() * 8) + 4) / 5); |
| 294 | + m_data.push_back(bech32::internal::CHARSET_REV['0' + k]); |
| 295 | + m_data.push_back(bech32::internal::CHARSET_REV[(unsigned char) id[0]]); |
| 296 | + m_data.push_back(bech32::internal::CHARSET_REV[(unsigned char) id[1]]); |
| 297 | + m_data.push_back(bech32::internal::CHARSET_REV[(unsigned char) id[2]]); |
| 298 | + m_data.push_back(bech32::internal::CHARSET_REV[(unsigned char) id[3]]); |
| 299 | + m_data.push_back(sidx); |
| 300 | + ConvertBits<8, 5, true>([&](unsigned char c) { m_data.push_back(c); }, data.begin(), data.end()); |
| 301 | +} |
| 302 | + |
| 303 | +std::string Result::GetIdString() const { |
| 304 | + assert(IsValid()); |
| 305 | + |
| 306 | + std::string ret; |
| 307 | + ret.reserve(4); |
| 308 | + ret.push_back(bech32::internal::CHARSET[m_data[1]]); |
| 309 | + ret.push_back(bech32::internal::CHARSET[m_data[2]]); |
| 310 | + ret.push_back(bech32::internal::CHARSET[m_data[3]]); |
| 311 | + ret.push_back(bech32::internal::CHARSET[m_data[4]]); |
| 312 | + return ret; |
| 313 | +} |
| 314 | + |
| 315 | +size_t Result::GetK() const { |
| 316 | + assert(IsValid()); |
| 317 | + return bech32::internal::CHARSET[m_data[0]] - '0'; |
| 318 | +} |
| 319 | + |
| 320 | +} // namespace codex32 |
0 commit comments