1#include <algorithm>
2#include <array>
3#include <cassert>
4#include <cstdint>
5#include <string>
6#include <vector>
7
8namespace {
9template<std::size_t N, std::size_t... Is>
10constexpr std::array<char, N - 1>
11to_array(const char (&a)[N], std::index_sequence<Is...>)
12{
13 return {{a[Is]...}};
14}
15
16template<std::size_t N>
17constexpr std::array<char, N - 1>
18to_array(const char (&a)[N])
19{
20 return to_array(a, std::make_index_sequence<N - 1>());
21}
22
23static constexpr const std::array base64_alphabet =
24 to_array(a: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
25static constexpr const std::array base64_urlsafe_alphabet =
26 to_array(a: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
27static constexpr const std::array base58_alphabet =
28 to_array(a: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
29
30static_assert(base64_alphabet.size() == 64);
31static_assert(base58_alphabet.size() == 58);
32
33template<std::size_t N>
34constexpr std::array<uint8_t, 256>
35invert_alphabet(std::array<char, N> alphabet)
36{
37 std::array<uint8_t, 256> inverted{};
38
39 for (auto &e : inverted)
40 e = 0xff;
41
42 for (std::size_t i = 0; i < N; i++) {
43 inverted[static_cast<uint8_t>(alphabet[i])] = static_cast<uint8_t>(i);
44 }
45
46 return inverted;
47}
48
49static constexpr const std::array base64_to_int = invert_alphabet(alphabet: base64_alphabet);
50static constexpr const std::array base64_urlsafe_to_int = invert_alphabet(alphabet: base64_urlsafe_alphabet);
51static constexpr const std::array base58_to_int = invert_alphabet(alphabet: base58_alphabet);
52
53static_assert(base64_to_int['A'] == 0);
54static_assert(base64_to_int['B'] == 1);
55static_assert(base64_to_int['C'] == 2);
56static_assert(base64_to_int[0] == 0xff);
57
58// algorithm from https://github.com/miguelmota/cpp-base58 MIT Licensed
59inline std::string
60encode_base58(const std::array<char, 58> &alphabet, const std::string &input)
61{
62 if (input.empty())
63 return "";
64
65 std::vector<uint8_t> digits(input.size() * 138 / 100 + 1);
66 std::size_t digitslen = 1;
67 for (uint8_t carry_ : input) {
68 uint32_t carry = static_cast<uint32_t>(carry_);
69 for (size_t j = 0; j < digitslen; j++) {
70 carry += (uint32_t)(digits[j]) * 256;
71 digits[j] = static_cast<uint8_t>(carry % 58);
72 carry /= 58;
73 }
74 while (carry > 0) {
75 assert(digitslen < digits.size());
76 digits[digitslen++] = static_cast<uint8_t>(carry % 58);
77 carry /= 58;
78 }
79 }
80 std::string result(digits.size(), ' ');
81
82 // leading zero bytes
83 std::size_t resultlen = 0;
84 for (; resultlen < input.length() && input[resultlen] == 0;)
85 result[resultlen++] = '1';
86
87 // reverse
88 for (size_t i = 0; i < digitslen; i++)
89 result[resultlen + i] = alphabet[digits[digitslen - 1 - i]];
90 result.resize(n: digitslen + resultlen);
91
92 return result;
93}
94
95inline std::string
96decode_base58(const std::array<uint8_t, 256> &reverse_alphabet, const std::string &input)
97{
98 std::string result;
99 if (input.empty())
100 return result;
101
102 result.reserve(res: input.size() * 733 / 1000 + 1);
103
104 // result.push_back(0);
105
106 for (uint8_t b : input) {
107 if (b == ' ')
108 continue;
109
110 if (b == 0xff)
111 return "";
112
113 uint32_t carry = reverse_alphabet[b];
114 for (char &j : result) {
115 carry += static_cast<uint8_t>(j) * 58;
116 j = static_cast<char>(static_cast<uint8_t>(carry % 0x100));
117 carry /= 0x100;
118 }
119 while (carry > 0) {
120 result.push_back(c: static_cast<char>(static_cast<uint8_t>(carry % 0x100)));
121 carry /= 0x100;
122 }
123 }
124
125 for (size_t i = 0; i < input.length() && input[i] == '1'; i++)
126 result.push_back(c: 0);
127
128 std::reverse(first: result.begin(), last: result.end());
129 return result;
130}
131
132template<bool pad>
133inline std::string
134encode_base64(const std::array<char, 64> &alphabet, std::string input)
135{
136 std::string encoded;
137
138 size_t missing = 0;
139
140 while ((input.size() + missing) % 3)
141 missing++;
142
143 encoded.reserve(res: (input.size() * 4 + 2) / 3);
144
145 for (size_t i = 0; i < input.size(); i += 3) {
146 uint32_t bytes = static_cast<uint8_t>(input[i]) << 16;
147 if (i + 1 < input.size())
148 bytes += static_cast<uint8_t>(input[i + 1]) << 8;
149 if (i + 2 < input.size())
150 bytes += static_cast<uint8_t>(input[i + 2]);
151 encoded.push_back(c: alphabet[(bytes >> 18) & 0b11'1111]);
152 encoded.push_back(c: alphabet[(bytes >> 12) & 0b11'1111]);
153 encoded.push_back(c: alphabet[(bytes >> 6) & 0b11'1111]);
154 encoded.push_back(c: alphabet[bytes & 0b11'1111]);
155 }
156
157 if constexpr (pad) {
158 while (missing) {
159 encoded[encoded.size() - missing] = '=';
160 missing--;
161 }
162 }
163
164 encoded.resize(n: encoded.size() - missing);
165 return encoded;
166}
167
168inline std::string
169decode_base64(const std::array<uint8_t, 256> &reverse_alphabet, const std::string &input)
170{
171 std::string decoded;
172 decoded.reserve(res: (input.size() * 3 + 2) / 4);
173
174 int bit_index = 0;
175 uint8_t d = 0;
176 for (uint8_t b : input) {
177 if (b == '=')
178 break;
179
180 d = reverse_alphabet[b];
181
182 if (d > 64)
183 break;
184
185 switch (bit_index++) {
186 case 0:
187 decoded.push_back(c: static_cast<char>(d << 2));
188 break;
189 case 1:
190 decoded.back() = static_cast<char>(decoded.back() + (d >> 4));
191 decoded.push_back(c: static_cast<char>(d << 4));
192 break;
193 case 2:
194 decoded.back() = static_cast<char>(decoded.back() + (d >> 2));
195 decoded.push_back(c: static_cast<char>(d << 6));
196 break;
197 case 3:
198 decoded.back() = static_cast<char>(decoded.back() + d);
199 bit_index = 0;
200 }
201 }
202
203 if ((bit_index == 2 && static_cast<uint8_t>(d << 4) == 0) ||
204 (bit_index == 3 && static_cast<uint8_t>(d << 6) == 0))
205 decoded.pop_back();
206
207 return decoded;
208}
209}
210
211namespace mtx {
212namespace crypto {
213std::string
214base642bin(const std::string &b64)
215{
216 return decode_base64(reverse_alphabet: base64_to_int, input: b64);
217}
218
219std::string
220bin2base64(const std::string &bin)
221{
222 return encode_base64<true>(alphabet: base64_alphabet, input: bin);
223}
224
225std::string
226base642bin_unpadded(const std::string &b64)
227{
228 return decode_base64(reverse_alphabet: base64_to_int, input: b64);
229}
230
231std::string
232bin2base64_unpadded(const std::string &bin)
233{
234 return encode_base64<false>(alphabet: base64_alphabet, input: bin);
235}
236
237std::string
238base642bin_urlsafe_unpadded(const std::string &b64)
239{
240 return decode_base64(reverse_alphabet: base64_urlsafe_to_int, input: b64);
241}
242
243std::string
244bin2base64_urlsafe_unpadded(const std::string &bin)
245{
246 return encode_base64<false>(alphabet: base64_urlsafe_alphabet, input: bin);
247}
248
249std::string
250bin2base58(const std::string &bin)
251{
252 return encode_base58(alphabet: base58_alphabet, input: bin);
253}
254
255std::string
256base582bin(const std::string &bin)
257{
258 return decode_base58(reverse_alphabet: base58_to_int, input: bin);
259}
260}
261}
262