Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/keyrings/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ADD_SUBDIRECTORY(common)

# Keyring_file component
ADD_SUBDIRECTORY(keyring_file)
ADD_SUBDIRECTORY(percona_keyring_encrypted_file)
ADD_SUBDIRECTORY(keyring_kmip)
ADD_SUBDIRECTORY(keyring_kms)
ADD_SUBDIRECTORY(keyring_vault)
134 changes: 97 additions & 37 deletions components/keyrings/common/encryption/aes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */

#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>

#include <openssl/sha.h>

Expand Down Expand Up @@ -134,13 +136,10 @@ size_t get_ciphertext_size(size_t input_size, const Keyring_aes_opmode mode) {
: input_size;
}

aes_return_status aes_encrypt(const unsigned char *source,
unsigned int source_length, unsigned char *dest,
const unsigned char *key, unsigned int key_length,
Keyring_aes_opmode mode, const unsigned char *iv,
bool padding, size_t *encrypted_length) {
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;

static aes_return_status aes_evp_encrypt(
const unsigned char *source, unsigned int source_length,
unsigned char *dest, const EVP_CIPHER *cipher, const unsigned char *raw_key,
const unsigned char *iv, bool padding, size_t *encrypted_length) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_CIPHER_CTX stack_ctx;
EVP_CIPHER_CTX *ctx = &stack_ctx;
Expand All @@ -159,21 +158,11 @@ aes_return_status aes_encrypt(const unsigned char *source,
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
});

const EVP_CIPHER *cipher = aes_evp_type(mode);
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;

/* The real key to be used for encryption */
std::unique_ptr<unsigned char[]> rkey;
size_t rkey_size;
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
return AES_KEY_TRANSFORMATION_ERROR;

if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;

int u_len, f_len;

if (!EVP_EncryptInit(ctx, cipher, rkey.get(), iv))
return AES_ENCRYPTION_ERROR;
if (!EVP_EncryptInit(ctx, cipher, raw_key, iv)) return AES_ENCRYPTION_ERROR;
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_ENCRYPTION_ERROR;
if (!EVP_EncryptUpdate(ctx, dest, &u_len, source, source_length))
return AES_ENCRYPTION_ERROR;
Expand All @@ -184,14 +173,10 @@ aes_return_status aes_encrypt(const unsigned char *source,
return AES_OP_OK;
}

aes_return_status aes_decrypt(const unsigned char *source,
unsigned int source_length, unsigned char *dest,
const unsigned char *key, unsigned int key_length,
enum Keyring_aes_opmode mode,
const unsigned char *iv, bool padding,
size_t *decrypted_length) {
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;

static aes_return_status aes_evp_decrypt(
const unsigned char *source, unsigned int source_length,
unsigned char *dest, const EVP_CIPHER *cipher, const unsigned char *raw_key,
const unsigned char *iv, bool padding, size_t *decrypted_length) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_CIPHER_CTX stack_ctx;
EVP_CIPHER_CTX *ctx = &stack_ctx;
Expand All @@ -210,21 +195,11 @@ aes_return_status aes_decrypt(const unsigned char *source,
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
});

const EVP_CIPHER *cipher = aes_evp_type(mode);
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;

/* The real key to be used for encryption */
std::unique_ptr<unsigned char[]> rkey;
size_t rkey_size;
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
return AES_KEY_TRANSFORMATION_ERROR;

if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;

int u_len, f_len;

if (!EVP_DecryptInit(ctx, aes_evp_type(mode), rkey.get(), iv))
return AES_DECRYPTION_ERROR;
if (!EVP_DecryptInit(ctx, cipher, raw_key, iv)) return AES_DECRYPTION_ERROR;
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_DECRYPTION_ERROR;
if (!EVP_DecryptUpdate(ctx, dest, &u_len, source, source_length))
return AES_DECRYPTION_ERROR;
Expand All @@ -236,4 +211,89 @@ aes_return_status aes_decrypt(const unsigned char *source,
return AES_OP_OK;
}

aes_return_status aes_encrypt(const unsigned char *source,
unsigned int source_length, unsigned char *dest,
const unsigned char *key, unsigned int key_length,
Keyring_aes_opmode mode, const unsigned char *iv,
bool padding, size_t *encrypted_length) {
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;

const EVP_CIPHER *cipher = aes_evp_type(mode);
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;

/* The real key to be used for encryption */
std::unique_ptr<unsigned char[]> rkey;
size_t rkey_size;
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
return AES_KEY_TRANSFORMATION_ERROR;

return aes_evp_encrypt(source, source_length, dest, cipher, rkey.get(), iv,
padding, encrypted_length);
}

aes_return_status aes_decrypt(const unsigned char *source,
unsigned int source_length, unsigned char *dest,
const unsigned char *key, unsigned int key_length,
enum Keyring_aes_opmode mode,
const unsigned char *iv, bool padding,
size_t *decrypted_length) {
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;

const EVP_CIPHER *cipher = aes_evp_type(mode);
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;

/* The real key to be used for encryption */
std::unique_ptr<unsigned char[]> rkey;
size_t rkey_size;
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
return AES_KEY_TRANSFORMATION_ERROR;

return aes_evp_decrypt(source, source_length, dest, cipher, rkey.get(), iv,
padding, decrypted_length);
}

