mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-28 02:21:59 +00:00
encrypt, aes_encrypt_mysql, decrypt, aes_decrypt_mysql functions
Functions to encrypt/decrypt any input data with OpenSSL's ciphers with custom key, iv, and add (-gcm mode only). _mysql versions are 100% compatitable with corresponding MySQL functions Supported modes depend on OpenSSL version, but generally are: aes-{128,192,56}-{ecb,cbc,cfb1,cfb8,cfb128,ofb,gcm} Please note that in a -gcm mode a 16-byte tag is appended to the ciphertext on encryption and is expected to be found at the end of ciphertext on decryption. Added tests that verify compatibility with MySQL functions, and test vectors for GCM mode from OpenSSL. Added masking rules for aes_X funtions Rules are installed by default to config.d/query_masking_rules.xml
This commit is contained in:
parent
05b10048a6
commit
b147ffcd43
@ -70,10 +70,12 @@
|
||||
# define NO_SANITIZE_UNDEFINED __attribute__((__no_sanitize__("undefined")))
|
||||
# define NO_SANITIZE_ADDRESS __attribute__((__no_sanitize__("address")))
|
||||
# define NO_SANITIZE_THREAD __attribute__((__no_sanitize__("thread")))
|
||||
# define NO_SANITIZE_MEMORY __attribute__((__no_sanitize__("memory")))
|
||||
#else /// It does not work in GCC. GCC 7 cannot recognize this attribute and GCC 8 simply ignores it.
|
||||
# define NO_SANITIZE_UNDEFINED
|
||||
# define NO_SANITIZE_ADDRESS
|
||||
# define NO_SANITIZE_THREAD
|
||||
# define NO_SANITIZE_MEMORY
|
||||
#endif
|
||||
|
||||
#if defined __GNUC__ && !defined __clang__
|
||||
|
@ -48,8 +48,8 @@ if (SANITIZE)
|
||||
endif ()
|
||||
|
||||
elseif (SANITIZE STREQUAL "undefined")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SAN_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all -fno-sanitize=float-divide-by-zero")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SAN_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all -fno-sanitize=float-divide-by-zero")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SAN_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all -fno-sanitize=float-divide-by-zero -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/tests/ubsan_suppressions.txt")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SAN_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all -fno-sanitize=float-divide-by-zero -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/tests/ubsan_suppressions.txt")
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
|
||||
endif()
|
||||
|
1
debian/clickhouse-server.install
vendored
1
debian/clickhouse-server.install
vendored
@ -2,5 +2,6 @@ usr/bin/clickhouse-server
|
||||
usr/bin/clickhouse-copier
|
||||
usr/bin/clickhouse-report
|
||||
etc/clickhouse-server/config.xml
|
||||
etc/clickhouse-server/config.d/*.xml
|
||||
etc/clickhouse-server/users.xml
|
||||
etc/systemd/system/clickhouse-server.service
|
||||
|
@ -29,6 +29,8 @@ set (CLICKHOUSE_SERVER_LINK
|
||||
clickhouse_program_add(server)
|
||||
|
||||
install(FILES config.xml users.xml DESTINATION ${CLICKHOUSE_ETC_DIR}/clickhouse-server COMPONENT clickhouse)
|
||||
install(FILES config.xml users.xml DESTINATION ${CLICKHOUSE_ETC_DIR}/clickhouse-server COMPONENT clickhouse)
|
||||
install(FILES config.d/query_masking_rules.xml DESTINATION ${CLICKHOUSE_ETC_DIR}/clickhouse-server/config.d COMPONENT clickhouse)
|
||||
|
||||
# TODO We actually need this on Mac, FreeBSD.
|
||||
if (OS_LINUX)
|
||||
|
@ -15,5 +15,14 @@
|
||||
<regexp>TOPSECRET.TOPSECRET</regexp>
|
||||
<replace>[hidden]</replace>
|
||||
</rule>
|
||||
|
||||
<rule>
|
||||
<name>hide encrypt/decrypt arguments</name>
|
||||
<regexp>((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)</regexp>
|
||||
<!-- or more secure, but also more invasive:
|
||||
(aes_\w+)\s*\(.*\)
|
||||
-->
|
||||
<replace>\1(???)</replace>
|
||||
</rule>
|
||||
</query_masking_rules>
|
||||
</yandex>
|
||||
|
@ -612,4 +612,10 @@ void ColumnString::protect()
|
||||
getOffsets().protect();
|
||||
}
|
||||
|
||||
void ColumnString::validate() const
|
||||
{
|
||||
if (!offsets.empty() && offsets.back() != chars.size())
|
||||
throw Exception(fmt::format("ColumnString validation failed: size mismatch (internal logical error) {} != {}", offsets.back(), chars.size()), ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -267,6 +267,9 @@ public:
|
||||
|
||||
Offsets & getOffsets() { return offsets; }
|
||||
const Offsets & getOffsets() const { return offsets; }
|
||||
|
||||
// Throws an exception if offsets/chars are messed up
|
||||
void validate() const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -137,7 +137,7 @@ void validateArgumentsImpl(const IFunction & func,
|
||||
const auto & arg = arguments[i + argument_offset];
|
||||
const auto descriptor = descriptors[i];
|
||||
if (int error_code = descriptor.isValid(arg.type, arg.column); error_code != 0)
|
||||
throw Exception("Illegal type of argument #" + std::to_string(i)
|
||||
throw Exception("Illegal type of argument #" + std::to_string(argument_offset + i + 1) // +1 is for human-friendly 1-based indexing
|
||||
+ (descriptor.argument_name ? " '" + std::string(descriptor.argument_name) + "'" : String{})
|
||||
+ " of function " + func.getName()
|
||||
+ (descriptor.expected_type_description ? String(", expected ") + descriptor.expected_type_description : String{})
|
||||
|
56
src/Functions/FunctionsAES.cpp
Normal file
56
src/Functions/FunctionsAES.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include <Functions/FunctionsAES.h>
|
||||
|
||||
#if USE_SSL
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int OPENSSL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
namespace OpenSSLDetails
|
||||
{
|
||||
void onError(std::string error_message)
|
||||
{
|
||||
error_message += ". OpenSSL error code: " + std::to_string(ERR_get_error());
|
||||
throw DB::Exception(error_message, DB::ErrorCodes::OPENSSL_ERROR);
|
||||
}
|
||||
|
||||
StringRef foldEncryptionKeyInMySQLCompatitableMode(size_t cipher_key_size, const StringRef & key, std::array<char, EVP_MAX_KEY_LENGTH> & folded_key)
|
||||
{
|
||||
memcpy(folded_key.data(), key.data, cipher_key_size);
|
||||
|
||||
for (size_t i = cipher_key_size; i < key.size; ++i)
|
||||
{
|
||||
folded_key[i % cipher_key_size] ^= key.data[i];
|
||||
}
|
||||
|
||||
return StringRef(folded_key.data(), cipher_key_size);
|
||||
}
|
||||
|
||||
const EVP_CIPHER * getCipherByName(const StringRef & cipher_name)
|
||||
{
|
||||
const auto *evp_cipher = EVP_get_cipherbyname(cipher_name.data);
|
||||
if (evp_cipher == nullptr)
|
||||
{
|
||||
// For some reasons following ciphers can't be found by name.
|
||||
if (cipher_name == "aes-128-cfb128")
|
||||
evp_cipher = EVP_aes_128_cfb128();
|
||||
else if (cipher_name == "aes-192-cfb128")
|
||||
evp_cipher = EVP_aes_192_cfb128();
|
||||
else if (cipher_name == "aes-256-cfb128")
|
||||
evp_cipher = EVP_aes_256_cfb128();
|
||||
}
|
||||
|
||||
return evp_cipher;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
656
src/Functions/FunctionsAES.h
Normal file
656
src/Functions/FunctionsAES.h
Normal file
@ -0,0 +1,656 @@
|
||||
#pragma once
|
||||
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SSL
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnNothing.h>
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
|
||||
#include <Functions/FunctionHelpers.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
}
|
||||
}
|
||||
|
||||
namespace OpenSSLDetails
|
||||
{
|
||||
[[noreturn]] void onError(std::string error_message);
|
||||
StringRef foldEncryptionKeyInMySQLCompatitableMode(size_t cipher_key_size, const StringRef & key, std::array<char, EVP_MAX_KEY_LENGTH> & folded_key);
|
||||
const EVP_CIPHER * getCipherByName(const StringRef & name);
|
||||
|
||||
enum class CompatibilityMode
|
||||
{
|
||||
MySQL,
|
||||
OpenSSL
|
||||
};
|
||||
|
||||
enum class CipherMode
|
||||
{
|
||||
MySQLCompatibility, // with key folding
|
||||
OpenSSLCompatibility, // just as regular openssl's enc application does (AEAD modes, like GCM and CCM are not supported)
|
||||
RFC5116_AEAD_AES_GCM // AEAD GCM with custom IV length and tag (HMAC) appended to the ciphertext, see https://tools.ietf.org/html/rfc5116#section-5.1
|
||||
};
|
||||
|
||||
|
||||
template <CipherMode mode>
|
||||
struct KeyHolder
|
||||
{
|
||||
inline StringRef setKey(size_t cipher_key_size, const StringRef & key) const
|
||||
{
|
||||
if (key.size != cipher_key_size)
|
||||
throw DB::Exception(fmt::format("Invalid key size: {} expected {}", key.size, cipher_key_size),
|
||||
DB::ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct KeyHolder<CipherMode::MySQLCompatibility>
|
||||
{
|
||||
inline StringRef setKey(size_t cipher_key_size, const StringRef & key)
|
||||
{
|
||||
if (key.size < cipher_key_size)
|
||||
throw DB::Exception(fmt::format("Invalid key size: {} expected {}", key.size, cipher_key_size),
|
||||
DB::ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
// MySQL does something fancy with the keys that are too long,
|
||||
// ruining compatibility with OpenSSL and not improving security.
|
||||
// But we have to do the same to be compatitable with MySQL.
|
||||
// see https://github.com/mysql/mysql-server/blob/8.0/router/src/harness/src/my_aes_openssl.cc#L71
|
||||
// (my_aes_create_key function)
|
||||
return foldEncryptionKeyInMySQLCompatitableMode(cipher_key_size, key, folded_key);
|
||||
}
|
||||
|
||||
~KeyHolder()
|
||||
{
|
||||
OPENSSL_cleanse(folded_key.data(), folded_key.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<char, EVP_MAX_KEY_LENGTH> folded_key;
|
||||
};
|
||||
|
||||
template <CompatibilityMode compatibility_mode>
|
||||
inline void validateCipherMode(const EVP_CIPHER * evp_cipher)
|
||||
{
|
||||
if constexpr (compatibility_mode == CompatibilityMode::MySQL)
|
||||
{
|
||||
switch (EVP_CIPHER_mode(evp_cipher))
|
||||
{
|
||||
case EVP_CIPH_ECB_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_CBC_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_CFB_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_OFB_MODE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if constexpr (compatibility_mode == CompatibilityMode::OpenSSL)
|
||||
{
|
||||
switch (EVP_CIPHER_mode(evp_cipher))
|
||||
{
|
||||
case EVP_CIPH_ECB_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_CBC_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_CFB_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_OFB_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_CTR_MODE: [[fallthrough]];
|
||||
case EVP_CIPH_GCM_MODE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw DB::Exception("Unsupported cipher mode " + std::string(EVP_CIPHER_name(evp_cipher)), DB::ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
template <CipherMode mode>
|
||||
inline void validateIV(const StringRef & iv_value, const size_t cipher_iv_size)
|
||||
{
|
||||
// In MySQL mode we don't care if IV is longer than expected, only if shorter.
|
||||
if ((mode == CipherMode::MySQLCompatibility && iv_value.size != 0 && iv_value.size < cipher_iv_size)
|
||||
|| (mode == CipherMode::OpenSSLCompatibility && iv_value.size != 0 && iv_value.size != cipher_iv_size))
|
||||
throw DB::Exception(fmt::format("Invalid IV size: {} expected {}", iv_value.size, cipher_iv_size),
|
||||
DB::ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
template <typename Impl>
|
||||
class FunctionEncrypt : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr OpenSSLDetails::CompatibilityMode compatibility_mode = Impl::compatibility_mode;
|
||||
static constexpr auto name = Impl::name;
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionEncrypt>(); }
|
||||
|
||||
private:
|
||||
using CipherMode = OpenSSLDetails::CipherMode;
|
||||
|
||||
String getName() const override { return name; }
|
||||
bool isVariadic() const override { return true; }
|
||||
size_t getNumberOfArguments() const override { return 0; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
auto optional_args = FunctionArgumentDescriptors{
|
||||
{"IV", isStringOrFixedString, nullptr, "Initialization vector binary string"},
|
||||
};
|
||||
|
||||
if constexpr (compatibility_mode == OpenSSLDetails::CompatibilityMode::OpenSSL)
|
||||
{
|
||||
optional_args.emplace_back(FunctionArgumentDescriptor{
|
||||
"AAD", isStringOrFixedString, nullptr, "Additional authenticated data binary string for GCM mode"
|
||||
});
|
||||
}
|
||||
|
||||
validateFunctionArgumentTypes(*this, arguments,
|
||||
FunctionArgumentDescriptors{
|
||||
{"mode", isStringOrFixedString, isColumnConst, "encryption mode string"},
|
||||
{"input", nullptr, nullptr, "plaintext"},
|
||||
{"key", isStringOrFixedString, nullptr, "encryption key binary string"},
|
||||
},
|
||||
optional_args
|
||||
);
|
||||
|
||||
return std::make_shared<DataTypeString>();
|
||||
}
|
||||
|
||||
void executeImplDryRun(Block & block, const ColumnNumbers & /*arguments*/, size_t result, size_t /*input_rows_count*/) const override
|
||||
{
|
||||
block.getByPosition(result).column = block.getByPosition(result).type->createColumn();
|
||||
}
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const override
|
||||
{
|
||||
using namespace OpenSSLDetails;
|
||||
|
||||
const auto mode = block.getByPosition(arguments[0]).column->getDataAt(0);
|
||||
|
||||
if (mode.size == 0 || !std::string_view(mode).starts_with("aes-"))
|
||||
throw Exception("Invalid mode: " + mode.toString(), ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const auto * evp_cipher = getCipherByName(mode);
|
||||
if (evp_cipher == nullptr)
|
||||
throw Exception("Invalid mode: " + mode.toString(), ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const auto cipher_mode = EVP_CIPHER_mode(evp_cipher);
|
||||
|
||||
const auto input_column = block.getByPosition(arguments[1]).column;
|
||||
const auto key_column = block.getByPosition(arguments[2]).column;
|
||||
|
||||
OpenSSLDetails::validateCipherMode<compatibility_mode>(evp_cipher);
|
||||
|
||||
ColumnPtr result_column;
|
||||
if (arguments.size() <= 3)
|
||||
result_column = doEncrypt(evp_cipher, input_rows_count, input_column, key_column, nullptr, nullptr);
|
||||
else
|
||||
{
|
||||
const auto iv_column = block.getByPosition(arguments[3]).column;
|
||||
if (compatibility_mode != OpenSSLDetails::CompatibilityMode::MySQL && EVP_CIPHER_iv_length(evp_cipher) == 0)
|
||||
throw Exception(mode.toString() + " does not support IV", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (arguments.size() <= 4)
|
||||
{
|
||||
result_column = doEncrypt(evp_cipher, input_rows_count, input_column, key_column, iv_column, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cipher_mode != EVP_CIPH_GCM_MODE)
|
||||
throw Exception("AAD can be only set for GCM-mode", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const auto aad_column = block.getByPosition(arguments[4]).column;
|
||||
result_column = doEncrypt(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
}
|
||||
|
||||
block.getByPosition(result).column = std::move(result_column);
|
||||
}
|
||||
|
||||
template <typename InputColumnType, typename KeyColumnType, typename IvColumnType, typename AadColumnType>
|
||||
static ColumnPtr doEncrypt(const EVP_CIPHER * evp_cipher,
|
||||
size_t input_rows_count,
|
||||
const InputColumnType & input_column,
|
||||
const KeyColumnType & key_column,
|
||||
const IvColumnType & iv_column,
|
||||
const AadColumnType & aad_column)
|
||||
{
|
||||
if constexpr (compatibility_mode == OpenSSLDetails::CompatibilityMode::MySQL)
|
||||
{
|
||||
return doEncryptImpl<CipherMode::MySQLCompatibility>(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EVP_CIPHER_mode(evp_cipher) == EVP_CIPH_GCM_MODE)
|
||||
{
|
||||
return doEncryptImpl<CipherMode::RFC5116_AEAD_AES_GCM>(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
else
|
||||
{
|
||||
return doEncryptImpl<CipherMode::OpenSSLCompatibility>(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <CipherMode mode, typename InputColumnType, typename KeyColumnType, typename IvColumnType, typename AadColumnType>
|
||||
static ColumnPtr doEncryptImpl(const EVP_CIPHER * evp_cipher,
|
||||
size_t input_rows_count,
|
||||
const InputColumnType & input_column,
|
||||
const KeyColumnType & key_column,
|
||||
[[maybe_unused]] const IvColumnType & iv_column,
|
||||
[[maybe_unused]] const AadColumnType & aad_column)
|
||||
{
|
||||
using namespace OpenSSLDetails;
|
||||
|
||||
auto evp_ctx_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), &EVP_CIPHER_CTX_free);
|
||||
auto evp_ctx = evp_ctx_ptr.get();
|
||||
|
||||
const auto block_size = static_cast<size_t>(EVP_CIPHER_block_size(evp_cipher));
|
||||
const auto key_size = static_cast<size_t>(EVP_CIPHER_key_length(evp_cipher));
|
||||
[[maybe_unused]] const auto iv_size = static_cast<size_t>(EVP_CIPHER_iv_length(evp_cipher));
|
||||
const auto tag_size = 16; // https://tools.ietf.org/html/rfc5116#section-5.1
|
||||
|
||||
auto encrypted_result_column = ColumnString::create();
|
||||
auto & encrypted_result_column_data = encrypted_result_column->getChars();
|
||||
auto & encrypted_result_column_offsets = encrypted_result_column->getOffsets();
|
||||
|
||||
{
|
||||
size_t resulting_size = 0;
|
||||
// for modes with block_size > 1, plaintext is padded up to a block_size,
|
||||
// which may result in allocating to much for block_size = 1.
|
||||
// That may lead later to reading unallocated data from underlying PaddedPODArray
|
||||
// due to assumption that it is safe to read up to 15 bytes past end.
|
||||
const auto pad_to_next_block = block_size == 1 ? 0 : 1;
|
||||
for (size_t r = 0; r < input_rows_count; ++r)
|
||||
{
|
||||
resulting_size += (input_column->getDataAt(r).size / block_size + pad_to_next_block) * block_size + 1;
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
resulting_size += tag_size;
|
||||
}
|
||||
#if defined(MEMORY_SANITIZER)
|
||||
encrypted_result_column_data.resize_fill(resulting_size, 0xFF);
|
||||
#else
|
||||
encrypted_result_column_data.resize(resulting_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto encrypted = encrypted_result_column_data.data();
|
||||
|
||||
KeyHolder<mode> key_holder;
|
||||
|
||||
for (size_t r = 0; r < input_rows_count; ++r)
|
||||
{
|
||||
const auto key_value = key_holder.setKey(key_size, key_column->getDataAt(r));
|
||||
auto iv_value = StringRef{};
|
||||
if constexpr (!std::is_same_v<nullptr_t, std::decay_t<IvColumnType>>)
|
||||
{
|
||||
iv_value = iv_column->getDataAt(r);
|
||||
}
|
||||
|
||||
const auto input_value = input_column->getDataAt(r);
|
||||
|
||||
// 1: Init CTX
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
{
|
||||
// 1.a.1: Init CTX with custom IV length and optionally with AAD
|
||||
if (EVP_EncryptInit_ex(evp_ctx, evp_cipher, nullptr, nullptr, nullptr) != 1)
|
||||
onError("Failed to initialize encryption context with cipher");
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(evp_ctx, EVP_CTRL_AEAD_SET_IVLEN, iv_value.size, nullptr) != 1)
|
||||
onError("Failed to set custom IV length to " + std::to_string(iv_value.size));
|
||||
|
||||
if (EVP_EncryptInit_ex(evp_ctx, nullptr, nullptr,
|
||||
reinterpret_cast<const unsigned char*>(key_value.data),
|
||||
reinterpret_cast<const unsigned char*>(iv_value.data)) != 1)
|
||||
onError("Failed to set key and IV");
|
||||
|
||||
// 1.a.2 Set AAD
|
||||
if constexpr (!std::is_same_v<nullptr_t, std::decay_t<AadColumnType>>)
|
||||
{
|
||||
const auto aad_data = aad_column->getDataAt(r);
|
||||
int tmp_len = 0;
|
||||
if (aad_data.size != 0 && EVP_EncryptUpdate(evp_ctx, nullptr, &tmp_len,
|
||||
reinterpret_cast<const unsigned char *>(aad_data.data), aad_data.size) != 1)
|
||||
onError("Failed to set AAD data");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1.b: Init CTX
|
||||
validateIV<mode>(iv_value, iv_size);
|
||||
|
||||
if (EVP_EncryptInit_ex(evp_ctx, evp_cipher, nullptr,
|
||||
reinterpret_cast<const unsigned char*>(key_value.data),
|
||||
reinterpret_cast<const unsigned char*>(iv_value.data)) != 1)
|
||||
onError("Failed to initialize cipher context");
|
||||
}
|
||||
|
||||
int output_len = 0;
|
||||
// 2: Feed the data to the cipher
|
||||
if (EVP_EncryptUpdate(evp_ctx,
|
||||
reinterpret_cast<unsigned char*>(encrypted), &output_len,
|
||||
reinterpret_cast<const unsigned char*>(input_value.data), static_cast<int>(input_value.size)) != 1)
|
||||
onError("Failed to encrypt");
|
||||
encrypted += output_len;
|
||||
|
||||
// 3: retrieve encrypted data (ciphertext)
|
||||
if (EVP_EncryptFinal_ex(evp_ctx,
|
||||
reinterpret_cast<unsigned char*>(encrypted), &output_len) != 1)
|
||||
onError("Failed to fetch ciphertext");
|
||||
encrypted += output_len;
|
||||
|
||||
// 4: optionally retrieve a tag and append it to the ciphertext (RFC5116):
|
||||
// https://tools.ietf.org/html/rfc5116#section-5.1
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
{
|
||||
if (EVP_CIPHER_CTX_ctrl(evp_ctx, EVP_CTRL_AEAD_GET_TAG, tag_size, encrypted) != 1)
|
||||
onError("Failed to retrieve GCM tag");
|
||||
encrypted += tag_size;
|
||||
}
|
||||
*encrypted = '\0';
|
||||
++encrypted;
|
||||
|
||||
encrypted_result_column_offsets.push_back(encrypted - encrypted_result_column_data.data());
|
||||
|
||||
if (EVP_CIPHER_CTX_reset(evp_ctx) != 1)
|
||||
onError("Failed to reset context");
|
||||
}
|
||||
|
||||
// in case of block size of 1, we overestimate buffer required for encrypted data, fix it up.
|
||||
if (!encrypted_result_column_offsets.empty() && encrypted_result_column_data.size() > encrypted_result_column_offsets.back())
|
||||
{
|
||||
encrypted_result_column_data.resize(encrypted_result_column_offsets.back());
|
||||
}
|
||||
|
||||
encrypted_result_column->validate();
|
||||
return encrypted_result_column;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// AES_decrypt(string, key, block_mode[, init_vector])
|
||||
template <typename Impl>
|
||||
class FunctionDecrypt : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr OpenSSLDetails::CompatibilityMode compatibility_mode = Impl::compatibility_mode;
|
||||
static constexpr auto name = Impl::name;
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionDecrypt>(); }
|
||||
|
||||
private:
|
||||
using CipherMode = OpenSSLDetails::CipherMode;
|
||||
|
||||
String getName() const override { return name; }
|
||||
bool isVariadic() const override { return true; }
|
||||
size_t getNumberOfArguments() const override { return 0; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
auto optional_args = FunctionArgumentDescriptors{
|
||||
{"IV", isStringOrFixedString, nullptr, "Initialization vector binary string"},
|
||||
};
|
||||
|
||||
if constexpr (compatibility_mode == OpenSSLDetails::CompatibilityMode::OpenSSL)
|
||||
{
|
||||
optional_args.emplace_back(FunctionArgumentDescriptor{
|
||||
"AAD", isStringOrFixedString, nullptr, "Additional authenticated data binary string for GCM mode"
|
||||
});
|
||||
}
|
||||
|
||||
validateFunctionArgumentTypes(*this, arguments,
|
||||
FunctionArgumentDescriptors{
|
||||
{"mode", isStringOrFixedString, isColumnConst, "decryption mode string"},
|
||||
{"input", nullptr, nullptr, "ciphertext"},
|
||||
{"key", isStringOrFixedString, nullptr, "decryption key binary string"},
|
||||
},
|
||||
optional_args
|
||||
);
|
||||
|
||||
return std::make_shared<DataTypeString>();
|
||||
}
|
||||
|
||||
void executeImplDryRun(Block & block, const ColumnNumbers & /*arguments*/, size_t result, size_t /*input_rows_count*/) const override
|
||||
{
|
||||
block.getByPosition(result).column = block.getByPosition(result).type->createColumn();
|
||||
}
|
||||
|
||||
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const override
|
||||
{
|
||||
using namespace OpenSSLDetails;
|
||||
|
||||
const auto mode = block.getByPosition(arguments[0]).column->getDataAt(0);
|
||||
|
||||
if (mode.size == 0 || !std::string_view(mode).starts_with("aes-"))
|
||||
throw Exception("Invalid mode: " + mode.toString(), ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const auto * evp_cipher = getCipherByName(mode);
|
||||
if (evp_cipher == nullptr)
|
||||
throw Exception("Invalid mode: " + mode.toString(), ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
OpenSSLDetails::validateCipherMode<compatibility_mode>(evp_cipher);
|
||||
|
||||
const auto input_column = block.getByPosition(arguments[1]).column;
|
||||
const auto key_column = block.getByPosition(arguments[2]).column;
|
||||
|
||||
ColumnPtr result_column;
|
||||
if (arguments.size() <= 3)
|
||||
result_column = doDecrypt(evp_cipher, input_rows_count, input_column, key_column, nullptr, nullptr);
|
||||
else
|
||||
{
|
||||
const auto iv_column = block.getByPosition(arguments[3]).column;
|
||||
if (compatibility_mode != OpenSSLDetails::CompatibilityMode::MySQL && EVP_CIPHER_iv_length(evp_cipher) == 0)
|
||||
throw Exception(mode.toString() + " does not support IV", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (arguments.size() <= 4)
|
||||
{
|
||||
result_column = doDecrypt(evp_cipher, input_rows_count, input_column, key_column, iv_column, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EVP_CIPHER_mode(evp_cipher) != EVP_CIPH_GCM_MODE)
|
||||
throw Exception("AAD can be only set for GCM-mode", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const auto aad_column = block.getByPosition(arguments[4]).column;
|
||||
result_column = doDecrypt(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
}
|
||||
|
||||
block.getByPosition(result).column = std::move(result_column);
|
||||
}
|
||||
|
||||
template <typename InputColumnType, typename KeyColumnType, typename IvColumnType, typename AadColumnType>
|
||||
static ColumnPtr doDecrypt(const EVP_CIPHER * evp_cipher,
|
||||
size_t input_rows_count,
|
||||
const InputColumnType & input_column,
|
||||
const KeyColumnType & key_column,
|
||||
const IvColumnType & iv_column,
|
||||
const AadColumnType & aad_column)
|
||||
{
|
||||
if constexpr (compatibility_mode == OpenSSLDetails::CompatibilityMode::MySQL)
|
||||
{
|
||||
return doDecryptImpl<CipherMode::MySQLCompatibility>(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cipher_mode = EVP_CIPHER_mode(evp_cipher);
|
||||
if (cipher_mode == EVP_CIPH_GCM_MODE)
|
||||
{
|
||||
return doDecryptImpl<CipherMode::RFC5116_AEAD_AES_GCM>(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
else
|
||||
{
|
||||
return doDecryptImpl<CipherMode::OpenSSLCompatibility>(evp_cipher, input_rows_count, input_column, key_column, iv_column, aad_column);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <CipherMode mode, typename InputColumnType, typename KeyColumnType, typename IvColumnType, typename AadColumnType>
|
||||
static ColumnPtr doDecryptImpl(const EVP_CIPHER * evp_cipher,
|
||||
size_t input_rows_count,
|
||||
const InputColumnType & input_column,
|
||||
const KeyColumnType & key_column,
|
||||
[[maybe_unused]] const IvColumnType & iv_column,
|
||||
[[maybe_unused]] const AadColumnType & aad_column)
|
||||
{
|
||||
using namespace OpenSSLDetails;
|
||||
|
||||
auto evp_ctx_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), &EVP_CIPHER_CTX_free);
|
||||
auto evp_ctx = evp_ctx_ptr.get();
|
||||
|
||||
[[maybe_unused]] const auto block_size = static_cast<size_t>(EVP_CIPHER_block_size(evp_cipher));
|
||||
[[maybe_unused]] const auto iv_size = static_cast<size_t>(EVP_CIPHER_iv_length(evp_cipher));
|
||||
const auto key_size = static_cast<size_t>(EVP_CIPHER_key_length(evp_cipher));
|
||||
const auto tag_size = 16; // https://tools.ietf.org/html/rfc5116#section-5.1
|
||||
|
||||
auto decrypted_result_column = ColumnString::create();
|
||||
auto & decrypted_result_column_data = decrypted_result_column->getChars();
|
||||
auto & decrypted_result_column_offsets = decrypted_result_column->getOffsets();
|
||||
|
||||
{
|
||||
size_t resulting_size = 0;
|
||||
for (size_t r = 0; r < input_rows_count; ++r)
|
||||
{
|
||||
resulting_size += input_column->getDataAt(r).size + 1;
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
resulting_size -= tag_size;
|
||||
}
|
||||
|
||||
#if defined(MEMORY_SANITIZER)
|
||||
// Pre-fill result column with values to prevent MSAN from dropping dead on
|
||||
// aes-X-ecb mode with "WARNING: MemorySanitizer: use-of-uninitialized-value".
|
||||
// This is most likely to be caused by the underlying assembler implementation:
|
||||
// see crypto/aes/aesni-x86_64.s, function aesni_ecb_encrypt
|
||||
// which msan seems to fail instrument correctly.
|
||||
decrypted_result_column_data.resize_fill(resulting_size, 0xFF);
|
||||
#else
|
||||
decrypted_result_column_data.resize(resulting_size);
|
||||
#endif
|
||||
}
|
||||
auto decrypted = decrypted_result_column_data.data();
|
||||
|
||||
KeyHolder<mode> key_holder;
|
||||
for (size_t r = 0; r < input_rows_count; ++r)
|
||||
{
|
||||
// 0: prepare key if required
|
||||
auto key_value = key_holder.setKey(key_size, key_column->getDataAt(r));
|
||||
auto iv_value = StringRef{};
|
||||
if constexpr (!std::is_same_v<nullptr_t, std::decay_t<IvColumnType>>)
|
||||
{
|
||||
iv_value = iv_column->getDataAt(r);
|
||||
}
|
||||
|
||||
auto input_value = input_column->getDataAt(r);
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
{
|
||||
// empty plaintext results in empty ciphertext + tag, means there should be atleast tag_size bytes.
|
||||
if (input_value.size < tag_size)
|
||||
throw Exception(fmt::format("Encrypted data is too short: only {} bytes, "
|
||||
"should contain at least {} bytes of a tag.",
|
||||
input_value.size, block_size, tag_size), ErrorCodes::BAD_ARGUMENTS);
|
||||
input_value.size -= tag_size;
|
||||
}
|
||||
|
||||
// 1: Init CTX
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
{
|
||||
// 1.a.1 : Init CTX with custom IV length and optionally with AAD
|
||||
if (EVP_DecryptInit_ex(evp_ctx, evp_cipher, nullptr, nullptr, nullptr) != 1)
|
||||
onError("Failed to initialize cipher context");
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(evp_ctx, EVP_CTRL_AEAD_SET_IVLEN, iv_value.size, nullptr) != 1)
|
||||
onError("Failed to set custom IV length to " + std::to_string(iv_value.size));
|
||||
|
||||
if (EVP_DecryptInit_ex(evp_ctx, nullptr, nullptr,
|
||||
reinterpret_cast<const unsigned char*>(key_value.data),
|
||||
reinterpret_cast<const unsigned char*>(iv_value.data)) != 1)
|
||||
onError("Failed to set key and IV");
|
||||
|
||||
// 1.a.2: Set AAD if present
|
||||
if constexpr (!std::is_same_v<nullptr_t, std::decay_t<AadColumnType>>)
|
||||
{
|
||||
const auto aad_data = aad_column->getDataAt(r);
|
||||
int tmp_len = 0;
|
||||
if (aad_data.size != 0 && EVP_DecryptUpdate(evp_ctx, nullptr, &tmp_len,
|
||||
reinterpret_cast<const unsigned char *>(aad_data.data), aad_data.size) != 1)
|
||||
onError("Failed to sed AAD data");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1.b: Init CTX
|
||||
validateIV<mode>(iv_value, iv_size);
|
||||
|
||||
if (EVP_DecryptInit_ex(evp_ctx, evp_cipher, nullptr,
|
||||
reinterpret_cast<const unsigned char*>(key_value.data),
|
||||
reinterpret_cast<const unsigned char*>(iv_value.data)) != 1)
|
||||
onError("Failed to initialize cipher context");
|
||||
}
|
||||
|
||||
// 2: Feed the data to the cipher
|
||||
int output_len = 0;
|
||||
if (EVP_DecryptUpdate(evp_ctx,
|
||||
reinterpret_cast<unsigned char*>(decrypted), &output_len,
|
||||
reinterpret_cast<const unsigned char*>(input_value.data), static_cast<int>(input_value.size)) != 1)
|
||||
onError("Failed to decrypt");
|
||||
decrypted += output_len;
|
||||
|
||||
// 3: optionally get tag from the ciphertext (RFC5116) and feed it to the context
|
||||
if constexpr (mode == CipherMode::RFC5116_AEAD_AES_GCM)
|
||||
{
|
||||
void * tag = const_cast<void *>(reinterpret_cast<const void *>(input_value.data + input_value.size));
|
||||
if (EVP_CIPHER_CTX_ctrl(evp_ctx, EVP_CTRL_AEAD_SET_TAG, tag_size, tag) != 1)
|
||||
onError("Failed to set tag");
|
||||
}
|
||||
|
||||
// 4: retrieve encrypted data (ciphertext)
|
||||
if (EVP_DecryptFinal_ex(evp_ctx,
|
||||
reinterpret_cast<unsigned char*>(decrypted), &output_len) != 1)
|
||||
onError("Failed to decrypt");
|
||||
decrypted += output_len;
|
||||
|
||||
*decrypted = '\0';
|
||||
++decrypted;
|
||||
|
||||
decrypted_result_column_offsets.push_back(decrypted - decrypted_result_column_data.data());
|
||||
|
||||
if (EVP_CIPHER_CTX_reset(evp_ctx) != 1)
|
||||
onError("Failed to reset context");
|
||||
}
|
||||
|
||||
// in case we overestimate buffer required for decrypted data, fix it up.
|
||||
if (!decrypted_result_column_offsets.empty() && decrypted_result_column_data.size() > decrypted_result_column_offsets.back())
|
||||
{
|
||||
decrypted_result_column_data.resize(decrypted_result_column_offsets.back());
|
||||
}
|
||||
|
||||
decrypted_result_column->validate();
|
||||
return decrypted_result_column;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
29
src/Functions/aes_decrypt_mysql.cpp
Normal file
29
src/Functions/aes_decrypt_mysql.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SSL
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionsAES.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct DecryptMySQLModeImpl
|
||||
{
|
||||
static constexpr auto name = "aes_decrypt_mysql";
|
||||
static constexpr auto compatibility_mode = OpenSSLDetails::CompatibilityMode::MySQL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void registerFunctionAESDecryptMysql(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionDecrypt<DecryptMySQLModeImpl>>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
29
src/Functions/aes_encrypt_mysql.cpp
Normal file
29
src/Functions/aes_encrypt_mysql.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SSL
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionsAES.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct EncryptMySQLModeImpl
|
||||
{
|
||||
static constexpr auto name = "aes_encrypt_mysql";
|
||||
static constexpr auto compatibility_mode = OpenSSLDetails::CompatibilityMode::MySQL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void registerFunctionAESEncryptMysql(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionEncrypt<EncryptMySQLModeImpl>>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
29
src/Functions/decrypt.cpp
Normal file
29
src/Functions/decrypt.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SSL
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionsAES.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct DecryptImpl
|
||||
{
|
||||
static constexpr auto name = "decrypt";
|
||||
static constexpr auto compatibility_mode = OpenSSLDetails::CompatibilityMode::OpenSSL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void registerFunctionDecrypt(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionDecrypt<DecryptImpl>>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
29
src/Functions/encrypt.cpp
Normal file
29
src/Functions/encrypt.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <Common/config.h>
|
||||
|
||||
#if USE_SSL
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionsAES.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct EncryptImpl
|
||||
{
|
||||
static constexpr auto name = "encrypt";
|
||||
static constexpr auto compatibility_mode = OpenSSLDetails::CompatibilityMode::OpenSSL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void registerFunctionEncrypt(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionEncrypt<EncryptImpl>>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,3 +1,5 @@
|
||||
#include <Common/config.h>
|
||||
|
||||
#include <Functions/FunctionFactory.h>
|
||||
|
||||
|
||||
@ -38,10 +40,19 @@ void registerFunctionsNull(FunctionFactory &);
|
||||
void registerFunctionsJSON(FunctionFactory &);
|
||||
void registerFunctionsConsistentHashing(FunctionFactory & factory);
|
||||
void registerFunctionsUnixTimestamp64(FunctionFactory & factory);
|
||||
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
void registerFunctionBayesAB(FunctionFactory &);
|
||||
#endif
|
||||
|
||||
#if USE_SSL
|
||||
void registerFunctionEncrypt(FunctionFactory & factory);
|
||||
void registerFunctionDecrypt(FunctionFactory & factory);
|
||||
void registerFunctionAESEncryptMysql(FunctionFactory & factory);
|
||||
void registerFunctionAESDecryptMysql(FunctionFactory & factory);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void registerFunctions()
|
||||
{
|
||||
@ -83,9 +94,17 @@ void registerFunctions()
|
||||
registerFunctionsIntrospection(factory);
|
||||
registerFunctionsConsistentHashing(factory);
|
||||
registerFunctionsUnixTimestamp64(factory);
|
||||
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
registerFunctionBayesAB(factory);
|
||||
#endif
|
||||
|
||||
#if USE_SSL
|
||||
registerFunctionEncrypt(factory);
|
||||
registerFunctionDecrypt(factory);
|
||||
registerFunctionAESEncryptMysql(factory);
|
||||
registerFunctionAESDecryptMysql(factory);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ SRCS(
|
||||
addSeconds.cpp
|
||||
addWeeks.cpp
|
||||
addYears.cpp
|
||||
aes_decrypt_mysql.cpp
|
||||
aes_encrypt_mysql.cpp
|
||||
appendTrailingCharIfAbsent.cpp
|
||||
array/arrayAll.cpp
|
||||
array/arrayAUC.cpp
|
||||
@ -138,6 +140,7 @@ SRCS(
|
||||
currentUser.cpp
|
||||
dateDiff.cpp
|
||||
date_trunc.cpp
|
||||
decrypt.cpp
|
||||
defaultValueOfArgumentType.cpp
|
||||
defaultValueOfTypeName.cpp
|
||||
demange.cpp
|
||||
@ -145,6 +148,7 @@ SRCS(
|
||||
dumpColumnStructure.cpp
|
||||
e.cpp
|
||||
empty.cpp
|
||||
encrypt.cpp
|
||||
endsWith.cpp
|
||||
equals.cpp
|
||||
erfc.cpp
|
||||
@ -170,6 +174,7 @@ SRCS(
|
||||
FunctionFQDN.cpp
|
||||
FunctionHelpers.cpp
|
||||
FunctionJoinGet.cpp
|
||||
FunctionsAES.cpp
|
||||
FunctionsCoding.cpp
|
||||
FunctionsConversion.cpp
|
||||
FunctionsEmbeddedDictionaries.cpp
|
||||
|
80
tests/queries/0_stateless/01318_decrypt.reference
Normal file
80
tests/queries/0_stateless/01318_decrypt.reference
Normal file
@ -0,0 +1,80 @@
|
||||
0
|
||||
0
|
||||
0
|
||||
1
|
||||
MySQL-specific key folding and decrpyting
|
||||
aes-128-ecb 1
|
||||
aes-128-ecb 1
|
||||
aes-128-ecb 1
|
||||
aes-192-ecb 1
|
||||
aes-192-ecb 1
|
||||
aes-192-ecb 1
|
||||
aes-256-ecb 1
|
||||
aes-256-ecb 1
|
||||
aes-256-ecb 1
|
||||
aes-128-cbc 1
|
||||
aes-128-cbc 1
|
||||
aes-128-cbc 1
|
||||
aes-192-cbc 1
|
||||
aes-192-cbc 1
|
||||
aes-192-cbc 1
|
||||
aes-256-cbc 1
|
||||
aes-256-cbc 1
|
||||
aes-256-cbc 1
|
||||
aes-128-cfb1 1
|
||||
aes-128-cfb1 1
|
||||
aes-128-cfb1 1
|
||||
aes-192-cfb1 1
|
||||
aes-192-cfb1 1
|
||||
aes-192-cfb1 1
|
||||
aes-256-cfb1 1
|
||||
aes-256-cfb1 1
|
||||
aes-256-cfb1 1
|
||||
aes-128-cfb8 1
|
||||
aes-128-cfb8 1
|
||||
aes-128-cfb8 1
|
||||
aes-192-cfb8 1
|
||||
aes-192-cfb8 1
|
||||
aes-192-cfb8 1
|
||||
aes-256-cfb8 1
|
||||
aes-256-cfb8 1
|
||||
aes-256-cfb8 1
|
||||
aes-128-cfb128 1
|
||||
aes-128-cfb128 1
|
||||
aes-128-cfb128 1
|
||||
aes-192-cfb128 1
|
||||
aes-192-cfb128 1
|
||||
aes-192-cfb128 1
|
||||
aes-256-cfb128 1
|
||||
aes-256-cfb128 1
|
||||
aes-256-cfb128 1
|
||||
aes-128-ofb 1
|
||||
aes-128-ofb 1
|
||||
aes-128-ofb 1
|
||||
aes-192-ofb 1
|
||||
aes-192-ofb 1
|
||||
aes-192-ofb 1
|
||||
aes-256-ofb 1
|
||||
aes-256-ofb 1
|
||||
aes-256-ofb 1
|
||||
GCM mode with IV
|
||||
aes-128-gcm FB9958E2E897EF3FDB49067B51A24AF6 1
|
||||
aes-128-gcm FB9958E2E897EF3FDB49067B51A24AF6 1
|
||||
aes-128-gcm FB9958E2E897EF3FDB49067B51A24AF6 1
|
||||
aes-192-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1 1
|
||||
aes-192-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1 1
|
||||
aes-192-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1 1
|
||||
aes-256-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1DC7FD4DD71B7E38F 1
|
||||
aes-256-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1DC7FD4DD71B7E38F 1
|
||||
aes-256-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1DC7FD4DD71B7E38F 1
|
||||
GCM mode with IV and AAD
|
||||
aes-128-gcm FB9958E2E897EF3FDB49067B51A24AF6 1
|
||||
aes-128-gcm FB9958E2E897EF3FDB49067B51A24AF6 1
|
||||
aes-128-gcm FB9958E2E897EF3FDB49067B51A24AF6 1
|
||||
aes-192-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1 1
|
||||
aes-192-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1 1
|
||||
aes-192-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1 1
|
||||
aes-256-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1DC7FD4DD71B7E38F 1
|
||||
aes-256-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1DC7FD4DD71B7E38F 1
|
||||
aes-256-gcm FB9958E2E897EF3FDB49067B51A24AF645B3626EED2F9EA1DC7FD4DD71B7E38F 1
|
||||
F56E87055BC32D0EEB31B2EACC2BF2A5 1
|
123
tests/queries/0_stateless/01318_decrypt.sql
Normal file
123
tests/queries/0_stateless/01318_decrypt.sql
Normal file
@ -0,0 +1,123 @@
|
||||
--- aes_decrypt_mysql(string, key, block_mode[, init_vector, AAD])
|
||||
-- The MySQL-compatitable encryption, only ecb, cbc, cfb1, cfb8, cfb128 and ofb modes are supported,
|
||||
-- just like for MySQL
|
||||
-- https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-encrypt
|
||||
-- https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_block_encryption_mode
|
||||
-- Please note that for keys that exceed mode-specific length, keys are folded in a MySQL-specific way,
|
||||
-- meaning that whole key is used, but effective key length is still determined by mode.
|
||||
-- when key doesn't exceed the default mode length, ecryption result equals with AES_encypt()
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
-- error cases
|
||||
-----------------------------------------------------------------------------------------
|
||||
SELECT aes_decrypt_mysql(); --{serverError 42} not enough arguments
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb'); --{serverError 42} not enough arguments
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb', 'text'); --{serverError 42} not enough arguments
|
||||
|
||||
|
||||
-- Mode
|
||||
SELECT aes_decrypt_mysql(789, 'text', 'key'); --{serverError 43} bad mode type
|
||||
SELECT aes_decrypt_mysql('blah blah blah', 'text', 'key'); -- {serverError 36} garbage mode value
|
||||
SELECT aes_decrypt_mysql('des-ede3-ecb', 'text', 'key'); -- {serverError 36} bad mode value of valid cipher name
|
||||
SELECT aes_decrypt_mysql('aes-128-gcm', 'text', 'key'); -- {serverError 36} mode is not supported by _mysql-functions
|
||||
|
||||
SELECT decrypt(789, 'text', 'key'); --{serverError 43} bad mode type
|
||||
SELECT decrypt('blah blah blah', 'text', 'key'); -- {serverError 36} garbage mode value
|
||||
SELECT decrypt('des-ede3-ecb', 'text', 'key'); -- {serverError 36} bad mode value of valid cipher name
|
||||
|
||||
|
||||
-- Key
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb', 'text', 456); --{serverError 43} bad key type
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb', 'text', 'key'); -- {serverError 36} key is too short
|
||||
|
||||
SELECT decrypt('aes-128-ecb', 'text'); --{serverError 42} key is missing
|
||||
SELECT decrypt('aes-128-ecb', 'text', 456); --{serverError 43} bad key type
|
||||
SELECT decrypt('aes-128-ecb', 'text', 'key'); -- {serverError 36} key is too short
|
||||
SELECT decrypt('aes-128-ecb', 'text', 'keykeykeykeykeykeykeykeykeykeykeykey'); -- {serverError 36} key is to long
|
||||
|
||||
|
||||
-- IV
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb', 'text', 'key', 1011); --{serverError 43} bad IV type 6
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb', 'text', 'key', 'iv'); --{serverError 36} IV is too short 4
|
||||
|
||||
SELECT decrypt('aes-128-cbc', 'text', 'keykeykeykeykeyk', 1011); --{serverError 43} bad IV type 1
|
||||
SELECT decrypt('aes-128-cbc', 'text', 'keykeykeykeykeyk', 'iviviviviviviviviviviviviviviviviviviviviv'); --{serverError 36} IV is too long 3
|
||||
SELECT decrypt('aes-128-cbc', 'text', 'keykeykeykeykeyk', 'iv'); --{serverError 36} IV is too short 2
|
||||
|
||||
|
||||
--AAD
|
||||
SELECT aes_decrypt_mysql('aes-128-ecb', 'text', 'key', 'IV', 1213); --{serverError 42} too many arguments
|
||||
|
||||
SELECT decrypt('aes-128-ecb', 'text', 'key', 'IV', 1213); --{serverError 43} bad AAD type
|
||||
SELECT decrypt('aes-128-gcm', 'text', 'key', 'IV', 1213); --{serverError 43} bad AAD type
|
||||
|
||||
|
||||
-- decrypting invalid cipher text, should cause an error or produce garbage
|
||||
SELECT ignore(decrypt('aes-128-ecb', 'hello there', '1111111111111111')); -- {serverError 454} 1
|
||||
SELECT ignore(decrypt('aes-128-cbc', 'hello there', '1111111111111111')); -- {serverError 454} 2
|
||||
SELECT ignore(decrypt('aes-128-cfb1', 'hello there', '1111111111111111')); -- GIGO
|
||||
SELECT ignore(decrypt('aes-128-ofb', 'hello there', '1111111111111111')); -- GIGO
|
||||
SELECT ignore(decrypt('aes-128-ctr', 'hello there', '1111111111111111')); -- GIGO
|
||||
SELECT decrypt('aes-128-ctr', '', '1111111111111111') == '';
|
||||
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
-- Validate against predefined ciphertext,plaintext,key and IV for MySQL compatibility mode
|
||||
-----------------------------------------------------------------------------------------
|
||||
CREATE TABLE encryption_test
|
||||
(
|
||||
input String,
|
||||
key String DEFAULT unhex('fb9958e2e897ef3fdb49067b51a24af645b3626eed2f9ea1dc7fd4dd71b7e38f9a68db2a3184f952382c783785f9d77bf923577108a88adaacae5c141b1576b0'),
|
||||
iv String DEFAULT unhex('8CA3554377DFF8A369BC50A89780DD85')
|
||||
) Engine = Memory;
|
||||
|
||||
INSERT INTO encryption_test (input)
|
||||
VALUES (''), ('text'), ('What Is ClickHouse? ClickHouse is a column-oriented database management system (DBMS) for online analytical processing of queries (OLAP).');
|
||||
|
||||
SELECT 'MySQL-specific key folding and decrpyting';
|
||||
SELECT 'aes-128-ecb' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-ecb' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-ecb' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cbc' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-cbc' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-cbc' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cfb1' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-cfb1' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-cfb1' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cfb8' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-cfb8' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-cfb8' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cfb128' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-cfb128' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-cfb128' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-ofb' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-ofb' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-ofb' as mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key, iv), key, iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'GCM mode with IV';
|
||||
SELECT 'aes-128-gcm' as mode, hex(substr(key, 1, 16)) as key, decrypt(mode, encrypt(mode, input, unhex(key), iv), unhex(key), iv) == input FROM encryption_test;
|
||||
SELECT 'aes-192-gcm' as mode, hex(substr(key, 1, 24)) as key, decrypt(mode, encrypt(mode, input, unhex(key), iv), unhex(key), iv) == input FROM encryption_test;
|
||||
SELECT 'aes-256-gcm' as mode, hex(substr(key, 1, 32)) as key, decrypt(mode, encrypt(mode, input, unhex(key), iv), unhex(key), iv) == input FROM encryption_test;
|
||||
|
||||
SELECT 'GCM mode with IV and AAD';
|
||||
SELECT 'aes-128-gcm' as mode, hex(substr(key, 1, 16)) AS key, decrypt(mode, encrypt(mode, input, unhex(key), iv, 'AAD'), unhex(key), iv, 'AAD') == input FROM encryption_test;
|
||||
SELECT 'aes-192-gcm' as mode, hex(substr(key, 1, 24)) AS key, decrypt(mode, encrypt(mode, input, unhex(key), iv, 'AAD'), unhex(key), iv, 'AAD') == input FROM encryption_test;
|
||||
SELECT 'aes-256-gcm' as mode, hex(substr(key, 1, 32)) AS key, decrypt(mode, encrypt(mode, input, unhex(key), iv, 'AAD'), unhex(key), iv, 'AAD') == input FROM encryption_test;
|
||||
|
||||
|
||||
-- based on https://github.com/openssl/openssl/blob/master/demos/evp/aesgcm.c#L20
|
||||
WITH
|
||||
unhex('eebc1f57487f51921c0465665f8ae6d1658bb26de6f8a069a3520293a572078f') as key,
|
||||
unhex('67ba0510262ae487d737ee6298f77e0c') as tag,
|
||||
unhex('99aa3e68ed8173a0eed06684') as iv,
|
||||
unhex('f56e87055bc32d0eeb31b2eacc2bf2a5') as plaintext,
|
||||
unhex('4d23c3cec334b49bdb370c437fec78de') as aad,
|
||||
unhex('f7264413a84c0e7cd536867eb9f21736') as ciphertext
|
||||
SELECT
|
||||
hex(decrypt('aes-256-gcm', concat(ciphertext, tag), key, iv, aad)) as plaintext_actual,
|
||||
plaintext_actual = hex(plaintext);
|
86
tests/queries/0_stateless/01318_encrypt.reference
Normal file
86
tests/queries/0_stateless/01318_encrypt.reference
Normal file
@ -0,0 +1,86 @@
|
||||
UInt64
|
||||
5417DEA8D67A1A03FD561809C62402FF
|
||||
Float64
|
||||
9B66D0AA685DC0F1EFFA2E385F7EA2F2
|
||||
Decimal64
|
||||
5417DEA8D67A1A03FD561809C62402FF
|
||||
MySQL-specific key folding
|
||||
aes-128-ecb 861BA71B647390651F75F0EB4A18DCA1
|
||||
aes-128-ecb 557F8E81CBBBB2515A33500768018C3C
|
||||
aes-128-ecb C508CC3350317F61AB1793DB6D93AEFAB000F51C8651ABB578F5EEF362F8560FB3655364CEC9B4D2758C71BC03E4D72FBC54385094A20E20949F70D91462442C5ABA90EF581BC3309C7F2E9E468E34D83C73346C05627D4E1634615F6F5B01E1B388664AECCAD26E4508B537082CEA572222DDBFC9BD0CB5D1D6FEE26A8CCD57BAE76655DCAF0952B80C7F1903990B60
|
||||
aes-192-ecb 04793D6184FFAD00C6457B54D30FED91
|
||||
aes-192-ecb 0028EDF20F6C08FD7097653CE4DB9C39
|
||||
aes-192-ecb 733ECCEEBD7C760CA38FC8ED736A053DCCA5C8DE06035689C765BE53DBFEB9BA9B98C9169C884278E227A3BAFA38F53E837857DF96D6A9B09C58BD196553FFDF1B6F191EAF5A82950EDCEDBE46B91BB8FDC8DDAA6566481B807FA5FCA40D687CF14E2F1A318E0B4CE5C2305FB43A411B4B65EC5B525FD4AB08CDDE49212FC2E99B1096EA5B5F4957594654CA3B369145
|
||||
aes-256-ecb 3FEBF71206304655B6451E02EBFDB965
|
||||
aes-256-ecb EBB295D0F05E820370629399AD7B04DB
|
||||
aes-256-ecb 54D9B7BF0FEE21A848051927FB29D9F621FDD4DEA6D320D80D3102E46A92F17D7E2B9B7AB3C0C4B34B1A7ABABDF98E7ACC4066DFCC385AC799A8D686655311886152E49D3AF8F0D4EF321E05E22E3CE19D0CDCA1C05035C86C6EA4D2D2C7B31AA0D496E03CEB7661905F9463A140E5F8875E876CBD1A72A2B4DE0F98533E1C87D06FE4A68ADF572DD9A74A562DE9A45F
|
||||
aes-128-cbc 9617622E3D3A2BB45B3D0922D63F7C1E
|
||||
aes-128-cbc 700AED2DCC265D7E8D98D0DBBD7653C4
|
||||
aes-128-cbc 63A26A3E2FC9DD48E3BA6CD6BF94D3196181BF85D43301DD7E129C95C90A6760F762317A5E868FECB48CCC9F789B2552A40D12D8B8D8DF062115968751BFD36281D47D63EA4A1629337A0EC5051128FECFE573D6EA055175C17C39C79AF5ECAEB4764ED2AF89784C5BF9F43C75AA4781E5DD483DDCD529D8D30C22710CA930F79BBACBDA51086B227F7A3B087D4EBD7F
|
||||
aes-192-cbc ABF263E5090CC373E7D92CAE91A9136C
|
||||
aes-192-cbc B3DBB188BC9DEF8AF436D68A23FEAA99
|
||||
aes-192-cbc 99E393B279CB125B11F830262800D00A2E4CEFD59A2D1441AAEC11334DDD2AD2CCE75900BA42BE1A78F95C79CEEA85CB0FA401463A53229F8B520E6883011445AE90C2A3A1ECBC2589E1429E631474B5B269AA7617CB818384E93B51C1B3A73F9183CA27899B41BE3E9BB95A45F70C0BA94331E3B7E4849808F83195979079DAC69953C7208D00D6C2C4E6304CDA6B9E
|
||||
aes-256-cbc 221820CEBE1F8B472AC06C8C0DE52BA7
|
||||
aes-256-cbc ADC9060184FE5A23B6CE35327CE5309A
|
||||
aes-256-cbc 09E8AE34C02AB3A50BF4FC49A70344C6B956FCA52D764287B3E935D192D80D7F3E22DDA0E42D4911796A3E4D47A4CD884C511D7AEEF89AD739F4A8A519F03C70C5FE0C0C65A8916B3BA1A64D6964693999C002B96CDE2D53327D2614D8D8D473D6F7B537DC5B459F694196ECF0F034156DBB0A91E98735531E5E756908390F262456D104393F099934E1F8E5406D537E
|
||||
aes-128-cfb1
|
||||
aes-128-cfb1 6B939C8E
|
||||
aes-128-cfb1 50CACD46A945BD61615CFA5638EB313236AE7611B3EA6653E844D7D9F3D4D05442492390DD33D1DA753AD94F6C49D43EB40CF096734AC36D95D1FB6FEDB1AC9D1C0A59C7688C5B5C3D992F6F3FF06418E4E4F5372E51B22F9B7C155AB2B763CD47F5EA5C5F1454D912589DB8E24D46BFE1D429D6F57EEFAFCA91D9C2513705913B3205BFFDA952F65C
|
||||
aes-192-cfb1
|
||||
aes-192-cfb1 B90F814C
|
||||
aes-192-cfb1 8A3503A6DA7547D161B5F778492910321B570C795450FDF2B67DD571467745397769B919CF00ADA1FFBF9DEEFBA4170E625F2E0F3B371DABF5D9439FB154E09890AB951D18F7C1D2D041E697E6AB1779112C31F068AD59A4E4D9ABF30CA5504174EE62BCA04C092C29C60B29BB633DB31F111F905E33D63418A5048256711EF6A276C8C2417EF94C45
|
||||
aes-256-cfb1
|
||||
aes-256-cfb1 88876A00
|
||||
aes-256-cfb1 A31D52BADDE4E79FA12A3C6E145C86E0DDA653EACFDC934541E4D4E2166A06E801A5BC7D30C430B65BE5F8AF5E8BE0A900F03A54BD16D8CD27BBA3411BA00B10A53FEEF7691398BCE0FFB548A4F856219364DD44C4BD9E5515809018045CBC5CFA5308B25498B9F16E437F10EF6E38F08FDBE8424481B18147690A7A63BE674DB566356A1A6FCD642F
|
||||
aes-128-cfb8
|
||||
aes-128-cfb8 76698980
|
||||
aes-128-cfb8 5505B55E6BD7BB69C38FFC9953E1729111D99EB2A6F6392BC1B211141B455923D25FC9387A92C95D91448AA46FD08B0C8977A0CF0123D4503681760D2BAC1203EABB6D6BCD52A9DD99ECD69FA28648229E8F1BC7D194DB817BF39CEC2880722915A542B098FBDE142206A3418B6024B7961BB42358FDB9CB7FC742E387353C961AEB2240F7ABA6EC29
|
||||
aes-192-cfb8
|
||||
aes-192-cfb8 FB065B88
|
||||
aes-192-cfb8 D8015BD1B2CBA6EA04D8A299E17E1D7D0DEE7D31B20711EDF9CEACB158CDDFE4ED569B5C6425DAF6FB0B2B0CA1C87D18C867D87AC760FDD3CF4E94A9FDF1325593F6C36600F4105FEFF5E9B733AB7B28908376DCF4C6FA5F90B720071958C8C69FCABCE9D4D97B33E4A902A41B6F5C72387ADC9F2FD9B787FC0E09FB603CD74BE25DE276D140A00C28
|
||||
aes-256-cfb8
|
||||
aes-256-cfb8 A62542C4
|
||||
aes-256-cfb8 85406B1C10E1F8D7208BD274FD166558C541FF6B761B59BB64BB74A253FE8FE9C6F59BAB49314B2D286270CCC05E6C6C569EB279558E9218162B5CC7D11ECE21CE8CD5805B85F0879EE277AB742C0D07C08A3CA037EAA8D5C643E2E9116D574E9E2284FE7D1EE123640E5F698ACEB407BD8855741472E0BECE67B3760CA2C9F3EB656A6A884AB09C8C
|
||||
aes-128-cfb128
|
||||
aes-128-cfb128 761CC7B1
|
||||
aes-128-cfb128 5511DEB1CD27BED6062453F99912A690AA3628279677D02473DDA04CB6763AF2C5DD191F388E16AC7E122D1A070DB9BEE68B403FAB2FBEF09B224C646EDE9A773E2671D8D8B89B263FDD218FE5E8E6FB37CCA8AEC51C8B4BE0BA5FBC1496B95731E405D14B269CEFCAF7244CE893F9F8B950A66BD30540F0F1E1BF6ECB3ABB71F6470B4875A38C5372
|
||||
aes-192-cfb128
|
||||
aes-192-cfb128 FBD2EB05
|
||||
aes-192-cfb128 D8DFF205334E1E67A0FBC960069219C7D75497A6FCD47E98AB572823BCB1CC472FB94A502AD597B565021F78EAFF3BD7D8B4E4C175A76F97B039FB53E73F35B997EBB693A0AB42AA1003946F84FFBEB32F4E1AC57312C05C8F4E315F6275C097862F347CD3B20B56BFFD73A95FC441AEA0DCFB9E3EABE231114C013A769ADA1D0E46A76D4979A27B08
|
||||
aes-256-cfb128
|
||||
aes-256-cfb128 A6DE9438
|
||||
aes-256-cfb128 85D38D383998E41817837668832DA0068BB5B64BB612FFF9C87D8E5773375C6503B50105A6F96F462A62E1E6C5C7FD7081C629818C10E91A23DA38F1421247B83AE2DEBE317C2F221122DB2E6222DC4C274EEFDE5E425D1FCAD20A3B6C82EF3363335F423F9C1E88AE69F017D53F4ADE0FD748B0BDFF7F15400E27E641FC674EBC40B38EF44D911C8B
|
||||
aes-128-ofb
|
||||
aes-128-ofb 761CC7B1
|
||||
aes-128-ofb 5511DEB1CD27BED6062453F99912A6905E263DE7ABC54E8CF73A9D9FB2F05F643D7E15B468FFF70EB7EFF3A4DD69A60725852D39D4C389231FDD8B82AC16374F101D34D231113E8ACA9762529B38B49313D8700C4650933C3A4E2CE624C0253AEE0ADC8BCB9E6042D1EE5BA75E2327A5D5B039EA4DA20076E8CDFE0E597AD18710DAFC4B261BC02E32
|
||||
aes-192-ofb
|
||||
aes-192-ofb FBD2EB05
|
||||
aes-192-ofb D8DFF205334E1E67A0FBC960069219C7F891621DCBF5B36DE32C2BDC0F37DF021B9106560DBEC7FDE953CC5DAA05C5FD2E37EF1ABD955DD77407CF631BFCBE33873E4F1365E316C707F63DE258C0077E403BD2E8F567601F0D5A5B01920E790B358F2B6B73D9BCBFFBCF718C4223A004B46CBB4BA70E31B542263C0902E03A8EF8FA00ACA70770050A
|
||||
aes-256-ofb
|
||||
aes-256-ofb A6DE9438
|
||||
aes-256-ofb 85D38D383998E41817837668832DA00685DA2D37602CB00AD4DAFB8EB0B85840BCC0CDAD6E229ED356EB2D1E8819E9951D4F6D0F6EA4894F3FD5401938529F388077AC5373CA5D383510769C08256551E6718B236FE29D27C284BB1A3B4E0F6AC5A7625607668F05ED92E7FF54B02771D5ED2AA1381D427E64010EDF21E11CDCDB9B8C35B542720430
|
||||
Nullable and LowCardinality
|
||||
Nullable(String) \N
|
||||
Nullable(String) A6DE9438
|
||||
LowCardinality(String) A6DE9438
|
||||
GCM mode with IV
|
||||
aes-128-gcm 3D67D2B8D8F49A24C482085FEC494231
|
||||
aes-128-gcm C08B1CF60C5A2C92C55DAC62223CBA22C736446C
|
||||
aes-128-gcm E38605F61AE032292F25A35A6CDF6E827980625B9A50BB3C77CD2AD54DB9BE5578322CC55569D1B0C5B82577060C0053388F511DB7BF9C898BF4B05FB6C8C0D0F50449C06A2E89E086610CB2BAEF25B206312836884DCBCC6CD8329B2A43E2BA751183B1696AB3F070BE94FA823F1E1A5E2372A06E1AD2719AF37983D23FCD199820B7769E72EDC20AF48466DAEB6550DC7FDBA041F77D5231
|
||||
aes-192-gcm FC2C8C63A570E584AB71F19BA6E79A8F
|
||||
aes-192-gcm 9A6CF0FDA93F1614F982B5570CC1216D84E356BD
|
||||
aes-192-gcm B961E9FD9B940EBAD7ADDA75C9F198A40797A598AC7FA183AC58705EF6E4E295504D71B85D81978B4CE196AFFFA019B941F44B14DF06375688FCA986F2F3088C24E955392F0DB378F033052822D560CD8DDFF5472C66029E997AE2D63935DAAA10D6703E5AB627E8168F16CF5CDB1112DD2D49F10E087BA20831DCCE592465C95AAA5AF8F766BAEDC3FD3949EDD2E667333C83E58786504137
|
||||
aes-256-gcm E99DBEBC01F021758352D7FBD9039EFA
|
||||
aes-256-gcm 8742CE3A7B0595B281C712600D274CA881F47414
|
||||
aes-256-gcm A44FD73ACEB1A64BDE2D03808A2576EDBB6076F61614CC84A960CCBE55FBABF365671B7017BC89C8A2E0A633E0A05E40B2681B33AD3E7A0AC4925DBD9735C4D1C1E33726B1D6A83CBD337A65C50D7CC33CC4E64369D54C1B6AF3A82D206DF0698BEB61EF9AB2DF81B03DF3829A2EC42D667D87376B8A1351C69BB7A11CCBE50DA88ABA991E98D3BD71129682F35422AD73B05EC624357E77FC
|
||||
GCM mode with IV and AAD
|
||||
aes-128-gcm 5AB059BB98F087E8134B19E7EB5CD9C7
|
||||
aes-128-gcm C08B1CF67AD5D38FE0F3508689794961B8D1FAB8
|
||||
aes-128-gcm E38605F61AE032292F25A35A6CDF6E827980625B9A50BB3C77CD2AD54DB9BE5578322CC55569D1B0C5B82577060C0053388F511DB7BF9C898BF4B05FB6C8C0D0F50449C06A2E89E086610CB2BAEF25B206312836884DCBCC6CD8329B2A43E2BA751183B1696AB3F070BE94FA823F1E1A5E2372A06E1AD2719AF37983D23FCD199820B7769E72EDC20A0826DB2A479DB59F7216A9BDCBD0C989
|
||||
aes-192-gcm 04C13E4B1D62481ED22B3644595CB5DB
|
||||
aes-192-gcm 9A6CF0FD2B329B04EAD18301818F016DF8F77447
|
||||
aes-192-gcm B961E9FD9B940EBAD7ADDA75C9F198A40797A598AC7FA183AC58705EF6E4E295504D71B85D81978B4CE196AFFFA019B941F44B14DF06375688FCA986F2F3088C24E955392F0DB378F033052822D560CD8DDFF5472C66029E997AE2D63935DAAA10D6703E5AB627E8168F16CF5CDB1112DD2D49F10E087BA20831DCCE592465C95AAA5AF8F766BAEDC3668E035498D8C46FB662833CCC12C9D6
|
||||
aes-256-gcm E94F5F6ED4A99B741D492D7EA10B7147
|
||||
aes-256-gcm 8742CE3A3EA5153952DB4C0D94B501FE878FF9A7
|
||||
aes-256-gcm A44FD73ACEB1A64BDE2D03808A2576EDBB6076F61614CC84A960CCBE55FBABF365671B7017BC89C8A2E0A633E0A05E40B2681B33AD3E7A0AC4925DBD9735C4D1C1E33726B1D6A83CBD337A65C50D7CC33CC4E64369D54C1B6AF3A82D206DF0698BEB61EF9AB2DF81B03DF3829A2EC42D667D87376B8A1351C69BB7A11CCBE50DA88ABA991E98D3BD712F56268961DDAB59FA4D2B50578602C4
|
||||
F7264413A84C0E7CD536867EB9F2173667BA0510262AE487D737EE6298F77E0C 1
|
125
tests/queries/0_stateless/01318_encrypt.sql
Normal file
125
tests/queries/0_stateless/01318_encrypt.sql
Normal file
@ -0,0 +1,125 @@
|
||||
--- aes_encrypt_mysql(string, key, block_mode[, init_vector, AAD])
|
||||
-- The MySQL-compatitable encryption, only ecb, cbc, cfb1, cfb8, cfb128 and ofb modes are supported,
|
||||
-- just like for MySQL
|
||||
-- https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-encrypt
|
||||
-- https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_block_encryption_mode
|
||||
-- Please note that for keys that exceed mode-specific length, keys are folded in a MySQL-specific way,
|
||||
-- meaning that whole key is used, but effective key length is still determined by mode.
|
||||
-- when key doesn't exceed the default mode length, ecryption result equals with AES_encypt()
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
-- error cases
|
||||
-----------------------------------------------------------------------------------------
|
||||
SELECT aes_encrypt_mysql(); --{serverError 42} not enough arguments
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb'); --{serverError 42} not enough arguments
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb', 'text'); --{serverError 42} not enough arguments
|
||||
|
||||
-- Mode
|
||||
SELECT aes_encrypt_mysql(789, 'text', 'key'); --{serverError 43} bad mode type
|
||||
SELECT aes_encrypt_mysql('blah blah blah', 'text', 'key'); -- {serverError 36} garbage mode value
|
||||
SELECT aes_encrypt_mysql('des-ede3-ecb', 'text', 'key'); -- {serverError 36} bad mode value of valid cipher name
|
||||
SELECT aes_encrypt_mysql('aes-128-gcm', 'text', 'key'); -- {serverError 36} mode is not supported by _mysql-functions
|
||||
|
||||
SELECT encrypt(789, 'text', 'key'); --{serverError 43} bad mode type
|
||||
SELECT encrypt('blah blah blah', 'text', 'key'); -- {serverError 36} garbage mode value
|
||||
SELECT encrypt('des-ede3-ecb', 'text', 'key'); -- {serverError 36} bad mode value of valid cipher name
|
||||
|
||||
|
||||
-- Key
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb', 'text', 456); --{serverError 43} bad key type
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb', 'text', 'key'); -- {serverError 36} key is too short
|
||||
|
||||
SELECT encrypt('aes-128-ecb', 'text'); --{serverError 42} key is missing
|
||||
SELECT encrypt('aes-128-ecb', 'text', 456); --{serverError 43} bad key type
|
||||
SELECT encrypt('aes-128-ecb', 'text', 'key'); -- {serverError 36} key is too short
|
||||
SELECT encrypt('aes-128-ecb', 'text', 'keykeykeykeykeykeykeykeykeykeykeykey'); -- {serverError 36} key is to long
|
||||
|
||||
-- IV
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb', 'text', 'key', 1011); --{serverError 43} bad IV type 6
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb', 'text', 'key', 'iv'); --{serverError 36} IV is too short 4
|
||||
|
||||
SELECT encrypt('aes-128-cbc', 'text', 'keykeykeykeykeyk', 1011); --{serverError 43} bad IV type 1
|
||||
SELECT encrypt('aes-128-cbc', 'text', 'keykeykeykeykeyk', 'iviviviviviviviviviviviviviviviviviviviviv'); --{serverError 36} IV is too long 3
|
||||
SELECT encrypt('aes-128-cbc', 'text', 'keykeykeykeykeyk', 'iv'); --{serverError 36} IV is too short 2
|
||||
|
||||
--AAD
|
||||
SELECT aes_encrypt_mysql('aes-128-ecb', 'text', 'key', 'IV', 1213); --{serverError 42} too many arguments
|
||||
|
||||
SELECT encrypt('aes-128-ecb', 'text', 'key', 'IV', 1213); --{serverError 43} bad AAD type
|
||||
SELECT encrypt('aes-128-gcm', 'text', 'key', 'IV', 1213); --{serverError 43} bad AAD type
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
-- Valid cases
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
SELECT 'UInt64';
|
||||
SELECT hex(aes_encrypt_mysql('aes-128-ecb', 123456789101112, 'keykeykeykeykeykeykeykeykeykeyke'));
|
||||
SELECT 'Float64';
|
||||
SELECT hex(aes_encrypt_mysql('aes-128-ecb', 1234567891011.12, 'keykeykeykeykeykeykeykeykeykeyke'));
|
||||
SELECT 'Decimal64';
|
||||
SELECT hex(aes_encrypt_mysql('aes-128-ecb', toDecimal64(1234567891011.12, 2), 'keykeykeykeykeykeykeykeykeykeyke'));
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
-- Validate against predefined ciphertext,plaintext,key and IV for MySQL compatibility mode
|
||||
-----------------------------------------------------------------------------------------
|
||||
CREATE TABLE encryption_test
|
||||
(
|
||||
input String,
|
||||
key String DEFAULT unhex('fb9958e2e897ef3fdb49067b51a24af645b3626eed2f9ea1dc7fd4dd71b7e38f9a68db2a3184f952382c783785f9d77bf923577108a88adaacae5c141b1576b0'),
|
||||
iv String DEFAULT unhex('8CA3554377DFF8A369BC50A89780DD85')
|
||||
) Engine = Memory;
|
||||
|
||||
INSERT INTO encryption_test (input)
|
||||
VALUES (''), ('text'), ('What Is ClickHouse? ClickHouse is a column-oriented database management system (DBMS) for online analytical processing of queries (OLAP).');
|
||||
|
||||
SELECT 'MySQL-specific key folding';
|
||||
SELECT 'aes-128-ecb' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-ecb' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-ecb' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cbc' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-cbc' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-cbc' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cfb1' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-cfb1' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-cfb1' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cfb8' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-cfb8' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-cfb8' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-cfb128' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-cfb128' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-cfb128' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'aes-128-ofb' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-ofb' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-ofb' as mode, hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'Nullable and LowCardinality';
|
||||
WITH CAST(NULL as Nullable(String)) as input, 'aes-256-ofb' as mode SELECT toTypeName(input), hex(aes_encrypt_mysql(mode, input, key,iv)) FROM encryption_test LIMIT 1;
|
||||
WITH CAST('text' as Nullable(String)) as input, 'aes-256-ofb' as mode SELECT toTypeName(input), hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test LIMIT 1;
|
||||
WITH CAST('text' as LowCardinality(String)) as input, 'aes-256-ofb' as mode SELECT toTypeName(input), hex(aes_encrypt_mysql(mode, input, key, iv)) FROM encryption_test LIMIT 1;
|
||||
|
||||
SELECT 'GCM mode with IV';
|
||||
SELECT 'aes-128-gcm' as mode, hex(encrypt(mode, input, substr(key, 1, 16), iv)) FROM encryption_test;
|
||||
SELECT 'aes-192-gcm' as mode, hex(encrypt(mode, input, substr(key, 1, 24), iv)) FROM encryption_test;
|
||||
SELECT 'aes-256-gcm' as mode, hex(encrypt(mode, input, substr(key, 1, 32), iv)) FROM encryption_test;
|
||||
|
||||
SELECT 'GCM mode with IV and AAD';
|
||||
SELECT 'aes-128-gcm' as mode, hex(encrypt(mode, input, substr(key, 1, 16), iv, 'AAD')) FROM encryption_test;
|
||||
SELECT 'aes-192-gcm' as mode, hex(encrypt(mode, input, substr(key, 1, 24), iv, 'AAD')) FROM encryption_test;
|
||||
SELECT 'aes-256-gcm' as mode, hex(encrypt(mode, input, substr(key, 1, 32), iv, 'AAD')) FROM encryption_test;
|
||||
|
||||
-- based on https://github.com/openssl/openssl/blob/master/demos/evp/aesgcm.c#L20
|
||||
WITH
|
||||
unhex('eebc1f57487f51921c0465665f8ae6d1658bb26de6f8a069a3520293a572078f') as key,
|
||||
unhex('67ba0510262ae487d737ee6298f77e0c') as tag,
|
||||
unhex('99aa3e68ed8173a0eed06684') as iv,
|
||||
unhex('f56e87055bc32d0eeb31b2eacc2bf2a5') as plaintext,
|
||||
unhex('4d23c3cec334b49bdb370c437fec78de') as aad,
|
||||
unhex('f7264413a84c0e7cd536867eb9f21736') as ciphertext
|
||||
SELECT
|
||||
hex(encrypt('aes-256-gcm', plaintext, key, iv, aad)) as ciphertext_actual,
|
||||
ciphertext_actual = concat(hex(ciphertext), hex(tag));
|
3
tests/ubsan_suppressions.txt
Normal file
3
tests/ubsan_suppressions.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# Suppress some failures in contrib so that we can enable UBSan in CI.
|
||||
# Ideally, we should report these upstream.
|
||||
src:*/contrib/openssl/*
|
Loading…
Reference in New Issue
Block a user