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:
Vasily Nemkov 2020-06-16 12:22:55 +03:00
parent 05b10048a6
commit b147ffcd43
21 changed files with 1295 additions and 3 deletions

View File

@ -70,10 +70,12 @@
# define NO_SANITIZE_UNDEFINED __attribute__((__no_sanitize__("undefined"))) # define NO_SANITIZE_UNDEFINED __attribute__((__no_sanitize__("undefined")))
# define NO_SANITIZE_ADDRESS __attribute__((__no_sanitize__("address"))) # define NO_SANITIZE_ADDRESS __attribute__((__no_sanitize__("address")))
# define NO_SANITIZE_THREAD __attribute__((__no_sanitize__("thread"))) # 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. #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_UNDEFINED
# define NO_SANITIZE_ADDRESS # define NO_SANITIZE_ADDRESS
# define NO_SANITIZE_THREAD # define NO_SANITIZE_THREAD
# define NO_SANITIZE_MEMORY
#endif #endif
#if defined __GNUC__ && !defined __clang__ #if defined __GNUC__ && !defined __clang__

View File

@ -48,8 +48,8 @@ if (SANITIZE)
endif () endif ()
elseif (SANITIZE STREQUAL "undefined") 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_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") 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") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
endif() endif()

View File

@ -2,5 +2,6 @@ usr/bin/clickhouse-server
usr/bin/clickhouse-copier usr/bin/clickhouse-copier
usr/bin/clickhouse-report usr/bin/clickhouse-report
etc/clickhouse-server/config.xml etc/clickhouse-server/config.xml
etc/clickhouse-server/config.d/*.xml
etc/clickhouse-server/users.xml etc/clickhouse-server/users.xml
etc/systemd/system/clickhouse-server.service etc/systemd/system/clickhouse-server.service

View File

@ -29,6 +29,8 @@ set (CLICKHOUSE_SERVER_LINK
clickhouse_program_add(server) 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.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. # TODO We actually need this on Mac, FreeBSD.
if (OS_LINUX) if (OS_LINUX)

View File

@ -15,5 +15,14 @@
<regexp>TOPSECRET.TOPSECRET</regexp> <regexp>TOPSECRET.TOPSECRET</regexp>
<replace>[hidden]</replace> <replace>[hidden]</replace>
</rule> </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> </query_masking_rules>
</yandex> </yandex>

View File

@ -612,4 +612,10 @@ void ColumnString::protect()
getOffsets().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);
}
} }

View File

@ -267,6 +267,9 @@ public:
Offsets & getOffsets() { return offsets; } Offsets & getOffsets() { return offsets; }
const Offsets & getOffsets() const { return offsets; } const Offsets & getOffsets() const { return offsets; }
// Throws an exception if offsets/chars are messed up
void validate() const;
}; };

View File

@ -137,7 +137,7 @@ void validateArgumentsImpl(const IFunction & func,
const auto & arg = arguments[i + argument_offset]; const auto & arg = arguments[i + argument_offset];
const auto descriptor = descriptors[i]; const auto descriptor = descriptors[i];
if (int error_code = descriptor.isValid(arg.type, arg.column); error_code != 0) 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{}) + (descriptor.argument_name ? " '" + std::string(descriptor.argument_name) + "'" : String{})
+ " of function " + func.getName() + " of function " + func.getName()
+ (descriptor.expected_type_description ? String(", expected ") + descriptor.expected_type_description : String{}) + (descriptor.expected_type_description ? String(", expected ") + descriptor.expected_type_description : String{})

View 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

View 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

View 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

View 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
View 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
View 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

View File

@ -1,3 +1,5 @@
#include <Common/config.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
@ -38,10 +40,19 @@ void registerFunctionsNull(FunctionFactory &);
void registerFunctionsJSON(FunctionFactory &); void registerFunctionsJSON(FunctionFactory &);
void registerFunctionsConsistentHashing(FunctionFactory & factory); void registerFunctionsConsistentHashing(FunctionFactory & factory);
void registerFunctionsUnixTimestamp64(FunctionFactory & factory); void registerFunctionsUnixTimestamp64(FunctionFactory & factory);
#if !defined(ARCADIA_BUILD) #if !defined(ARCADIA_BUILD)
void registerFunctionBayesAB(FunctionFactory &); void registerFunctionBayesAB(FunctionFactory &);
#endif #endif
#if USE_SSL
void registerFunctionEncrypt(FunctionFactory & factory);
void registerFunctionDecrypt(FunctionFactory & factory);
void registerFunctionAESEncryptMysql(FunctionFactory & factory);
void registerFunctionAESDecryptMysql(FunctionFactory & factory);
#endif
void registerFunctions() void registerFunctions()
{ {
@ -83,9 +94,17 @@ void registerFunctions()
registerFunctionsIntrospection(factory); registerFunctionsIntrospection(factory);
registerFunctionsConsistentHashing(factory); registerFunctionsConsistentHashing(factory);
registerFunctionsUnixTimestamp64(factory); registerFunctionsUnixTimestamp64(factory);
#if !defined(ARCADIA_BUILD) #if !defined(ARCADIA_BUILD)
registerFunctionBayesAB(factory); registerFunctionBayesAB(factory);
#endif #endif
#if USE_SSL
registerFunctionEncrypt(factory);
registerFunctionDecrypt(factory);
registerFunctionAESEncryptMysql(factory);
registerFunctionAESDecryptMysql(factory);
#endif
} }
} }

View File

@ -46,6 +46,8 @@ SRCS(
addSeconds.cpp addSeconds.cpp
addWeeks.cpp addWeeks.cpp
addYears.cpp addYears.cpp
aes_decrypt_mysql.cpp
aes_encrypt_mysql.cpp
appendTrailingCharIfAbsent.cpp appendTrailingCharIfAbsent.cpp
array/arrayAll.cpp array/arrayAll.cpp
array/arrayAUC.cpp array/arrayAUC.cpp
@ -138,6 +140,7 @@ SRCS(
currentUser.cpp currentUser.cpp
dateDiff.cpp dateDiff.cpp
date_trunc.cpp date_trunc.cpp
decrypt.cpp
defaultValueOfArgumentType.cpp defaultValueOfArgumentType.cpp
defaultValueOfTypeName.cpp defaultValueOfTypeName.cpp
demange.cpp demange.cpp
@ -145,6 +148,7 @@ SRCS(
dumpColumnStructure.cpp dumpColumnStructure.cpp
e.cpp e.cpp
empty.cpp empty.cpp
encrypt.cpp
endsWith.cpp endsWith.cpp
equals.cpp equals.cpp
erfc.cpp erfc.cpp
@ -170,6 +174,7 @@ SRCS(
FunctionFQDN.cpp FunctionFQDN.cpp
FunctionHelpers.cpp FunctionHelpers.cpp
FunctionJoinGet.cpp FunctionJoinGet.cpp
FunctionsAES.cpp
FunctionsCoding.cpp FunctionsCoding.cpp
FunctionsConversion.cpp FunctionsConversion.cpp
FunctionsEmbeddedDictionaries.cpp FunctionsEmbeddedDictionaries.cpp

View 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

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

View 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

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

View 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/*