aes_return_status aes_encrypt_pbkdf2(
const unsigned char *source, unsigned int source_length,
unsigned char *dest, const unsigned char *password, size_t password_len,
const unsigned char *salt, size_t salt_len, unsigned int iterations,
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
size_t *encrypted_length) {
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
const EVP_CIPHER *cipher = aes_evp_type(mode);
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
unsigned char raw_key[32];
auto zero_key =
create_scope_guard([&] { OPENSSL_cleanse(raw_key, sizeof(raw_key)); });
if (PKCS5_PBKDF2_HMAC(reinterpret_cast<const char *>(password),
static_cast<int>(password_len), salt,
static_cast<int>(salt_len),
static_cast<int>(iterations), EVP_sha256(),
static_cast<int>(sizeof(raw_key)), raw_key) != 1)
return AES_KEY_TRANSFORMATION_ERROR;
return aes_evp_encrypt(source, source_length, dest, cipher, raw_key, iv,
padding, encrypted_length);
}

aes_return_status aes_decrypt_pbkdf2(
const unsigned char *source, unsigned int source_length,
unsigned char *dest, const unsigned char *password, size_t password_len,
const unsigned char *salt, size_t salt_len, unsigned int iterations,
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
size_t *decrypted_length) {
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
const EVP_CIPHER *cipher = aes_evp_type(mode);
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
unsigned char raw_key[32];
auto zero_key =
create_scope_guard([&] { OPENSSL_cleanse(raw_key, sizeof(raw_key)); });
if (PKCS5_PBKDF2_HMAC(reinterpret_cast<const char *>(password),
static_cast<int>(password_len), salt,
static_cast<int>(salt_len),
static_cast<int>(iterations), EVP_sha256(),
static_cast<int>(sizeof(raw_key)), raw_key) != 1)
return AES_KEY_TRANSFORMATION_ERROR;
return aes_evp_decrypt(source, source_length, dest, cipher, raw_key, iv,
padding, decrypted_length);
}

} // namespace keyring_common::aes_encryption
22 changes: 22 additions & 0 deletions components/keyrings/common/encryption/aes.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,28 @@ aes_return_status aes_decrypt(const unsigned char *source,
Keyring_aes_opmode mode, const unsigned char *iv,
bool padding, size_t *decrypted_length);

/**
Encrypt using a password: derives a 256-bit AES key via PBKDF2-HMAC-SHA256
and then encrypts with the requested mode.
*/
aes_return_status aes_encrypt_pbkdf2(
const unsigned char *source, unsigned int source_length,
unsigned char *dest, const unsigned char *password, size_t password_len,
const unsigned char *salt, size_t salt_len, unsigned int iterations,
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
size_t *encrypted_length);

/**
Decrypt using a password: derives a 256-bit AES key via PBKDF2-HMAC-SHA256
and then decrypts with the requested mode.
*/
aes_return_status aes_decrypt_pbkdf2(
const unsigned char *source, unsigned int source_length,
unsigned char *dest, const unsigned char *password, size_t password_len,
const unsigned char *salt, size_t salt_len, unsigned int iterations,
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
size_t *decrypted_length);

} // namespace keyring_common::aes_encryption

#endif // !AES_INCLUDED
96 changes: 96 additions & 0 deletions components/keyrings/percona_keyring_encrypted_file/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright (c) 2021, 2026, Oracle and/or its affiliates.
# Copyright (c) 2026 Percona LLC and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is designed to work with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have either included with
# the program or referenced in the documentation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

IF (NOT DEFINED WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE AND
NOT DEFINED WITHOUT_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE)
SET(WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE 1)
ENDIF()

IF(NOT WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE)
RETURN()
ENDIF()

ADD_DEFINITIONS(-DLOG_COMPONENT_TAG="component_percona_keyring_encrypted_file")

INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR}
${BOOST_PATCHES_DIR}
${BOOST_INCLUDE_DIR}
)


SET(PERCONA_KEYRING_ENCRYPTED_FILE_SOURCE
# Encryption handling
service_implementation/keyring_encryption_service_definition.cc

# Generator handling
service_implementation/keyring_generator_service_definition.cc

# Keyring load handling
service_implementation/keyring_load_service_definition.cc

# Keys metadata iterator handling
service_implementation/keyring_keys_metadata_iterator_service_definition.cc

# Metadata query handling
service_implementation/keyring_metadata_query_service_definition.cc

# Reader handling
service_implementation/keyring_reader_service_definition.cc

# Writer handling
service_implementation/keyring_writer_service_definition.cc

# Backend handling
backend/backend.cc

# Config handling
config/config.cc

# Keyring file component handling
percona_keyring_encrypted_file.cc

# Component callbacks
component_callbacks.cc
)

SET(PERCONA_KEYRING_ENCRYPTED_FILE_LIBRARIES
keyring_common
OpenSSL::SSL OpenSSL::Crypto
library_mysys
)

MYSQL_ADD_COMPONENT(percona_keyring_encrypted_file
${PERCONA_KEYRING_ENCRYPTED_FILE_SOURCE}
LINK_LIBRARIES ${PERCONA_KEYRING_ENCRYPTED_FILE_LIBRARIES}
MODULE_ONLY
)

TARGET_LINK_OPTIONS(component_percona_keyring_encrypted_file PRIVATE "${LINK_FLAG_NO_UNDEFINED}")

IF(APPLE)
SET_TARGET_PROPERTIES(component_percona_keyring_encrypted_file PROPERTIES
LINK_FLAGS "-undefined dynamic_lookup")
ENDIF()

Loading