Add encryption support with openssl

This commit is contained in:
Boris Kuschel 2023-01-13 11:28:47 -08:00 committed by bkuschel
parent 496cacf25e
commit 1d4cf4fe69
No known key found for this signature in database
GPG Key ID: 41F888B501034AF3
4 changed files with 156 additions and 35 deletions

View File

@ -82,9 +82,7 @@
#include <Common/ThreadFuzzer.h>
#include <Common/getHashOfLoadedBinary.h>
#include <Common/filesystemHelpers.h>
#if USE_BORINGSSL
#include <Compression/CompressionCodecEncrypted.h>
#endif
#include <Server/HTTP/HTTPServerConnectionFactory.h>
#include <Server/MySQLHandlerFactory.h>
#include <Server/PostgreSQLHandlerFactory.h>
@ -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();

View File

@ -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)

View File

@ -11,10 +11,14 @@
// This depends on BoringSSL-specific API, notably <openssl/aead.h>.
#if USE_SSL
#include <openssl/digest.h>
#include <openssl/err.h>
#include <boost/algorithm/hex.hpp>
#if USE_BORINGSSL
#include <openssl/digest.h>
#include <openssl/aead.h>
#else
#include <openssl/evp.h>
#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<int32_t>(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<const uint8_t*>(key.data()),
reinterpret_cast<const uint8_t *>(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<uint8_t *>(ciphertext_and_tag),
reinterpret_cast<int32_t *>(&out_len),
reinterpret_cast<const uint8_t *>(plaintext.data()),
static_cast<int32_t>(plaintext.size()));
if (!ok_encryptudpate)
throw Exception(lastErrorString(), ErrorCodes::OPENSSL_ERROR);
const int ok_encryptfinal = EVP_EncryptFinal_ex(encrypt_ctx,
reinterpret_cast<uint8_t *>(ciphertext_and_tag) + out_len,
reinterpret_cast<int32_t *>(&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<uint8_t *>(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<int32_t>(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<const uint8_t*>(key.data()),
reinterpret_cast<const uint8_t *>(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<uint8_t *>(plaintext),
reinterpret_cast<int32_t *>(&out_len),
reinterpret_cast<const uint8_t *>(ciphertext.data()),
static_cast<int32_t>(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<uint8_t *>(const_cast<char *>(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<uint8_t *>(plaintext) + out_len,
reinterpret_cast<int32_t *>(&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)

View File

@ -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);