diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/utils/codec_base.h')
-rw-r--r-- | comm/third_party/botan/src/lib/utils/codec_base.h | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/utils/codec_base.h b/comm/third_party/botan/src/lib/utils/codec_base.h new file mode 100644 index 0000000000..2338fdfd7e --- /dev/null +++ b/comm/third_party/botan/src/lib/utils/codec_base.h @@ -0,0 +1,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 |