diff options
author | Alexander Bluhm <[email protected]> | 2017-04-03 18:00:54 +0200 |
---|---|---|
committer | Alexander Bluhm <[email protected]> | 2017-04-25 15:50:33 +0200 |
commit | a9b91783b734d0cb1b3515f091564d126218275f (patch) | |
tree | 6ea2804824dc3286865cdd368952c3922171a3eb /src/lib | |
parent | 3a560e25b2ab197e54935eb047090446be6c10f5 (diff) |
Implement cipher modes with OpenSSL.
Use the OpenSSL provider to implement AES CBC mode. Also pass down
the provider to the encryption layer if there is no matching OpenSSL
mode. Add a test with empty nonce.
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; + } + +} |