Skip to content

Commit 0615a17

Browse files
committed
PS-11106 [8.4] 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 0cd9cc9 commit 0615a17

101 files changed

Lines changed: 1588 additions & 346 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

@@ -138,13 +140,10 @@ 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;
147-
143+
static aes_return_status aes_evp_encrypt(
144+
const unsigned char *source, unsigned int source_length,
145+
unsigned char *dest, const EVP_CIPHER *cipher, const unsigned char *raw_key,
146+
const unsigned char *iv, bool padding, size_t *encrypted_length) {
148147
#if OPENSSL_VERSION_NUMBER < 0x10100000L
149148
EVP_CIPHER_CTX stack_ctx;
150149
EVP_CIPHER_CTX *ctx = &stack_ctx;
@@ -163,21 +162,11 @@ aes_return_status aes_encrypt(const unsigned char *source,
163162
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
164163
});
165164

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-
175165
if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;
176166

177167
int u_len, f_len;
178168

179-
if (!EVP_EncryptInit(ctx, cipher, rkey.get(), iv))
180-
return AES_ENCRYPTION_ERROR;
169+
if (!EVP_EncryptInit(ctx, cipher, raw_key, iv)) return AES_ENCRYPTION_ERROR;
181170
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_ENCRYPTION_ERROR;
182171
if (!EVP_EncryptUpdate(ctx, dest, &u_len, source, source_length))
183172
return AES_ENCRYPTION_ERROR;
@@ -188,14 +177,10 @@ aes_return_status aes_encrypt(const unsigned char *source,
188177
return AES_OP_OK;
189178
}
190179

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-
180+
static aes_return_status aes_evp_decrypt(
181+
const unsigned char *source, unsigned int source_length,
182+
unsigned char *dest, const EVP_CIPHER *cipher, const unsigned char *raw_key,
183+
const unsigned char *iv, bool padding, size_t *decrypted_length) {
199184
#if OPENSSL_VERSION_NUMBER < 0x10100000L
200185
EVP_CIPHER_CTX stack_ctx;
201186
EVP_CIPHER_CTX *ctx = &stack_ctx;
@@ -214,21 +199,11 @@ aes_return_status aes_decrypt(const unsigned char *source,
214199
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
215200
});
216201

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-
226202
if (EVP_CIPHER_iv_length(cipher) > 0 && !iv) return AES_IV_EMPTY;
227203

228204
int u_len, f_len;
229205

230-
if (!EVP_DecryptInit(ctx, aes_evp_type(mode), rkey.get(), iv))
231-
return AES_DECRYPTION_ERROR;
206+
if (!EVP_DecryptInit(ctx, cipher, raw_key, iv)) return AES_DECRYPTION_ERROR;
232207
if (!EVP_CIPHER_CTX_set_padding(ctx, padding)) return AES_DECRYPTION_ERROR;
233208
if (!EVP_DecryptUpdate(ctx, dest, &u_len, source, source_length))
234209
return AES_DECRYPTION_ERROR;
@@ -240,5 +215,90 @@ aes_return_status aes_decrypt(const unsigned char *source,
240215
return AES_OP_OK;
241216
}
242217

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

components/keyrings/percona_keyring_encrypted_file/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright (c) 2021, 2025, 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,

0 commit comments

Comments
 (0)