diff options
author | Jack Lloyd <[email protected]> | 2017-04-27 11:22:35 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-04-27 11:22:35 -0400 |
commit | 001db437fcb9fcbfce63a9b5cff7d8f767237c95 (patch) | |
tree | b9472f9a06fc579c4e3f85f4b8d24ef4fb36ff54 /src/lib | |
parent | 14ee7e88c2041c26c6d8fa7e0cae6a200b6f9799 (diff) | |
parent | a9b91783b734d0cb1b3515f091564d126218275f (diff) |
Merge GH #1022 Support CBC ciphers via OpenSSL
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/modes/cipher_mode.cpp | 38 | ||||
-rw-r--r-- | src/lib/modes/cipher_mode.h | 13 | ||||
-rw-r--r-- | src/lib/prov/openssl/openssl.h | 7 | ||||
-rw-r--r-- | src/lib/prov/openssl/openssl_mode.cpp | 200 |
4 files changed, 253 insertions, 5 deletions
diff --git a/src/lib/modes/cipher_mode.cpp b/src/lib/modes/cipher_mode.cpp index 843e49581..74d565f33 100644 --- a/src/lib/modes/cipher_mode.cpp +++ b/src/lib/modes/cipher_mode.cpp @@ -34,10 +34,26 @@ #include <botan/xts.h> #endif +#if defined(BOTAN_HAS_OPENSSL) + #include <botan/internal/openssl.h> +#endif + namespace Botan { -Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction) +Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction, + const std::string& provider) { +#if defined(BOTAN_HAS_OPENSSL) + if(provider.empty() || provider == "openssl") + { + if(Cipher_Mode* bc = make_openssl_cipher_mode(algo, direction)) + return bc; + + if(!provider.empty()) + return nullptr; + } +#endif + if(auto sc = StreamCipher::create(algo)) { return new Stream_Cipher_Mode(sc.release()); @@ -69,7 +85,7 @@ Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction) alg_args << ')'; const std::string mode_name = mode_info[0] + alg_args.str(); - return get_cipher_mode(mode_name, direction); + return get_cipher_mode(mode_name, direction, provider); } #if defined(BOTAN_HAS_BLOCK_CIPHER) @@ -81,7 +97,7 @@ Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction) return nullptr; } - std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0))); + std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0), provider)); if(!bc) { @@ -141,4 +157,20 @@ Cipher_Mode* get_cipher_mode(const std::string& algo, Cipher_Dir direction) return nullptr; } +//static +std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec) + { + const std::vector<std::string>& possible = { "base", "openssl" }; + std::vector<std::string> providers; + for(auto&& prov : possible) + { + std::unique_ptr<Cipher_Mode> mode(get_cipher_mode(algo_spec, ENCRYPTION, prov)); + if(mode) + { + providers.push_back(prov); // available + } + } + return providers; + } + } diff --git a/src/lib/modes/cipher_mode.h b/src/lib/modes/cipher_mode.h index 411a1b3e5..bf1821256 100644 --- a/src/lib/modes/cipher_mode.h +++ b/src/lib/modes/cipher_mode.h @@ -25,6 +25,12 @@ class BOTAN_DLL Cipher_Mode public: virtual ~Cipher_Mode() = default; + /** + * @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); + /* * Prepare for processing a message under the specified nonce */ @@ -209,14 +215,17 @@ class BOTAN_DLL Cipher_Mode * The two possible directions for cipher filters, determining whether they * actually perform encryption or decryption. */ -enum Cipher_Dir { ENCRYPTION, DECRYPTION }; +enum Cipher_Dir : int { ENCRYPTION, DECRYPTION }; /** * 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 */ -BOTAN_DLL Cipher_Mode* get_cipher_mode(const std::string& algo_spec, Cipher_Dir direction); +BOTAN_DLL Cipher_Mode* get_cipher_mode(const std::string& algo_spec, + Cipher_Dir direction, + const std::string& provider = ""); } diff --git a/src/lib/prov/openssl/openssl.h b/src/lib/prov/openssl/openssl.h index e28fb2931..3cd39113b 100644 --- a/src/lib/prov/openssl/openssl.h +++ b/src/lib/prov/openssl/openssl.h @@ -24,8 +24,10 @@ namespace Botan { class BlockCipher; +class Cipher_Mode; class StreamCipher; class HashFunction; +enum Cipher_Dir : int; class OpenSSL_Error : public Exception { @@ -39,6 +41,11 @@ class OpenSSL_Error : public Exception std::unique_ptr<BlockCipher> make_openssl_block_cipher(const std::string& name); +/* Cipher Modes */ + +Cipher_Mode* +make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction); + /* Hash */ std::unique_ptr<HashFunction> diff --git a/src/lib/prov/openssl/openssl_mode.cpp b/src/lib/prov/openssl/openssl_mode.cpp new file mode 100644 index 000000000..77682bf5c --- /dev/null +++ b/src/lib/prov/openssl/openssl_mode.cpp @@ -0,0 +1,200 @@ +/* +* Cipher Modes via OpenSSL +* (C) 1999-2010,2015 Jack Lloyd +* (C) 2017 Alexander Bluhm (genua GmbH) +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cipher_mode.h> +#include <botan/internal/rounding.h> +#include <botan/internal/openssl.h> +#include <openssl/evp.h> + +namespace Botan { + +namespace { + +class BOTAN_DLL OpenSSL_Cipher_Mode : public Cipher_Mode + { + public: + OpenSSL_Cipher_Mode(const std::string& name, + const EVP_CIPHER* cipher, + Cipher_Dir direction); + ~OpenSSL_Cipher_Mode(); + + std::string provider() const override { return "openssl"; } + std::string name() const override { return m_mode_name; } + + void start_msg(const uint8_t nonce[], size_t nonce_len) override; + size_t process(uint8_t msg[], size_t msg_len) override; + void finish(secure_vector<uint8_t>& final_block, size_t offset0) override; + size_t output_length(size_t input_length) const override; + size_t update_granularity() const override; + size_t minimum_final_size() const override; + size_t default_nonce_length() const override; + bool valid_nonce_length(size_t nonce_len) const override; + void clear() override; + void reset() override; + Key_Length_Specification key_spec() const override; + + private: + void key_schedule(const uint8_t key[], size_t length) override; + + const std::string m_mode_name; + const Cipher_Dir m_direction; + size_t m_block_size; + EVP_CIPHER_CTX m_cipher; + }; + +OpenSSL_Cipher_Mode::OpenSSL_Cipher_Mode(const std::string& name, + const EVP_CIPHER* algo, + Cipher_Dir direction) : + m_mode_name(name), + m_direction(direction) + { + m_block_size = EVP_CIPHER_block_size(algo); + + if(EVP_CIPHER_mode(algo) != EVP_CIPH_CBC_MODE) + throw Invalid_Argument("OpenSSL_BlockCipher: Non-CBC EVP was passed in"); + + EVP_CIPHER_CTX_init(&m_cipher); + if(!EVP_CipherInit_ex(&m_cipher, algo, nullptr, nullptr, nullptr, + m_direction == ENCRYPTION ? 1 : 0)) + throw Internal_Error("EVP_CipherInit_ex failed"); + if(!EVP_CIPHER_CTX_set_padding(&m_cipher, 0)) + throw Internal_Error("EVP_CIPHER_CTX_set_padding failed"); + } + +OpenSSL_Cipher_Mode::~OpenSSL_Cipher_Mode() + { + EVP_CIPHER_CTX_cleanup(&m_cipher); + } + +void OpenSSL_Cipher_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) + { + if(!EVP_CipherInit_ex(&m_cipher, nullptr, nullptr, nullptr, nonce, -1)) + throw Internal_Error("EVP_CipherInit_ex nonce failed"); + } + } + +size_t OpenSSL_Cipher_Mode::process(uint8_t msg[], size_t msg_len) + { + if(msg_len == 0) + return 0; + if(msg_len > INT_MAX) + throw Internal_Error("msg_len overflow"); + int outl = msg_len; + secure_vector<uint8_t> out(outl); + + if(!EVP_CipherUpdate(&m_cipher, out.data(), &outl, msg, msg_len)) + throw Internal_Error("EVP_CipherUpdate failed"); + memcpy(msg, out.data(), outl); + return outl; + } + +void OpenSSL_Cipher_Mode::finish(secure_vector<uint8_t>& buffer, + size_t offset) + { + BOTAN_ASSERT(buffer.size() >= offset, "Offset ok"); + uint8_t* buf = buffer.data() + offset; + const size_t buf_size = buffer.size() - offset; + + size_t written = process(buf, buf_size); + int outl = buf_size - written; + secure_vector<uint8_t> out(outl); + + if(!EVP_CipherFinal_ex(&m_cipher, out.data(), &outl)) + throw Internal_Error("EVP_CipherFinal_ex failed"); + memcpy(buf + written, out.data(), outl); + written += outl; + buffer.resize(offset + written); + } + +size_t OpenSSL_Cipher_Mode::update_granularity() const + { + return m_block_size * BOTAN_BLOCK_CIPHER_PAR_MULT; + } + +size_t OpenSSL_Cipher_Mode::minimum_final_size() const + { + return 0; // no padding + } + +size_t OpenSSL_Cipher_Mode::default_nonce_length() const + { + return m_block_size; + } + +bool OpenSSL_Cipher_Mode::valid_nonce_length(size_t nonce_len) const + { + return (nonce_len == 0 || nonce_len == m_block_size); + } + +size_t OpenSSL_Cipher_Mode::output_length(size_t input_length) const + { + if(input_length == 0) + return m_block_size; + else + return round_up(input_length, m_block_size); + } + +void OpenSSL_Cipher_Mode::clear() + { + const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(&m_cipher); + + if(!EVP_CIPHER_CTX_cleanup(&m_cipher)) + throw Internal_Error("EVP_CIPHER_CTX_cleanup failed"); + EVP_CIPHER_CTX_init(&m_cipher); + if(!EVP_CipherInit_ex(&m_cipher, algo, nullptr, nullptr, nullptr, + m_direction == ENCRYPTION ? 1 : 0)) + throw Internal_Error("EVP_CipherInit_ex clear failed"); + if(!EVP_CIPHER_CTX_set_padding(&m_cipher, 0)) + throw Internal_Error("EVP_CIPHER_CTX_set_padding clear failed"); + } + +void OpenSSL_Cipher_Mode::reset() + { + if(!EVP_CipherInit_ex(&m_cipher, nullptr, nullptr, nullptr, nullptr, -1)) + throw Internal_Error("EVP_CipherInit_ex clear failed"); + } + +Key_Length_Specification OpenSSL_Cipher_Mode::key_spec() const + { + return Key_Length_Specification(EVP_CIPHER_CTX_key_length(&m_cipher)); + } + +void OpenSSL_Cipher_Mode::key_schedule(const uint8_t key[], size_t length) + { + if(!EVP_CIPHER_CTX_set_key_length(&m_cipher, length)) + throw Invalid_Argument("EVP_CIPHER_CTX_set_key_length failed"); + if(!EVP_CipherInit_ex(&m_cipher, nullptr, nullptr, key, nullptr, -1)) + throw Internal_Error("EVP_CipherInit_ex key failed"); + } + +} + +Cipher_Mode* +make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction) + { +#define MAKE_OPENSSL_MODE(evp_fn) \ + new OpenSSL_Cipher_Mode(name, (evp_fn)(), direction) + +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && !defined(OPENSSL_NO_AES) + if(name == "AES-128/CBC/NoPadding") + return MAKE_OPENSSL_MODE(EVP_aes_128_cbc); + if(name == "AES-192/CBC/NoPadding") + return MAKE_OPENSSL_MODE(EVP_aes_192_cbc); + if(name == "AES-256/CBC/NoPadding") + return MAKE_OPENSSL_MODE(EVP_aes_256_cbc); +#endif + +#undef MAKE_OPENSSL_MODE + return nullptr; + } + +} |