// Copyright (c) 2023 The Limenka developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // codex32 is a string encoding format for BIP-32 seeds. Like bech32 and // bech32m, the outputs consist of a human-readable part (alphanumeric), // a separator character (1), and a base32 data section. The final 13 // characters are a checksum. // // For more information, see BIP 93. #ifndef LIMENKA_CODEX32_H #define LIMENKA_CODEX32_H #include #include #include #include #include #include #include namespace codex32 { enum Error { OK, BAD_CHECKSUM, BECH32_DECODE, INVALID_HRP, INVALID_ID_LEN, INVALID_ID_CHAR, INVALID_LENGTH, INVALID_K, INVALID_SHARE_IDX, TOO_FEW_SHARES, DUPLICATE_SHARE, MISMATCH_K, MISMATCH_ID, MISMATCH_LENGTH, }; std::string ErrorString(Error e); class Result { public: /** Construct a codex32 result by parsing a string */ Result(const std::string& str); /** Construct a codex32 directly from a HRP, k, seed ID, share index and payload * * This constructor requires the hrp to be the lowercase string "ms", but will * ignore the case of `id` and `share_idx`. */ Result(std::string&& hrp, size_t k, const std::string& id, char share_idx, const std::vector& data); /** Construct a codex32 result by interpolating a set of input shares to obtain an output share * * Requires that all input shares have the same k and seed ID */ Result(const std::vector& shares, char output_idx); /** Boolean indicating whether the data was successfully parsed. * * If this returns false, most of the other methods on this class will assert. */ bool IsValid() const { return m_valid == OK; } /** Accessor for the specific parsing/construction error */ Error error() const { return m_valid; } /** Accessor for the human-readable part of the codex32 string */ const std::string& GetHrp() const { assert(IsValid()); return m_hrp; } /** Accessor for the seed ID, in string form */ std::string GetIdString() const; /** Accessor for the secret sharing threshold; 0 for a bare seed; (size_t)-1 if unavailable/invalid */ size_t GetK() const; /** Accessor for the share index; (uint8_t)-1 if unavailable/invalid */ char GetShareIndex() const { assert(IsValid()); return bech32::internal::CHARSET[m_data[5]]; } /** Accessor for the binary payload data (in base 256, not gf32) */ std::vector GetPayload() const { assert(IsValid()); std::vector ret; ret.reserve(((m_data.size() - 6) * 5) / 8); // Note that `ConvertBits` returns a bool indicating whether or not nonzero bits // were discarded. In BIP 93, we discard bits regardless of whether they are 0, // so this is not an error and does not need to be checked. ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, m_data.begin() + 6, m_data.end()); return ret; }; /** (Re-)encode the codex32 data as a hrp string */ std::string Encode() const; private: Error m_valid; //!< codex32::OK if the string was decoded correctly std::string m_hrp; //!< The human readable part std::vector m_data; //!< The payload (remaining data, excluding checksum) }; } // namespace codex32 #endif // LIMENKA_CODEX32_H