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