diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/modes')
37 files changed, 4859 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/modes/aead/aead.cpp b/comm/third_party/botan/src/lib/modes/aead/aead.cpp new file mode 100644 index 0000000000..9ad550ffd7 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/aead.cpp @@ -0,0 +1,176 @@ +/* +* (C) 2013,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/aead.h> +#include <botan/scan_name.h> +#include <botan/parsing.h> +#include <sstream> + +#if defined(BOTAN_HAS_BLOCK_CIPHER) + #include <botan/block_cipher.h> +#endif + +#if defined(BOTAN_HAS_AEAD_CCM) + #include <botan/ccm.h> +#endif + +#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) + #include <botan/chacha20poly1305.h> +#endif + +#if defined(BOTAN_HAS_AEAD_EAX) + #include <botan/eax.h> +#endif + +#if defined(BOTAN_HAS_AEAD_GCM) + #include <botan/gcm.h> +#endif + +#if defined(BOTAN_HAS_AEAD_OCB) + #include <botan/ocb.h> +#endif + +#if defined(BOTAN_HAS_AEAD_SIV) + #include <botan/siv.h> +#endif + +namespace Botan { + +void AEAD_Mode::set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len) + { + if(i == 0) + this->set_associated_data(ad, ad_len); + else + throw Invalid_Argument("AEAD '" + name() + "' does not support multiple associated data"); + } + +std::unique_ptr<AEAD_Mode> AEAD_Mode::create_or_throw(const std::string& algo, + Cipher_Dir dir, + const std::string& provider) + { + if(auto aead = AEAD_Mode::create(algo, dir, provider)) + return aead; + + throw Lookup_Error("AEAD", algo, provider); + } + +std::unique_ptr<AEAD_Mode> AEAD_Mode::create(const std::string& algo, + Cipher_Dir dir, + const std::string& provider) + { + BOTAN_UNUSED(provider); +#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) + if(algo == "ChaCha20Poly1305") + { + if(dir == ENCRYPTION) + return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Encryption); + else + return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Decryption); + + } +#endif + + if(algo.find('/') != std::string::npos) + { + const std::vector<std::string> algo_parts = split_on(algo, '/'); + const std::string cipher_name = algo_parts[0]; + const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]); + + if(mode_info.empty()) + return std::unique_ptr<AEAD_Mode>(); + + std::ostringstream alg_args; + + alg_args << '(' << cipher_name; + for(size_t i = 1; i < mode_info.size(); ++i) + alg_args << ',' << mode_info[i]; + for(size_t i = 2; i < algo_parts.size(); ++i) + alg_args << ',' << algo_parts[i]; + alg_args << ')'; + + const std::string mode_name = mode_info[0] + alg_args.str(); + return AEAD_Mode::create(mode_name, dir); + } + +#if defined(BOTAN_HAS_BLOCK_CIPHER) + + SCAN_Name req(algo); + + if(req.arg_count() == 0) + { + return std::unique_ptr<AEAD_Mode>(); + } + + std::unique_ptr<BlockCipher> bc(BlockCipher::create(req.arg(0), provider)); + + if(!bc) + { + return std::unique_ptr<AEAD_Mode>(); + } + +#if defined(BOTAN_HAS_AEAD_CCM) + if(req.algo_name() == "CCM") + { + size_t tag_len = req.arg_as_integer(1, 16); + size_t L_len = req.arg_as_integer(2, 3); + if(dir == ENCRYPTION) + return std::unique_ptr<AEAD_Mode>(new CCM_Encryption(bc.release(), tag_len, L_len)); + else + return std::unique_ptr<AEAD_Mode>(new CCM_Decryption(bc.release(), tag_len, L_len)); + } +#endif + +#if defined(BOTAN_HAS_AEAD_GCM) + if(req.algo_name() == "GCM") + { + size_t tag_len = req.arg_as_integer(1, 16); + if(dir == ENCRYPTION) + return std::unique_ptr<AEAD_Mode>(new GCM_Encryption(bc.release(), tag_len)); + else + return std::unique_ptr<AEAD_Mode>(new GCM_Decryption(bc.release(), tag_len)); + } +#endif + +#if defined(BOTAN_HAS_AEAD_OCB) + if(req.algo_name() == "OCB") + { + size_t tag_len = req.arg_as_integer(1, 16); + if(dir == ENCRYPTION) + return std::unique_ptr<AEAD_Mode>(new OCB_Encryption(bc.release(), tag_len)); + else + return std::unique_ptr<AEAD_Mode>(new OCB_Decryption(bc.release(), tag_len)); + } +#endif + +#if defined(BOTAN_HAS_AEAD_EAX) + if(req.algo_name() == "EAX") + { + size_t tag_len = req.arg_as_integer(1, bc->block_size()); + if(dir == ENCRYPTION) + return std::unique_ptr<AEAD_Mode>(new EAX_Encryption(bc.release(), tag_len)); + else + return std::unique_ptr<AEAD_Mode>(new EAX_Decryption(bc.release(), tag_len)); + } +#endif + +#if defined(BOTAN_HAS_AEAD_SIV) + if(req.algo_name() == "SIV") + { + if(dir == ENCRYPTION) + return std::unique_ptr<AEAD_Mode>(new SIV_Encryption(bc.release())); + else + return std::unique_ptr<AEAD_Mode>(new SIV_Decryption(bc.release())); + } +#endif + +#endif + + return std::unique_ptr<AEAD_Mode>(); + } + + + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/aead.h b/comm/third_party/botan/src/lib/modes/aead/aead.h new file mode 100644 index 0000000000..442eb8ed7f --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/aead.h @@ -0,0 +1,147 @@ +/* +* Interface for AEAD modes +* (C) 2013 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_MODE_H_ +#define BOTAN_AEAD_MODE_H_ + +#include <botan/cipher_mode.h> + +namespace Botan { + +/** +* Interface for AEAD (Authenticated Encryption with Associated Data) +* modes. These modes provide both encryption and message +* authentication, and can authenticate additional per-message data +* which is not included in the ciphertext (for instance a sequence +* number). +*/ +class BOTAN_PUBLIC_API(2,0) AEAD_Mode : public Cipher_Mode + { + public: + /** + * Create an AEAD mode + * @param algo the algorithm to create + * @param direction specify if this should be an encryption or decryption AEAD + * @param provider optional specification for provider to use + * @return an AEAD mode or a null pointer if not available + */ + static std::unique_ptr<AEAD_Mode> create(const std::string& algo, + Cipher_Dir direction, + const std::string& provider = ""); + + /** + * Create an AEAD mode, or throw + * @param algo the algorithm to create + * @param direction specify if this should be an encryption or decryption AEAD + * @param provider optional specification for provider to use + * @return an AEAD mode, or throw an exception + */ + static std::unique_ptr<AEAD_Mode> create_or_throw(const std::string& algo, + Cipher_Dir direction, + const std::string& provider = ""); + + bool authenticated() const override { return true; } + + /** + * Set associated data that is not included in the ciphertext but + * that should be authenticated. Must be called after set_key and + * before start. + * + * Unless reset by another call, the associated data is kept + * between messages. Thus, if the AD does not change, calling + * once (after set_key) is the optimum. + * + * @param ad the associated data + * @param ad_len length of add in bytes + */ + virtual void set_associated_data(const uint8_t ad[], size_t ad_len) = 0; + + /** + * Set associated data that is not included in the ciphertext but + * that should be authenticated. Must be called after set_key and + * before start. + * + * Unless reset by another call, the associated data is kept + * between messages. Thus, if the AD does not change, calling + * once (after set_key) is the optimum. + * + * Some AEADs (namely SIV) support multiple AD inputs. For + * all other modes only nominal AD input 0 is supported; all + * other values of i will cause an exception. + * + * @param ad the associated data + * @param ad_len length of add in bytes + */ + virtual void set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len); + + /** + * Returns the maximum supported number of associated data inputs which + * can be provided to set_associated_data_n + * + * If returns 0, then no associated data is supported. + */ + virtual size_t maximum_associated_data_inputs() const { return 1; } + + /** + * Most AEADs require the key to be set prior to setting the AD + * A few allow the AD to be set even before the cipher is keyed. + * Such ciphers would return false from this function. + */ + virtual bool associated_data_requires_key() const { return true; } + + /** + * Set associated data that is not included in the ciphertext but + * that should be authenticated. Must be called after set_key and + * before start. + * + * See @ref set_associated_data(). + * + * @param ad the associated data + */ + template<typename Alloc> + void set_associated_data_vec(const std::vector<uint8_t, Alloc>& ad) + { + set_associated_data(ad.data(), ad.size()); + } + + /** + * Set associated data that is not included in the ciphertext but + * that should be authenticated. Must be called after set_key and + * before start. + * + * See @ref set_associated_data(). + * + * @param ad the associated data + */ + template<typename Alloc> + void set_ad(const std::vector<uint8_t, Alloc>& ad) + { + set_associated_data(ad.data(), ad.size()); + } + + /** + * @return default AEAD nonce size (a commonly supported value among AEAD + * modes, and large enough that random collisions are unlikely) + */ + size_t default_nonce_length() const override { return 12; } + + virtual ~AEAD_Mode() = default; + }; + +/** +* Get an AEAD mode by name (eg "AES-128/GCM" or "Serpent/EAX") +* @param name AEAD name +* @param direction ENCRYPTION or DECRYPTION +*/ +inline AEAD_Mode* get_aead(const std::string& name, Cipher_Dir direction) + { + return AEAD_Mode::create(name, direction, "").release(); + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp new file mode 100644 index 0000000000..ab2cfcb950 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp @@ -0,0 +1,279 @@ +/* +* CCM Mode Encryption +* (C) 2013,2018 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/ccm.h> +#include <botan/loadstor.h> + +namespace Botan { + +// 128-bit cipher is intrinsic to CCM definition +static const size_t CCM_BS = 16; + +/* +* CCM_Mode Constructor +*/ +CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) : + m_tag_size(tag_size), + m_L(L), + m_cipher(cipher) + { + if(m_cipher->block_size() != CCM_BS) + throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode"); + + if(L < 2 || L > 8) + throw Invalid_Argument("Invalid CCM L value " + std::to_string(L)); + + if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) + throw Invalid_Argument("invalid CCM tag length " + std::to_string(tag_size)); + } + +void CCM_Mode::clear() + { + m_cipher->clear(); + reset(); + } + +void CCM_Mode::reset() + { + m_nonce.clear(); + m_msg_buf.clear(); + m_ad_buf.clear(); + } + +std::string CCM_Mode::name() const + { + return (m_cipher->name() + "/CCM(" + std::to_string(tag_size()) + "," + std::to_string(L())) + ")"; + } + +bool CCM_Mode::valid_nonce_length(size_t n) const + { + return (n == (15-L())); + } + +size_t CCM_Mode::default_nonce_length() const + { + return (15-L()); + } + +size_t CCM_Mode::update_granularity() const + { + /* + This value does not particularly matter as regardless CCM_Mode::update + buffers all input, so in theory this could be 1. However as for instance + Transform_Filter creates update_granularity() uint8_t buffers, use a + somewhat large size to avoid bouncing on a tiny buffer. + */ + return m_cipher->parallel_bytes(); + } + +Key_Length_Specification CCM_Mode::key_spec() const + { + return m_cipher->key_spec(); + } + +void CCM_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + } + +void CCM_Mode::set_associated_data(const uint8_t ad[], size_t length) + { + m_ad_buf.clear(); + + if(length) + { + // FIXME: support larger AD using length encoding rules + BOTAN_ARG_CHECK(length < (0xFFFF - 0xFF), "Supported CCM AD length"); + + m_ad_buf.push_back(get_byte(0, static_cast<uint16_t>(length))); + m_ad_buf.push_back(get_byte(1, static_cast<uint16_t>(length))); + m_ad_buf += std::make_pair(ad, length); + while(m_ad_buf.size() % CCM_BS) + m_ad_buf.push_back(0); // pad with zeros to full block size + } + } + +void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + m_nonce.assign(nonce, nonce + nonce_len); + m_msg_buf.clear(); + } + +size_t CCM_Mode::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(m_nonce.size() > 0); + m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz); + return 0; // no output until finished + } + +void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) + { + const size_t len_bytes = L(); + + BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8); + + for(size_t i = 0; i != len_bytes; ++i) + out[len_bytes-1-i] = get_byte(sizeof(uint64_t)-1-i, len); + + if(len_bytes < 8 && (len >> (len_bytes*8)) > 0) + throw Encoding_Error("CCM message length too long to encode in L field"); + } + +void CCM_Mode::inc(secure_vector<uint8_t>& C) + { + for(size_t i = 0; i != C.size(); ++i) + if(++C[C.size()-i-1]) + break; + } + +secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) + { + if(m_nonce.size() != 15-L()) + throw Invalid_State("CCM mode must set nonce"); + secure_vector<uint8_t> B0(CCM_BS); + + const uint8_t b_flags = + static_cast<uint8_t>((m_ad_buf.size() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1)); + + B0[0] = b_flags; + copy_mem(&B0[1], m_nonce.data(), m_nonce.size()); + encode_length(sz, &B0[m_nonce.size()+1]); + + return B0; + } + +secure_vector<uint8_t> CCM_Mode::format_c0() + { + if(m_nonce.size() != 15-L()) + throw Invalid_State("CCM mode must set nonce"); + secure_vector<uint8_t> C(CCM_BS); + + const uint8_t a_flags = static_cast<uint8_t>(L() - 1); + + C[0] = a_flags; + copy_mem(&C[1], m_nonce.data(), m_nonce.size()); + + return C; + } + +void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane"); + + buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); + + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + const secure_vector<uint8_t>& ad = ad_buf(); + BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple"); + + const BlockCipher& E = cipher(); + + secure_vector<uint8_t> T(CCM_BS); + E.encrypt(format_b0(sz), T); + + for(size_t i = 0; i != ad.size(); i += CCM_BS) + { + xor_buf(T.data(), &ad[i], CCM_BS); + E.encrypt(T); + } + + secure_vector<uint8_t> C = format_c0(); + secure_vector<uint8_t> S0(CCM_BS); + E.encrypt(C, S0); + inc(C); + + secure_vector<uint8_t> X(CCM_BS); + + const uint8_t* buf_end = &buf[sz]; + + while(buf != buf_end) + { + const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); + + xor_buf(T.data(), buf, to_proc); + E.encrypt(T); + + E.encrypt(C, X); + xor_buf(buf, X.data(), to_proc); + inc(C); + + buf += to_proc; + } + + T ^= S0; + + buffer += std::make_pair(T.data(), tag_size()); + + reset(); + } + +void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane"); + + buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); + + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + BOTAN_ASSERT(sz >= tag_size(), "We have the tag"); + + const secure_vector<uint8_t>& ad = ad_buf(); + BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple"); + + const BlockCipher& E = cipher(); + + secure_vector<uint8_t> T(CCM_BS); + E.encrypt(format_b0(sz - tag_size()), T); + + for(size_t i = 0; i != ad.size(); i += CCM_BS) + { + xor_buf(T.data(), &ad[i], CCM_BS); + E.encrypt(T); + } + + secure_vector<uint8_t> C = format_c0(); + + secure_vector<uint8_t> S0(CCM_BS); + E.encrypt(C, S0); + inc(C); + + secure_vector<uint8_t> X(CCM_BS); + + const uint8_t* buf_end = &buf[sz - tag_size()]; + + while(buf != buf_end) + { + const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); + + E.encrypt(C, X); + xor_buf(buf, X.data(), to_proc); + inc(C); + + xor_buf(T.data(), buf, to_proc); + E.encrypt(T); + + buf += to_proc; + } + + T ^= S0; + + if(!constant_time_compare(T.data(), buf_end, tag_size())) + throw Invalid_Authentication_Tag("CCM tag check failed"); + + buffer.resize(buffer.size() - tag_size()); + + reset(); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h new file mode 100644 index 0000000000..9b4bcecbf2 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h @@ -0,0 +1,130 @@ +/* +* CCM Mode +* (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_CCM_H_ +#define BOTAN_AEAD_CCM_H_ + +#include <botan/aead.h> +#include <botan/block_cipher.h> + +BOTAN_FUTURE_INTERNAL_HEADER(ccm.h) + +namespace Botan { + +/** +* Base class for CCM encryption and decryption +* @see RFC 3610 +*/ +class BOTAN_PUBLIC_API(2,0) CCM_Mode : public AEAD_Mode + { + public: + size_t process(uint8_t buf[], size_t sz) override; + + void set_associated_data(const uint8_t ad[], size_t ad_len) override; + + bool associated_data_requires_key() const override { return false; } + + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + bool valid_nonce_length(size_t) const override; + + size_t default_nonce_length() const override; + + void clear() override; + + void reset() override; + + size_t tag_size() const override { return m_tag_size; } + + protected: + CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L); + + size_t L() const { return m_L; } + + const BlockCipher& cipher() const { return *m_cipher; } + + void encode_length(uint64_t len, uint8_t out[]); + + void inc(secure_vector<uint8_t>& C); + + const secure_vector<uint8_t>& ad_buf() const { return m_ad_buf; } + + secure_vector<uint8_t>& msg_buf() { return m_msg_buf; } + + secure_vector<uint8_t> format_b0(size_t msg_size); + secure_vector<uint8_t> format_c0(); + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + const size_t m_tag_size; + const size_t m_L; + + std::unique_ptr<BlockCipher> m_cipher; + secure_vector<uint8_t> m_nonce, m_msg_buf, m_ad_buf; + }; + +/** +* CCM Encryption +*/ +class BOTAN_PUBLIC_API(2,0) CCM_Encryption final : public CCM_Mode + { + public: + /** + * @param cipher a 128-bit block cipher + * @param tag_size is how big the auth tag will be (even values + * between 4 and 16 are accepted) + * @param L length of L parameter. The total message length + * must be less than 2**L bytes, and the nonce is 15-L bytes. + */ + CCM_Encryption(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) : + CCM_Mode(cipher, tag_size, L) {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override + { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + }; + +/** +* CCM Decryption +*/ +class BOTAN_PUBLIC_API(2,0) CCM_Decryption final : public CCM_Mode + { + public: + /** + * @param cipher a 128-bit block cipher + * @param tag_size is how big the auth tag will be (even values + * between 4 and 16 are accepted) + * @param L length of L parameter. The total message length + * must be less than 2**L bytes, and the nonce is 15-L bytes. + */ + CCM_Decryption(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) : + CCM_Mode(cipher, tag_size, L) {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override + { + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt b/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt new file mode 100644 index 0000000000..9341cea7fc --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt @@ -0,0 +1,7 @@ +<defines> +AEAD_CCM -> 20131128 +</defines> + +<requires> +block +</requires> diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp new file mode 100644 index 0000000000..ca02ca5048 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp @@ -0,0 +1,171 @@ +/* +* ChaCha20Poly1305 AEAD +* (C) 2014,2016,2018 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/chacha20poly1305.h> +#include <botan/loadstor.h> + +namespace Botan { + +ChaCha20Poly1305_Mode::ChaCha20Poly1305_Mode() : + m_chacha(StreamCipher::create("ChaCha")), + m_poly1305(MessageAuthenticationCode::create("Poly1305")) + { + if(!m_chacha || !m_poly1305) + throw Algorithm_Not_Found("ChaCha20Poly1305"); + } + +bool ChaCha20Poly1305_Mode::valid_nonce_length(size_t n) const + { + return (n == 8 || n == 12 || n == 24); + } + +void ChaCha20Poly1305_Mode::clear() + { + m_chacha->clear(); + m_poly1305->clear(); + reset(); + } + +void ChaCha20Poly1305_Mode::reset() + { + m_ad.clear(); + m_ctext_len = 0; + m_nonce_len = 0; + } + +void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_chacha->set_key(key, length); + } + +void ChaCha20Poly1305_Mode::set_associated_data(const uint8_t ad[], size_t length) + { + if(m_ctext_len > 0 || m_nonce_len > 0) + throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message"); + m_ad.assign(ad, ad + length); + } + +void ChaCha20Poly1305_Mode::update_len(size_t len) + { + uint8_t len8[8] = { 0 }; + store_le(static_cast<uint64_t>(len), len8); + m_poly1305->update(len8, 8); + } + +void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + m_ctext_len = 0; + m_nonce_len = nonce_len; + + m_chacha->set_iv(nonce, nonce_len); + + uint8_t first_block[64]; + m_chacha->write_keystream(first_block, sizeof(first_block)); + + m_poly1305->set_key(first_block, 32); + // Remainder of first block is discarded + secure_scrub_memory(first_block, sizeof(first_block)); + + m_poly1305->update(m_ad); + + if(cfrg_version()) + { + if(m_ad.size() % 16) + { + const uint8_t zeros[16] = { 0 }; + m_poly1305->update(zeros, 16 - m_ad.size() % 16); + } + } + else + { + update_len(m_ad.size()); + } + } + +size_t ChaCha20Poly1305_Encryption::process(uint8_t buf[], size_t sz) + { + m_chacha->cipher1(buf, sz); + m_poly1305->update(buf, sz); // poly1305 of ciphertext + m_ctext_len += sz; + return sz; + } + +void ChaCha20Poly1305_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + update(buffer, offset); + if(cfrg_version()) + { + if(m_ctext_len % 16) + { + const uint8_t zeros[16] = { 0 }; + m_poly1305->update(zeros, 16 - m_ctext_len % 16); + } + update_len(m_ad.size()); + } + update_len(m_ctext_len); + + buffer.resize(buffer.size() + tag_size()); + m_poly1305->final(&buffer[buffer.size() - tag_size()]); + m_ctext_len = 0; + m_nonce_len = 0; + } + +size_t ChaCha20Poly1305_Decryption::process(uint8_t buf[], size_t sz) + { + m_poly1305->update(buf, sz); // poly1305 of ciphertext + m_chacha->cipher1(buf, sz); + m_ctext_len += sz; + return sz; + } + +void ChaCha20Poly1305_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input"); + + const size_t remaining = sz - tag_size(); + + if(remaining) + { + m_poly1305->update(buf, remaining); // poly1305 of ciphertext + m_chacha->cipher1(buf, remaining); + m_ctext_len += remaining; + } + + if(cfrg_version()) + { + if(m_ctext_len % 16) + { + const uint8_t zeros[16] = { 0 }; + m_poly1305->update(zeros, 16 - m_ctext_len % 16); + } + update_len(m_ad.size()); + } + + update_len(m_ctext_len); + + uint8_t mac[16]; + m_poly1305->final(mac); + + const uint8_t* included_tag = &buf[remaining]; + + m_ctext_len = 0; + m_nonce_len = 0; + + if(!constant_time_compare(mac, included_tag, tag_size())) + throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed"); + buffer.resize(offset + remaining); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h new file mode 100644 index 0000000000..dbba58cc93 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h @@ -0,0 +1,104 @@ +/* +* ChaCha20Poly1305 AEAD +* (C) 2014 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_CHACHA20_POLY1305_H_ +#define BOTAN_AEAD_CHACHA20_POLY1305_H_ + +#include <botan/aead.h> +#include <botan/stream_cipher.h> +#include <botan/mac.h> + +BOTAN_FUTURE_INTERNAL_HEADER(chacha20poly1305.h) + +namespace Botan { + +/** +* Base class +* See draft-irtf-cfrg-chacha20-poly1305-03 for specification +* If a nonce of 64 bits is used the older version described in +* draft-agl-tls-chacha20poly1305-04 is used instead. +* If a nonce of 192 bits is used, XChaCha20Poly1305 is selected. +*/ +class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Mode : public AEAD_Mode + { + public: + void set_associated_data(const uint8_t ad[], size_t ad_len) override; + + bool associated_data_requires_key() const override { return false; } + + std::string name() const override { return "ChaCha20Poly1305"; } + + size_t update_granularity() const override { return 64; } + + Key_Length_Specification key_spec() const override + { return Key_Length_Specification(32); } + + bool valid_nonce_length(size_t n) const override; + + size_t tag_size() const override { return 16; } + + void clear() override; + + void reset() override; + + protected: + std::unique_ptr<StreamCipher> m_chacha; + std::unique_ptr<MessageAuthenticationCode> m_poly1305; + + ChaCha20Poly1305_Mode(); + + secure_vector<uint8_t> m_ad; + size_t m_nonce_len = 0; + size_t m_ctext_len = 0; + + bool cfrg_version() const { return m_nonce_len == 12 || m_nonce_len == 24; } + void update_len(size_t len); + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + }; + +/** +* ChaCha20Poly1305 Encryption +*/ +class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Encryption final : public ChaCha20Poly1305_Mode + { + public: + size_t output_length(size_t input_length) const override + { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +/** +* ChaCha20Poly1305 Decryption +*/ +class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Decryption final : public ChaCha20Poly1305_Mode + { + public: + size_t output_length(size_t input_length) const override + { + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt new file mode 100644 index 0000000000..c3ea53db09 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt @@ -0,0 +1,8 @@ +<defines> +AEAD_CHACHA20_POLY1305 -> 20180807 +</defines> + +<requires> +chacha +poly1305 +</requires> diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp b/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp new file mode 100644 index 0000000000..4f6b540ffb --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp @@ -0,0 +1,194 @@ +/* +* EAX Mode Encryption +* (C) 1999-2007 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/eax.h> +#include <botan/cmac.h> +#include <botan/ctr.h> + +namespace Botan { + +namespace { + +/* +* EAX MAC-based PRF +*/ +secure_vector<uint8_t> eax_prf(uint8_t tag, size_t block_size, + MessageAuthenticationCode& mac, + const uint8_t in[], size_t length) + { + for(size_t i = 0; i != block_size - 1; ++i) + { + mac.update(0); + } + mac.update(tag); + mac.update(in, length); + return mac.final(); + } + +} + +/* +* EAX_Mode Constructor +*/ +EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size) : + m_tag_size(tag_size), + m_cipher(cipher), + m_ctr(new CTR_BE(m_cipher->clone())), + m_cmac(new CMAC(m_cipher->clone())) + { + if(m_tag_size < 8 || m_tag_size > m_cmac->output_length()) + throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(tag_size)); + } + +void EAX_Mode::clear() + { + m_cipher->clear(); + m_ctr->clear(); + m_cmac->clear(); + reset(); + } + +void EAX_Mode::reset() + { + m_ad_mac.clear(); + m_nonce_mac.clear(); + + // Clear out any data added to the CMAC calculation + try { + m_cmac->final(); + } + catch(Key_Not_Set&) {} + } + +std::string EAX_Mode::name() const + { + return (m_cipher->name() + "/EAX"); + } + +size_t EAX_Mode::update_granularity() const + { + /* + * For EAX this actually can be as low as 1 but that causes problems + * for applications which use update_granularity as the buffer size. + */ + return m_cipher->parallel_bytes(); + } + +Key_Length_Specification EAX_Mode::key_spec() const + { + return m_cipher->key_spec(); + } + +/* +* Set the EAX key +*/ +void EAX_Mode::key_schedule(const uint8_t key[], size_t length) + { + /* + * These could share the key schedule, which is one nice part of EAX, + * but it's much easier to ignore that here... + */ + m_ctr->set_key(key, length); + m_cmac->set_key(key, length); + } + +/* +* Set the EAX associated data +*/ +void EAX_Mode::set_associated_data(const uint8_t ad[], size_t length) + { + if(m_nonce_mac.empty() == false) + throw Invalid_State("Cannot set AD for EAX while processing a message"); + m_ad_mac = eax_prf(1, block_size(), *m_cmac, ad, length); + } + +void EAX_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + m_nonce_mac = eax_prf(0, block_size(), *m_cmac, nonce, nonce_len); + + m_ctr->set_iv(m_nonce_mac.data(), m_nonce_mac.size()); + + for(size_t i = 0; i != block_size() - 1; ++i) + m_cmac->update(0); + m_cmac->update(2); + } + +size_t EAX_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(m_nonce_mac.size() > 0); + m_ctr->cipher(buf, buf, sz); + m_cmac->update(buf, sz); + return sz; + } + +void EAX_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT_NOMSG(m_nonce_mac.empty() == false); + update(buffer, offset); + + secure_vector<uint8_t> data_mac = m_cmac->final(); + xor_buf(data_mac, m_nonce_mac, data_mac.size()); + + if(m_ad_mac.empty()) + { + m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0); + } + + xor_buf(data_mac, m_ad_mac, data_mac.size()); + + buffer += std::make_pair(data_mac.data(), tag_size()); + } + +size_t EAX_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(m_nonce_mac.size() > 0); + m_cmac->update(buf, sz); + m_ctr->cipher(buf, buf, sz); + return sz; + } + +void EAX_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input"); + + const size_t remaining = sz - tag_size(); + + if(remaining) + { + m_cmac->update(buf, remaining); + m_ctr->cipher(buf, buf, remaining); + } + + const uint8_t* included_tag = &buf[remaining]; + + secure_vector<uint8_t> mac = m_cmac->final(); + mac ^= m_nonce_mac; + + if(m_ad_mac.empty()) + { + m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0); + } + + mac ^= m_ad_mac; + + if(!constant_time_compare(mac.data(), included_tag, tag_size())) + throw Invalid_Authentication_Tag("EAX tag check failed"); + + buffer.resize(offset + remaining); + + m_nonce_mac.clear(); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/eax.h b/comm/third_party/botan/src/lib/modes/aead/eax/eax.h new file mode 100644 index 0000000000..b9b02c192b --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/eax/eax.h @@ -0,0 +1,119 @@ +/* +* EAX Mode +* (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_EAX_H_ +#define BOTAN_AEAD_EAX_H_ + +#include <botan/aead.h> +#include <botan/block_cipher.h> +#include <botan/stream_cipher.h> +#include <botan/mac.h> + +BOTAN_FUTURE_INTERNAL_HEADER(eax.h) + +namespace Botan { + +/** +* EAX base class +*/ +class BOTAN_PUBLIC_API(2,0) EAX_Mode : public AEAD_Mode + { + public: + void set_associated_data(const uint8_t ad[], size_t ad_len) override; + + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + // EAX supports arbitrary nonce lengths + bool valid_nonce_length(size_t) const override { return true; } + + size_t tag_size() const override { return m_tag_size; } + + void clear() override; + + void reset() override; + + protected: + /** + * @param cipher the cipher to use + * @param tag_size is how big the auth tag will be + */ + EAX_Mode(BlockCipher* cipher, size_t tag_size); + + size_t block_size() const { return m_cipher->block_size(); } + + size_t m_tag_size; + + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<StreamCipher> m_ctr; + std::unique_ptr<MessageAuthenticationCode> m_cmac; + + secure_vector<uint8_t> m_ad_mac; + + secure_vector<uint8_t> m_nonce_mac; + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + }; + +/** +* EAX Encryption +*/ +class BOTAN_PUBLIC_API(2,0) EAX_Encryption final : public EAX_Mode + { + public: + /** + * @param cipher a 128-bit block cipher + * @param tag_size is how big the auth tag will be + */ + EAX_Encryption(BlockCipher* cipher, size_t tag_size = 0) : + EAX_Mode(cipher, tag_size) {} + + size_t output_length(size_t input_length) const override + { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +/** +* EAX Decryption +*/ +class BOTAN_PUBLIC_API(2,0) EAX_Decryption final : public EAX_Mode + { + public: + /** + * @param cipher a 128-bit block cipher + * @param tag_size is how big the auth tag will be + */ + EAX_Decryption(BlockCipher* cipher, size_t tag_size = 0) : + EAX_Mode(cipher, tag_size) {} + + size_t output_length(size_t input_length) const override + { + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/info.txt b/comm/third_party/botan/src/lib/modes/aead/eax/info.txt new file mode 100644 index 0000000000..6bc01ff704 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/eax/info.txt @@ -0,0 +1,8 @@ +<defines> +AEAD_EAX -> 20131128 +</defines> + +<requires> +cmac +ctr +</requires> diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp new file mode 100644 index 0000000000..4b9c5b7204 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp @@ -0,0 +1,179 @@ +/* +* GCM Mode Encryption +* (C) 2013,2015 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/gcm.h> +#include <botan/ghash.h> +#include <botan/block_cipher.h> +#include <botan/ctr.h> + +namespace Botan { + +/* +* GCM_Mode Constructor +*/ +GCM_Mode::GCM_Mode(BlockCipher* cipher, size_t tag_size) : + m_tag_size(tag_size), + m_cipher_name(cipher->name()), + m_ctr(new CTR_BE(cipher, 4)), + m_ghash(new GHASH) + { + if(cipher->block_size() != GCM_BS) + throw Invalid_Argument("Invalid block cipher for GCM"); + + /* We allow any of the values 128, 120, 112, 104, or 96 bits as a tag size */ + /* 64 bit tag is still supported but deprecated and will be removed in the future */ + if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16)) + throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(m_tag_size)); + } + +GCM_Mode::~GCM_Mode() { /* for unique_ptr */ } + +void GCM_Mode::clear() + { + m_ctr->clear(); + m_ghash->clear(); + reset(); + } + +void GCM_Mode::reset() + { + m_ghash->reset(); + } + +std::string GCM_Mode::name() const + { + return (m_cipher_name + "/GCM(" + std::to_string(tag_size()) + ")"); + } + +std::string GCM_Mode::provider() const + { + return m_ghash->provider(); + } + +size_t GCM_Mode::update_granularity() const + { + return GCM_BS * std::max<size_t>(2, BOTAN_BLOCK_CIPHER_PAR_MULT); + } + +bool GCM_Mode::valid_nonce_length(size_t len) const + { + // GCM does not support empty nonces + return (len > 0); + } + +Key_Length_Specification GCM_Mode::key_spec() const + { + return m_ctr->key_spec(); + } + +void GCM_Mode::key_schedule(const uint8_t key[], size_t keylen) + { + m_ctr->set_key(key, keylen); + + const std::vector<uint8_t> zeros(GCM_BS); + m_ctr->set_iv(zeros.data(), zeros.size()); + + secure_vector<uint8_t> H(GCM_BS); + m_ctr->encipher(H); + m_ghash->set_key(H); + } + +void GCM_Mode::set_associated_data(const uint8_t ad[], size_t ad_len) + { + m_ghash->set_associated_data(ad, ad_len); + } + +void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + if(m_y0.size() != GCM_BS) + m_y0.resize(GCM_BS); + + clear_mem(m_y0.data(), m_y0.size()); + + if(nonce_len == 12) + { + copy_mem(m_y0.data(), nonce, nonce_len); + m_y0[15] = 1; + } + else + { + m_ghash->nonce_hash(m_y0, nonce, nonce_len); + } + + m_ctr->set_iv(m_y0.data(), m_y0.size()); + + clear_mem(m_y0.data(), m_y0.size()); + m_ctr->encipher(m_y0); + + m_ghash->start(m_y0.data(), m_y0.size()); + clear_mem(m_y0.data(), m_y0.size()); + } + +size_t GCM_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size"); + m_ctr->cipher(buf, buf, sz); + m_ghash->update(buf, sz); + return sz; + } + +void GCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + m_ctr->cipher(buf, buf, sz); + m_ghash->update(buf, sz); + + uint8_t mac[16] = { 0 }; + m_ghash->final(mac, tag_size()); + buffer += std::make_pair(mac, tag_size()); + } + +size_t GCM_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size"); + m_ghash->update(buf, sz); + m_ctr->cipher(buf, buf, sz); + return sz; + } + +void GCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + if(sz < tag_size()) + throw Decoding_Error("Insufficient input for GCM decryption, tag missing"); + + const size_t remaining = sz - tag_size(); + + // handle any final input before the tag + if(remaining) + { + m_ghash->update(buf, remaining); + m_ctr->cipher(buf, buf, remaining); + } + + uint8_t mac[16] = { 0 }; + m_ghash->final(mac, tag_size()); + + const uint8_t* included_tag = &buffer[remaining+offset]; + + if(!constant_time_compare(mac, included_tag, tag_size())) + throw Invalid_Authentication_Tag("GCM tag check failed"); + + buffer.resize(offset + remaining); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h new file mode 100644 index 0000000000..fe3c405721 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h @@ -0,0 +1,117 @@ +/* +* GCM Mode +* (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_GCM_H_ +#define BOTAN_AEAD_GCM_H_ + +#include <botan/aead.h> +#include <botan/sym_algo.h> + +BOTAN_FUTURE_INTERNAL_HEADER(gcm.h) + +namespace Botan { + +class BlockCipher; +class StreamCipher; +class GHASH; + +/** +* GCM Mode +*/ +class BOTAN_PUBLIC_API(2,0) GCM_Mode : public AEAD_Mode + { + public: + void set_associated_data(const uint8_t ad[], size_t ad_len) override; + + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + bool valid_nonce_length(size_t len) const override; + + size_t tag_size() const override { return m_tag_size; } + + void clear() override; + + void reset() override; + + std::string provider() const override; + protected: + GCM_Mode(BlockCipher* cipher, size_t tag_size); + + ~GCM_Mode(); + + static const size_t GCM_BS = 16; + + const size_t m_tag_size; + const std::string m_cipher_name; + + std::unique_ptr<StreamCipher> m_ctr; + std::unique_ptr<GHASH> m_ghash; + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + secure_vector<uint8_t> m_y0; + }; + +/** +* GCM Encryption +*/ +class BOTAN_PUBLIC_API(2,0) GCM_Encryption final : public GCM_Mode + { + public: + /** + * @param cipher the 128 bit block cipher to use + * @param tag_size is how big the auth tag will be + */ + GCM_Encryption(BlockCipher* cipher, size_t tag_size = 16) : + GCM_Mode(cipher, tag_size) {} + + size_t output_length(size_t input_length) const override + { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +/** +* GCM Decryption +*/ +class BOTAN_PUBLIC_API(2,0) GCM_Decryption final : public GCM_Mode + { + public: + /** + * @param cipher the 128 bit block cipher to use + * @param tag_size is how big the auth tag will be + */ + GCM_Decryption(BlockCipher* cipher, size_t tag_size = 16) : + GCM_Mode(cipher, tag_size) {} + + size_t output_length(size_t input_length) const override + { + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt b/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt new file mode 100644 index 0000000000..94eb09ab64 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt @@ -0,0 +1,9 @@ +<defines> +AEAD_GCM -> 20131128 +</defines> + +<requires> +block +ctr +ghash +</requires> diff --git a/comm/third_party/botan/src/lib/modes/aead/info.txt b/comm/third_party/botan/src/lib/modes/aead/info.txt new file mode 100644 index 0000000000..15106e46ab --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/info.txt @@ -0,0 +1,3 @@ +<defines> +AEAD_MODES -> 20131128 +</defines> diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt b/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt new file mode 100644 index 0000000000..4e16e23624 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt @@ -0,0 +1,8 @@ +<defines> +AEAD_OCB -> 20131128 +</defines> + +<requires> +block +poly_dbl +</requires> diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp new file mode 100644 index 0000000000..6c16203296 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp @@ -0,0 +1,533 @@ +/* +* OCB Mode +* (C) 2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/ocb.h> +#include <botan/block_cipher.h> +#include <botan/internal/poly_dbl.h> +#include <botan/internal/bit_ops.h> + +namespace Botan { + +// Has to be in Botan namespace so unique_ptr can reference it +class L_computer final + { + public: + explicit L_computer(const BlockCipher& cipher) : + m_BS(cipher.block_size()), + m_max_blocks(cipher.parallel_bytes() / m_BS) + { + m_L_star.resize(m_BS); + cipher.encrypt(m_L_star); + m_L_dollar = poly_double(star()); + m_L.push_back(poly_double(dollar())); + + while(m_L.size() < 8) + m_L.push_back(poly_double(m_L.back())); + + m_offset_buf.resize(m_BS * m_max_blocks); + } + + void init(const secure_vector<uint8_t>& offset) + { + m_offset = offset; + } + + bool initialized() const { return m_offset.empty() == false; } + + const secure_vector<uint8_t>& star() const { return m_L_star; } + const secure_vector<uint8_t>& dollar() const { return m_L_dollar; } + const secure_vector<uint8_t>& offset() const { return m_offset; } + + const secure_vector<uint8_t>& get(size_t i) const + { + while(m_L.size() <= i) + m_L.push_back(poly_double(m_L.back())); + + return m_L[i]; + } + + const uint8_t* + compute_offsets(size_t block_index, size_t blocks) + { + BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets"); + + uint8_t* offsets = m_offset_buf.data(); + + if(block_index % 4 == 0) + { + const secure_vector<uint8_t>& L0 = get(0); + const secure_vector<uint8_t>& L1 = get(1); + + while(blocks >= 4) + { + // ntz(4*i+1) == 0 + // ntz(4*i+2) == 1 + // ntz(4*i+3) == 0 + block_index += 4; + const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index)); + + xor_buf(offsets, m_offset.data(), L0.data(), m_BS); + offsets += m_BS; + + xor_buf(offsets, offsets - m_BS, L1.data(), m_BS); + offsets += m_BS; + + xor_buf(m_offset.data(), L1.data(), m_BS); + copy_mem(offsets, m_offset.data(), m_BS); + offsets += m_BS; + + xor_buf(m_offset.data(), get(ntz4).data(), m_BS); + copy_mem(offsets, m_offset.data(), m_BS); + offsets += m_BS; + + blocks -= 4; + } + } + + for(size_t i = 0; i != blocks; ++i) + { // could be done in parallel + const size_t ntz = var_ctz32(static_cast<uint32_t>(block_index + i + 1)); + xor_buf(m_offset.data(), get(ntz).data(), m_BS); + copy_mem(offsets, m_offset.data(), m_BS); + offsets += m_BS; + } + + return m_offset_buf.data(); + } + + private: + secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) const + { + secure_vector<uint8_t> out(in.size()); + poly_double_n(out.data(), in.data(), out.size()); + return out; + } + + const size_t m_BS, m_max_blocks; + secure_vector<uint8_t> m_L_dollar, m_L_star; + secure_vector<uint8_t> m_offset; + mutable std::vector<secure_vector<uint8_t>> m_L; + secure_vector<uint8_t> m_offset_buf; + }; + +namespace { + +/* +* OCB's HASH +*/ +secure_vector<uint8_t> ocb_hash(const L_computer& L, + const BlockCipher& cipher, + const uint8_t ad[], size_t ad_len) + { + const size_t BS = cipher.block_size(); + secure_vector<uint8_t> sum(BS); + secure_vector<uint8_t> offset(BS); + + secure_vector<uint8_t> buf(BS); + + const size_t ad_blocks = (ad_len / BS); + const size_t ad_remainder = (ad_len % BS); + + for(size_t i = 0; i != ad_blocks; ++i) + { + // this loop could run in parallel + offset ^= L.get(var_ctz32(static_cast<uint32_t>(i+1))); + buf = offset; + xor_buf(buf.data(), &ad[BS*i], BS); + cipher.encrypt(buf); + sum ^= buf; + } + + if(ad_remainder) + { + offset ^= L.star(); + buf = offset; + xor_buf(buf.data(), &ad[BS*ad_blocks], ad_remainder); + buf[ad_remainder] ^= 0x80; + cipher.encrypt(buf); + sum ^= buf; + } + + return sum; + } + +} + +OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size) : + m_cipher(cipher), + m_checksum(m_cipher->parallel_bytes()), + m_ad_hash(m_cipher->block_size()), + m_tag_size(tag_size), + m_block_size(m_cipher->block_size()), + m_par_blocks(m_cipher->parallel_bytes() / m_block_size) + { + const size_t BS = block_size(); + + /* + * draft-krovetz-ocb-wide-d1 specifies OCB for several other block + * sizes but only 128, 192, 256 and 512 bit are currently supported + * by this implementation. + */ + BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64, + "Invalid block size for OCB"); + + BOTAN_ARG_CHECK(m_tag_size % 4 == 0 && + m_tag_size >= 8 && m_tag_size <= BS && + m_tag_size <= 32, + "Invalid OCB tag length"); + } + +OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ } + +void OCB_Mode::clear() + { + m_cipher->clear(); + m_L.reset(); // add clear here? + reset(); + } + +void OCB_Mode::reset() + { + m_block_index = 0; + zeroise(m_ad_hash); + zeroise(m_checksum); + m_last_nonce.clear(); + m_stretch.clear(); + } + +bool OCB_Mode::valid_nonce_length(size_t length) const + { + if(length == 0) + return false; + if(block_size() == 16) + return length < 16; + else + return length < (block_size() - 1); + } + +std::string OCB_Mode::name() const + { + return m_cipher->name() + "/OCB"; // include tag size? + } + +size_t OCB_Mode::update_granularity() const + { + return (m_par_blocks * block_size()); + } + +Key_Length_Specification OCB_Mode::key_spec() const + { + return m_cipher->key_spec(); + } + +void OCB_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + m_L.reset(new L_computer(*m_cipher)); + } + +void OCB_Mode::set_associated_data(const uint8_t ad[], size_t ad_len) + { + verify_key_set(m_L != nullptr); + m_ad_hash = ocb_hash(*m_L, *m_cipher, ad, ad_len); + } + +const secure_vector<uint8_t>& +OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len) + { + const size_t BS = block_size(); + + BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64, + "OCB block size is supported"); + + const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8)); + + const uint8_t BOTTOM_MASK = + static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1); + + m_nonce_buf.resize(BS); + clear_mem(&m_nonce_buf[0], m_nonce_buf.size()); + + copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len); + m_nonce_buf[0] = static_cast<uint8_t>(((tag_size()*8) % (BS*8)) << (BS <= 16 ? 1 : 0)); + + m_nonce_buf[BS - nonce_len - 1] ^= 1; + + const uint8_t bottom = m_nonce_buf[BS-1] & BOTTOM_MASK; + m_nonce_buf[BS-1] &= ~BOTTOM_MASK; + + const bool need_new_stretch = (m_last_nonce != m_nonce_buf); + + if(need_new_stretch) + { + m_last_nonce = m_nonce_buf; + + m_cipher->encrypt(m_nonce_buf); + + /* + The loop bounds (BS vs BS/2) are derived from the relation + between the block size and the MASKLEN. Using the terminology + of draft-krovetz-ocb-wide, we have to derive enough bits in + ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch. + + +----------+---------+-------+---------+ + | BLOCKLEN | RESIDUE | SHIFT | MASKLEN | + +----------+---------+-------+---------+ + | 32 | 141 | 17 | 4 | + | 64 | 27 | 25 | 5 | + | 96 | 1601 | 33 | 6 | + | 128 | 135 | 8 | 6 | + | 192 | 135 | 40 | 7 | + | 256 | 1061 | 1 | 8 | + | 384 | 4109 | 80 | 8 | + | 512 | 293 | 176 | 8 | + | 1024 | 524355 | 352 | 9 | + +----------+---------+-------+---------+ + */ + if(BS == 16) + { + for(size_t i = 0; i != BS / 2; ++i) + m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+1]); + } + else if(BS == 24) + { + for(size_t i = 0; i != 16; ++i) + m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+5]); + } + else if(BS == 32) + { + for(size_t i = 0; i != BS; ++i) + m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i+1] >> 7)); + } + else if(BS == 64) + { + for(size_t i = 0; i != BS / 2; ++i) + m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+22]); + } + + m_stretch = m_nonce_buf; + } + + // now set the offset from stretch and bottom + const size_t shift_bytes = bottom / 8; + const size_t shift_bits = bottom % 8; + + BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok"); + + m_offset.resize(BS); + for(size_t i = 0; i != BS; ++i) + { + m_offset[i] = (m_stretch[i+shift_bytes] << shift_bits); + m_offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits)); + } + + return m_offset; + } + +void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + verify_key_set(m_L != nullptr); + + m_L->init(update_nonce(nonce, nonce_len)); + zeroise(m_checksum); + m_block_index = 0; + } + +void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks) + { + verify_key_set(m_L != nullptr); + BOTAN_STATE_CHECK(m_L->initialized()); + + const size_t BS = block_size(); + + while(blocks) + { + const size_t proc_blocks = std::min(blocks, par_blocks()); + const size_t proc_bytes = proc_blocks * BS; + + const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks); + + xor_buf(m_checksum.data(), buffer, proc_bytes); + + m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks); + + buffer += proc_bytes; + blocks -= proc_blocks; + m_block_index += proc_blocks; + } + } + +size_t OCB_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size"); + encrypt(buf, sz / block_size()); + return sz; + } + +void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + verify_key_set(m_L != nullptr); + + const size_t BS = block_size(); + + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + secure_vector<uint8_t> mac(BS); + + if(sz) + { + const size_t final_full_blocks = sz / BS; + const size_t remainder_bytes = sz - (final_full_blocks * BS); + + encrypt(buf, final_full_blocks); + mac = m_L->offset(); + + if(remainder_bytes) + { + BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left"); + uint8_t* remainder = &buf[sz - remainder_bytes]; + + xor_buf(m_checksum.data(), remainder, remainder_bytes); + m_checksum[remainder_bytes] ^= 0x80; + + // Offset_* + mac ^= m_L->star(); + + secure_vector<uint8_t> pad(BS); + m_cipher->encrypt(mac, pad); + xor_buf(remainder, pad.data(), remainder_bytes); + } + } + else + { + mac = m_L->offset(); + } + + // now compute the tag + + // fold checksum + for(size_t i = 0; i != m_checksum.size(); i += BS) + { + xor_buf(mac.data(), m_checksum.data() + i, BS); + } + + xor_buf(mac.data(), m_L->dollar().data(), BS); + m_cipher->encrypt(mac); + xor_buf(mac.data(), m_ad_hash.data(), BS); + + buffer += std::make_pair(mac.data(), tag_size()); + + zeroise(m_checksum); + m_block_index = 0; + } + +void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks) + { + verify_key_set(m_L != nullptr); + BOTAN_STATE_CHECK(m_L->initialized()); + + const size_t BS = block_size(); + + while(blocks) + { + const size_t proc_blocks = std::min(blocks, par_blocks()); + const size_t proc_bytes = proc_blocks * BS; + + const uint8_t* offsets = m_L->compute_offsets(m_block_index, proc_blocks); + + m_cipher->decrypt_n_xex(buffer, offsets, proc_blocks); + + xor_buf(m_checksum.data(), buffer, proc_bytes); + + buffer += proc_bytes; + blocks -= proc_blocks; + m_block_index += proc_blocks; + } + } + +size_t OCB_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size"); + decrypt(buf, sz / block_size()); + return sz; + } + +void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + verify_key_set(m_L != nullptr); + + const size_t BS = block_size(); + + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + BOTAN_ASSERT(sz >= tag_size(), "We have the tag"); + + const size_t remaining = sz - tag_size(); + + secure_vector<uint8_t> mac(BS); + + if(remaining) + { + const size_t final_full_blocks = remaining / BS; + const size_t final_bytes = remaining - (final_full_blocks * BS); + + decrypt(buf, final_full_blocks); + mac ^= m_L->offset(); + + if(final_bytes) + { + BOTAN_ASSERT(final_bytes < BS, "Only a partial block left"); + + uint8_t* remainder = &buf[remaining - final_bytes]; + + mac ^= m_L->star(); + secure_vector<uint8_t> pad(BS); + m_cipher->encrypt(mac, pad); // P_* + xor_buf(remainder, pad.data(), final_bytes); + + xor_buf(m_checksum.data(), remainder, final_bytes); + m_checksum[final_bytes] ^= 0x80; + } + } + else + mac = m_L->offset(); + + // compute the mac + + // fold checksum + for(size_t i = 0; i != m_checksum.size(); i += BS) + { + xor_buf(mac.data(), m_checksum.data() + i, BS); + } + + mac ^= m_L->dollar(); + m_cipher->encrypt(mac); + mac ^= m_ad_hash; + + // reset state + zeroise(m_checksum); + m_block_index = 0; + + // compare mac + const uint8_t* included_tag = &buf[remaining]; + + if(!constant_time_compare(mac.data(), included_tag, tag_size())) + throw Invalid_Authentication_Tag("OCB tag check failed"); + + // remove tag from end of message + buffer.resize(remaining + offset); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h new file mode 100644 index 0000000000..4029fcda25 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h @@ -0,0 +1,137 @@ +/* +* OCB Mode +* (C) 2013,2014 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_OCB_H_ +#define BOTAN_AEAD_OCB_H_ + +#include <botan/aead.h> + +BOTAN_FUTURE_INTERNAL_HEADER(ocb.h) + +namespace Botan { + +class BlockCipher; +class L_computer; + +/** +* OCB Mode (base class for OCB_Encryption and OCB_Decryption). Note +* that OCB is patented, but is freely licensed in some circumstances. +* +* @see "The OCB Authenticated-Encryption Algorithm" RFC 7253 +* https://tools.ietf.org/html/rfc7253 +* @see "OCB For Block Ciphers Without 128-Bit Blocks" +* (draft-krovetz-ocb-wide-d3) for the extension of OCB to +* block ciphers with larger block sizes. +* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm +* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb +*/ +class BOTAN_PUBLIC_API(2,0) OCB_Mode : public AEAD_Mode + { + public: + void set_associated_data(const uint8_t ad[], size_t ad_len) override; + + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + bool valid_nonce_length(size_t) const override; + + size_t tag_size() const override { return m_tag_size; } + + void clear() override; + + void reset() override; + + ~OCB_Mode(); + protected: + /** + * @param cipher the block cipher to use + * @param tag_size is how big the auth tag will be + */ + OCB_Mode(BlockCipher* cipher, size_t tag_size); + + size_t block_size() const { return m_block_size; } + size_t par_blocks() const { return m_par_blocks; } + size_t par_bytes() const { return m_checksum.size(); } + + // fixme make these private + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<L_computer> m_L; + + size_t m_block_index = 0; + + secure_vector<uint8_t> m_checksum; + secure_vector<uint8_t> m_ad_hash; + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + const secure_vector<uint8_t>& update_nonce(const uint8_t nonce[], size_t nonce_len); + + const size_t m_tag_size; + const size_t m_block_size; + const size_t m_par_blocks; + secure_vector<uint8_t> m_last_nonce; + secure_vector<uint8_t> m_stretch; + secure_vector<uint8_t> m_nonce_buf; + secure_vector<uint8_t> m_offset; + }; + +class BOTAN_PUBLIC_API(2,0) OCB_Encryption final : public OCB_Mode + { + public: + /** + * @param cipher the block cipher to use + * @param tag_size is how big the auth tag will be + */ + OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) : + OCB_Mode(cipher, tag_size) {} + + size_t output_length(size_t input_length) const override + { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + private: + void encrypt(uint8_t input[], size_t blocks); + }; + +class BOTAN_PUBLIC_API(2,0) OCB_Decryption final : public OCB_Mode + { + public: + /** + * @param cipher the block cipher to use + * @param tag_size is how big the auth tag will be + */ + OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) : + OCB_Mode(cipher, tag_size) {} + + size_t output_length(size_t input_length) const override + { + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + private: + void decrypt(uint8_t input[], size_t blocks); + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/info.txt b/comm/third_party/botan/src/lib/modes/aead/siv/info.txt new file mode 100644 index 0000000000..4894896665 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/siv/info.txt @@ -0,0 +1,9 @@ +<defines> +AEAD_SIV -> 20131202 +</defines> + +<requires> +cmac +ctr +poly_dbl +</requires> diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp b/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp new file mode 100644 index 0000000000..8bd82a4684 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp @@ -0,0 +1,211 @@ +/* +* SIV Mode Encryption +* (C) 2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/siv.h> +#include <botan/block_cipher.h> +#include <botan/cmac.h> +#include <botan/internal/poly_dbl.h> +#include <botan/ctr.h> + +namespace Botan { + +SIV_Mode::SIV_Mode(BlockCipher* cipher) : + m_name(cipher->name() + "/SIV"), + m_ctr(new CTR_BE(cipher->clone(), 8)), + m_mac(new CMAC(cipher)), + m_bs(cipher->block_size()) + { + // Not really true but only 128 bit allowed at the moment + if(m_bs != 16) + throw Invalid_Argument("SIV requires a 128 bit block cipher"); + } + +SIV_Mode::~SIV_Mode() + { + // for ~unique_ptr + } + +void SIV_Mode::clear() + { + m_ctr->clear(); + m_mac->clear(); + reset(); + } + +void SIV_Mode::reset() + { + m_nonce.clear(); + m_msg_buf.clear(); + m_ad_macs.clear(); + } + +std::string SIV_Mode::name() const + { + return m_name; + } + +bool SIV_Mode::valid_nonce_length(size_t) const + { + return true; + } + +size_t SIV_Mode::update_granularity() const + { + /* + This value does not particularly matter as regardless SIV_Mode::update + buffers all input, so in theory this could be 1. However as for instance + Transform_Filter creates update_granularity() uint8_t buffers, use a + somewhat large size to avoid bouncing on a tiny buffer. + */ + return 128; + } + +Key_Length_Specification SIV_Mode::key_spec() const + { + return m_mac->key_spec().multiple(2); + } + +void SIV_Mode::key_schedule(const uint8_t key[], size_t length) + { + const size_t keylen = length / 2; + m_mac->set_key(key, keylen); + m_ctr->set_key(key + keylen, keylen); + m_ad_macs.clear(); + } + +size_t SIV_Mode::maximum_associated_data_inputs() const + { + return block_size() * 8 - 2; + } + +void SIV_Mode::set_associated_data_n(size_t n, const uint8_t ad[], size_t length) + { + const size_t max_ads = maximum_associated_data_inputs(); + if(n > max_ads) + throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs"); + + if(n >= m_ad_macs.size()) + m_ad_macs.resize(n+1); + + m_ad_macs[n] = m_mac->process(ad, length); + } + +void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + if(nonce_len) + m_nonce = m_mac->process(nonce, nonce_len); + else + m_nonce.clear(); + + m_msg_buf.clear(); + } + +size_t SIV_Mode::process(uint8_t buf[], size_t sz) + { + // all output is saved for processing in finish + m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz); + return 0; + } + +secure_vector<uint8_t> SIV_Mode::S2V(const uint8_t* text, size_t text_len) + { + const std::vector<uint8_t> zeros(block_size()); + + secure_vector<uint8_t> V = m_mac->process(zeros.data(), zeros.size()); + + for(size_t i = 0; i != m_ad_macs.size(); ++i) + { + poly_double_n(V.data(), V.size()); + V ^= m_ad_macs[i]; + } + + if(m_nonce.size()) + { + poly_double_n(V.data(), V.size()); + V ^= m_nonce; + } + + if(text_len < block_size()) + { + poly_double_n(V.data(), V.size()); + xor_buf(V.data(), text, text_len); + V[text_len] ^= 0x80; + return m_mac->process(V); + } + + m_mac->update(text, text_len - block_size()); + xor_buf(V.data(), &text[text_len - block_size()], block_size()); + m_mac->update(V); + + return m_mac->final(); + } + +void SIV_Mode::set_ctr_iv(secure_vector<uint8_t> V) + { + V[m_bs-8] &= 0x7F; + V[m_bs-4] &= 0x7F; + + ctr().set_iv(V.data(), V.size()); + } + +void SIV_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + + buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); + msg_buf().clear(); + + const secure_vector<uint8_t> V = S2V(buffer.data() + offset, buffer.size() - offset); + + buffer.insert(buffer.begin() + offset, V.begin(), V.end()); + + if(buffer.size() != offset + V.size()) + { + set_ctr_iv(V); + ctr().cipher1(&buffer[offset + V.size()], buffer.size() - offset - V.size()); + } + } + +void SIV_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + + if(msg_buf().size() > 0) + { + buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); + msg_buf().clear(); + } + + const size_t sz = buffer.size() - offset; + + BOTAN_ASSERT(sz >= tag_size(), "We have the tag"); + + secure_vector<uint8_t> V(buffer.data() + offset, + buffer.data() + offset + block_size()); + + if(buffer.size() != offset + V.size()) + { + set_ctr_iv(V); + + ctr().cipher(buffer.data() + offset + V.size(), + buffer.data() + offset, + buffer.size() - offset - V.size()); + } + + const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size()); + + if(!constant_time_compare(T.data(), V.data(), T.size())) + throw Invalid_Authentication_Tag("SIV tag check failed"); + + buffer.resize(buffer.size() - tag_size()); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/siv.h b/comm/third_party/botan/src/lib/modes/aead/siv/siv.h new file mode 100644 index 0000000000..c76fd32296 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/aead/siv/siv.h @@ -0,0 +1,129 @@ +/* +* SIV Mode +* (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AEAD_SIV_H_ +#define BOTAN_AEAD_SIV_H_ + +#include <botan/aead.h> +#include <botan/stream_cipher.h> + +BOTAN_FUTURE_INTERNAL_HEADER(siv.h) + +namespace Botan { + +class BlockCipher; +class MessageAuthenticationCode; + +/** +* Base class for SIV encryption and decryption (@see RFC 5297) +*/ +class BOTAN_PUBLIC_API(2,0) SIV_Mode : public AEAD_Mode + { + public: + size_t process(uint8_t buf[], size_t size) override; + + /** + * Sets the nth element of the vector of associated data + * @param n index into the AD vector + * @param ad associated data + * @param ad_len length of associated data in bytes + */ + void set_associated_data_n(size_t n, const uint8_t ad[], size_t ad_len) override; + + size_t maximum_associated_data_inputs() const override; + + void set_associated_data(const uint8_t ad[], size_t ad_len) override + { + set_associated_data_n(0, ad, ad_len); + } + + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + bool valid_nonce_length(size_t) const override; + + void clear() override; + + void reset() override; + + size_t tag_size() const override { return 16; } + + ~SIV_Mode(); + + protected: + explicit SIV_Mode(BlockCipher* cipher); + + size_t block_size() const { return m_bs; } + + StreamCipher& ctr() { return *m_ctr; } + + void set_ctr_iv(secure_vector<uint8_t> V); + + secure_vector<uint8_t>& msg_buf() { return m_msg_buf; } + + secure_vector<uint8_t> S2V(const uint8_t text[], size_t text_len); + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + const std::string m_name; + std::unique_ptr<StreamCipher> m_ctr; + std::unique_ptr<MessageAuthenticationCode> m_mac; + secure_vector<uint8_t> m_nonce, m_msg_buf; + std::vector<secure_vector<uint8_t>> m_ad_macs; + const size_t m_bs; + }; + +/** +* SIV Encryption +*/ +class BOTAN_PUBLIC_API(2,0) SIV_Encryption final : public SIV_Mode + { + public: + /** + * @param cipher a block cipher + */ + explicit SIV_Encryption(BlockCipher* cipher) : SIV_Mode(cipher) {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override + { return input_length + tag_size(); } + + size_t minimum_final_size() const override { return 0; } + }; + +/** +* SIV Decryption +*/ +class BOTAN_PUBLIC_API(2,0) SIV_Decryption final : public SIV_Mode + { + public: + /** + * @param cipher a 128-bit block cipher + */ + explicit SIV_Decryption(BlockCipher* cipher) : SIV_Mode(cipher) {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override + { + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); + return input_length - tag_size(); + } + + size_t minimum_final_size() const override { return tag_size(); } + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp b/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp new file mode 100644 index 0000000000..cde08f64a9 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp @@ -0,0 +1,323 @@ +/* +* CBC Mode +* (C) 1999-2007,2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cbc.h> +#include <botan/mode_pad.h> +#include <botan/internal/rounding.h> + +namespace Botan { + +CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) : + m_cipher(cipher), + m_padding(padding), + m_block_size(cipher->block_size()) + { + if(m_padding && !m_padding->valid_blocksize(m_block_size)) + throw Invalid_Argument("Padding " + m_padding->name() + + " cannot be used with " + + cipher->name() + "/CBC"); + } + +void CBC_Mode::clear() + { + m_cipher->clear(); + reset(); + } + +void CBC_Mode::reset() + { + m_state.clear(); + } + +std::string CBC_Mode::name() const + { + if(m_padding) + return cipher().name() + "/CBC/" + padding().name(); + else + return cipher().name() + "/CBC/CTS"; + } + +size_t CBC_Mode::update_granularity() const + { + return cipher().parallel_bytes(); + } + +Key_Length_Specification CBC_Mode::key_spec() const + { + return cipher().key_spec(); + } + +size_t CBC_Mode::default_nonce_length() const + { + return block_size(); + } + +bool CBC_Mode::valid_nonce_length(size_t n) const + { + return (n == 0 || n == block_size()); + } + +void CBC_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + m_state.clear(); + } + +void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + /* + * A nonce of zero length means carry the last ciphertext value over + * as the new IV, as unfortunately some protocols require this. If + * this is the first message then we use an IV of all zeros. + */ + if(nonce_len) + m_state.assign(nonce, nonce + nonce_len); + else if(m_state.empty()) + m_state.resize(m_cipher->block_size()); + // else leave the state alone + } + +size_t CBC_Encryption::minimum_final_size() const + { + return 0; + } + +size_t CBC_Encryption::output_length(size_t input_length) const + { + if(input_length == 0) + return block_size(); + else + return round_up(input_length, block_size()); + } + +size_t CBC_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks"); + const size_t blocks = sz / BS; + + if(blocks > 0) + { + xor_buf(&buf[0], state_ptr(), BS); + cipher().encrypt(&buf[0]); + + for(size_t i = 1; i != blocks; ++i) + { + xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS); + cipher().encrypt(&buf[BS*i]); + } + + state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]); + } + + return sz; + } + +void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + + const size_t BS = block_size(); + + const size_t bytes_in_final_block = (buffer.size()-offset) % BS; + + padding().add_padding(buffer, bytes_in_final_block, BS); + + BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary"); + + update(buffer, offset); + } + +bool CTS_Encryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Encryption::minimum_final_size() const + { + return block_size() + 1; + } + +size_t CTS_Encryption::output_length(size_t input_length) const + { + return input_length; // no ciphertext expansion in CTS + } + +void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + uint8_t* buf = buffer.data() + offset; + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to encrypt"); + + if(sz % BS == 0) + { + update(buffer, offset); + + // swap last two blocks + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + xor_buf(last.data(), state_ptr(), BS); + cipher().encrypt(last.data()); + + for(size_t i = 0; i != final_bytes - BS; ++i) + { + last[i] ^= last[i + BS]; + last[i + BS] ^= last[i]; + } + + cipher().encrypt(last.data()); + + buffer += last; + } + } + +size_t CBC_Decryption::output_length(size_t input_length) const + { + return input_length; // precise for CTS, worst case otherwise + } + +size_t CBC_Decryption::minimum_final_size() const + { + return block_size(); + } + +size_t CBC_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(state().empty() == false); + + const size_t BS = block_size(); + + BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); + size_t blocks = sz / BS; + + while(blocks) + { + const size_t to_proc = std::min(BS * blocks, m_tempbuf.size()); + + cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS); + + xor_buf(m_tempbuf.data(), state_ptr(), BS); + xor_buf(&m_tempbuf[BS], buf, to_proc - BS); + copy_mem(state_ptr(), buf + (to_proc - BS), BS); + + copy_mem(buf, m_tempbuf.data(), to_proc); + + buf += to_proc; + blocks -= to_proc / BS; + } + + return sz; + } + +void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + + const size_t BS = block_size(); + + if(sz == 0 || sz % BS) + throw Decoding_Error(name() + ": Ciphertext not a multiple of block size"); + + update(buffer, offset); + + const size_t pad_bytes = BS - padding().unpad(&buffer[buffer.size()-BS], BS); + buffer.resize(buffer.size() - pad_bytes); // remove padding + if(pad_bytes == 0 && padding().name() != "NoPadding") + { + throw Decoding_Error("Invalid CBC padding"); + } + } + +void CBC_Decryption::reset() + { + CBC_Mode::reset(); + zeroise(m_tempbuf); + } + +bool CTS_Decryption::valid_nonce_length(size_t n) const + { + return (n == block_size()); + } + +size_t CTS_Decryption::minimum_final_size() const + { + return block_size() + 1; + } + +void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_STATE_CHECK(state().empty() == false); + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + const size_t BS = block_size(); + + if(sz < BS + 1) + throw Encoding_Error(name() + ": insufficient data to decrypt"); + + if(sz % BS == 0) + { + // swap last two blocks + + for(size_t i = 0; i != BS; ++i) + std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]); + + update(buffer, offset); + } + else + { + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + cipher().decrypt(last.data()); + + xor_buf(last.data(), &last[BS], final_bytes - BS); + + for(size_t i = 0; i != final_bytes - BS; ++i) + std::swap(last[i], last[i + BS]); + + cipher().decrypt(last.data()); + xor_buf(last.data(), state_ptr(), BS); + + buffer += last; + } + } + +} diff --git a/comm/third_party/botan/src/lib/modes/cbc/cbc.h b/comm/third_party/botan/src/lib/modes/cbc/cbc.h new file mode 100644 index 0000000000..7a488dbd50 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cbc/cbc.h @@ -0,0 +1,157 @@ +/* +* CBC mode +* (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MODE_CBC_H_ +#define BOTAN_MODE_CBC_H_ + +#include <botan/cipher_mode.h> +#include <botan/block_cipher.h> +#include <botan/mode_pad.h> + +BOTAN_FUTURE_INTERNAL_HEADER(cbc.h) + +namespace Botan { + +/** +* CBC Mode +*/ +class BOTAN_PUBLIC_API(2,0) CBC_Mode : public Cipher_Mode + { + public: + std::string name() const override; + + size_t update_granularity() const override; + + Key_Length_Specification key_spec() const override; + + size_t default_nonce_length() const override; + + bool valid_nonce_length(size_t n) const override; + + void clear() override; + + void reset() override; + + protected: + CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding); + + const BlockCipher& cipher() const { return *m_cipher; } + + const BlockCipherModePaddingMethod& padding() const + { + BOTAN_ASSERT_NONNULL(m_padding); + return *m_padding; + } + + size_t block_size() const { return m_block_size; } + + secure_vector<uint8_t>& state() { return m_state; } + + uint8_t* state_ptr() { return m_state.data(); } + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + + void key_schedule(const uint8_t key[], size_t length) override; + + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<BlockCipherModePaddingMethod> m_padding; + secure_vector<uint8_t> m_state; + size_t m_block_size; + }; + +/** +* CBC Encryption +*/ +class BOTAN_PUBLIC_API(2,0) CBC_Encryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Encryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) : + CBC_Mode(cipher, padding) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + }; + +/** +* CBC Encryption with ciphertext stealing (CBC-CS3 variant) +*/ +class BOTAN_PUBLIC_API(2,0) CTS_Encryption final : public CBC_Encryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Encryption(BlockCipher* cipher) : CBC_Encryption(cipher, nullptr) {} + + size_t output_length(size_t input_length) const override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +/** +* CBC Decryption +*/ +class BOTAN_PUBLIC_API(2,0) CBC_Decryption : public CBC_Mode + { + public: + /** + * @param cipher block cipher to use + * @param padding padding method to use + */ + CBC_Decryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) : + CBC_Mode(cipher, padding), m_tempbuf(update_granularity()) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + + size_t minimum_final_size() const override; + + void reset() override; + + private: + secure_vector<uint8_t> m_tempbuf; + }; + +/** +* CBC Decryption with ciphertext stealing (CBC-CS3 variant) +*/ +class BOTAN_PUBLIC_API(2,0) CTS_Decryption final : public CBC_Decryption + { + public: + /** + * @param cipher block cipher to use + */ + explicit CTS_Decryption(BlockCipher* cipher) : CBC_Decryption(cipher, nullptr) {} + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t minimum_final_size() const override; + + bool valid_nonce_length(size_t n) const override; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/cbc/info.txt b/comm/third_party/botan/src/lib/modes/cbc/info.txt new file mode 100644 index 0000000000..778ba1e252 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cbc/info.txt @@ -0,0 +1,8 @@ +<defines> +MODE_CBC -> 20131128 +</defines> + +<requires> +block +mode_pad +</requires> diff --git a/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp b/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp new file mode 100644 index 0000000000..0c619e322c --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp @@ -0,0 +1,229 @@ +/* +* CFB Mode +* (C) 1999-2007,2013,2017 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cfb.h> + +namespace Botan { + +CFB_Mode::CFB_Mode(BlockCipher* cipher, size_t feedback_bits) : + m_cipher(cipher), + m_block_size(m_cipher->block_size()), + m_feedback_bytes(feedback_bits ? feedback_bits / 8 : m_block_size) + { + if(feedback_bits % 8 || feedback() > cipher->block_size()) + throw Invalid_Argument(name() + ": feedback bits " + + std::to_string(feedback_bits) + " not supported"); + } + +void CFB_Mode::clear() + { + m_cipher->clear(); + m_keystream.clear(); + reset(); + } + +void CFB_Mode::reset() + { + m_state.clear(); + zeroise(m_keystream); + } + +std::string CFB_Mode::name() const + { + if(feedback() == cipher().block_size()) + return cipher().name() + "/CFB"; + else + return cipher().name() + "/CFB(" + std::to_string(feedback()*8) + ")"; + } + +size_t CFB_Mode::output_length(size_t input_length) const + { + return input_length; + } + +size_t CFB_Mode::update_granularity() const + { + return feedback(); + } + +size_t CFB_Mode::minimum_final_size() const + { + return 0; + } + +Key_Length_Specification CFB_Mode::key_spec() const + { + return cipher().key_spec(); + } + +size_t CFB_Mode::default_nonce_length() const + { + return block_size(); + } + +bool CFB_Mode::valid_nonce_length(size_t n) const + { + return (n == 0 || n == block_size()); + } + +void CFB_Mode::key_schedule(const uint8_t key[], size_t length) + { + m_cipher->set_key(key, length); + m_keystream.resize(m_cipher->block_size()); + } + +void CFB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + verify_key_set(!m_keystream.empty()); + + if(nonce_len == 0) + { + if(m_state.empty()) + { + throw Invalid_State("CFB requires a non-empty initial nonce"); + } + // No reason to encrypt state->keystream_buf, because no change + } + else + { + m_state.assign(nonce, nonce + nonce_len); + cipher().encrypt(m_state, m_keystream); + m_keystream_pos = 0; + } + } + +void CFB_Mode::shift_register() + { + const size_t shift = feedback(); + const size_t carryover = block_size() - shift; + + if(carryover > 0) + { + copy_mem(m_state.data(), &m_state[shift], carryover); + } + copy_mem(&m_state[carryover], m_keystream.data(), shift); + cipher().encrypt(m_state, m_keystream); + m_keystream_pos = 0; + } + +size_t CFB_Encryption::process(uint8_t buf[], size_t sz) + { + verify_key_set(!m_keystream.empty()); + BOTAN_STATE_CHECK(m_state.empty() == false); + + const size_t shift = feedback(); + + size_t left = sz; + + if(m_keystream_pos != 0) + { + const size_t take = std::min<size_t>(left, shift - m_keystream_pos); + + xor_buf(m_keystream.data() + m_keystream_pos, buf, take); + copy_mem(buf, m_keystream.data() + m_keystream_pos, take); + + m_keystream_pos += take; + left -= take; + buf += take; + + if(m_keystream_pos == shift) + { + shift_register(); + } + } + + while(left >= shift) + { + xor_buf(m_keystream.data(), buf, shift); + copy_mem(buf, m_keystream.data(), shift); + + left -= shift; + buf += shift; + shift_register(); + } + + if(left > 0) + { + xor_buf(m_keystream.data(), buf, left); + copy_mem(buf, m_keystream.data(), left); + m_keystream_pos += left; + } + + return sz; + } + +void CFB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + update(buffer, offset); + } + +namespace { + +inline void xor_copy(uint8_t buf[], uint8_t key_buf[], size_t len) + { + for(size_t i = 0; i != len; ++i) + { + uint8_t k = key_buf[i]; + key_buf[i] = buf[i]; + buf[i] ^= k; + } + } + +} + +size_t CFB_Decryption::process(uint8_t buf[], size_t sz) + { + verify_key_set(!m_keystream.empty()); + BOTAN_STATE_CHECK(m_state.empty() == false); + + const size_t shift = feedback(); + + size_t left = sz; + + if(m_keystream_pos != 0) + { + const size_t take = std::min<size_t>(left, shift - m_keystream_pos); + + xor_copy(buf, m_keystream.data() + m_keystream_pos, take); + + m_keystream_pos += take; + left -= take; + buf += take; + + if(m_keystream_pos == shift) + { + shift_register(); + } + } + + while(left >= shift) + { + xor_copy(buf, m_keystream.data(), shift); + left -= shift; + buf += shift; + shift_register(); + } + + if(left > 0) + { + xor_copy(buf, m_keystream.data(), left); + m_keystream_pos += left; + } + + return sz; + } + +void CFB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + update(buffer, offset); + } + +} diff --git a/comm/third_party/botan/src/lib/modes/cfb/cfb.h b/comm/third_party/botan/src/lib/modes/cfb/cfb.h new file mode 100644 index 0000000000..1f9e554872 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cfb/cfb.h @@ -0,0 +1,106 @@ +/* +* CFB mode +* (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MODE_CFB_H_ +#define BOTAN_MODE_CFB_H_ + +#include <botan/cipher_mode.h> +#include <botan/block_cipher.h> + +BOTAN_FUTURE_INTERNAL_HEADER(cfb.h) + +namespace Botan { + +/** +* CFB Mode +*/ +class BOTAN_PUBLIC_API(2,0) CFB_Mode : public Cipher_Mode + { + public: + std::string name() const override final; + + size_t update_granularity() const override final; + + size_t minimum_final_size() const override final; + + Key_Length_Specification key_spec() const override final; + + size_t output_length(size_t input_length) const override final; + + size_t default_nonce_length() const override final; + + bool valid_nonce_length(size_t n) const override final; + + void clear() override final; + + void reset() override final; + protected: + CFB_Mode(BlockCipher* cipher, size_t feedback_bits); + + void shift_register(); + + size_t feedback() const { return m_feedback_bytes; } + const BlockCipher& cipher() const { return *m_cipher; } + size_t block_size() const { return m_block_size; } + + secure_vector<uint8_t> m_state; + secure_vector<uint8_t> m_keystream; + size_t m_keystream_pos = 0; + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + void key_schedule(const uint8_t key[], size_t length) override; + + std::unique_ptr<BlockCipher> m_cipher; + const size_t m_block_size; + const size_t m_feedback_bytes; + }; + +/** +* CFB Encryption +*/ +class BOTAN_PUBLIC_API(2,0) CFB_Encryption final : public CFB_Mode + { + public: + /** + * If feedback_bits is zero, cipher->block_size() bytes will be used. + * @param cipher block cipher to use + * @param feedback_bits number of bits fed back into the shift register, + * must be a multiple of 8 + */ + CFB_Encryption(BlockCipher* cipher, size_t feedback_bits) : + CFB_Mode(cipher, feedback_bits) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +/** +* CFB Decryption +*/ +class BOTAN_PUBLIC_API(2,0) CFB_Decryption final : public CFB_Mode + { + public: + /** + * If feedback_bits is zero, cipher->block_size() bytes will be used. + * @param cipher block cipher to use + * @param feedback_bits number of bits fed back into the shift register, + * must be a multiple of 8 + */ + CFB_Decryption(BlockCipher* cipher, size_t feedback_bits) : + CFB_Mode(cipher, feedback_bits) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/cfb/info.txt b/comm/third_party/botan/src/lib/modes/cfb/info.txt new file mode 100644 index 0000000000..77a6af448e --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cfb/info.txt @@ -0,0 +1,7 @@ +<defines> +MODE_CFB -> 20131128 +</defines> + +<requires> +block +</requires> diff --git a/comm/third_party/botan/src/lib/modes/cipher_mode.cpp b/comm/third_party/botan/src/lib/modes/cipher_mode.cpp new file mode 100644 index 0000000000..710f16ba22 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cipher_mode.cpp @@ -0,0 +1,205 @@ +/* +* Cipher Modes +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cipher_mode.h> +#include <botan/stream_mode.h> +#include <botan/scan_name.h> +#include <botan/parsing.h> +#include <sstream> + +#if defined(BOTAN_HAS_BLOCK_CIPHER) + #include <botan/block_cipher.h> +#endif + +#if defined(BOTAN_HAS_AEAD_MODES) + #include <botan/aead.h> +#endif + +#if defined(BOTAN_HAS_MODE_CBC) + #include <botan/cbc.h> +#endif + +#if defined(BOTAN_HAS_MODE_CFB) + #include <botan/cfb.h> +#endif + +#if defined(BOTAN_HAS_MODE_XTS) + #include <botan/xts.h> +#endif + +#if defined(BOTAN_HAS_OPENSSL) + #include <botan/internal/openssl.h> +#endif + +#if defined(BOTAN_HAS_COMMONCRYPTO) + #include <botan/internal/commoncrypto.h> +#endif + +namespace Botan { + +std::unique_ptr<Cipher_Mode> Cipher_Mode::create_or_throw(const std::string& algo, + Cipher_Dir direction, + const std::string& provider) + { + if(auto mode = Cipher_Mode::create(algo, direction, provider)) + return mode; + + throw Lookup_Error("Cipher mode", algo, provider); + } + +std::unique_ptr<Cipher_Mode> Cipher_Mode::create(const std::string& algo, + Cipher_Dir direction, + const std::string& provider) + { +#if defined(BOTAN_HAS_COMMONCRYPTO) + if(provider.empty() || provider == "commoncrypto") + { + std::unique_ptr<Cipher_Mode> commoncrypto_cipher(make_commoncrypto_cipher_mode(algo, direction)); + + if(commoncrypto_cipher) + return commoncrypto_cipher; + + if(!provider.empty()) + return std::unique_ptr<Cipher_Mode>(); + } +#endif + +#if defined(BOTAN_HAS_OPENSSL) + if(provider.empty() || provider == "openssl") + { + std::unique_ptr<Cipher_Mode> openssl_cipher(make_openssl_cipher_mode(algo, direction)); + + if(openssl_cipher) + return openssl_cipher; + + if(!provider.empty()) + return std::unique_ptr<Cipher_Mode>(); + } +#endif + +#if defined(BOTAN_HAS_STREAM_CIPHER) + if(auto sc = StreamCipher::create(algo)) + { + return std::unique_ptr<Cipher_Mode>(new Stream_Cipher_Mode(sc.release())); + } +#endif + +#if defined(BOTAN_HAS_AEAD_MODES) + if(auto aead = AEAD_Mode::create(algo, direction)) + { + return std::unique_ptr<Cipher_Mode>(aead.release()); + } +#endif + + if(algo.find('/') != std::string::npos) + { + const std::vector<std::string> algo_parts = split_on(algo, '/'); + const std::string cipher_name = algo_parts[0]; + const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]); + + if(mode_info.empty()) + return std::unique_ptr<Cipher_Mode>(); + + std::ostringstream alg_args; + + alg_args << '(' << cipher_name; + for(size_t i = 1; i < mode_info.size(); ++i) + alg_args << ',' << mode_info[i]; + for(size_t i = 2; i < algo_parts.size(); ++i) + alg_args << ',' << algo_parts[i]; + alg_args << ')'; + + const std::string mode_name = mode_info[0] + alg_args.str(); + return Cipher_Mode::create(mode_name, direction, provider); + } + +#if defined(BOTAN_HAS_BLOCK_CIPHER) + + SCAN_Name spec(algo); + + if(spec.arg_count() == 0) + { + return std::unique_ptr<Cipher_Mode>(); + } + + std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0), provider)); + + if(!bc) + { + return std::unique_ptr<Cipher_Mode>(); + } + +#if defined(BOTAN_HAS_MODE_CBC) + if(spec.algo_name() == "CBC") + { + const std::string padding = spec.arg(1, "PKCS7"); + + if(padding == "CTS") + { + if(direction == ENCRYPTION) + return std::unique_ptr<Cipher_Mode>(new CTS_Encryption(bc.release())); + else + return std::unique_ptr<Cipher_Mode>(new CTS_Decryption(bc.release())); + } + else + { + std::unique_ptr<BlockCipherModePaddingMethod> pad(get_bc_pad(padding)); + + if(pad) + { + if(direction == ENCRYPTION) + return std::unique_ptr<Cipher_Mode>(new CBC_Encryption(bc.release(), pad.release())); + else + return std::unique_ptr<Cipher_Mode>(new CBC_Decryption(bc.release(), pad.release())); + } + } + } +#endif + +#if defined(BOTAN_HAS_MODE_XTS) + if(spec.algo_name() == "XTS") + { + if(direction == ENCRYPTION) + return std::unique_ptr<Cipher_Mode>(new XTS_Encryption(bc.release())); + else + return std::unique_ptr<Cipher_Mode>(new XTS_Decryption(bc.release())); + } +#endif + +#if defined(BOTAN_HAS_MODE_CFB) + if(spec.algo_name() == "CFB") + { + const size_t feedback_bits = spec.arg_as_integer(1, 8*bc->block_size()); + if(direction == ENCRYPTION) + return std::unique_ptr<Cipher_Mode>(new CFB_Encryption(bc.release(), feedback_bits)); + else + return std::unique_ptr<Cipher_Mode>(new CFB_Decryption(bc.release(), feedback_bits)); + } +#endif + +#endif + + return std::unique_ptr<Cipher_Mode>(); + } + +//static +std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec) + { + const std::vector<std::string>& possible = { "base", "openssl", "commoncrypto" }; + std::vector<std::string> providers; + for(auto&& prov : possible) + { + std::unique_ptr<Cipher_Mode> mode = Cipher_Mode::create(algo_spec, ENCRYPTION, prov); + if(mode) + { + providers.push_back(prov); // available + } + } + return providers; + } + +} diff --git a/comm/third_party/botan/src/lib/modes/cipher_mode.h b/comm/third_party/botan/src/lib/modes/cipher_mode.h new file mode 100644 index 0000000000..9bf0b6811e --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/cipher_mode.h @@ -0,0 +1,198 @@ +/* +* Cipher Modes +* (C) 2013,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CIPHER_MODE_H_ +#define BOTAN_CIPHER_MODE_H_ + +#include <botan/secmem.h> +#include <botan/sym_algo.h> +#include <botan/exceptn.h> +#include <string> +#include <vector> + +namespace Botan { + +/** +* The two possible directions for cipher filters, determining whether they +* actually perform encryption or decryption. +*/ +enum Cipher_Dir : int { ENCRYPTION, DECRYPTION }; + +/** +* Interface for cipher modes +*/ +class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm + { + public: + /** + * @return list of available providers for this algorithm, empty if not available + * @param algo_spec algorithm name + */ + static std::vector<std::string> providers(const std::string& algo_spec); + + /** + * Create an AEAD mode + * @param algo the algorithm to create + * @param direction specify if this should be an encryption or decryption AEAD + * @param provider optional specification for provider to use + * @return an AEAD mode or a null pointer if not available + */ + static std::unique_ptr<Cipher_Mode> create(const std::string& algo, + Cipher_Dir direction, + const std::string& provider = ""); + + /** + * Create an AEAD mode, or throw + * @param algo the algorithm to create + * @param direction specify if this should be an encryption or decryption AEAD + * @param provider optional specification for provider to use + * @return an AEAD mode, or throw an exception + */ + static std::unique_ptr<Cipher_Mode> create_or_throw(const std::string& algo, + Cipher_Dir direction, + const std::string& provider = ""); + + /* + * Prepare for processing a message under the specified nonce + */ + virtual void start_msg(const uint8_t nonce[], size_t nonce_len) = 0; + + /** + * Begin processing a message. + * @param nonce the per message nonce + */ + template<typename Alloc> + void start(const std::vector<uint8_t, Alloc>& nonce) + { + start_msg(nonce.data(), nonce.size()); + } + + /** + * Begin processing a message. + * @param nonce the per message nonce + * @param nonce_len length of nonce + */ + void start(const uint8_t nonce[], size_t nonce_len) + { + start_msg(nonce, nonce_len); + } + + /** + * Begin processing a message. + */ + void start() + { + return start_msg(nullptr, 0); + } + + /** + * Process message blocks + * + * Input must be a multiple of update_granularity + * + * Processes msg in place and returns bytes written. Normally + * this will be either msg_len (indicating the entire message was + * processed) or for certain AEAD modes zero (indicating that the + * mode requires the entire message be processed in one pass). + * + * @param msg the message to be processed + * @param msg_len length of the message in bytes + */ + virtual size_t process(uint8_t msg[], size_t msg_len) = 0; + + /** + * Process some data. Input must be in size update_granularity() uint8_t blocks. + * @param buffer in/out parameter which will possibly be resized + * @param offset an offset into blocks to begin processing + */ + void update(secure_vector<uint8_t>& buffer, size_t offset = 0) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset ok"); + uint8_t* buf = buffer.data() + offset; + const size_t buf_size = buffer.size() - offset; + + const size_t written = process(buf, buf_size); + buffer.resize(offset + written); + } + + /** + * Complete processing of a message. + * + * @param final_block in/out parameter which must be at least + * minimum_final_size() bytes, and will be set to any final output + * @param offset an offset into final_block to begin processing + */ + virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0; + + /** + * Returns the size of the output if this transform is used to process a + * message with input_length bytes. In most cases the answer is precise. + * If it is not possible to precise (namely for CBC decryption) instead a + * lower bound is returned. + */ + virtual size_t output_length(size_t input_length) const = 0; + + /** + * @return size of required blocks to update + */ + virtual size_t update_granularity() const = 0; + + /** + * @return required minimium size to finalize() - may be any + * length larger than this. + */ + virtual size_t minimum_final_size() const = 0; + + /** + * @return the default size for a nonce + */ + virtual size_t default_nonce_length() const = 0; + + /** + * @return true iff nonce_len is a valid length for the nonce + */ + virtual bool valid_nonce_length(size_t nonce_len) const = 0; + + /** + * Resets just the message specific state and allows encrypting again under the existing key + */ + virtual void reset() = 0; + + /** + * @return true iff this mode provides authentication as well as + * confidentiality. + */ + virtual bool authenticated() const { return false; } + + /** + * @return the size of the authentication tag used (in bytes) + */ + virtual size_t tag_size() const { return 0; } + + /** + * @return provider information about this implementation. Default is "base", + * might also return "sse2", "avx2", "openssl", or some other arbitrary string. + */ + virtual std::string provider() const { return "base"; } + }; + +/** +* Get a cipher mode by name (eg "AES-128/CBC" or "Serpent/XTS") +* @param algo_spec cipher name +* @param direction ENCRYPTION or DECRYPTION +* @param provider provider implementation to choose +*/ +inline Cipher_Mode* get_cipher_mode(const std::string& algo_spec, + Cipher_Dir direction, + const std::string& provider = "") + { + return Cipher_Mode::create(algo_spec, direction, provider).release(); + } + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/info.txt b/comm/third_party/botan/src/lib/modes/info.txt new file mode 100644 index 0000000000..4c19db04ca --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/info.txt @@ -0,0 +1,9 @@ +<defines> +MODES -> 20150626 +CIPHER_MODES -> 20180124 +</defines> + +<header:public> +cipher_mode.h +stream_mode.h +</header:public> diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/info.txt b/comm/third_party/botan/src/lib/modes/mode_pad/info.txt new file mode 100644 index 0000000000..12b6e5b3a9 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/mode_pad/info.txt @@ -0,0 +1,3 @@ +<defines> +CIPHER_MODE_PADDING -> 20131128 +</defines> diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp new file mode 100644 index 0000000000..18bb71af5d --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp @@ -0,0 +1,333 @@ +/* +* CBC Padding Methods +* (C) 1999-2007,2013,2018,2020 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/mode_pad.h> +#include <botan/exceptn.h> +#include <botan/internal/ct_utils.h> + +namespace Botan { + +/** +* Get a block cipher padding method by name +*/ +BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec) + { + if(algo_spec == "NoPadding") + return new Null_Padding; + + if(algo_spec == "PKCS7") + return new PKCS7_Padding; + + if(algo_spec == "OneAndZeros") + return new OneAndZeros_Padding; + + if(algo_spec == "X9.23") + return new ANSI_X923_Padding; + + if(algo_spec == "ESP") + return new ESP_Padding; + + return nullptr; + } + +/* +* Pad with PKCS #7 Method +*/ +void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0202 + 030303 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(padding_len, buffer[i]); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with PKCS #7 Method +*/ +size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t last_byte = input[input_length-1]; + + /* + The input should == the block size so if the last byte exceeds + that then the padding is certainly invalid + */ + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Does this byte equal the expected pad byte? + const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte); + + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + bad_input |= in_range & (~pad_eq); + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ANSI X9.23 Method +*/ +void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0002 + 000003 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_zero_padding = buffer.size() - 1; + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(0, buffer[i]); + } + + buffer[buffer.size()-1] = padding_len; + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ANSI X9.23 Method +*/ +size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const size_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); + + const size_t pad_pos = input_length - last_byte; + + for(size_t i = 0; i != input_length - 1; ++i) + { + // Ignore values that are not part of the padding + const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); + const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]); + bad_input |= pad_is_nonzero & in_range; + } + + CT::unpoison(input, input_length); + + return bad_input.select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with One and Zeros Method +*/ +void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 80 + 8000 + 800000 + ... + */ + + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding)); + auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding)); + buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i])); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with One and Zeros Method +*/ +size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + auto bad_input = CT::Mask<uint8_t>::cleared(); + auto seen_0x80 = CT::Mask<uint8_t>::cleared(); + + size_t pad_pos = input_length - 1; + size_t i = input_length; + + while(i) + { + const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80); + const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i-1]); + + seen_0x80 |= is_0x80; + pad_pos -= seen_0x80.if_not_set_return(1); + bad_input |= ~seen_0x80 & ~is_zero; + i--; + } + bad_input |= ~seen_0x80; + + CT::unpoison(input, input_length); + + return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos); + } + +/* +* Pad with ESP Padding Method +*/ +void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer, + size_t last_byte_pos, + size_t BS) const + { + /* + Padding format is + 01 + 0102 + 010203 + ... + */ + BOTAN_DEBUG_ASSERT(last_byte_pos < BS); + + const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); + + buffer.resize(buffer.size() + padding_len); + + CT::poison(&last_byte_pos, 1); + CT::poison(buffer.data(), buffer.size()); + + BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); + BOTAN_DEBUG_ASSERT(buffer.size() >= BS); + + const size_t start_of_last_block = buffer.size() - BS; + const size_t end_of_last_block = buffer.size(); + const size_t start_of_padding = buffer.size() - padding_len; + + uint8_t pad_ctr = 0x01; + + for(size_t i = start_of_last_block; i != end_of_last_block; ++i) + { + auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); + buffer[i] = needs_padding.select(pad_ctr, buffer[i]); + pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr); + } + + CT::unpoison(buffer.data(), buffer.size()); + CT::unpoison(last_byte_pos); + } + +/* +* Unpad with ESP Padding Method +*/ +size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const + { + if(!valid_blocksize(input_length)) + return input_length; + + CT::poison(input, input_length); + + const uint8_t input_length_8 = static_cast<uint8_t>(input_length); + const uint8_t last_byte = input[input_length-1]; + + auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) | + CT::Mask<uint8_t>::is_gt(last_byte, input_length_8); + + const uint8_t pad_pos = input_length_8 - last_byte; + size_t i = input_length_8 - 1; + while(i) + { + const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos); + const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1); + + bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing; + --i; + } + + CT::unpoison(input, input_length); + return bad_input.select_and_unpoison(input_length_8, pad_pos); + } + + +} diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h new file mode 100644 index 0000000000..b0e4a3cfae --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h @@ -0,0 +1,160 @@ +/* +* CBC Padding Methods +* (C) 1999-2008,2013 Jack Lloyd +* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MODE_PADDING_H_ +#define BOTAN_MODE_PADDING_H_ + +#include <botan/secmem.h> +#include <string> + +BOTAN_FUTURE_INTERNAL_HEADER(mode_pad.h) + +namespace Botan { + +/** +* Block Cipher Mode Padding Method +* This class is pretty limited, it cannot deal well with +* randomized padding methods, or any padding method that +* wants to add more than one block. For instance, it should +* be possible to define cipher text stealing mode as simply +* a padding mode for CBC, which happens to consume the last +* two block (and requires use of the block cipher). +*/ +class BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod + { + public: + /** + * Add padding bytes to buffer. + * @param buffer data to pad + * @param final_block_bytes size of the final block in bytes + * @param block_size size of each block in bytes + */ + virtual void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const = 0; + + /** + * Remove padding bytes from block + * @param block the last block + * @param len the size of the block in bytes + * @return number of data bytes, or if the padding is invalid returns len + */ + virtual size_t unpad(const uint8_t block[], size_t len) const = 0; + + /** + * @param block_size of the cipher + * @return valid block size for this padding mode + */ + virtual bool valid_blocksize(size_t block_size) const = 0; + + /** + * @return name of the mode + */ + virtual std::string name() const = 0; + + /** + * virtual destructor + */ + virtual ~BlockCipherModePaddingMethod() = default; + }; + +/** +* PKCS#7 Padding +*/ +class BOTAN_PUBLIC_API(2,0) PKCS7_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "PKCS7"; } + }; + +/** +* ANSI X9.23 Padding +*/ +class BOTAN_PUBLIC_API(2,0) ANSI_X923_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "X9.23"; } + }; + +/** +* One And Zeros Padding (ISO/IEC 9797-1, padding method 2) +*/ +class BOTAN_PUBLIC_API(2,0) OneAndZeros_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2); } + + std::string name() const override { return "OneAndZeros"; } + }; + +/** +* ESP Padding (RFC 4304) +*/ +class BOTAN_PUBLIC_API(2,0) ESP_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>& buffer, + size_t final_block_bytes, + size_t block_size) const override; + + size_t unpad(const uint8_t[], size_t) const override; + + bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); } + + std::string name() const override { return "ESP"; } + }; + +/** +* Null Padding +*/ +class BOTAN_PUBLIC_API(2,0) Null_Padding final : public BlockCipherModePaddingMethod + { + public: + void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override + { + /* no padding */ + } + + size_t unpad(const uint8_t[], size_t size) const override { return size; } + + bool valid_blocksize(size_t) const override { return true; } + + std::string name() const override { return "NoPadding"; } + }; + +/** +* Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7") +* @param algo_spec block cipher padding mode name +*/ +BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec); + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/stream_mode.h b/comm/third_party/botan/src/lib/modes/stream_mode.h new file mode 100644 index 0000000000..da3fc38cf0 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/stream_mode.h @@ -0,0 +1,84 @@ +/* +* (C) 2015 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_STREAM_MODE_H_ +#define BOTAN_STREAM_MODE_H_ + +#include <botan/cipher_mode.h> + +#if defined(BOTAN_HAS_STREAM_CIPHER) + #include <botan/stream_cipher.h> +#endif + +BOTAN_FUTURE_INTERNAL_HEADER(stream_mode.h) + +namespace Botan { + +#if defined(BOTAN_HAS_STREAM_CIPHER) + +class BOTAN_PUBLIC_API(2,0) Stream_Cipher_Mode final : public Cipher_Mode + { + public: + /** + * @param cipher underyling stream cipher + */ + explicit Stream_Cipher_Mode(StreamCipher* cipher) : m_cipher(cipher) {} + + size_t process(uint8_t buf[], size_t sz) override + { + m_cipher->cipher1(buf, sz); + return sz; + } + + void finish(secure_vector<uint8_t>& buf, size_t offset) override + { return update(buf, offset); } + + size_t output_length(size_t input_length) const override { return input_length; } + + size_t update_granularity() const override { return 1; } + + size_t minimum_final_size() const override { return 0; } + + size_t default_nonce_length() const override { return 0; } + + bool valid_nonce_length(size_t nonce_len) const override + { return m_cipher->valid_iv_length(nonce_len); } + + Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); } + + std::string name() const override { return m_cipher->name(); } + + void clear() override + { + m_cipher->clear(); + reset(); + } + + void reset() override { /* no msg state */ } + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override + { + if(nonce_len > 0) + { + m_cipher->set_iv(nonce, nonce_len); + } + } + + void key_schedule(const uint8_t key[], size_t length) override + { + m_cipher->set_key(key, length); + } + + std::unique_ptr<StreamCipher> m_cipher; + }; + +#endif + +} + +#endif diff --git a/comm/third_party/botan/src/lib/modes/xts/info.txt b/comm/third_party/botan/src/lib/modes/xts/info.txt new file mode 100644 index 0000000000..cee850be78 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/xts/info.txt @@ -0,0 +1,8 @@ +<defines> +MODE_XTS -> 20131128 +</defines> + +<requires> +block +poly_dbl +</requires> diff --git a/comm/third_party/botan/src/lib/modes/xts/xts.cpp b/comm/third_party/botan/src/lib/modes/xts/xts.cpp new file mode 100644 index 0000000000..559584b082 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/xts/xts.cpp @@ -0,0 +1,248 @@ +/* +* XTS Mode +* (C) 2009,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/xts.h> +#include <botan/internal/poly_dbl.h> + +namespace Botan { + +XTS_Mode::XTS_Mode(BlockCipher* cipher) : + m_cipher(cipher), + m_cipher_block_size(m_cipher->block_size()), + m_cipher_parallelism(m_cipher->parallel_bytes()) + { + if(poly_double_supported_size(m_cipher_block_size) == false) + { + throw Invalid_Argument("Cannot use " + cipher->name() + " with XTS"); + } + + m_tweak_cipher.reset(m_cipher->clone()); + } + +void XTS_Mode::clear() + { + m_cipher->clear(); + m_tweak_cipher->clear(); + reset(); + } + +void XTS_Mode::reset() + { + m_tweak.clear(); + } + +std::string XTS_Mode::name() const + { + return cipher().name() + "/XTS"; + } + +size_t XTS_Mode::minimum_final_size() const + { + return cipher_block_size(); + } + +Key_Length_Specification XTS_Mode::key_spec() const + { + return cipher().key_spec().multiple(2); + } + +size_t XTS_Mode::default_nonce_length() const + { + return cipher_block_size(); + } + +bool XTS_Mode::valid_nonce_length(size_t n) const + { + return cipher_block_size() == n; + } + +void XTS_Mode::key_schedule(const uint8_t key[], size_t length) + { + const size_t key_half = length / 2; + + if(length % 2 == 1 || !m_cipher->valid_keylength(key_half)) + throw Invalid_Key_Length(name(), length); + + m_cipher->set_key(key, key_half); + m_tweak_cipher->set_key(&key[key_half], key_half); + } + +void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) + { + if(!valid_nonce_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + m_tweak.resize(update_granularity()); + copy_mem(m_tweak.data(), nonce, nonce_len); + m_tweak_cipher->encrypt(m_tweak.data()); + + update_tweak(0); + } + +void XTS_Mode::update_tweak(size_t which) + { + const size_t BS = m_tweak_cipher->block_size(); + + if(which > 0) + poly_double_n_le(m_tweak.data(), &m_tweak[(which-1)*BS], BS); + + const size_t blocks_in_tweak = update_granularity() / BS; + + for(size_t i = 1; i < blocks_in_tweak; ++i) + poly_double_n_le(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS); + } + +size_t XTS_Encryption::output_length(size_t input_length) const + { + return input_length; + } + +size_t XTS_Encryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(tweak_set()); + const size_t BS = cipher_block_size(); + + BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); + size_t blocks = sz / BS; + + const size_t blocks_in_tweak = update_granularity() / BS; + + while(blocks) + { + const size_t to_proc = std::min(blocks, blocks_in_tweak); + + cipher().encrypt_n_xex(buf, tweak(), to_proc); + + buf += to_proc * BS; + blocks -= to_proc; + + update_tweak(to_proc); + } + + return sz; + } + +void XTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS encrypt"); + + const size_t BS = cipher_block_size(); + + if(sz % BS == 0) + { + update(buffer, offset); + } + else + { + // steal ciphertext + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + xor_buf(last, tweak(), BS); + cipher().encrypt(last); + xor_buf(last, tweak(), BS); + + for(size_t i = 0; i != final_bytes - BS; ++i) + { + last[i] ^= last[i + BS]; + last[i + BS] ^= last[i]; + last[i] ^= last[i + BS]; + } + + xor_buf(last, tweak() + BS, BS); + cipher().encrypt(last); + xor_buf(last, tweak() + BS, BS); + + buffer += last; + } + } + +size_t XTS_Decryption::output_length(size_t input_length) const + { + return input_length; + } + +size_t XTS_Decryption::process(uint8_t buf[], size_t sz) + { + BOTAN_STATE_CHECK(tweak_set()); + const size_t BS = cipher_block_size(); + + BOTAN_ASSERT(sz % BS == 0, "Input is full blocks"); + size_t blocks = sz / BS; + + const size_t blocks_in_tweak = update_granularity() / BS; + + while(blocks) + { + const size_t to_proc = std::min(blocks, blocks_in_tweak); + + cipher().decrypt_n_xex(buf, tweak(), to_proc); + + buf += to_proc * BS; + blocks -= to_proc; + + update_tweak(to_proc); + } + + return sz; + } + +void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); + const size_t sz = buffer.size() - offset; + uint8_t* buf = buffer.data() + offset; + + BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS decrypt"); + + const size_t BS = cipher_block_size(); + + if(sz % BS == 0) + { + update(buffer, offset); + } + else + { + // steal ciphertext + const size_t full_blocks = ((sz / BS) - 1) * BS; + const size_t final_bytes = sz - full_blocks; + BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range"); + + secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes); + buffer.resize(full_blocks + offset); + update(buffer, offset); + + xor_buf(last, tweak() + BS, BS); + cipher().decrypt(last); + xor_buf(last, tweak() + BS, BS); + + for(size_t i = 0; i != final_bytes - BS; ++i) + { + last[i] ^= last[i + BS]; + last[i + BS] ^= last[i]; + last[i] ^= last[i + BS]; + } + + xor_buf(last, tweak(), BS); + cipher().decrypt(last); + xor_buf(last, tweak(), BS); + + buffer += last; + } + } + +} diff --git a/comm/third_party/botan/src/lib/modes/xts/xts.h b/comm/third_party/botan/src/lib/modes/xts/xts.h new file mode 100644 index 0000000000..75de93c088 --- /dev/null +++ b/comm/third_party/botan/src/lib/modes/xts/xts.h @@ -0,0 +1,103 @@ +/* +* XTS mode, from IEEE P1619 +* (C) 2009,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_MODE_XTS_H_ +#define BOTAN_MODE_XTS_H_ + +#include <botan/cipher_mode.h> +#include <botan/block_cipher.h> + +BOTAN_FUTURE_INTERNAL_HEADER(xts.h) + +namespace Botan { + +/** +* IEEE P1619 XTS Mode +*/ +class BOTAN_PUBLIC_API(2,0) XTS_Mode : public Cipher_Mode + { + public: + std::string name() const override; + + size_t update_granularity() const override { return m_cipher_parallelism; } + + size_t minimum_final_size() const override; + + Key_Length_Specification key_spec() const override; + + size_t default_nonce_length() const override; + + bool valid_nonce_length(size_t n) const override; + + void clear() override; + + void reset() override; + + protected: + explicit XTS_Mode(BlockCipher* cipher); + + const uint8_t* tweak() const { return m_tweak.data(); } + + bool tweak_set() const { return m_tweak.empty() == false; } + + const BlockCipher& cipher() const { return *m_cipher; } + + void update_tweak(size_t last_used); + + size_t cipher_block_size() const { return m_cipher_block_size; } + + private: + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + void key_schedule(const uint8_t key[], size_t length) override; + + std::unique_ptr<BlockCipher> m_cipher; + std::unique_ptr<BlockCipher> m_tweak_cipher; + secure_vector<uint8_t> m_tweak; + const size_t m_cipher_block_size; + const size_t m_cipher_parallelism; + }; + +/** +* IEEE P1619 XTS Encryption +*/ +class BOTAN_PUBLIC_API(2,0) XTS_Encryption final : public XTS_Mode + { + public: + /** + * @param cipher underlying block cipher + */ + explicit XTS_Encryption(BlockCipher* cipher) : XTS_Mode(cipher) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + }; + +/** +* IEEE P1619 XTS Decryption +*/ +class BOTAN_PUBLIC_API(2,0) XTS_Decryption final : public XTS_Mode + { + public: + /** + * @param cipher underlying block cipher + */ + explicit XTS_Decryption(BlockCipher* cipher) : XTS_Mode(cipher) {} + + size_t process(uint8_t buf[], size_t size) override; + + void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override; + + size_t output_length(size_t input_length) const override; + }; + +} + +#endif |