From 1d4cf4fe69442e7594a665a33a36be8b1a6f4848 Mon Sep 17 00:00:00 2001 From: Boris Kuschel Date: Fri, 13 Jan 2023 11:28:47 -0800 Subject: [PATCH] Add encryption support with openssl --- programs/server/Server.cpp | 6 - src/CMakeLists.txt | 8 +- src/Compression/CompressionCodecEncrypted.cpp | 173 ++++++++++++++++-- src/Compression/CompressionFactory.cpp | 4 - 4 files changed, 156 insertions(+), 35 deletions(-) diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 47901cfbf70..40ee0ba918e 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -82,9 +82,7 @@ #include #include #include -#if USE_BORINGSSL #include -#endif #include #include #include @@ -1348,9 +1346,7 @@ try global_context->updateStorageConfiguration(*config); global_context->updateInterserverCredentials(*config); -#if USE_BORINGSSL CompressionCodecEncrypted::Configuration::instance().tryLoad(*config, "encryption_codecs"); -#endif #if USE_SSL CertificateReloader::instance().tryLoad(*config); #endif @@ -1564,10 +1560,8 @@ try global_context->getMergeTreeSettings().sanityCheck(background_pool_tasks); global_context->getReplicatedMergeTreeSettings().sanityCheck(background_pool_tasks); } -#if USE_BORINGSSL /// try set up encryption. There are some errors in config, error will be printed and server wouldn't start. CompressionCodecEncrypted::Configuration::instance().load(config(), "encryption_codecs"); -#endif SCOPE_EXIT({ async_metrics.stop(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a74f10d2c3c..82958394224 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -226,13 +226,7 @@ add_object_library(clickhouse_access Access) add_object_library(clickhouse_backups Backups) add_object_library(clickhouse_core Core) add_object_library(clickhouse_core_mysql Core/MySQL) -if (ENABLE_OPENSSL OR ENABLE_OPENSSL_DYNAMIC) - add_headers_and_sources(dbms Compression) - list(REMOVE_ITEM dbms_headers Compression/CompressionCodecEncrypted.h) - list(REMOVE_ITEM dbms_sources Compression/CompressionCodecEncrypted.cpp) -else () - add_object_library(clickhouse_compression Compression) -endif () +add_object_library(clickhouse_compression Compression) add_object_library(clickhouse_querypipeline QueryPipeline) add_object_library(clickhouse_datatypes DataTypes) add_object_library(clickhouse_datatypes_serializations DataTypes/Serializations) diff --git a/src/Compression/CompressionCodecEncrypted.cpp b/src/Compression/CompressionCodecEncrypted.cpp index 17bac8234a5..28bd171d638 100644 --- a/src/Compression/CompressionCodecEncrypted.cpp +++ b/src/Compression/CompressionCodecEncrypted.cpp @@ -11,10 +11,14 @@ // This depends on BoringSSL-specific API, notably . #if USE_SSL -#include #include #include +#if USE_BORINGSSL +#include #include +#else +#include +#endif #endif // Common part for both parts (with SSL and without) @@ -87,23 +91,6 @@ constexpr size_t nonce_max_size = 13; /// Nonce size and one byte to show i constexpr size_t actual_nonce_size = 12; /// Nonce actual size const String empty_nonce = {"\0\0\0\0\0\0\0\0\0\0\0\0", actual_nonce_size}; -/// Get encryption/decryption algorithms. -auto getMethod(EncryptionMethod Method) -{ - if (Method == AES_128_GCM_SIV) - { - return EVP_aead_aes_128_gcm_siv; - } - else if (Method == AES_256_GCM_SIV) - { - return EVP_aead_aes_256_gcm_siv; - } - else - { - throw Exception(ErrorCodes::BAD_ARGUMENTS, "Wrong encryption Method. Got {}", getMethodName(Method)); - } -} - /// Find out key size for each algorithm UInt64 methodKeySize(EncryptionMethod Method) { @@ -128,6 +115,24 @@ std::string lastErrorString() return std::string(buffer.data()); } +#if USE_BORINGSSL +/// Get encryption/decryption algorithms. +auto getMethod(EncryptionMethod Method) +{ + if (Method == AES_128_GCM_SIV) + { + return EVP_aead_aes_128_gcm_siv; + } + else if (Method == AES_256_GCM_SIV) + { + return EVP_ae'grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&apikey=$API_KEY'ad_aes_256_gcm_siv; + } + else + { + throw Exception(ErrorCodes::BAD_ARGUMENTS, "Wrong encryption Method. Got {}", getMethodName(Method)); + } +} + /// Encrypt plaintext with particular algorithm and put result into ciphertext_and_tag. /// This function get key and nonce and encrypt text with their help. /// If something went wrong (can't init context or can't encrypt data) it throws exception. @@ -186,6 +191,138 @@ size_t decrypt(std::string_view ciphertext, char * plaintext, EncryptionMethod m return out_len; } +#else +/// Get encryption/decryption algorithms. +auto getMethod(EncryptionMethod Method) +{ + if (Method == AES_128_GCM_SIV) + { + return EVP_aes_128_gcm; + } + else if (Method == AES_256_GCM_SIV) + { + return EVP_aes_256_gcm; + } + else + { + throw Exception("Wrong encryption Method. Got " + getMethodName(Method), ErrorCodes::BAD_ARGUMENTS); + } +} + +/// Encrypt plaintext with particular algorithm and put result into ciphertext_and_tag. +/// This function get key and nonce and encrypt text with their help. +/// If something went wrong (can't init context or can't encrypt data) it throws exception. +/// It returns length of encrypted text. +size_t encrypt(std::string_view plaintext, char * ciphertext_and_tag, EncryptionMethod method, const String & key, const String & nonce) +{ + EVP_CIPHER_CTX *encrypt_ctx; + + if (!(encrypt_ctx = EVP_CIPHER_CTX_new())) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_cryptinit = EVP_EncryptInit_ex(encrypt_ctx, + getMethod(method)(), + nullptr, nullptr, nullptr); + if (!ok_cryptinit) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_cipherctrl = EVP_CIPHER_CTX_ctrl(encrypt_ctx, + EVP_CTRL_GCM_SET_IVLEN, + static_cast(nonce.size()), + nullptr); + if (!ok_cipherctrl) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_nonceinit = EVP_EncryptInit_ex(encrypt_ctx, nullptr, nullptr, + reinterpret_cast(key.data()), + reinterpret_cast(nonce.data())); + if (!ok_nonceinit) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + size_t out_len; + const int ok_encryptudpate = EVP_EncryptUpdate(encrypt_ctx, + reinterpret_cast(ciphertext_and_tag), + reinterpret_cast(&out_len), + reinterpret_cast(plaintext.data()), + static_cast(plaintext.size())); + if (!ok_encryptudpate) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_encryptfinal = EVP_EncryptFinal_ex(encrypt_ctx, + reinterpret_cast(ciphertext_and_tag) + out_len, + reinterpret_cast(&out_len)); + if (!ok_encryptfinal) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + /* Get the tag */ + const int ok_tag = EVP_CIPHER_CTX_ctrl(encrypt_ctx, + EVP_CTRL_GCM_GET_TAG, + tag_size, + reinterpret_cast(ciphertext_and_tag) + plaintext.size()); + if (!ok_tag) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + EVP_CIPHER_CTX_free(encrypt_ctx); + + return plaintext.size() + tag_size; +} + +/// Encrypt plaintext with particular algorithm and put result into ciphertext_and_tag. +/// This function get key and nonce and encrypt text with their help. +/// If something went wrong (can't init context or can't encrypt data) it throws exception. +/// It returns length of encrypted text. +size_t decrypt(std::string_view ciphertext, char * plaintext, EncryptionMethod method, const String & key, const String & nonce) +{ + EVP_CIPHER_CTX *decrypt_ctx; + + if (!(decrypt_ctx = EVP_CIPHER_CTX_new())) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_cryptinit = EVP_DecryptInit_ex(decrypt_ctx, + getMethod(method)(), + nullptr, nullptr, nullptr); + if (!ok_cryptinit) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_cipherctrl = EVP_CIPHER_CTX_ctrl(decrypt_ctx, + EVP_CTRL_GCM_SET_IVLEN, + static_cast(nonce.size()), nullptr); + if (!ok_cipherctrl) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_nonceinit = EVP_DecryptInit_ex(decrypt_ctx, nullptr, nullptr, + reinterpret_cast(key.data()), + reinterpret_cast(nonce.data())); + if (!ok_nonceinit) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + size_t out_len; + const int ok_decryptudpate = EVP_DecryptUpdate(decrypt_ctx, + reinterpret_cast(plaintext), + reinterpret_cast(&out_len), + reinterpret_cast(ciphertext.data()), + static_cast(ciphertext.size()) - tag_size); + if (!ok_decryptudpate) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_tag = EVP_CIPHER_CTX_ctrl(decrypt_ctx, + EVP_CTRL_GCM_SET_TAG, + tag_size, + reinterpret_cast(const_cast(ciphertext.data())) + ciphertext.size() - tag_size); + if (!ok_tag) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + const int ok_decryptfinal = EVP_DecryptFinal_ex(decrypt_ctx, + reinterpret_cast(plaintext) + out_len, + reinterpret_cast(&out_len)); + if (!ok_decryptfinal) + throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR); + + EVP_CIPHER_CTX_free(decrypt_ctx); + + return ciphertext.size() - tag_size; +} +#endif /// Register codec in factory void registerEncryptionCodec(CompressionCodecFactory & factory, EncryptionMethod Method) diff --git a/src/Compression/CompressionFactory.cpp b/src/Compression/CompressionFactory.cpp index 9174c53a6f4..e4be73c418c 100644 --- a/src/Compression/CompressionFactory.cpp +++ b/src/Compression/CompressionFactory.cpp @@ -178,9 +178,7 @@ void registerCodecDelta(CompressionCodecFactory & factory); void registerCodecT64(CompressionCodecFactory & factory); void registerCodecDoubleDelta(CompressionCodecFactory & factory); void registerCodecGorilla(CompressionCodecFactory & factory); -#if USE_BORINGSSL void registerCodecEncrypted(CompressionCodecFactory & factory); -#endif void registerCodecFPC(CompressionCodecFactory & factory); #endif @@ -197,9 +195,7 @@ CompressionCodecFactory::CompressionCodecFactory() registerCodecT64(*this); registerCodecDoubleDelta(*this); registerCodecGorilla(*this); -#if USE_BORINGSSL registerCodecEncrypted(*this); -#endif registerCodecFPC(*this); #ifdef ENABLE_QPL_COMPRESSION registerCodecDeflateQpl(*this);