Skip to content

Commit ecbe261

Browse files
committed
PS-11106 [8.4] Component Percona keyring encrypted file
- Created component_percona_keyring_encrypted_file based on component_keyring_file. - Added cmake flag: WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE - New PBKDF2-based encrypt/decrypt API (aes.cc): Added aes_encrypt_pbkdf2 and aes_decrypt_pbkdf2 functions that derive a 256-bit AES key from a password using PKCS5_PBKDF2_HMAC (SHA-256). Refactored the internal EVP encrypt/decrypt logic into private helpers to avoid code duplication. - Encrypted backend (backend.cc): Renamed Keyring_file_backend to Keyring_encrypted_file_backend and wired in password-based encryption. On write, generates a random salt, IV, and iteration count; on read, parses v1 on-disk header ([version:1][salt:32][iterations:4 BE][iv:16][ciphertext]) and decrypts before JSON parsing. - Password config options (config.cc): The component configuration now requires exactly one of password (inline) or password_file (path to a file containing the password). Validation errors are emitted for missing, empty, or conflicting combinations. The keyring_component_status table reports <SET> or <NONE> for the password field.
1 parent b6d91cf commit ecbe261

177 files changed

Lines changed: 27070 additions & 40 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

components/keyrings/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ADD_SUBDIRECTORY(common)
2727

2828
# Keyring_file component
2929
ADD_SUBDIRECTORY(keyring_file)
30+
ADD_SUBDIRECTORY(percona_keyring_encrypted_file)
3031
ADD_SUBDIRECTORY(keyring_kmip)
3132
ADD_SUBDIRECTORY(keyring_kms)
3233
ADD_SUBDIRECTORY(keyring_vault)

components/keyrings/common/encryption/aes.cc

Lines changed: 113 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
2929

3030
#include <openssl/aes.h>
3131
#include <openssl/bio.h>
32+
#include <openssl/crypto.h>
3233
#include <openssl/err.h>
34+
#include <openssl/evp.h>
3335

3436
#include <openssl/sha.h>
3537

@@ -138,13 +140,14 @@ size_t get_ciphertext_size(size_t input_size, const Keyring_aes_opmode mode) {
138140
: input_size;
139141
}
140142

