Botan 3.9.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/hex.h>
11#include <botan/mem_ops.h>
12#include <botan/internal/divide.h>
13#include <botan/internal/stl_util.h>
14
15namespace Botan {
16
17namespace {
18
19consteval word decimal_conversion_radix() {
20 if constexpr(sizeof(word) == 8) {
21 return 10000000000000000000U;
22 } else {
23 return 1000000000U;
24 }
25}
26
27consteval 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
37std::string BigInt::to_dec_string() const {
38 // Use the largest power of 10 that fits in a word
39 constexpr word conversion_radix = decimal_conversion_radix();
40 constexpr size_t radix_digits = decimal_conversion_radix_digits();
41
42 // (over-)estimate of the number of digits needed; log2(10) ~ 3.3219
43 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 const size_t digit_blocks = (digit_estimate + radix_digits - 1) / radix_digits;
47
48 BigInt value = *this;
49 value.set_sign(Positive);
50
51 // Extract groups of digits into words
52 std::vector<word> digit_groups(digit_blocks);
53
54 for(size_t i = 0; i != digit_blocks; ++i) {
55 word remainder = 0;
56 ct_divide_word(value, conversion_radix, value, remainder);
57 digit_groups[i] = remainder;
58 }
59
61
62 // Extract digits from the groups
63 std::vector<uint8_t> digits(digit_blocks * radix_digits);
64
65 for(size_t i = 0; i != digit_blocks; ++i) {
66 word remainder = digit_groups[i];
67 for(size_t j = 0; j != radix_digits; ++j) {
68 // Compiler should convert div/mod by 10 into mul by magic constant
69 const word digit = remainder % 10;
70 remainder /= 10;
71 digits[radix_digits * i + j] = static_cast<uint8_t>(digit);
72 }
73 }
74
75 // remove leading zeros
76 while(!digits.empty() && digits.back() == 0) {
77 digits.pop_back();
78 }
79
80 BOTAN_ASSERT_NOMSG(digit_estimate >= digits.size());
81
82 // Reverse the digits to big-endian and format to text
83 std::string s;
84 s.reserve(1 + digits.size());
85
86 if(is_negative()) {
87 s += "-";
88 }
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 for(auto i = digits.rbegin(); i != digits.rend(); ++i) {
94 s.push_back(*i + '0'); // assumes ASCII
95 }
96
97 if(s.empty()) {
98 s += "0";
99 }
100
101 return s;
102}
103
104std::string BigInt::to_hex_string() const {
105 const size_t this_bytes = this->bytes();
106 std::vector<uint8_t> bits(std::max<size_t>(1, this_bytes));
107
108 if(this_bytes > 0) {
109 this->serialize_to(bits);
110 }
111
112 std::string hrep;
113 if(is_negative()) {
114 hrep += "-";
115 }
116 hrep += "0x";
117 hrep += hex_encode(bits);
118 return hrep;
119}
120
121/*
122* Encode two BigInt, with leading 0s if needed, and concatenate
123*/
125 if(n1.is_negative() || n2.is_negative()) {
126 throw Encoding_Error("encode_fixed_length_int_pair: values must be positive");
127 }
128 if(n1.bytes() > bytes || n2.bytes() > bytes) {
129 throw Encoding_Error("encode_fixed_length_int_pair: values too large to encode properly");
130 }
131 secure_vector<uint8_t> output(2 * bytes);
132 BufferStuffer stuffer(output);
133 n1.serialize_to(stuffer.next(bytes));
134 n2.serialize_to(stuffer.next(bytes));
135 return output;
136}
137
138BigInt BigInt::decode(std::span<const uint8_t> buf, Base base) {
139 if(base == Binary) {
140 return BigInt::from_bytes(buf);
141 }
142 return BigInt::decode(buf.data(), buf.size(), base);
143}
144
145/*
146* Decode a BigInt
147*/
148BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base) {
149 if(base == Binary) {
150 return BigInt::from_bytes(std::span{buf, length});
151 } else if(base == Hexadecimal) {
152 BigInt r;
154
155 if(length % 2 == 1) {
156 // Handle lack of leading 0
157 const char buf0_with_leading_0[2] = {'0', static_cast<char>(buf[0])};
158
159 binary = hex_decode_locked(buf0_with_leading_0, 2);
160
161 if(length > 1) {
162 binary += hex_decode_locked(cast_uint8_ptr_to_char(&buf[1]), length - 1, false);
163 }
164 } else {
165 binary = hex_decode_locked(cast_uint8_ptr_to_char(buf), length, false);
166 }
167
168 r.assign_from_bytes(binary);
169 return r;
170 } else if(base == Decimal) {
171 BigInt r;
172 // This could be made faster using the same trick as to_dec_string
173 for(size_t i = 0; i != length; ++i) {
174 const char c = buf[i];
175
176 if(c < '0' || c > '9') {
177 throw Invalid_Argument("BigInt::decode: invalid decimal char");
178 }
179
180 const uint8_t x = c - '0';
181 BOTAN_ASSERT_NOMSG(x < 10);
182
183 r *= 10;
184 r += x;
185 }
186 return r;
187 } else {
188 throw Invalid_Argument("Unknown BigInt decoding method");
189 }
190}
191
192} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
BigInt()=default
static BigInt decode(const uint8_t buf[], size_t length)
Definition bigint.h:857
std::string to_dec_string() const
Definition big_code.cpp:37
std::string to_hex_string() const
Definition big_code.cpp:104
static secure_vector< uint8_t > encode_fixed_length_int_pair(const BigInt &n1, const BigInt &n2, size_t bytes)
Definition big_code.cpp:124
void serialize_to(std::span< uint8_t > out) const
Definition bigint.cpp:399
static BigInt from_bytes(std::span< const uint8_t > bytes)
Definition bigint.cpp:87
size_t bits() const
Definition bigint.cpp:311
bool is_zero() const
Definition bigint.h:457
bool is_negative() const
Definition bigint.h:559
size_t bytes() const
Definition bigint.cpp:298
void set_sign(Sign sign)
Definition bigint.h:592
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:134
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:142
secure_vector< uint8_t > hex_decode_locked(const char input[], size_t input_length, bool ignore_ws)
Definition hex.cpp:136
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:35
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:282
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69
std::conditional_t< HasNative64BitRegisters, std::uint64_t, uint32_t > word
Definition types.h:119