diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp')
-rw-r--r-- | comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp new file mode 100644 index 0000000000..7f9bdcf6c5 --- /dev/null +++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp @@ -0,0 +1,234 @@ +/* +* Block Ciphers via OpenSSL +* (C) 1999-2010,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/block_cipher.h> +#include <botan/internal/openssl.h> +#include <openssl/evp.h> + +namespace Botan { + +namespace { + +class OpenSSL_BlockCipher final : public BlockCipher + { + public: + OpenSSL_BlockCipher(const std::string& name, + const EVP_CIPHER* cipher); + + OpenSSL_BlockCipher(const std::string& name, + const EVP_CIPHER* cipher, + size_t kl_min, size_t kl_max, size_t kl_mod); + + ~OpenSSL_BlockCipher(); + + void clear() override; + std::string provider() const override { return "openssl"; } + std::string name() const override { return m_cipher_name; } + BlockCipher* clone() const override; + + size_t block_size() const override { return m_block_sz; } + + Key_Length_Specification key_spec() const override { return m_cipher_key_spec; } + + void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override + { + verify_key_set(m_key_set); + int out_len = 0; + if(!EVP_EncryptUpdate(m_encrypt, out, &out_len, in, blocks * m_block_sz)) + throw OpenSSL_Error("EVP_EncryptUpdate", ERR_get_error()); + } + + void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override + { + verify_key_set(m_key_set); + int out_len = 0; + if(!EVP_DecryptUpdate(m_decrypt, out, &out_len, in, blocks * m_block_sz)) + throw OpenSSL_Error("EVP_DecryptUpdate", ERR_get_error()); + } + + void key_schedule(const uint8_t key[], size_t key_len) override; + + size_t m_block_sz; + Key_Length_Specification m_cipher_key_spec; + std::string m_cipher_name; + EVP_CIPHER_CTX *m_encrypt; + EVP_CIPHER_CTX *m_decrypt; + bool m_key_set; + }; + +OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name, + const EVP_CIPHER* algo) : + m_block_sz(EVP_CIPHER_block_size(algo)), + m_cipher_key_spec(EVP_CIPHER_key_length(algo)), + m_cipher_name(algo_name), + m_key_set(false) + { + if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE) + throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in"); + + m_encrypt = EVP_CIPHER_CTX_new(); + m_decrypt = EVP_CIPHER_CTX_new(); + if (m_encrypt == nullptr || m_decrypt == nullptr) + throw OpenSSL_Error("Can't allocate new context", ERR_get_error()); + + EVP_CIPHER_CTX_init(m_encrypt); + EVP_CIPHER_CTX_init(m_decrypt); + + if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + + if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error()); + } + +OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name, + const EVP_CIPHER* algo, + size_t key_min, + size_t key_max, + size_t key_mod) : + m_block_sz(EVP_CIPHER_block_size(algo)), + m_cipher_key_spec(key_min, key_max, key_mod), + m_cipher_name(algo_name), + m_key_set(false) + { + if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE) + throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in"); + + m_encrypt = EVP_CIPHER_CTX_new(); + m_decrypt = EVP_CIPHER_CTX_new(); + if (m_encrypt == nullptr || m_decrypt == nullptr) + throw OpenSSL_Error("Can't allocate new context", ERR_get_error()); + + EVP_CIPHER_CTX_init(m_encrypt); + EVP_CIPHER_CTX_init(m_decrypt); + + if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + + if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error()); + } + +OpenSSL_BlockCipher::~OpenSSL_BlockCipher() + { + EVP_CIPHER_CTX_cleanup(m_encrypt); + EVP_CIPHER_CTX_cleanup(m_decrypt); + + EVP_CIPHER_CTX_free(m_encrypt); + EVP_CIPHER_CTX_free(m_decrypt); + } + +/* +* Set the key +*/ +void OpenSSL_BlockCipher::key_schedule(const uint8_t key[], size_t length) + { + secure_vector<uint8_t> full_key(key, key + length); + + if(m_cipher_name == "TripleDES" && length == 16) + { + full_key += std::make_pair(key, 8); + } + else + { + if(EVP_CIPHER_CTX_set_key_length(m_encrypt, length) == 0 || + EVP_CIPHER_CTX_set_key_length(m_decrypt, length) == 0) + throw Invalid_Argument("OpenSSL_BlockCipher: Bad key length for " + + m_cipher_name); + } + + if(!EVP_EncryptInit_ex(m_encrypt, nullptr, nullptr, full_key.data(), nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, nullptr, nullptr, full_key.data(), nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + + m_key_set = true; + } + +/* +* Return a clone of this object +*/ +BlockCipher* OpenSSL_BlockCipher::clone() const + { + return new OpenSSL_BlockCipher(m_cipher_name, + EVP_CIPHER_CTX_cipher(m_encrypt), + m_cipher_key_spec.minimum_keylength(), + m_cipher_key_spec.maximum_keylength(), + m_cipher_key_spec.keylength_multiple()); + } + +/* +* Clear memory of sensitive data +*/ +void OpenSSL_BlockCipher::clear() + { + const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_encrypt); + + m_key_set = false; + + if(!EVP_CIPHER_CTX_cleanup(m_encrypt)) + throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_cleanup(m_decrypt)) + throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup decrypt", ERR_get_error()); + EVP_CIPHER_CTX_init(m_encrypt); + EVP_CIPHER_CTX_init(m_decrypt); + if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error()); + if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr)) + throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error()); + if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0)) + throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error()); + } + +} + +std::unique_ptr<BlockCipher> +make_openssl_block_cipher(const std::string& name) + { +#define MAKE_OPENSSL_BLOCK(evp_fn) \ + std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn())) +#define MAKE_OPENSSL_BLOCK_KEYLEN(evp_fn, kl_min, kl_max, kl_mod) \ + std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn(), kl_min, kl_max, kl_mod)) + +#if defined(BOTAN_HAS_AES) && !defined(OPENSSL_NO_AES) + if(name == "AES-128") + return MAKE_OPENSSL_BLOCK(EVP_aes_128_ecb); + if(name == "AES-192") + return MAKE_OPENSSL_BLOCK(EVP_aes_192_ecb); + if(name == "AES-256") + return MAKE_OPENSSL_BLOCK(EVP_aes_256_ecb); +#endif + +#if defined(BOTAN_HAS_CAMELLIA) && !defined(OPENSSL_NO_CAMELLIA) + if(name == "Camellia-128") + return MAKE_OPENSSL_BLOCK(EVP_camellia_128_ecb); + if(name == "Camellia-192") + return MAKE_OPENSSL_BLOCK(EVP_camellia_192_ecb); + if(name == "Camellia-256") + return MAKE_OPENSSL_BLOCK(EVP_camellia_256_ecb); +#endif + +#if defined(BOTAN_HAS_DES) && !defined(OPENSSL_NO_DES) + if(name == "TripleDES") + return MAKE_OPENSSL_BLOCK_KEYLEN(EVP_des_ede3_ecb, 16, 24, 8); +#endif + + return nullptr; + } + +} + |