141-
aes_return_status aes_encrypt(const unsigned char *source,
142-
unsigned int source_length, unsigned char *dest,
143-
const unsigned char *key, unsigned int key_length,
144-
Keyring_aes_opmode mode, const unsigned char *iv,
145-
bool padding, size_t *encrypted_length) {
146-
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
143+
namespace {
147144

145+
aes_return_status aes_evp_encrypt(const unsigned char *source,
146+
unsigned int source_length,
147+
unsigned char *dest, const EVP_CIPHER *cipher,
148+
const unsigned char *raw_key,
149+
const unsigned char *iv, bool padding,
150+
size_t *encrypted_length) {
148151
#if OPENSSL_VERSION_NUMBER < 0x10100000L
149152
EVP_CIPHER_CTX stack_ctx;
150153
EVP_CIPHER_CTX *ctx = &stack_ctx;
@@ -163,39 +166,30 @@ aes_return_status aes_encrypt(const unsigned char *source,
163166
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
164167
});
165168

166-
const EVP_CIPHER *cipher = aes_evp_type(mode);
167-
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
168-
169-
/* The real key to be used for encryption */
170-
std::unique_ptr<unsigned char[]> rkey;
171-
size_t rkey_size;
172-
if (aes_create_key(key, key_length, rkey, &rkey_size, mode) == false)
173-
return AES_KEY_TRANSFORMATION_ERROR;
174-
175169
if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;
176170

177171
int u_len, f_len;
178172

179-
if (!EVP_EncryptInit(ctx, cipher, rkey.get(), iv))
173+
if (EVP_EncryptInit(ctx, cipher, raw_key, iv) == 0)
174+
return AES_ENCRYPTION_ERROR;
175+
if (EVP_CIPHER_CTX_set_padding(ctx, padding) == 0)
176+
return AES_ENCRYPTION_ERROR;
177+
if (EVP_EncryptUpdate(ctx, dest, &u_len, source, source_length) == 0)
180178
return AES_ENCRYPTION_ERROR;
181-
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_ENCRYPTION_ERROR;
182-
if (!EVP_EncryptUpdate(ctx, dest, &u_len, source, source_length))
179+
if (EVP_EncryptFinal(ctx, dest + u_len, &f_len) == 0)
183180
return AES_ENCRYPTION_ERROR;
184-
if (!EVP_EncryptFinal(ctx, dest + u_len, &f_len)) return AES_ENCRYPTION_ERROR;
185181

186182
/* All is well */
187183
*encrypted_length = static_cast<size_t>(u_len + f_len);
188184
return AES_OP_OK;
189185
}
190186

191-
aes_return_status aes_decrypt(const unsigned char *source,
192-
unsigned int source_length, unsigned char *dest,
193-
const unsigned char *key, unsigned int key_length,
194-
enum Keyring_aes_opmode mode,
195-
const unsigned char *iv, bool padding,
196-
size_t *decrypted_length) {
197-
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
198-
187+
aes_return_status aes_evp_decrypt(const unsigned char *source,
188+
unsigned int source_length,
189+
unsigned char *dest, const EVP_CIPHER *cipher,
190+
const unsigned char *raw_key,
191+
const unsigned char *iv, bool padding,
192+
size_t *decrypted_length) {
199193
#if OPENSSL_VERSION_NUMBER < 0x10100000L
200194
EVP_CIPHER_CTX stack_ctx;
201195
EVP_CIPHER_CTX *ctx = &stack_ctx;
@@ -214,31 +208,110 @@ aes_return_status aes_decrypt(const unsigned char *source,
214208
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
215209
});
216210

217-
const EVP_CIPHER *cipher = aes_evp_type(mode);
218-
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
219-
220-
/* The real key to be used for encryption */
221-
std::unique_ptr<unsigned char[]> rkey;
222-
size_t rkey_size;
223-
if (aes_create_key(key, key_length, rkey, &rkey_size, mode) == false)
224-
return AES_KEY_TRANSFORMATION_ERROR;
225-
226211
if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;
227212

228213
int u_len, f_len;
229214

230-
if (!EVP_DecryptInit(ctx, aes_evp_type(mode), rkey.get(), iv))
215+
if (EVP_DecryptInit(ctx, cipher, raw_key, iv) == 0)
231216
return AES_DECRYPTION_ERROR;
232-
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_DECRYPTION_ERROR;
233-
if (!EVP_DecryptUpdate(ctx, dest, &u_len, source, source_length))
217+
if (EVP_CIPHER_CTX_set_padding(ctx, padding) == 0)
234218
return AES_DECRYPTION_ERROR;
235-
if (!EVP_DecryptFinal_ex(ctx, dest + u_len, &f_len))
219+
if (EVP_DecryptUpdate(ctx, dest, &u_len, source, source_length) == 0)
220+
return AES_DECRYPTION_ERROR;
221+
if (EVP_DecryptFinal_ex(ctx, dest + u_len, &f_len) == 0)
236222
return AES_DECRYPTION_ERROR;
237223

238224
/* All is well */
239225
*decrypted_length = static_cast<size_t>(u_len + f_len);
240226
return AES_OP_OK;
241227
}
242228

229+
} // namespace
230+
231+
aes_return_status aes_encrypt(const unsigned char *source,
232+
unsigned int source_length, unsigned char *dest,
233+
const unsigned char *key, unsigned int key_length,
234+
Keyring_aes_opmode mode, const unsigned char *iv,
235+
bool padding, size_t *encrypted_length) {
236+
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
237+
238+
const EVP_CIPHER *cipher = aes_evp_type(mode);
239+
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
240+
241+
/* The real key to be used for encryption */
242+
std::unique_ptr<unsigned char[]> rkey;
243+
size_t rkey_size = 0;
244+
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
245+
return AES_KEY_TRANSFORMATION_ERROR;
246+
247+
return aes_evp_encrypt(source, source_length, dest, cipher, rkey.get(), iv,
248+
padding, encrypted_length);
249+
}
250+
251+
aes_return_status aes_decrypt(const unsigned char *source,
252+
unsigned int source_length, unsigned char *dest,
253+
const unsigned char *key, unsigned int key_length,
254+
enum Keyring_aes_opmode mode,
255+
const unsigned char *iv, bool padding,
256+
size_t *decrypted_length) {
257+
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
258+
259+
const EVP_CIPHER *cipher = aes_evp_type(mode);
260+
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
261+
262+
/* The real key to be used for encryption */
263+
std::unique_ptr<unsigned char[]> rkey;
264+
size_t rkey_size = 0;
265+
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
266+
return AES_KEY_TRANSFORMATION_ERROR;
267+
268+
return aes_evp_decrypt(source, source_length, dest, cipher, rkey.get(), iv,
269+
padding, decrypted_length);
270+
}
271+
272+
aes_return_status aes_encrypt_pbkdf2(
273+
const unsigned char *source, unsigned int source_length,
274+
unsigned char *dest, const unsigned char *password, size_t password_len,
275+
const unsigned char *salt, size_t salt_len, unsigned int iterations,
276+
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
277+
size_t *encrypted_length) {
278+
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
279+
const EVP_CIPHER *cipher = aes_evp_type(mode);
280+
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
281+
unsigned char raw_key[32];
282+
auto zero_key =
283+
create_scope_guard([&] { OPENSSL_cleanse(raw_key, sizeof(raw_key)); });
284+
if (PKCS5_PBKDF2_HMAC(reinterpret_cast<const char *>(password),
285+
static_cast<int>(password_len), salt,
286+
static_cast<int>(salt_len),
287+
static_cast<int>(iterations), EVP_sha256(),
288+
static_cast<int>(sizeof(raw_key)), raw_key) != 1)
289+
return AES_KEY_TRANSFORMATION_ERROR;
290+
return aes_evp_encrypt(source, source_length, dest, cipher, raw_key, iv,
291+
padding, encrypted_length);
292+
}
293+
294+
aes_return_status aes_decrypt_pbkdf2(
295+
const unsigned char *source, unsigned int source_length,
296+
unsigned char *dest, const unsigned char *password, size_t password_len,
297+
const unsigned char *salt, size_t salt_len, unsigned int iterations,
298+
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
299+
size_t *decrypted_length) {
300+
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
301+
const EVP_CIPHER *cipher = aes_evp_type(mode);
302+
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
303+
unsigned char raw_key[32];
304+
auto zero_key =
305+
create_scope_guard([&] { OPENSSL_cleanse(raw_key, sizeof(raw_key)); });
306+
if (PKCS5_PBKDF2_HMAC(reinterpret_cast<const char *>(password),
307+
static_cast<int>(password_len), salt,
308+
static_cast<int>(salt_len),
309+
static_cast<int>(iterations), EVP_sha256(),
310+
static_cast<int>(sizeof(raw_key)), raw_key) != 1)
311+
return AES_KEY_TRANSFORMATION_ERROR;
312+
return aes_evp_decrypt(source, source_length, dest, cipher, raw_key, iv,
313+
padding, decrypted_length);
314+
}
315+
243316
} // namespace aes_encryption
244317
} // namespace keyring_common

