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