• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 27737522519

18 Jun 2026 02:59AM UTC coverage: 89.397% (-2.3%) from 91.708%
27737522519

push

github

randombit
Remove unused member variable from Encrypted_PSK_Database

111425 of 124641 relevant lines covered (89.4%)

11108238.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.12
/src/lib/codec/base58/base58.cpp
1
/*
2
* (C) 2018,2020,2026 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include <botan/base58.h>
8

9
#include <botan/bigint.h>
10
#include <botan/exceptn.h>
11
#include <botan/hash.h>
12
#include <botan/internal/ct_utils.h>
13
#include <botan/internal/divide.h>
14
#include <botan/internal/int_utils.h>
15
#include <botan/internal/loadstor.h>
16
#include <botan/internal/mul128.h>
17

18
namespace Botan {
19

20
namespace {
21

22
uint32_t sha256_d_checksum(const uint8_t input[], size_t input_length) {
16✔
23
   auto sha256 = HashFunction::create_or_throw("SHA-256");
16✔
24

25
   std::vector<uint8_t> checksum(32);
16✔
26

27
   sha256->update(input, input_length);
16✔
28
   sha256->final(checksum);
16✔
29

30
   sha256->update(checksum);
16✔
31
   sha256->final(checksum);
16✔
32

33
   return load_be<uint32_t>(checksum.data(), 0);
16✔
34
}
32✔
35

36
char lookup_base58_char(uint8_t x) {
352✔
37
   // "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz"
38
   BOTAN_DEBUG_ASSERT(x < 58);
352✔
39

40
   // This works by computing offset(x) such that x + offset(x) is equal to the
41
   // desired character
42

43
   size_t offset = 49;
352✔
44

45
   offset += CT::Mask<uint8_t>::is_gt(x, 8).if_set_return(7);
352✔
46
   offset += CT::Mask<uint8_t>::is_gt(x, 16).if_set_return(1);
352✔
47
   offset += CT::Mask<uint8_t>::is_gt(x, 21).if_set_return(1);
352✔
48
   offset += CT::Mask<uint8_t>::is_gt(x, 32).if_set_return(6);
352✔
49
   offset += CT::Mask<uint8_t>::is_gt(x, 43).if_set_return(1);
352✔
50
   return static_cast<char>(x + offset);
352✔
51
}
52

53
consteval word base58_conversion_radix() {
54
   if constexpr(sizeof(word) == 8) {
55
      // 58^10 largest that fits into a 64 bit word
56
      return 430804206899405824U;
57
   } else {
58
      // 58^5 largest that fits into a 32 bit word
59
      return 656356768U;
60
   }
61
}
62

63
consteval size_t base58_conversion_radix_digits() {
64
   if constexpr(sizeof(word) == 8) {
65
      return 10;
66
   } else {
67
      return 5;
68
   }
69
}
70

71
constexpr std::pair<uint8_t, word> divmod_58(word x) {
670✔
72
   BOTAN_DEBUG_ASSERT(x < base58_conversion_radix());
670✔
73

74
   word q = 0;
670✔
75

76
   // Division by constant 58
77
   //
78
   // Compilers will *usually* convert an expression like `x / 58` into
79
   // exactly this kind of operation, but not necessarily always...
80
   if constexpr(sizeof(word) == 4) {
670✔
81
      const uint64_t magic = 2369637129;  // ceil(2**36 / 29)
82
      const uint64_t z = magic * x;
83
      q = z >> 37;
84
   } else {
85
      const uint64_t magic = 5088756985850910791;  // ceil(2**67 / 29)
670✔
86
      uint64_t lo = 0;                             // unused
670✔
87
      uint64_t hi = 0;
670✔
88
      mul64x64_128(magic, x >> 1, &lo, &hi);
670✔
89
      q = static_cast<word>(hi >> 3);
670✔
90
   }
91

92
   const uint8_t r = static_cast<uint8_t>(x - q * 58);
670✔
93
   return std::make_pair(r, q);
670✔
94
}
95

96
std::string base58_encode(BigInt v, size_t leading_zeros) {
46✔
97
   constexpr word radix = base58_conversion_radix();
46✔
98
   constexpr size_t radix_digits = base58_conversion_radix_digits();
46✔
99

100
   BigInt q;
46✔
101
   std::vector<uint8_t> digits;
46✔
102

103
   while(!v.is_zero()) {
226✔
104
      word r = 0;
67✔
105
      ct_divide_word(v, radix, q, r);
67✔
106

107
      for(size_t i = 0; i != radix_digits; ++i) {
737✔
108
         const auto [r58, q58] = divmod_58(r);
670✔
109
         digits.push_back(r58);
670✔
110
         r = q58;
670✔
111
      }
112
      v.swap(q);
67✔
113
   }
114

115
   // remove leading zeros
116
   while(!digits.empty() && digits.back() == 0) {
364✔
117
      digits.pop_back();
318✔
118
   }
119

120
   std::string result;
46✔
121

122
   for(const uint8_t d : digits) {
398✔
123
      result.push_back(lookup_base58_char(d));
352✔
124
   }
125

126
   for(size_t i = 0; i != leading_zeros; ++i) {
61✔
127
      result.push_back('1');  // 'zero' byte
15✔
128
   }
129

130
   return std::string(result.rbegin(), result.rend());
92✔
131
}
89✔
132

133
template <typename T, typename Z>
134
size_t count_leading_zeros(const T input[], size_t input_length, Z zero) {
68✔
135
   size_t leading_zeros = 0;
68✔
136

137
   while(leading_zeros < input_length && input[leading_zeros] == zero) {
137✔
138
      leading_zeros += 1;
30✔
139
   }
140

141
   return leading_zeros;
142
}
143

144
uint8_t base58_value_of(char input) {
364✔
145
   /*
146
   * Alphabet: "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz"
147
   *
148
   * Valid input ranges are:
149
   *
150
   * '1'-'9' (length 9)
151
   * 'A'-'H' (length 8)
152
   * 'J'-'N' (length 5)
153
   * 'P'-'Z' (length 11)
154
   * 'a'-'k' (length 11)
155
   * 'm'-'z' (length 14)
156
   */
