Botan 3.11.0
Crypto and TLS for C&
big_code.cpp
Go to the documentation of this file.
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/assert.h>
11#include <botan/exceptn.h>
12#include <botan/hex.h>
13#include <botan/mem_ops.h>
14#include <botan/internal/buffer_stuffer.h>
15#include <botan/internal/divide.h>
16#include <botan/internal/mp_core.h>
17
18namespace Botan {
19
20namespace {
21
22consteval word decimal_conversion_radix() {
23 if constexpr(sizeof(word) == 8) {
24 return 10000000000000000000U;
25 } else {
26 return 1000000000U;
27 }
28}
29
30consteval size_t decimal_conversion_radix_digits() {
31 if constexpr(sizeof(word) == 8) {
32 return 19;
33 } else {
34 return 9;
35 }
36}
37
38} // namespace
39
40std::string BigInt::to_dec_string() const {
41 // Use the largest power of 10 that fits in a word
42 constexpr word conversion_radix = decimal_conversion_radix();
43 constexpr size_t radix_digits = decimal_conversion_radix_digits();
44
45 // (over-)estimate of the number of digits needed; log2(10) ~ 3.3219
46 const size_t digit_estimate = static_cast<size_t>(1 + (static_cast<double>(this->bits()) / 3.32));
47
48 // (over-)estimate of db such that conversion_radix^db > *this
49 const size_t digit_blocks = (digit_estimate + radix_digits - 1) / radix_digits;
50
51 BigInt value = *this;
52 value.set_sign(Positive);
53
54 // Extract groups of digits into words
55 std::vector<word> digit_groups(digit_blocks);
56
57 for(size_t i = 0; i != digit_blocks; ++i) {
58 word remainder = 0;
59 ct_divide_word(value, conversion_radix, value, remainder);
60 digit_groups[i] = remainder;
61 }
62
64
65 // Extract digits from the groups
66 std::vector<uint8_t> digits(digit_blocks * radix_digits);
67
68 for(size_t i = 0; i != digit_blocks; ++i) {
69 word remainder = digit_groups[i];
70 for(size_t j = 0; j != radix_digits; ++j) {
71 const word new_remainder = divide_10(remainder);
72 const word digit = remainder - new_remainder * 10;
73 digits[radix_digits * i + j] = static_cast<uint8_t>(digit);
74 remainder = new_remainder;
75 }
76 }
77
78 // remove leading zeros
79 while(!digits.empty() && digits.back() == 0) {
80 digits.pop_back();
81 }
82
83 BOTAN_ASSERT_NOMSG(digit_estimate >= digits.size());
84
85 // Reverse the digits to big-endian and format to text
86 std::string s;
87 s.reserve(1 + digits.size());
88
89 if(is_negative()) {
90 s += "-";
91 }
92
93 // Reverse and convert to textual digits
94 // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
95 // NOLINTNEXTLINE(modernize-loop-convert)
96 for(auto i = digits.rbegin(); i != digits.rend(); ++i) {
97 s.push_back(*i + '0'); // assumes ASCII
98 }
99
100 if(s.empty()) {
101 s += "0";
102 }
103
104 return s;
105}
106
107std::string BigInt::to_hex_string() const {
108 const size_t this_bytes = this->bytes();
109 std::vector<uint8_t> bits(std::max<size_t>(1, this_bytes));
110
111 if(this_bytes > 0) {
112 this->serialize_to(bits);
113 }
114
115 std::string hrep;
116 if(is_negative()) {
117 hrep += "-";
118 }
119 hrep += "0x";
120 hrep += hex_encode(bits);
121 return hrep;
122}
123
124//static
125BigInt BigInt::from_radix_digits(std::string_view digits, size_t radix) {
126 if(radix == 16) {
128
129 if(digits.size() % 2 == 1) {
130 // Handle lack of leading 0
131 const char buf0_with_leading_0[2] = {'0', digits[0]};
132
133 binary = hex_decode_locked(buf0_with_leading_0, 2);
134
135 if(digits.size() > 1) {
136 binary += hex_decode_locked(&digits[1], digits.size() - 1, false);
137 }
138 } else {
139 binary = hex_decode_locked(digits, false);
140 }
141
142 return BigInt::from_bytes(binary);
143 } else if(radix == 10) {
144 // Use the largest power of 10 that fits in a word, accumulating
145 // groups of digits into word-sized chunks to minimize the number
146 // of multiprecision multiplications.
147 constexpr word conversion_radix = decimal_conversion_radix();
148 constexpr size_t radix_digits = decimal_conversion_radix_digits();
149
150 BigInt r;
151
152 // Handle the initial partial block (if digit count is not a multiple of radix_digits)
153 const size_t partial_block = digits.size() % radix_digits;
154
155 if(partial_block > 0) {
156 word acc = 0;
157 for(size_t i = 0; i < partial_block; ++i) {
158 const char c = digits[i];
159 BOTAN_ARG_CHECK(c >= '0' && c <= '9', "Invalid decimal character");
160 acc = acc * 10 + static_cast<word>(c - '0');
161 }
162 r += acc;
163 }
164
165 // Process full blocks of radix_digits
166 for(size_t i = partial_block; i != digits.size(); i += radix_digits) {
167 word acc = 0;
168 for(size_t j = 0; j < radix_digits; ++j) {
169 const char c = digits[i + j];
170 BOTAN_ARG_CHECK(c >= '0' && c <= '9', "Invalid decimal character");
171 acc = acc * 10 + static_cast<word>(c - '0');
172 }
173 r *= conversion_radix;
174 r += acc;
175 }
176
177 return r;
178 } else {
179 throw Invalid_Argument("BigInt::from_radix_digits unknown radix");
180 }
181}
182
183/*
184* Encode two BigInt, with leading 0s if needed, and concatenate
185*/
187 if(n1.is_negative() || n2.is_negative()) {
188 throw Encoding_Error("encode_fixed_length_int_pair: values must be positive");
189 }
190 if(n1.bytes() > bytes || n2.bytes() > bytes) {
191 throw Encoding_Error("encode_fixed_length_int_pair: values too large to encode properly");
192 }
193 secure_vector<uint8_t> output(2 * bytes);
194 BufferStuffer stuffer(output);
195 n1.serialize_to(stuffer.next(bytes));
196 n2.serialize_to(stuffer.next(bytes));
197 return output;
198}
199
200BigInt BigInt::decode(std::span<const uint8_t> buf, Base base) {
201 if(base == Binary) {
202 return BigInt::from_bytes(buf);
203 }
204 return BigInt::decode(buf.data(), buf.size(), base);
205}
206
207/*
208* Decode a BigInt
209*/
210BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base) {
211 if(base == Binary) {
212 return BigInt::from_bytes(std::span{buf, length});
213 } else if(base == Hexadecimal) {
214 const std::string_view sv{cast_uint8_ptr_to_char(buf), length};
215 return BigInt::from_radix_digits(sv, 16);
216 } else if(base == Decimal) {
217 const std::string_view sv{cast_uint8_ptr_to_char(buf), length};
218 return BigInt::from_radix_digits(sv, 10);
219 } else {
220 throw Invalid_Argument("Unknown BigInt decoding method");
221 }
222}
223
224} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
BigInt()=default
static BigInt decode(const uint8_t buf[], size_t length)
Definition bigint.h:873
std::string to_dec_string() const
Definition big_code.cpp:40
std::string to_hex_string() const
Definition big_code.cpp:107
static secure_vector< uint8_t > encode_fixed_length_int_pair(const BigInt &n1, const BigInt &n2, size_t bytes)
Definition big_code.cpp:186
void serialize_to(std::span< uint8_t > out) const
Definition bigint.cpp:395
static BigInt from_bytes(std::span< const uint8_t > bytes)
Definition bigint.cpp:83
size_t bits() const
Definition bigint.cpp:307
bool is_zero() const
Definition bigint.h:473
bool is_negative() const
Definition bigint.h:575
size_t bytes() const
Definition bigint.cpp:294
static BigInt from_radix_digits(std::string_view digits, size_t radix)
Definition big_code.cpp:125
void set_sign(Sign sign)
Definition bigint.h:608
Helper class to ease in-place marshalling of concatenated fixed-length values.
constexpr std::span< uint8_t > next(size_t bytes)
secure_vector< uint8_t > hex_decode_locked(const char input[], size_t input_length, bool ignore_ws)
Definition hex.cpp:135
constexpr W divide_10(W x)
Definition mp_core.h:545
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:34
void ct_divide_word(const BigInt &x, word y, BigInt &q_out, word &r_out)
Definition divide.cpp:123
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:281
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
std::conditional_t< HasNative64BitRegisters, std::uint64_t, uint32_t > word
Definition types.h:119