components/keyrings/common/encryption/aes.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,28 @@ aes_return_status aes_decrypt(const unsigned char *source,
9494
Keyring_aes_opmode mode, const unsigned char *iv,
9595
bool padding, size_t *decrypted_length);
9696

97+
/**
98+
Encrypt using a password: derives a 256-bit AES key via PBKDF2-HMAC-SHA256
99+
and then encrypts with the requested mode.
100+
*/
101+
aes_return_status aes_encrypt_pbkdf2(
102+
const unsigned char *source, unsigned int source_length,
103+
unsigned char *dest, const unsigned char *password, size_t password_len,
104+
const unsigned char *salt, size_t salt_len, unsigned int iterations,
105+
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
106+
size_t *encrypted_length);
107+
108+
/**
109+
Decrypt using a password: derives a 256-bit AES key via PBKDF2-HMAC-SHA256
110+
and then decrypts with the requested mode.
111+
*/
112+
aes_return_status aes_decrypt_pbkdf2(
113+
const unsigned char *source, unsigned int source_length,
114+
unsigned char *dest, const unsigned char *password, size_t password_len,
115+
const unsigned char *salt, size_t salt_len, unsigned int iterations,
116+
Keyring_aes_opmode mode, const unsigned char *iv, bool padding,
117+
size_t *decrypted_length);
118+
97119
} // namespace aes_encryption
98120
} // namespace keyring_common
99121

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright (c) 2021, 2025, Oracle and/or its affiliates.
2+
# Copyright (c) 2026 Percona LLC and/or its affiliates. All rights reserved.
3+
#
4+
# This program is free software; you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License, version 2.0,
6+
# as published by the Free Software Foundation.
7+
#
8+
# This program is designed to work with certain software (including
9+
# but not limited to OpenSSL) that is licensed under separate terms,
10+
# as designated in a particular file or component or in included license
11+
# documentation. The authors of MySQL hereby grant you an additional
12+
# permission to link the program and your derivative works with the
13+
# separately licensed software that they have either included with
14+
# the program or referenced in the documentation.
15+
#
16+
# This program is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License, version 2.0, for more details.
20+
#
21+
# You should have received a copy of the GNU General Public License
22+
# along with this program; if not, write to the Free Software
23+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24+
25+
IF (NOT DEFINED WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE AND
26+
NOT DEFINED WITHOUT_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE)
27+
SET(WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE 1)
28+
ENDIF()
29+
30+
IF(NOT WITH_COMPONENT_PERCONA_KEYRING_ENCRYPTED_FILE)
31+
RETURN()
32+
ENDIF()
33+
34+
ADD_DEFINITIONS(-DLOG_COMPONENT_TAG="component_percona_keyring_encrypted_file")
35+
36+
INCLUDE_DIRECTORIES(
37+
${CMAKE_CURRENT_SOURCE_DIR}
38+
${BOOST_PATCHES_DIR}
39+
${BOOST_INCLUDE_DIR}
40+
)
41+
42+
43+
SET(PERCONA_KEYRING_ENCRYPTED_FILE_SOURCE
44+
# Encryption handling
45+
service_implementation/keyring_encryption_service_definition.cc
46+
47+
# Generator handling
48+
service_implementation/keyring_generator_service_definition.cc
49+
50+
# Keyring load handling
51+
service_implementation/keyring_load_service_definition.cc
52+
53+
# Keys metadata iterator handling
54+
service_implementation/keyring_keys_metadata_iterator_service_definition.cc
55+
56+
# Metadata query handling
57+
service_implementation/keyring_metadata_query_service_definition.cc
58+
59+
# Reader handling
60+
service_implementation/keyring_reader_service_definition.cc
61+
62+
# Writer handling
63+
service_implementation/keyring_writer_service_definition.cc
64+
65+
# Backend handling
66+
backend/backend.cc
67+
68+
# Config handling
69+
config/config.cc
70+
71+
# Keyring file component handling
72+
percona_keyring_encrypted_file.cc
73+
74+
# Component callbacks
75+
component_callbacks.cc
76+
)
77+
78+
SET(PERCONA_KEYRING_ENCRYPTED_FILE_LIBRARIES
79+
keyring_common
80+
OpenSSL::SSL OpenSSL::Crypto
81+
)
82+
83+
MYSQL_ADD_COMPONENT(percona_keyring_encrypted_file
84+
${PERCONA_KEYRING_ENCRYPTED_FILE_SOURCE}
85+
LINK_LIBRARIES ${PERCONA_KEYRING_ENCRYPTED_FILE_LIBRARIES}
86+
MODULE_ONLY
87+
)
88+
89+
add_dependencies(component_percona_keyring_encrypted_file GenError)
90+
91+
MY_TARGET_LINK_OPTIONS(component_percona_keyring_encrypted_file "${LINK_FLAG_NO_UNDEFINED}")
92+
93+
IF(APPLE)
94+
SET_TARGET_PROPERTIES(component_percona_keyring_encrypted_file PROPERTIES
95+
LINK_FLAGS "-undefined dynamic_lookup")
96+
ENDIF()
97+

0 commit comments

Comments
 (0)