Skip to content

Commit 01d385f

Browse files
committed
PS-11106 [9.7] Component Percona keyring encrypted file [1, plugin]
- 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 4489b13 commit 01d385f

101 files changed

Lines changed: 1733 additions & 457 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: 97 additions & 37 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

@@ -134,13 +136,10 @@ size_t get_ciphertext_size(size_t input_size, const Keyring_aes_opmode mode) {
134136
: input_size;
135137
}
136138

137-
aes_return_status aes_encrypt(const unsigned char *source,
138-
unsigned int source_length, unsigned char *dest,
139-
const unsigned char *key, unsigned int key_length,
140-
Keyring_aes_opmode mode, const unsigned char *iv,
141-
bool padding, size_t *encrypted_length) {
142-
if (encrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
143-
139+
static aes_return_status aes_evp_encrypt(
140+
const unsigned char *source, unsigned int source_length,
141+
unsigned char *dest, const EVP_CIPHER *cipher, const unsigned char *raw_key,
142+
const unsigned char *iv, bool padding, size_t *encrypted_length) {
144143
#if OPENSSL_VERSION_NUMBER < 0x10100000L
145144
EVP_CIPHER_CTX stack_ctx;
146145
EVP_CIPHER_CTX *ctx = &stack_ctx;
@@ -159,21 +158,11 @@ aes_return_status aes_encrypt(const unsigned char *source,
159158
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
160159
});
161160

162-
const EVP_CIPHER *cipher = aes_evp_type(mode);
163-
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
164-
165-
/* The real key to be used for encryption */
166-
std::unique_ptr<unsigned char[]> rkey;
167-
size_t rkey_size;
168-
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
169-
return AES_KEY_TRANSFORMATION_ERROR;
170-
171161
if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;
172162

173163
int u_len, f_len;
174164

175-
if (!EVP_EncryptInit(ctx, cipher, rkey.get(), iv))
176-
return AES_ENCRYPTION_ERROR;
165+
if (!EVP_EncryptInit(ctx, cipher, raw_key, iv)) return AES_ENCRYPTION_ERROR;
177166
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_ENCRYPTION_ERROR;
178167
if (!EVP_EncryptUpdate(ctx, dest, &u_len, source, source_length))
179168
return AES_ENCRYPTION_ERROR;
@@ -184,14 +173,10 @@ aes_return_status aes_encrypt(const unsigned char *source,
184173
return AES_OP_OK;
185174
}
186175

187-
aes_return_status aes_decrypt(const unsigned char *source,
188-
unsigned int source_length, unsigned char *dest,
189-
const unsigned char *key, unsigned int key_length,
190-
enum Keyring_aes_opmode mode,
191-
const unsigned char *iv, bool padding,
192-
size_t *decrypted_length) {
193-
if (decrypted_length == nullptr) return AES_OUTPUT_SIZE_NULL;
194-
176+
static aes_return_status aes_evp_decrypt(
177+
const unsigned char *source, unsigned int source_length,
178+
unsigned char *dest, const EVP_CIPHER *cipher, const unsigned char *raw_key,
179+
const unsigned char *iv, bool padding, size_t *decrypted_length) {
195180
#if OPENSSL_VERSION_NUMBER < 0x10100000L
196181
EVP_CIPHER_CTX stack_ctx;
197182
EVP_CIPHER_CTX *ctx = &stack_ctx;
@@ -210,21 +195,11 @@ aes_return_status aes_decrypt(const unsigned char *source,
210195
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
211196
});
212197

213-
const EVP_CIPHER *cipher = aes_evp_type(mode);
214-
if (cipher == nullptr) return AES_INVALID_BLOCK_MODE;
215-
216-
/* The real key to be used for encryption */
217-
std::unique_ptr<unsigned char[]> rkey;
218-
size_t rkey_size;
219-
if (!aes_create_key(key, key_length, rkey, &rkey_size, mode))
220-
return AES_KEY_TRANSFORMATION_ERROR;
221-
222198
if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;
223199

224200
int u_len, f_len;
225201

226-
if (!EVP_DecryptInit(ctx, aes_evp_type(mode), rkey.get(), iv))
227-
return AES_DECRYPTION_ERROR;
202+
if (!EVP_DecryptInit(ctx, cipher, raw_key, iv)) return AES_DECRYPTION_ERROR;
228203
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_DECRYPTION_ERROR;
229204
if (!EVP_DecryptUpdate(ctx, dest, &u_len, source, source_length))
230205
return AES_DECRYPTION_ERROR;
@@ -236,4 +211,89 @@ aes_return_status aes_decrypt(const unsigned char *source,
236211
return AES_OP_OK;
237212
}
238213

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

components/keyrings/common/encryption/aes.h

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

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

97119
#endif // !AES_INCLUDED

components/keyrings/percona_keyring_encrypted_file/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# Copyright (c) 2021, 2025, Oracle and/or its affiliates.
1+
# Copyright (c) 2021, 2026, Oracle and/or its affiliates.
2+
# Copyright (c) 2026 Percona LLC and/or its affiliates. All rights reserved.
23
#
34
# This program is free software; you can redistribute it and/or modify
45
# it under the terms of the GNU General Public License, version 2.0,
@@ -77,6 +78,7 @@ SET(PERCONA_KEYRING_ENCRYPTED_FILE_SOURCE
7778
SET(PERCONA_KEYRING_ENCRYPTED_FILE_LIBRARIES
7879
keyring_common
7980
OpenSSL::SSL OpenSSL::Crypto
81+
library_mysys
8082
)
8183

8284
MYSQL_ADD_COMPONENT(percona_keyring_encrypted_file
@@ -85,7 +87,7 @@ MYSQL_ADD_COMPONENT(percona_keyring_encrypted_file
8587
MODULE_ONLY
8688
)
8789

88-
MY_TARGET_LINK_OPTIONS(component_percona_keyring_encrypted_file "${LINK_FLAG_NO_UNDEFINED}")
90+
TARGET_LINK_OPTIONS(component_percona_keyring_encrypted_file PRIVATE "${LINK_FLAG_NO_UNDEFINED}")
8991

9092
IF(APPLE)
9193
SET_TARGET_PROPERTIES(component_percona_keyring_encrypted_file PROPERTIES

0 commit comments

Comments
 (0)