Skip to content

Commit

Permalink
Few enhancements and code cleanup around tuple encryption/decryption (p…
Browse files Browse the repository at this point in the history
…ercona#52)

* Few enhancements and code cleanup around tuple encryption/decryption

Commit contains the following noteworthy changes
-- Getting rid of VLAs from the code base
-- Add an interface to move the encrypted data from one location to another.
-- Make the encryption and decryption happen in batches to eliminate the
   requirement of dynamic allocation in crypt functions.
  • Loading branch information
codeforall authored Oct 26, 2023
1 parent 7f8974b commit 4ab0539
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 206 deletions.
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ enc_test = executable('enc_test',
files('src/encryption/enc_aes.c', 'src/encryption/test.c'),
kwargs: mod_args,
include_directories: incdir,
c_args : '-DFRONTEND',
)

pg_tde_perf = executable('enc_perf_test',
files('src/encryption/enc_aes.c', 'src/encryption/test_perf.c'),
kwargs: contrib_mod_args,
include_directories: incdir,
c_args : '-DFRONTEND',
)

install_data(
Expand Down
120 changes: 44 additions & 76 deletions src/access/pg_tde_prune.c
Original file line number Diff line number Diff line change
Expand Up @@ -1341,30 +1341,22 @@ pgtde_compactify_tuples(Relation rel, Buffer buffer, itemIdCompact itemidbase, i

itemidptr = &itemidbase[i];

if(copy_head < copy_tail) { // TODO: recheck this condition
if(copy_head < copy_tail)
{
// TODO: recheck this condition
// We leave the original loop as-is, and recrypt tuples one by one
// This is definitely not the fastest, but simple
Oid tableOid = RelationGetRelid(rel);
BlockNumber bn = BufferGetBlockNumber(buffer);
unsigned long headerSize = sizeof(HeapTupleHeaderData);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
char tmpData[itemidptr->alignedlen];
#pragma GCC diagnostic pop


#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>> RELOCATING FROM %u to %u tail %u\n", copy_head, upper, copy_tail);
// Decrypt with old offset
fprintf(stderr, " >>>>> DECRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, copy_head, (char*)(page) + copy_head, tmpData, headerSize, itemidptr->len, keys);
// Reencrypt with new offset
#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>>> ENCRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, upper, tmpData, (char*)page + copy_head, headerSize, itemidptr->len, keys);

unsigned long header_size = sizeof(HeapTupleHeaderData);
uint64 read_offset_in_page = copy_head;
uint64 write_offset_in_page = upper;
uint32 data_len = itemidptr->len - header_size;
char* read_from = (char*)(page) + copy_head + header_size;
char* write_to = (char*)(page) + copy_head + header_size;

PG_TDE_RE_ENCRYPT_TUPLE_DATA(bn, read_offset_in_page, read_from,
bn, write_offset_in_page, write_to,
data_len, keys);
}

lp = PageGetItemId(page, itemidptr->offsetindex + 1);
Expand Down Expand Up @@ -1397,26 +1389,17 @@ pgtde_compactify_tuples(Relation rel, Buffer buffer, itemIdCompact itemidbase, i
// We leave the original loop as-is, and recrypt tuples one by one
// This is definitely not the fastest, but simple
//
Oid tableOid = RelationGetRelid(rel);
BlockNumber bn = BufferGetBlockNumber(buffer);
unsigned long headerSize = sizeof(HeapTupleHeaderData);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
char tmpData[itemidptr->alignedlen];
#pragma GCC diagnostic pop

#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>> RELOCATING FROM %u to %u tail %u\n", copy_head, upper, copy_tail);

// Decrypt with old offset
fprintf(stderr, " >>>>> DECRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, copy_head, (char*)(page) + copy_head, tmpData, headerSize, itemidptr->len, keys);
// Reencrypt with new offset
#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>>> ENCRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, upper, tmpData, (char*)page + copy_head, headerSize, itemidptr->len, keys);
unsigned long header_size = sizeof(HeapTupleHeaderData);
uint64 read_offset_in_page = copy_head;
uint64 write_offset_in_page = upper;
uint32 data_len = itemidptr->len - header_size;
char* read_from = (char*)(page) + copy_head + header_size;
char* write_to = (char*)(page) + copy_head + header_size;

PG_TDE_RE_ENCRYPT_TUPLE_DATA(bn, read_offset_in_page, read_from,
bn, write_offset_in_page, write_to,
data_len, keys);
}

/* move the remaining tuples. */
Expand Down Expand Up @@ -1502,25 +1485,17 @@ pgtde_compactify_tuples(Relation rel, Buffer buffer, itemIdCompact itemidbase, i
if(copy_head < copy_tail) { // TODO: recheck this condition
// We leave the original loop as-is, and recrypt tuples one by one
// This is definitely not the fastest, but simple
Oid tableOid = RelationGetRelid(rel);
BlockNumber bn = BufferGetBlockNumber(buffer);
unsigned long headerSize = sizeof(HeapTupleHeaderData);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
char tmpData[itemidptr->alignedlen];
#pragma GCC diagnostic pop

#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>> [non-sorted] RELOCATING FROM %u to %u tail %u\n", copy_head, upper, copy_tail);
/* Decrypt with old offset */
fprintf(stderr, " >>>>> DECRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, copy_head, scratchptr + copy_head, tmpData, headerSize, itemidptr->len, keys);
/* Reencrypt with new offset */
#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>>> ENCRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, upper, tmpData, scratchptr + copy_head, headerSize, itemidptr->len, keys);
unsigned long header_size = sizeof(HeapTupleHeaderData);
uint64 read_offset_in_page = copy_head;
uint64 write_offset_in_page = upper;
uint32 data_len = itemidptr->len - header_size;
char* read_from = scratchptr + copy_head + header_size;
char* write_to = scratchptr + copy_head + header_size;

PG_TDE_RE_ENCRYPT_TUPLE_DATA(bn, read_offset_in_page, read_from,
bn, write_offset_in_page, write_to,
data_len, keys);
}

lp = PageGetItemId(page, itemidptr->offsetindex + 1);
Expand Down Expand Up @@ -1550,25 +1525,18 @@ pgtde_compactify_tuples(Relation rel, Buffer buffer, itemIdCompact itemidbase, i

/* Recrypt the remaining chunk */
if(copy_head < copy_tail) { // TODO: recheck this condition
Oid tableOid = RelationGetRelid(rel);
BlockNumber bn = BufferGetBlockNumber(buffer);
unsigned long headerSize = sizeof(HeapTupleHeaderData);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
char tmpData[itemidptr->alignedlen];
#pragma GCC diagnostic pop

#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>> [non-sorted] RELOCATING FROM %u to %u tail %u\n", copy_head, upper, copy_tail);
/* Decrypt with old offset */
fprintf(stderr, " >>>>> DECRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, copy_head, scratchptr + copy_head, tmpData, headerSize, itemidptr->len, keys);
/* Reencrypt with new offset */
#ifdef ENCRYPTION_DEBUG
fprintf(stderr, " >>>>> ENCRYPTING\n");
#endif
PGTdeCryptTupInternal(tableOid, bn, upper, tmpData, scratchptr + copy_head, headerSize, itemidptr->len, keys);
unsigned long header_size = sizeof(HeapTupleHeaderData);
uint64 read_offset_in_page = copy_head;
uint64 write_offset_in_page = upper;
uint32 data_len = itemidptr->len - header_size;
char* read_from = scratchptr + copy_head + header_size;
char* write_to = scratchptr + copy_head + header_size;

PG_TDE_RE_ENCRYPT_TUPLE_DATA(bn, read_offset_in_page, read_from,
bn, write_offset_in_page, write_to,
data_len, keys);

}

/* Copy the remaining chunk */
Expand Down
4 changes: 2 additions & 2 deletions src/access/pg_tdeam.c
Original file line number Diff line number Diff line change
Expand Up @@ -3108,8 +3108,8 @@ pg_tde_update(Relation relation, ItemPointer otid, HeapTuple newtup,
oldtup.t_len = ItemIdGetLength(lp);
oldtup.t_self = *otid;
/* decrypt the old tuple */
RelKeysData *keys = GetRelationKeys(relation->rd_locator);
PGTdeDecryptTupData(BufferGetBlockNumber(buffer), page, &oldtup, keys);
PG_TDE_DECRYPT_TUPLE(BufferGetBlockNumber(buffer), page, &oldtup, &oldtup,
GetRelationKeys(relation->rd_locator));

/* the new tuple is ready, except for this: */
newtup->t_tableOid = RelationGetRelid(relation);
Expand Down
2 changes: 1 addition & 1 deletion src/access/pg_tdetoast.c
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ pg_tde_toast_tuple_externalize(ToastTupleContext *ttc, int attribute, int option
* for the toast table. Which is a performance penalty.
* TODO: come up with a better solution
*/
toastrel = table_open(ttc->ttc_rel->rd_rel->reltoastrelid, NoLock);
toastrel = table_open(ttc->ttc_rel->rd_rel->reltoastrelid, AccessShareLock);
keys = GetRelationKeys(toastrel->rd_locator);
table_close(toastrel, NoLock);

Expand Down
89 changes: 65 additions & 24 deletions src/encryption/enc_aes.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@

#ifndef FRONTEND
#include "postgres.h"
#else
#include <assert.h>
#define Assert(p) assert(p)
#endif

#include "encryption/enc_aes.h"

#include <stdio.h>
Expand All @@ -7,7 +14,6 @@
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

#include <openssl/ssl.h>
#include <openssl/crypto.h>
Expand Down Expand Up @@ -59,7 +65,8 @@ void AesInit(void)
}

// TODO: a few things could be optimized in this. It's good enough for a prototype.
static void AesRun2(EVP_CIPHER_CTX** ctxPtr, int enc, const unsigned char* key, const unsigned char* iv, const unsigned char* in, int in_len, unsigned char* out, int* out_len)
static void
AesRun2(EVP_CIPHER_CTX** ctxPtr, int enc, const unsigned char* key, const unsigned char* iv, const unsigned char* in, int in_len, unsigned char* out, int* out_len)
{
if (*ctxPtr == NULL)
{
Expand All @@ -68,14 +75,27 @@ static void AesRun2(EVP_CIPHER_CTX** ctxPtr, int enc, const unsigned char* key,

EVP_CIPHER_CTX_set_padding(*ctxPtr, 0);

if(EVP_CipherInit_ex(*ctxPtr, cipher2, NULL, key, iv, enc) == 0) {
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
if(EVP_CipherInit_ex(*ctxPtr, cipher2, NULL, key, iv, enc) == 0)
{
#ifdef FRONTEND
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
#else
ereport(ERROR,
(errmsg("EVP_CipherInit_ex failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))));
#endif

return;
}
}

if(EVP_CipherUpdate(*ctxPtr, out, out_len, in, in_len) == 0) {
fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
if(EVP_CipherUpdate(*ctxPtr, out, out_len, in, in_len) == 0)
{
#ifdef FRONTEND
fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
#else
ereport(ERROR,
(errmsg("EVP_CipherUpdate failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))));
#endif
return;
}
}
Expand All @@ -88,18 +108,36 @@ static void AesRun(int enc, const unsigned char* key, const unsigned char* iv, c

EVP_CIPHER_CTX_set_padding(ctx, 0);

if(EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, enc) == 0) {
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
if(EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, enc) == 0)
{
#ifdef FRONTEND
fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
#else
ereport(ERROR,
(errmsg("EVP_CipherInit_ex failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))));
#endif
goto cleanup;
}

if(EVP_CipherUpdate(ctx, out, out_len, in, in_len) == 0) {
fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
if(EVP_CipherUpdate(ctx, out, out_len, in, in_len) == 0)
{
#ifdef FRONTEND
fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
#else
ereport(ERROR,
(errmsg("EVP_CipherUpdate failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))));
#endif
goto cleanup;
}

if(EVP_CipherFinal_ex(ctx, out, out_len) == 0) {
fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
if(EVP_CipherFinal_ex(ctx, out, out_len) == 0)
{
#ifdef FRONTEND
fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
#else
ereport(ERROR,
(errmsg("EVP_CipherFinal_ex failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))));
#endif
goto cleanup;
}

Expand All @@ -113,13 +151,12 @@ void Aes128EncryptedZeroBlocks(const unsigned char* key, uint64_t blockNumber1,
unsigned char iv[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

unsigned dataLen = (blockNumber2 - blockNumber1 + 1) * 16;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
unsigned char data[dataLen];
#pragma GCC diagnostic pop
unsigned char data[MAX_AES_ENC_BATCH_KEY_SIZE];
int outLen;

assert(blockNumber2 >= blockNumber1);
Assert(blockNumber2 >= blockNumber1);
Assert(dataLen < MAX_AES_ENC_BATCH_KEY_SIZE);


// NOT memcpy: this is endian independent, and it's also how OpenSSL expects it
for(int i =0; i<8;++i) {
Expand All @@ -129,7 +166,7 @@ void Aes128EncryptedZeroBlocks(const unsigned char* key, uint64_t blockNumber1,
memset(data, 0, dataLen);

AesRun(1, key, iv, data, dataLen, out, &outLen);
assert(outLen == dataLen);
Assert(outLen == dataLen);
}

void AesEncrypt(const unsigned char* key, const unsigned char* iv, const unsigned char* in, int in_len, unsigned char* out, int* out_len)
Expand All @@ -142,18 +179,22 @@ void AesDecrypt(const unsigned char* key, const unsigned char* iv, const unsigne
AesRun(0, key, iv, in, in_len, out, out_len);
}

/*
* We want to avoid dynamic memory allocation, so the function only allows
* to process NUM_AES_BLOCKS_IN_BATCH number of blocks at a time.
* If the caller wants to process more than NUM_AES_BLOCKS_IN_BATCH * AES_BLOCK_SIZE
* data it should divide the data into batches and call this function for each batch.
*/
void Aes128EncryptedZeroBlocks2(void* ctxPtr, const unsigned char* key, uint64_t blockNumber1, uint64_t blockNumber2, unsigned char* out)
{
unsigned char iv[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

unsigned dataLen = (blockNumber2 - blockNumber1 + 1) * 16;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
unsigned char data[dataLen];
#pragma GCC diagnostic pop
unsigned char data[MAX_AES_ENC_BATCH_KEY_SIZE];
int outLen;

assert(blockNumber2 >= blockNumber1);
Assert(blockNumber2 >= blockNumber1);
Assert(dataLen < MAX_AES_ENC_BATCH_KEY_SIZE);

memset(data, 0, dataLen);
for(int j=blockNumber1;j<blockNumber2;++j)
Expand All @@ -164,5 +205,5 @@ void Aes128EncryptedZeroBlocks2(void* ctxPtr, const unsigned char* key, uint64_t
}

AesRun2(ctxPtr, 1, key, iv, data, dataLen, out, &outLen);
assert(outLen == dataLen);
Assert(outLen == dataLen);
}
Loading

0 comments on commit 4ab0539

Please sign in to comment.