Coverage Report

Created: 2026-02-14 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/math/bigint/big_code.cpp
Line
Count
Source
1
/*
2
* BigInt Encoding/Decoding
3
* (C) 1999-2010,2012,2019,2021 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/bigint.h>
9
10
#include <botan/hex.h>
11
#include <botan/mem_ops.h>
12
#include <botan/internal/divide.h>
13
#include <botan/internal/stl_util.h>
14
15
namespace Botan {
16
17
namespace {
18
19
consteval word decimal_conversion_radix() {
20
   if constexpr(sizeof(word) == 8) {
21
      return 10000000000000000000U;
22
   } else {
23
      return 1000000000U;
24
   }
25
}
26
27
consteval size_t decimal_conversion_radix_digits() {
28
   if constexpr(sizeof(word) == 8) {
29
      return 19;
30
   } else {
31
      return 9;
32
   }
33
}
34
35
}  // namespace
36
37
540
std::string BigInt::to_dec_string() const {
38
   // Use the largest power of 10 that fits in a word
39
540
   constexpr word conversion_radix = decimal_conversion_radix();
40
540
   constexpr size_t radix_digits = decimal_conversion_radix_digits();
41
42
   // (over-)estimate of the number of digits needed; log2(10) ~ 3.3219
43
540
   const size_t digit_estimate = static_cast<size_t>(1 + (static_cast<double>(this->bits()) / 3.32));
44
45
   // (over-)estimate of db such that conversion_radix^db > *this
46
540
   const size_t digit_blocks = (digit_estimate + radix_digits - 1) / radix_digits;
47
48
540
   BigInt value = *this;
49
540
   value.set_sign(Positive);
50
51
   // Extract groups of digits into words
52
540
   std::vector<word> digit_groups(digit_blocks);
53
54
4.29k
   for(size_t i = 0; i != digit_blocks; ++i) {
55
3.75k
      word remainder = 0;
56
3.75k
      ct_divide_word(value, conversion_radix, value, remainder);
57
3.75k
      digit_groups[i] = remainder;
58
3.75k
   }
59
60
540
   BOTAN_ASSERT_NOMSG(value.is_zero());
61
62
   // Extract digits from the groups
63
540
   std::vector<uint8_t> digits(digit_blocks * radix_digits);
64
65
4.29k
   for(size_t i = 0; i != digit_blocks; ++i) {
66
3.75k
      word remainder = digit_groups[i];
67
75.1k
      for(size_t j = 0; j != radix_digits; ++j) {
68
         // Compiler should convert div/mod by 10 into mul by magic constant
69
71.4k
         const word digit = remainder % 10;
70
71.4k
         remainder /= 10;
71
71.4k
         digits[radix_digits * i + j] = static_cast<uint8_t>(digit);
72
71.4k
      }
73
3.75k
   }
74
75
   // remove leading zeros
76
9.53k
   while(!digits.empty() && digits.back() == 0) {
77
8.99k
      digits.pop_back();
78
8.99k
   }
79
80
540
   BOTAN_ASSERT_NOMSG(digit_estimate >= digits.size());
81
82
   // Reverse the digits to big-endian and format to text
83
540
   std::string s;
84
540
   s.reserve(1 + digits.size());
85
86
540
   if(is_negative()) {
87
0
      s += "-";
88
0
   }
89
90
   // Reverse and convert to textual digits
91
   // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
92
   // NOLINTNEXTLINE(modernize-loop-convert)
93
62.9k
   for(auto i = digits.rbegin(); i != digits.rend(); ++i) {
94
62.4k
      s.push_back(*i + '0');  // assumes ASCII
95
62.4k
   }
96
97
540
   if(s.empty()) {
98
0
      s += "0";
99
0
   }
100
101
540
   return s;
102
540
}
103
104
2.58k
std::string BigInt::to_hex_string() const {
105
2.58k
   const size_t this_bytes = this->bytes();
106
2.58k
   std::vector<uint8_t> bits(std::max<size_t>(1, this_bytes));
107
108
2.58k
   if(this_bytes > 0) {
109
1.88k
      this->serialize_to(bits);
110
1.88k
   }
111
112
2.58k
   std::string hrep;
113
2.58k
   if(is_negative()) {
114
0
      hrep += "-";
115
0
   }
116
2.58k
   hrep += "0x";
117
2.58k
   hrep += hex_encode(bits);
118
2.58k
   return hrep;
119
2.58k
}
120
121
/*
122
* Encode two BigInt, with leading 0s if needed, and concatenate
123
*/
124
0
secure_vector<uint8_t> BigInt::encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes) {
125
0
   if(n1.is_negative() || n2.is_negative()) {
126
0
      throw Encoding_Error("encode_fixed_length_int_pair: values must be positive");
127
0
   }
128
0
   if(n1.bytes() > bytes || n2.bytes() > bytes) {
129
0
      throw Encoding_Error("encode_fixed_length_int_pair: values too large to encode properly");
130
0
   }
131
0
   secure_vector<uint8_t> output(2 * bytes);
132
0
   BufferStuffer stuffer(output);
133
0
   n1.serialize_to(stuffer.next(bytes));
134
0
   n2.serialize_to(stuffer.next(bytes));
135
0
   return output;
136
0
}
137
138
14.5k
BigInt BigInt::decode(std::span<const uint8_t> buf, Base base) {
139
14.5k
   if(base == Binary) {
140
0
      return BigInt::from_bytes(buf);
141
0
   }
142
14.5k
   return BigInt::decode(buf.data(), buf.size(), base);
143
14.5k
}
144
145
/*
146
* Decode a BigInt
147
*/
148
14.5k
BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base) {
149
14.5k
   if(base == Binary) {
150
0
      return BigInt::from_bytes(std::span{buf, length});
151
14.5k
   } else if(base == Hexadecimal) {
152
6
      BigInt r;
153
6
      secure_vector<uint8_t> binary;
154
155
6
      if(length % 2 == 1) {
156
         // Handle lack of leading 0
157
0
         const char buf0_with_leading_0[2] = {'0', static_cast<char>(buf[0])};
158
159
0
         binary = hex_decode_locked(buf0_with_leading_0, 2);
160
161
0
         if(length > 1) {
162
0
            binary += hex_decode_locked(cast_uint8_ptr_to_char(&buf[1]), length - 1, false);
163
0
         }
164
6
      } else {
165
6
         binary = hex_decode_locked(cast_uint8_ptr_to_char(buf), length, false);
166
6
      }
167
168
6
      r.assign_from_bytes(binary);
169
6
      return r;
170
14.5k
   } else if(base == Decimal) {
171
14.5k
      BigInt r;
172
      // This could be made faster using the same trick as to_dec_string
173
1.23M
      for(size_t i = 0; i != length; ++i) {
174
1.22M
         const char c = buf[i];
175
176
1.22M
         if(c < '0' || c > '9') {
177
0
            throw Invalid_Argument("BigInt::decode: invalid decimal char");
178
0
         }
179
180
1.22M
         const uint8_t x = c - '0';
181
1.22M
         BOTAN_ASSERT_NOMSG(x < 10);
182
183
1.22M
         r *= 10;
184
1.22M
         r += x;
185
1.22M
      }
186
14.5k
      return r;
187
14.5k
   } else {
188
0
      throw Invalid_Argument("Unknown BigInt decoding method");
189
0
   }
190
14.5k
}
191
192
}  // namespace Botan