157
   constexpr uint64_t v_lo = make_uint64(0, '1', 'A', 'J', 'P', 'a', 'm', 0);
364✔
158
   constexpr uint64_t v_range = make_uint64(0, 9, 8, 5, 11, 11, 14, 0);
364✔
159

160
   constexpr uint64_t expand8 = 0x0101010101010101;
364✔
161

162
   const uint8_t x = static_cast<uint8_t>(input);
364✔
163
   const uint64_t x8 = x * expand8;  // replicate x to each byte
364✔
164

165
   // is x8 in any of the ranges?
166
   const uint64_t v_mask = swar_in_range<uint64_t>(x8, v_lo, v_range) ^ 0x8000000000000000;
364✔
167

168
   /*
169
   * Offsets mapping from the character code x to the base58 value of x in each range
170
   *
171
   * For example '2' (50) + 0xCF == 1
172
   *
173
   * Fallback byte 7 is set to 0xFF - x so that if used it results in 0xFF to indicate invalid.
174
   */
175
   constexpr uint64_t val_v_const = make_uint64(0, 0xCF, 0xC8, 0xC7, 0xC6, 0xC0, 0xBF, 0);
364✔
176
   const uint64_t val_v = val_v_const ^ (static_cast<uint64_t>(0xFF - x) << 56);
364✔
177

178
   return x + static_cast<uint8_t>(val_v >> (8 * index_of_first_set_byte(v_mask)));
364✔
179
}
180

181
}  // namespace
182

183
std::string base58_encode(const uint8_t input[], size_t input_length) {
39✔
184
   const BigInt v(input, input_length);
39✔
185
   return base58_encode(v, count_leading_zeros(input, input_length, 0));
78✔
186
}
39✔
187

188
std::string base58_check_encode(const uint8_t input[], size_t input_length) {
7✔
189
   BigInt v(input, input_length);
7✔
190
   v <<= 32;
7✔
191
   v += sha256_d_checksum(input, input_length);
7✔
192
   return base58_encode(v, count_leading_zeros(input, input_length, 0));
21✔
193
}
7✔
194

195
std::vector<uint8_t> base58_decode(const char input[], size_t input_length) {
61✔
196
   const size_t leading_zeros = count_leading_zeros(input, input_length, '1');
61✔
197

198
   std::vector<uint8_t> digits;
61✔
199

200
   for(size_t i = leading_zeros; i != input_length; ++i) {
411✔
201
      const char c = input[i];
364✔
202

203
      if(c == ' ' || c == '\n') {
364✔
204
         continue;
×
205
      }
206

207
      const uint8_t idx = base58_value_of(c);
364✔
208

209
      if(idx == 0xFF) {
364✔
210
         throw Decoding_Error("Invalid base58");
14✔
211
      }
212

213
      digits.push_back(idx);
350✔
214
   }
215

216
   BigInt v;
47✔
217

218
   constexpr word radix1 = 58;
47✔
219
   constexpr word radix2 = 58 * 58;
47✔
220
   constexpr word radix3 = 58 * 58 * 58;
47✔
221
   constexpr word radix4 = 58 * 58 * 58 * 58;
47✔
222

223
   std::span<uint8_t> remaining{digits};
47✔
224

225
   while(remaining.size() >= 4) {
111✔
226
      const word accum = radix3 * remaining[0] + radix2 * remaining[1] + radix1 * remaining[2] + remaining[3];
64✔
227
      v *= radix4;
64✔
228
      v += accum;
64✔
229
      remaining = remaining.subspan(4);
64✔
230
   }
231

232
   while(!remaining.empty()) {
100✔
233
      v *= 58;
53✔
234
      v += remaining[0];
53✔
235
      remaining = remaining.subspan(1);
53✔
236
   }
237

238
   return v.serialize(v.bytes() + leading_zeros);
47✔
239
}
91✔
240

241
std::vector<uint8_t> base58_check_decode(const char input[], size_t input_length) {
10✔
242
   std::vector<uint8_t> dec = base58_decode(input, input_length);
10✔
243

244
   if(dec.size() < 4) {
10✔
245
      throw Decoding_Error("Invalid base58 too short for checksum");
1✔
246
   }
247

248
   const uint32_t computed_checksum = sha256_d_checksum(dec.data(), dec.size() - 4);
9✔
249
   const uint32_t checksum = load_be<uint32_t>(&dec[dec.size() - 4], 0);
9✔
250

251
   if(checksum != computed_checksum) {
9✔
252
      throw Decoding_Error("Invalid base58 checksum");
4✔
253
   }
254

255
   dec.resize(dec.size() - 4);
5✔
256

257
   return dec;
5✔
258
}
5✔
259

260
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc