1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
/*
* Base Encoding and Decoding
* (C) 2018 Erwan Chaussy
* (C) 2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#ifndef BOTAN_BASE_CODEC_H_
#define BOTAN_BASE_CODEC_H_
#include <botan/secmem.h>
#include <botan/exceptn.h>
#include <vector>
#include <string>
namespace Botan {
/**
* Perform encoding using the base provided
* @param base object giving access to the encodings specifications
* @param output an array of at least base.encode_max_output bytes
* @param input is some binary data
* @param input_length length of input in bytes
* @param input_consumed is an output parameter which says how many
* bytes of input were actually consumed. If less than
* input_length, then the range input[consumed:length]
* should be passed in later along with more input.
* @param final_inputs true iff this is the last input, in which case
padding chars will be applied if needed
* @return number of bytes written to output
*/
template <class Base>
size_t base_encode(Base&& base,
char output[],
const uint8_t input[],
size_t input_length,
size_t& input_consumed,
bool final_inputs)
{
input_consumed = 0;
const size_t encoding_bytes_in = base.encoding_bytes_in();
const size_t encoding_bytes_out = base.encoding_bytes_out();
size_t input_remaining = input_length;
size_t output_produced = 0;
while(input_remaining >= encoding_bytes_in)
{
base.encode(output + output_produced, input + input_consumed);
input_consumed += encoding_bytes_in;
output_produced += encoding_bytes_out;
input_remaining -= encoding_bytes_in;
}
if(final_inputs && input_remaining)
{
std::vector<uint8_t> remainder(encoding_bytes_in, 0);
for(size_t i = 0; i != input_remaining; ++i)
{ remainder[i] = input[input_consumed + i]; }
base.encode(output + output_produced, remainder.data());
const size_t bits_consumed = base.bits_consumed();
const size_t remaining_bits_before_padding = base.remaining_bits_before_padding();
size_t empty_bits = 8 * (encoding_bytes_in - input_remaining);
size_t index = output_produced + encoding_bytes_out - 1;
while(empty_bits >= remaining_bits_before_padding)
{
output[index--] = '=';
empty_bits -= bits_consumed;
}
input_consumed += input_remaining;
output_produced += encoding_bytes_out;
}
return output_produced;
}
template <typename Base>
std::string base_encode_to_string(Base&& base, const uint8_t input[], size_t input_length)
{
const size_t output_length = base.encode_max_output(input_length);
std::string output(output_length, 0);
size_t consumed = 0;
size_t produced = 0;
if(output_length > 0)
{
produced = base_encode(base, &output.front(),
input, input_length,
consumed, true);
}
BOTAN_ASSERT_EQUAL(consumed, input_length, "Consumed the entire input");
BOTAN_ASSERT_EQUAL(produced, output.size(), "Produced expected size");
return output;
}
/**
* Perform decoding using the base provided
* @param base object giving access to the encodings specifications
* @param output an array of at least Base::decode_max_output bytes
* @param input some base input
* @param input_length length of input in bytes
* @param input_consumed is an output parameter which says how many
* bytes of input were actually consumed. If less than
* input_length, then the range input[consumed:length]
* should be passed in later along with more input.
* @param final_inputs true iff this is the last input, in which case
padding is allowed
* @param ignore_ws ignore whitespace on input; if false, throw an
exception if whitespace is encountered
* @return number of bytes written to output
*/
template <typename Base>
size_t base_decode(Base&& base,
uint8_t output[],
const char input[],
size_t input_length,
size_t& input_consumed,
bool final_inputs,
bool ignore_ws = true)
{
const size_t decoding_bytes_in = base.decoding_bytes_in();
const size_t decoding_bytes_out = base.decoding_bytes_out();
uint8_t* out_ptr = output;
std::vector<uint8_t> decode_buf(decoding_bytes_in, 0);
size_t decode_buf_pos = 0;
size_t final_truncate = 0;
clear_mem(output, base.decode_max_output(input_length));
for(size_t i = 0; i != input_length; ++i)
{
const uint8_t bin = base.lookup_binary_value(input[i]);
if(base.check_bad_char(bin, input[i], ignore_ws)) // May throw Invalid_Argument
{
decode_buf[decode_buf_pos] = bin;
++decode_buf_pos;
}
/*
* If we're at the end of the input, pad with 0s and truncate
*/
if(final_inputs && (i == input_length - 1))
{
if(decode_buf_pos)
{
for(size_t j = decode_buf_pos; j < decoding_bytes_in; ++j)
{ decode_buf[j] = 0; }
final_truncate = decoding_bytes_in - decode_buf_pos;
decode_buf_pos = decoding_bytes_in;
}
}
if(decode_buf_pos == decoding_bytes_in)
{
base.decode(out_ptr, decode_buf.data());
out_ptr += decoding_bytes_out;
decode_buf_pos = 0;
input_consumed = i+1;
}
}
while(input_consumed < input_length &&
base.lookup_binary_value(input[input_consumed]) == 0x80)
{
++input_consumed;
}
size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate);
return written;
}
template<typename Base>
size_t base_decode_full(Base&& base, uint8_t output[], const char input[], size_t input_length, bool ignore_ws)
{
size_t consumed = 0;
const size_t written = base_decode(base, output, input, input_length, consumed, true, ignore_ws);
if(consumed != input_length)
{
throw Invalid_Argument(base.name() + " decoding failed, input did not have full bytes");
}
return written;
}
template<typename Vector, typename Base>
Vector base_decode_to_vec(Base&& base,
const char input[],
size_t input_length,
bool ignore_ws)
{
const size_t output_length = base.decode_max_output(input_length);
Vector bin(output_length);
const size_t written =
base_decode_full(base, bin.data(), input, input_length, ignore_ws);
bin.resize(written);
return bin;
}
}
#endif
|