codex32.h raw

   1  // Copyright (c) 2023 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  // codex32 is a string encoding format for BIP-32 seeds. Like bech32 and
   6  // bech32m, the outputs consist of a human-readable part (alphanumeric),
   7  // a separator character (1), and a base32 data section. The final 13
   8  // characters are a checksum.
   9  //
  10  // For more information, see BIP 93.
  11  
  12  #ifndef LIMENKA_CODEX32_H
  13  #define LIMENKA_CODEX32_H
  14  
  15  #include <assert.h>
  16  #include <stdint.h>
  17  #include <array>
  18  #include <string>
  19  #include <vector>
  20  
  21  #include <bech32.h>
  22  #include <util/strencodings.h>
  23  
  24  namespace codex32
  25  {
  26  
  27  enum Error {
  28      OK,
  29      BAD_CHECKSUM,
  30      BECH32_DECODE,
  31      INVALID_HRP,
  32      INVALID_ID_LEN,
  33      INVALID_ID_CHAR,
  34      INVALID_LENGTH,
  35      INVALID_K,
  36      INVALID_SHARE_IDX,
  37      TOO_FEW_SHARES,
  38      DUPLICATE_SHARE,
  39      MISMATCH_K,
  40      MISMATCH_ID,
  41      MISMATCH_LENGTH,
  42  };
  43  
  44  std::string ErrorString(Error e);
  45  
  46  class Result
  47  {
  48  public:
  49      /** Construct a codex32 result by parsing a string */
  50      Result(const std::string& str);
  51  
  52      /** Construct a codex32 directly from a HRP, k, seed ID, share index and payload
  53       *
  54       * This constructor requires the hrp to be the lowercase string "ms", but will
  55       * ignore the case of `id` and `share_idx`. */
  56      Result(std::string&& hrp, size_t k, const std::string& id, char share_idx, const std::vector<unsigned char>& data);
  57  
  58      /** Construct a codex32 result by interpolating a set of input shares to obtain an output share
  59       *
  60       * Requires that all input shares have the same k and seed ID */
  61      Result(const std::vector<Result>& shares, char output_idx);
  62  
  63      /** Boolean indicating whether the data was successfully parsed.
  64       *
  65       * If this returns false, most of the other methods on this class will assert. */
  66      bool IsValid() const {
  67          return m_valid == OK;
  68      }
  69  
  70      /** Accessor for the specific parsing/construction error */
  71      Error error() const {
  72          return m_valid;
  73      }
  74  
  75      /** Accessor for the human-readable part of the codex32 string */
  76      const std::string& GetHrp() const {
  77          assert(IsValid());
  78          return m_hrp;
  79      }
  80  
  81      /** Accessor for the seed ID, in string form */
  82      std::string GetIdString() const;
  83  
  84      /** Accessor for the secret sharing threshold; 0 for a bare seed; (size_t)-1 if unavailable/invalid */
  85      size_t GetK() const;
  86  
  87      /** Accessor for the share index; (uint8_t)-1 if unavailable/invalid */
  88      char GetShareIndex() const {
  89          assert(IsValid());
  90          return bech32::internal::CHARSET[m_data[5]];
  91      }
  92  
  93      /** Accessor for the binary payload data (in base 256, not gf32) */
  94      std::vector<unsigned char> GetPayload() const {
  95          assert(IsValid());
  96  
  97          std::vector<unsigned char> ret;
  98          ret.reserve(((m_data.size() - 6) * 5) / 8);
  99          // Note that `ConvertBits` returns a bool indicating whether or not nonzero bits
 100          // were discarded. In BIP 93, we discard bits regardless of whether they are 0,
 101          // so this is not an error and does not need to be checked.
 102          ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, m_data.begin() + 6, m_data.end());
 103          return ret;
 104      };
 105  
 106      /** (Re-)encode the codex32 data as a hrp string */
 107      std::string Encode() const;
 108  
 109  private:
 110      Error m_valid;               //!< codex32::OK if the string was decoded correctly
 111  
 112      std::string m_hrp;           //!< The human readable part
 113      std::vector<uint8_t> m_data; //!< The payload (remaining data, excluding checksum)
 114  };
 115  
 116  } // namespace codex32
 117  
 118  #endif // LIMENKA_CODEX32_H
 119