Skip to content

Commit 5855d48

Browse files
committed
PG-2278 Re-use cipher contexts for SMGR encryption
Profiling with perf revealed that EVP_CipherInit_ex() showed up quite a lot when encrypting and decrypting tables, so to decrease how much it shows up we make sure to only allocate and initialize contexts for SMGR encryption once on loading pg_tde and then re-use those contexts but re-initializing them with different keys and IVs on use, which is a lot cheaper than doing a full initialization. This way of calling EVP_CipherInit_ex() is poorly documented in OpenSSL. While at it also remove a useless call to the finalize function which is not necessary due to the lack of padding. It did not show up in profiling but it also adds no value and there is precedence for us skipping it in our AES-CTR code. WAL encryption already has logic for reusing contexts so this optimization is not as relevant there. Also removes an no longer true comment related to the WAL encryption.
1 parent 6ca54ed commit 5855d48

1 file changed

Lines changed: 34 additions & 27 deletions

File tree

src/encryption/enc_aes.c

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,42 @@
3232
* 16 byte blocks.
3333
*/
3434

35-
static const EVP_CIPHER *cipher_cbc_128 = NULL;
3635
static const EVP_CIPHER *cipher_gcm_128 = NULL;
3736
static const EVP_CIPHER *cipher_ctr_ecb_128 = NULL;
3837

39-
static const EVP_CIPHER *cipher_cbc_256 = NULL;
4038
static const EVP_CIPHER *cipher_gcm_256 = NULL;
4139
static const EVP_CIPHER *cipher_ctr_ecb_256 = NULL;
4240

41+
static EVP_CIPHER_CTX *ctx_cbc_128 = NULL;
42+
static EVP_CIPHER_CTX *ctx_cbc_256 = NULL;
43+
44+
static EVP_CIPHER_CTX *
45+
AesCbcInitCtx(const EVP_CIPHER *cipher, const char *name)
46+
{
47+
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
48+
49+
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, 1) == 0)
50+
ereport(ERROR,
51+
errmsg("EVP_CipherInit_ex of %s failed. OpenSSL error: %s",
52+
name, ERR_error_string(ERR_get_error(), NULL)));
53+
54+
EVP_CIPHER_CTX_set_padding(ctx, 0);
55+
56+
return ctx;
57+
}
58+
4359
void
4460
AesInit(void)
4561
{
4662
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
4763

48-
cipher_cbc_128 = EVP_aes_128_cbc();
4964
cipher_gcm_128 = EVP_aes_128_gcm();
5065
cipher_ctr_ecb_128 = EVP_aes_128_ecb();
66+
ctx_cbc_128 = AesCbcInitCtx(EVP_aes_128_cbc(), "AES-128-CBC");
5167

52-
cipher_cbc_256 = EVP_aes_256_cbc();
5368
cipher_gcm_256 = EVP_aes_256_gcm();
5469
cipher_ctr_ecb_256 = EVP_aes_256_ecb();
70+
ctx_cbc_256 = AesCbcInitCtx(EVP_aes_256_cbc(), "AES-256-CBC");
5571
}
5672

5773
static void
@@ -63,10 +79,6 @@ AesEcbEncrypt(EVP_CIPHER_CTX **ctxPtr, const unsigned char *key, int key_len, co
6379
Assert(key_len == 16 || key_len == 32);
6480
cipher = key_len == 32 ? cipher_ctr_ecb_256 : cipher_ctr_ecb_128;
6581

66-
/*
67-
* TODO: Currently, only Ecb (WAL) use cached context. This caching was
68-
* done for optimisation. Do we need it anymore?
69-
*/
7082
if (*ctxPtr == NULL)
7183
{
7284
Assert(cipher != NULL);
@@ -89,44 +101,39 @@ AesEcbEncrypt(EVP_CIPHER_CTX **ctxPtr, const unsigned char *key, int key_len, co
89101
Assert(out_len == in_len);
90102
}
91103

104+
/*
105+
* Used to encrypt or decrypt a page in shared buffers
106+
*
107+
* For performance reasons the cipher context is created once on startup and
108+
* re-used with different key and IV. We also skip calling
109+
* EVP_CipherFinal_ex() since we know the input is an even multiple of the
110+
* cipher's block size.
111+
*/
92112
static void
93113
AesRunCbc(int enc, const unsigned char *key, int key_len, const unsigned char *iv, const unsigned char *in, int in_len, unsigned char *out)
94114
{
95115
int out_len;
96-
int out_len_final;
97-
EVP_CIPHER_CTX *ctx = NULL;
98-
const EVP_CIPHER *cipher;
116+
EVP_CIPHER_CTX *ctx;
99117

100118
Assert(key_len == 16 || key_len == 32);
101-
cipher = key_len == 32 ? cipher_cbc_256 : cipher_cbc_128;
102-
103-
Assert(cipher != NULL);
104-
Assert(in_len % EVP_CIPHER_block_size(cipher) == 0);
119+
ctx = key_len == 32 ? ctx_cbc_256 : ctx_cbc_128;
105120

106-
ctx = EVP_CIPHER_CTX_new();
121+
Assert(ctx != NULL);
122+
Assert(in_len % EVP_CIPHER_CTX_block_size(ctx) == 0);
107123

108-
if (EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, enc) == 0)
124+
if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, enc) == 0)
109125
ereport(ERROR,
110126
errmsg("EVP_CipherInit_ex failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL)));
111127

112-
EVP_CIPHER_CTX_set_padding(ctx, 0);
113-
114128
if (EVP_CipherUpdate(ctx, out, &out_len, in, in_len) == 0)
115129
ereport(ERROR,
116130
errmsg("EVP_CipherUpdate failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL)));
117131

118-
if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len_final) == 0)
119-
ereport(ERROR,
120-
errmsg("EVP_CipherFinal_ex failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL)));
121-
122132
/*
123133
* We encrypt one block (16 bytes) Our expectation is that the result
124-
* should also be 16 bytes, without any additional padding
134+
* should also be 16 bytes, without any additional padding.
125135
*/
126-
out_len += out_len_final;
127136
Assert(in_len == out_len);
128-
129-
EVP_CIPHER_CTX_free(ctx);
130137
}
131138

132139
void

0 commit comments

Comments
 (0)