From dc401a7bedb7ae854dd0a0c249ada64ac87f4c06 Mon Sep 17 00:00:00 2001 From: Andrew Pogrebnoy Date: Thu, 18 Sep 2025 17:33:09 +0300 Subject: [PATCH] PG-1923 WIP 256-bit encryption Store 32-byte keys split into multiple disk entries. --- contrib/pg_tde/src/access/pg_tde_tdemap.c | 15 +- contrib/pg_tde/src/access/pg_tde_xlog_keys.c | 145 ++++++++++++------ contrib/pg_tde/src/access/pg_tde_xlog_smgr.c | 14 +- .../pg_tde/src/catalog/tde_principal_key.c | 5 +- contrib/pg_tde/src/encryption/enc_aes.c | 24 ++- contrib/pg_tde/src/encryption/enc_tde.c | 6 +- .../src/include/access/pg_tde_fe_init.h | 4 +- .../src/include/access/pg_tde_keys_common.h | 6 + .../src/include/access/pg_tde_xlog_keys.h | 2 +- .../src/include/access/pg_tde_xlog_smgr.h | 2 +- .../src/include/catalog/tde_principal_key.h | 3 +- .../pg_tde/src/include/encryption/enc_aes.h | 3 +- .../pg_tde/src/include/encryption/enc_tde.h | 8 +- .../pg_tde/src/include/keyring/keyring_api.h | 5 +- contrib/pg_tde/src/include/pg_tde_guc.h | 7 + contrib/pg_tde/src/keyring/keyring_api.c | 4 +- contrib/pg_tde/src/keyring/keyring_vault.c | 1 + contrib/pg_tde/src/pg_tde.c | 4 +- contrib/pg_tde/src/pg_tde_guc.c | 37 +++++ contrib/pg_tde/src/smgr/pg_tde_smgr.c | 12 +- src/bin/pg_basebackup/pg_basebackup.c | 2 +- src/bin/pg_resetwal/pg_resetwal.c | 2 +- 22 files changed, 218 insertions(+), 93 deletions(-) diff --git a/contrib/pg_tde/src/access/pg_tde_tdemap.c b/contrib/pg_tde/src/access/pg_tde_tdemap.c index 70b6722a2ebc4..2e57db7bf4cc2 100644 --- a/contrib/pg_tde/src/access/pg_tde_tdemap.c +++ b/contrib/pg_tde/src/access/pg_tde_tdemap.c @@ -70,10 +70,12 @@ typedef struct TDEMapEntry uint32 type; /* Part of AAD */ uint32 _unused3; /* Part of AAD */ - uint8 encrypted_key_data[INTERNAL_KEY_LEN]; + uint8 encrypted_key_data[INTERNAL_KEY_BLOCK_LEN]; uint8 key_base_iv[INTERNAL_KEY_IV_LEN]; - uint32 _unused1; /* Will be 1 in existing files entries. */ + uint16 key_size; /* The total size of the key. Currently 16 or 32 bytes */ + uint16 block_num; /* If the key is bigger than 16 bytes it is split in blocks */ + uint32 _unused4; uint64 _unused2; /* Will be 0 in existing files entries. */ @@ -398,10 +400,9 @@ pg_tde_initialize_map_entry(TDEMapEntry *map_entry, const TDEPrincipalKey *princ memcpy(map_entry->key_base_iv, rel_key_data->base_iv, INTERNAL_KEY_IV_LEN); /* - * We set these fields here so that existing file entries will be - * consistent and future use of these fields easier. + * We set this field here so that existing file entries will be + * consistent and future use of this field easier. */ - map_entry->_unused1 = 1; map_entry->_unused2 = 0; if (!RAND_bytes(map_entry->entry_iv, MAP_ENTRY_IV_SIZE)) @@ -412,7 +413,7 @@ pg_tde_initialize_map_entry(TDEMapEntry *map_entry, const TDEPrincipalKey *princ AesGcmEncrypt(principal_key->keyData, map_entry->entry_iv, MAP_ENTRY_IV_SIZE, (unsigned char *) map_entry, offsetof(TDEMapEntry, encrypted_key_data), - rel_key_data->key, INTERNAL_KEY_LEN, + rel_key_data->key, INTERNAL_KEY_BLOCK_LEN, map_entry->encrypted_key_data, map_entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE); } @@ -591,7 +592,7 @@ tde_decrypt_rel_key(const TDEPrincipalKey *principal_key, TDEMapEntry *map_entry if (!AesGcmDecrypt(principal_key->keyData, map_entry->entry_iv, MAP_ENTRY_IV_SIZE, (unsigned char *) map_entry, offsetof(TDEMapEntry, encrypted_key_data), - map_entry->encrypted_key_data, INTERNAL_KEY_LEN, + map_entry->encrypted_key_data, INTERNAL_KEY_BLOCK_LEN, key->key, map_entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE)) ereport(ERROR, diff --git a/contrib/pg_tde/src/access/pg_tde_xlog_keys.c b/contrib/pg_tde/src/access/pg_tde_xlog_keys.c index 06253573fc9ac..c203bb086d8a3 100644 --- a/contrib/pg_tde/src/access/pg_tde_xlog_keys.c +++ b/contrib/pg_tde/src/access/pg_tde_xlog_keys.c @@ -45,11 +45,13 @@ typedef struct WalKeyFileEntry uint32 _unused1; /* Part of AAD, is 1 or 2 in existing entries */ uint32 _unused2; /* Part of AAD */ - uint8 encrypted_key_data[INTERNAL_KEY_LEN]; + uint8 encrypted_key_data[INTERNAL_KEY_BLOCK_LEN]; uint8 key_base_iv[INTERNAL_KEY_IV_LEN]; uint32 range_type; /* WalEncryptionRangeType */ - uint32 _unused3; + uint16 key_size; /* The total size of the key. Currently 16 or 32 bytes */ + uint16 block_num; /* If the key is bigger than 16 bytes it is split in blocks */ + WalLocation range_start; /* IV and tag used when encrypting the key itself */ @@ -63,8 +65,8 @@ static WALKeyCacheRec *tde_wal_key_last_rec = NULL; static WalEncryptionRange *tde_wal_prealloc_range = NULL; static WALKeyCacheRec *pg_tde_add_wal_range_to_cache(WalEncryptionRange *cached_range); -static WalEncryptionRange *pg_tde_wal_range_from_entry(const TDEPrincipalKey *principal_key, WalKeyFileEntry *entry); -static void pg_tde_initialize_wal_key_file_entry(WalKeyFileEntry *entry, const TDEPrincipalKey *principal_key, const WalEncryptionRange *range); +static WalEncryptionRange *pg_tde_wal_range_from_entry(const TDEPrincipalKey *principal_key, WalKeyFileEntry *entry, int entries_len); +static void pg_tde_initialize_wal_key_file_entry(WalKeyFileEntry *entry, const TDEPrincipalKey *principal_key, const WalEncryptionRange *range, uint8 key_offset); static int pg_tde_open_wal_key_file_basic(const char *filename, int flags, bool ignore_missing); static int pg_tde_open_wal_key_file_read(const char *filename, bool ignore_missing, off_t *curr_pos); static int pg_tde_open_wal_key_file_write(const char *filename, const TDESignedPrincipalKeyInfo *signed_key_info, bool truncate, off_t *curr_pos); @@ -72,7 +74,7 @@ static bool pg_tde_read_one_wal_key_file_entry(int fd, WalKeyFileEntry *entry, o static void pg_tde_read_one_wal_key_file_entry2(int fd, int32 key_index, WalKeyFileEntry *entry); static void pg_tde_wal_key_file_header_read(const char *filename, int fd, WalKeyFileHeader *fheader, off_t *bytes_read); static int pg_tde_wal_key_file_header_write(const char *filename, int fd, const TDESignedPrincipalKeyInfo *signed_key_info, off_t *bytes_written); -static void pg_tde_write_one_wal_key_file_entry(int fd, const WalKeyFileEntry *entry, off_t *offset, const char *db_map_path); +static void pg_tde_write_wal_file_entries(int fd, const WalKeyFileEntry *entry, int entries_len, off_t *offset, const char *db_map_path); static void pg_tde_write_wal_key_file_entry(const WalEncryptionRange *range, const TDEPrincipalKey *principal_key); static char * @@ -178,7 +180,7 @@ pg_tde_wal_last_range_set_location(WalLocation loc) * with the actual lsn by the first WAL write. */ void -pg_tde_create_wal_range(WalEncryptionRange *range, WalEncryptionRangeType type) +pg_tde_create_wal_range(WalEncryptionRange *range, WalEncryptionRangeType type, int key_size) { TDEPrincipalKey *principal_key; @@ -199,7 +201,7 @@ pg_tde_create_wal_range(WalEncryptionRange *range, WalEncryptionRangeType type) range->end.lsn = MaxXLogRecPtr; range->end.tli = MaxTimeLineID; - pg_tde_generate_internal_key(&range->key); + pg_tde_generate_internal_key(&range->key, key_size); pg_tde_write_wal_key_file_entry(range, principal_key); @@ -232,7 +234,8 @@ pg_tde_read_last_wal_range(void) TDEPrincipalKey *principal_key; int fd; int file_idx; - WalKeyFileEntry entry; + WalKeyFileEntry entry[2]; + int entries_len = 1; WalEncryptionRange *range; off_t fsize; @@ -259,9 +262,20 @@ pg_tde_read_last_wal_range(void) } file_idx = ((fsize - sizeof(WalKeyFileHeader)) / sizeof(WalKeyFileEntry)) - 1; - pg_tde_read_one_wal_key_file_entry2(fd, file_idx, &entry); + pg_tde_read_one_wal_key_file_entry2(fd, file_idx, &entry[0]); + /* + * If the last record is the part of the 32 byte key, we need to read the + * prevous record as well. + */ + if (entry[0].block_num == 1) + { + memcpy(&entry[1], &entry[0], sizeof(WalKeyFileEntry)); + file_idx = ((fsize - sizeof(WalKeyFileHeader)) / sizeof(WalKeyFileEntry)) - 2; + pg_tde_read_one_wal_key_file_entry2(fd, file_idx, &entry[0]); + entries_len = 2; + } - range = pg_tde_wal_range_from_entry(principal_key, &entry); + range = pg_tde_wal_range_from_entry(principal_key, entry, entries_len); #ifdef FRONTEND pfree(principal_key); #endif @@ -321,27 +335,42 @@ pg_tde_fetch_wal_keys(WalLocation start) for (int file_idx = 0; file_idx < keys_count; file_idx++) { - WalKeyFileEntry entry; + WalKeyFileEntry entry[2]; + WalEncryptionRange *range; + WALKeyCacheRec *wal_rec; + int entries_idx = 0; - pg_tde_read_one_wal_key_file_entry2(fd, file_idx, &entry); + pg_tde_read_one_wal_key_file_entry2(fd, file_idx, &entry[0]); + + if (entry[0].range_type == WAL_ENCRYPTION_RANGE_INVALID) + continue; + + /* + * This is a 32 byte key, hence should be split into two disk entries + */ + if (entry[0].key_size == KEY_DATA_SIZE_256 && entry[0].block_num == 0) + { + pg_tde_read_one_wal_key_file_entry2(fd, ++file_idx, &entry[1]); + entries_idx++; + } /* - * Skip new (just created but not updated by write) and invalid keys + * Skip new (just created but not updated by write) keys */ - if (entry.range_type != WAL_ENCRYPTION_RANGE_INVALID && - wal_location_valid(entry.range_start) && - wal_location_cmp(entry.range_start, start) >= 0) + if (!wal_location_valid(entry[entries_idx].range_start) || + wal_location_cmp(entry[entries_idx].range_start, start) < 0) { - WalEncryptionRange *range = pg_tde_wal_range_from_entry(principal_key, &entry); - WALKeyCacheRec *wal_rec; + continue; + } - wal_rec = pg_tde_add_wal_range_to_cache(range); + range = pg_tde_wal_range_from_entry(principal_key, entry, entries_idx+1); - pfree(range); + wal_rec = pg_tde_add_wal_range_to_cache(range); - if (!return_wal_rec) - return_wal_rec = wal_rec; - } + pfree(range); + + if (!return_wal_rec) + return_wal_rec = wal_rec; } #ifdef FRONTEND pfree(principal_key); @@ -582,7 +611,7 @@ pg_tde_write_wal_key_file_entry(const WalEncryptionRange *range, { int fd; off_t curr_pos = 0; - WalKeyFileEntry write_entry; + WalKeyFileEntry write_entry[2]; TDESignedPrincipalKeyInfo signed_key_Info; pg_tde_sign_principal_key_info(&signed_key_Info, principal_key); @@ -594,52 +623,71 @@ pg_tde_write_wal_key_file_entry(const WalEncryptionRange *range, curr_pos = lseek(fd, 0, SEEK_END); /* Initialize WAL key file entry and encrypt key */ - pg_tde_initialize_wal_key_file_entry(&write_entry, principal_key, range); + pg_tde_initialize_wal_key_file_entry(&write_entry[0], principal_key, range, 0); + if (range->key.key_size > 16) + { + pg_tde_initialize_wal_key_file_entry(&write_entry[1], principal_key, range, 16); + write_entry[0].block_num = 0; + write_entry[1].block_num = 1; + } /* Write the given entry at curr_pos; i.e. the free entry. */ - pg_tde_write_one_wal_key_file_entry(fd, &write_entry, &curr_pos, get_wal_key_file_path()); + pg_tde_write_wal_file_entries(fd, write_entry, + range->key.key_size / INTERNAL_KEY_BLOCK_LEN, + &curr_pos, get_wal_key_file_path()); CloseTransientFile(fd); } static WalEncryptionRange * -pg_tde_wal_range_from_entry(const TDEPrincipalKey *principal_key, WalKeyFileEntry *entry) +pg_tde_wal_range_from_entry(const TDEPrincipalKey *principal_key, WalKeyFileEntry *entries, int entries_len) { WalEncryptionRange *range = tde_wal_prealloc_range == NULL ? palloc0_object(WalEncryptionRange) : tde_wal_prealloc_range; + WalKeyFileEntry *entry; tde_wal_prealloc_range = NULL; Assert(principal_key); + for (int i = 0; i < entries_len; i++) + { + entry = &entries[i]; + + memcpy(range->key.base_iv, entry->key_base_iv, INTERNAL_KEY_IV_LEN); + + if (!AesGcmDecrypt(principal_key->keyData, + entry->entry_iv, MAP_ENTRY_IV_SIZE, + (unsigned char *) entry, offsetof(WalKeyFileEntry, encrypted_key_data), + entry->encrypted_key_data, INTERNAL_KEY_BLOCK_LEN, + &range->key.key[INTERNAL_KEY_BLOCK_LEN * i], + entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE)) + ereport(ERROR, + errmsg("Failed to decrypt key, incorrect principal key or corrupted key file")); + } + + /* + * TLI and LSN are set only for the last entry of the composite (32 byte) key + */ range->type = entry->range_type; range->start = entry->range_start; range->end.tli = MaxTimeLineID; range->end.lsn = MaxXLogRecPtr; - memcpy(range->key.base_iv, entry->key_base_iv, INTERNAL_KEY_IV_LEN); - if (!AesGcmDecrypt(principal_key->keyData, - entry->entry_iv, MAP_ENTRY_IV_SIZE, - (unsigned char *) entry, offsetof(WalKeyFileEntry, encrypted_key_data), - entry->encrypted_key_data, INTERNAL_KEY_LEN, - range->key.key, - entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE)) - ereport(ERROR, - errmsg("Failed to decrypt key, incorrect principal key or corrupted key file")); - return range; } static void -pg_tde_write_one_wal_key_file_entry(int fd, +pg_tde_write_wal_file_entries(int fd, const WalKeyFileEntry *entry, + int entries_len, off_t *offset, const char *db_map_path) { int bytes_written = 0; - bytes_written = pg_pwrite(fd, entry, sizeof(WalKeyFileEntry), *offset); + bytes_written = pg_pwrite(fd, entry, sizeof(WalKeyFileEntry) * entries_len, *offset); - if (bytes_written != sizeof(WalKeyFileEntry)) + if (bytes_written != sizeof(WalKeyFileEntry) * entries_len) { ereport(ERROR, errcode_for_file_access(), @@ -655,10 +703,16 @@ pg_tde_write_one_wal_key_file_entry(int fd, *offset += bytes_written; } +/* + * Creates a disk entry with encrypted the internal key based on the given + * range. The entry has room only for the 16 bytes of the internal key. Hence + * bigger keys should be split into to cuople of entries. `key_offset` sets the + * part of the internal key's 16-byte-block that should go into this entry. + */ static void pg_tde_initialize_wal_key_file_entry(WalKeyFileEntry *entry, const TDEPrincipalKey *principal_key, - const WalEncryptionRange *range) + const WalEncryptionRange *range, uint8 key_offset) { Assert(range->type == WAL_ENCRYPTION_RANGE_ENCRYPTED || range->type == WAL_ENCRYPTION_RANGE_UNENCRYPTED); @@ -673,6 +727,7 @@ pg_tde_initialize_wal_key_file_entry(WalKeyFileEntry *entry, entry->range_type = range->type; entry->range_start = range->start; + entry->key_size = range->key.key_size; memcpy(entry->key_base_iv, range->key.base_iv, INTERNAL_KEY_IV_LEN); if (!RAND_bytes(entry->entry_iv, MAP_ENTRY_IV_SIZE)) @@ -683,7 +738,7 @@ pg_tde_initialize_wal_key_file_entry(WalKeyFileEntry *entry, AesGcmEncrypt(principal_key->keyData, entry->entry_iv, MAP_ENTRY_IV_SIZE, (unsigned char *) entry, offsetof(WalKeyFileEntry, encrypted_key_data), - range->key.key, INTERNAL_KEY_LEN, + range->key.key + key_offset, INTERNAL_KEY_BLOCK_LEN, entry->encrypted_key_data, entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE); } @@ -725,9 +780,9 @@ pg_tde_perform_rotate_server_key(const TDEPrincipalKey *principal_key, break; /* Decrypt and re-encrypt key */ - range = pg_tde_wal_range_from_entry(principal_key, &read_map_entry); - pg_tde_initialize_wal_key_file_entry(&write_map_entry, new_principal_key, range); - pg_tde_write_one_wal_key_file_entry(new_fd, &write_map_entry, &new_curr_pos, tmp_path); + range = pg_tde_wal_range_from_entry(principal_key, &read_map_entry, 1); + pg_tde_initialize_wal_key_file_entry(&write_map_entry, new_principal_key, range, 0); + pg_tde_write_wal_file_entries(new_fd, &write_map_entry, 1, &new_curr_pos, tmp_path); pfree(range); } diff --git a/contrib/pg_tde/src/access/pg_tde_xlog_smgr.c b/contrib/pg_tde/src/access/pg_tde_xlog_smgr.c index c8e62a1edc144..e05eedb320ca1 100644 --- a/contrib/pg_tde/src/access/pg_tde_xlog_smgr.c +++ b/contrib/pg_tde/src/access/pg_tde_xlog_smgr.c @@ -221,7 +221,7 @@ TDEXLogSmgrInit() } void -TDEXLogSmgrInitWrite(bool encrypt_xlog) +TDEXLogSmgrInitWrite(bool encrypt_xlog, int key_size) { WalEncryptionRange *range; WALKeyCacheRec *keys; @@ -242,11 +242,11 @@ TDEXLogSmgrInitWrite(bool encrypt_xlog) */ if (encrypt_xlog) { - pg_tde_create_wal_range(&CurrentWalEncryptionRange, WAL_ENCRYPTION_RANGE_ENCRYPTED); + pg_tde_create_wal_range(&CurrentWalEncryptionRange, WAL_ENCRYPTION_RANGE_ENCRYPTED, key_size); } else if (range && range->type == WAL_ENCRYPTION_RANGE_ENCRYPTED) { - pg_tde_create_wal_range(&CurrentWalEncryptionRange, WAL_ENCRYPTION_RANGE_UNENCRYPTED); + pg_tde_create_wal_range(&CurrentWalEncryptionRange, WAL_ENCRYPTION_RANGE_UNENCRYPTED, key_size); } else if (range) { @@ -332,7 +332,7 @@ static ssize_t TDEXLogWriteEncryptedPages(int fd, const void *buf, size_t count, off_t offset, TimeLineID tli, XLogSegNo segno) { - char iv_prefix[16]; + char iv_prefix[INTERNAL_KEY_IV_LEN]; WalEncryptionRange *range = &CurrentWalEncryptionRange; char *enc_buff = EncryptionBuf; @@ -516,7 +516,7 @@ TDEXLogCryptBuffer(const void *buf, void *out_buf, size_t count, off_t offset, if (wal_location_cmp(data_start, curr_key->range.end) < 0 && wal_location_cmp(data_end, curr_key->range.start) > 0) { - char iv_prefix[16]; + char iv_prefix[INTERNAL_KEY_IV_LEN]; /* * We want to calculate where to start / end encrypting. This @@ -613,7 +613,7 @@ CalcXLogPageIVPrefix(TimeLineID tli, XLogRecPtr lsn, const unsigned char *base_i union u128cast iv; unsigned __int128 offset; - for (int i = 0; i < 16; i++) + for (int i = 0; i < INTERNAL_KEY_IV_LEN; i++) #ifdef WORDS_BIGENDIAN base.a[i] = base_iv[i]; #else @@ -627,7 +627,7 @@ CalcXLogPageIVPrefix(TimeLineID tli, XLogRecPtr lsn, const unsigned char *base_i iv.i = base.i + offset; - for (int i = 0; i < 16; i++) + for (int i = 0; i < INTERNAL_KEY_IV_LEN; i++) #ifdef WORDS_BIGENDIAN iv_prefix[i] = iv.a[i]; #else diff --git a/contrib/pg_tde/src/catalog/tde_principal_key.c b/contrib/pg_tde/src/catalog/tde_principal_key.c index 97ae2a612babe..2a8056076d506 100644 --- a/contrib/pg_tde/src/catalog/tde_principal_key.c +++ b/contrib/pg_tde/src/catalog/tde_principal_key.c @@ -62,9 +62,6 @@ typedef struct TdePrincipalKeylocalState dshash_table *sharedHash; } TdePrincipalKeylocalState; -/* Length of newly generated principal keys */ -#define PRINCIPAL_KEY_LEN 16 - /* Parameters for the principal key info shared hash */ static dshash_parameters principal_key_dsh_params = { .key_size = sizeof(Oid), @@ -534,7 +531,7 @@ pg_tde_create_principal_key_internal(Oid providerOid, errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot create key \"%s\" because it already exists", key_name)); - key_info = KeyringGenerateNewKeyAndStore(provider, key_name, PRINCIPAL_KEY_LEN); + key_info = KeyringGenerateNewKeyAndStore(provider, key_name, TdeKeySize); pfree(key_info); pfree(provider); diff --git a/contrib/pg_tde/src/encryption/enc_aes.c b/contrib/pg_tde/src/encryption/enc_aes.c index 8c98a3750741a..6f71e7e937c79 100644 --- a/contrib/pg_tde/src/encryption/enc_aes.c +++ b/contrib/pg_tde/src/encryption/enc_aes.c @@ -4,6 +4,7 @@ #include #include "encryption/enc_aes.h" +#include "pg_tde_guc.h" #ifdef FRONTEND #include "pg_tde_fe.h" @@ -37,14 +38,26 @@ static const EVP_CIPHER *cipher_gcm = NULL; static const EVP_CIPHER *cipher_ctr_ecb = NULL; void -AesInit(void) +AesInit(CipherOption Cipher) { OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); - cipher_cbc = EVP_aes_128_cbc(); - cipher_gcm = EVP_aes_128_gcm(); - cipher_ctr_ecb = EVP_aes_128_ecb(); + switch (Cipher) + { + case TDE_CIPHER_AES_128: + cipher_cbc = EVP_aes_128_cbc(); + cipher_gcm = EVP_aes_128_gcm(); + cipher_ctr_ecb = EVP_aes_128_ecb(); + break; + case TDE_CIPHER_AES_256: + cipher_cbc = EVP_aes_256_cbc(); + cipher_gcm = EVP_aes_256_gcm(); + cipher_ctr_ecb = EVP_aes_256_ecb(); + break; + default: + Assert(false); + } } static void @@ -210,7 +223,8 @@ AesGcmDecrypt(const unsigned char *key, const unsigned char *iv, int iv_len, con if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag) == 0) ereport(ERROR, errmsg("EVP_CTRL_GCM_SET_TAG failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))); - +// int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, + // int *outl, const unsigned char *in, int inl); if (EVP_DecryptUpdate(ctx, NULL, &out_len, aad, aad_len) == 0) ereport(ERROR, errmsg("EVP_CipherUpdate failed. OpenSSL error: %s", ERR_error_string(ERR_get_error(), NULL))); diff --git a/contrib/pg_tde/src/encryption/enc_tde.c b/contrib/pg_tde/src/encryption/enc_tde.c index bceaaf7cf8b52..b8512c0edf766 100644 --- a/contrib/pg_tde/src/encryption/enc_tde.c +++ b/contrib/pg_tde/src/encryption/enc_tde.c @@ -27,9 +27,9 @@ iv_prefix_debug(const char *iv_prefix, char *out_hex) #endif void -pg_tde_generate_internal_key(InternalKey *int_key) +pg_tde_generate_internal_key(InternalKey *int_key, int size) { - if (!RAND_bytes(int_key->key, INTERNAL_KEY_LEN)) + if (!RAND_bytes(int_key->key, size)) ereport(ERROR, errcode(ERRCODE_INTERNAL_ERROR), errmsg("could not generate internal key: %s", @@ -39,6 +39,8 @@ pg_tde_generate_internal_key(InternalKey *int_key) errcode(ERRCODE_INTERNAL_ERROR), errmsg("could not generate IV: %s", ERR_error_string(ERR_get_error(), NULL))); + + int_key->key_size = size; } /* diff --git a/contrib/pg_tde/src/include/access/pg_tde_fe_init.h b/contrib/pg_tde/src/include/access/pg_tde_fe_init.h index 784c8952dee84..9c68a92cc72c6 100644 --- a/contrib/pg_tde/src/include/access/pg_tde_fe_init.h +++ b/contrib/pg_tde/src/include/access/pg_tde_fe_init.h @@ -10,12 +10,14 @@ #include "keyring/keyring_file.h" #include "keyring/keyring_vault.h" #include "keyring/keyring_kmip.h" +#include "pg_tde_guc.h" /* Frontend has to call this to access keys */ static inline void pg_tde_fe_init(const char *kring_dir) { - AesInit(); + /* TODO: should be an option */ + AesInit(TDE_CIPHER_AES_128); InstallFileKeyring(); InstallVaultV2Keyring(); InstallKmipKeyring(); diff --git a/contrib/pg_tde/src/include/access/pg_tde_keys_common.h b/contrib/pg_tde/src/include/access/pg_tde_keys_common.h index 69d7b76484ac1..912c02a812daa 100644 --- a/contrib/pg_tde/src/include/access/pg_tde_keys_common.h +++ b/contrib/pg_tde/src/include/access/pg_tde_keys_common.h @@ -6,6 +6,12 @@ #define MAP_ENTRY_IV_SIZE 16 #define MAP_ENTRY_AEAD_TAG_SIZE 16 +#define KEY_DATA_SIZE_128 16 /* 128 bit encryption */ +#define KEY_DATA_SIZE_256 32 /* 256 bit encryption */ +#define MAX_KEY_DATA_SIZE KEY_DATA_SIZE_256 /* maximum 256 bit encryption */ + +extern int TdeKeySize; + typedef struct { TDEPrincipalKeyInfo data; diff --git a/contrib/pg_tde/src/include/access/pg_tde_xlog_keys.h b/contrib/pg_tde/src/include/access/pg_tde_xlog_keys.h index 047d76414e962..68562e6a17058 100644 --- a/contrib/pg_tde/src/include/access/pg_tde_xlog_keys.h +++ b/contrib/pg_tde/src/include/access/pg_tde_xlog_keys.h @@ -75,7 +75,7 @@ typedef struct WALKeyCacheRec } WALKeyCacheRec; extern int pg_tde_count_wal_ranges_in_file(void); -extern void pg_tde_create_wal_range(WalEncryptionRange *range, WalEncryptionRangeType type); +extern void pg_tde_create_wal_range(WalEncryptionRange *range, WalEncryptionRangeType type, int key_size); extern void pg_tde_delete_server_key(void); extern WALKeyCacheRec *pg_tde_fetch_wal_keys(WalLocation start); extern void pg_tde_free_wal_key_cache(void); diff --git a/contrib/pg_tde/src/include/access/pg_tde_xlog_smgr.h b/contrib/pg_tde/src/include/access/pg_tde_xlog_smgr.h index 8d5ec4bc08b42..ee9aef593bf78 100644 --- a/contrib/pg_tde/src/include/access/pg_tde_xlog_smgr.h +++ b/contrib/pg_tde/src/include/access/pg_tde_xlog_smgr.h @@ -12,7 +12,7 @@ extern Size TDEXLogEncryptStateSize(void); extern void TDEXLogShmemInit(void); extern void TDEXLogSmgrInit(void); -extern void TDEXLogSmgrInitWrite(bool encrypt_xlog); +extern void TDEXLogSmgrInitWrite(bool encrypt_xlog, int key_size); extern void TDEXLogSmgrInitWriteOldKeys(void); extern void TDEXLogCryptBuffer(const void *buf, void *out_buf, size_t count, off_t offset, diff --git a/contrib/pg_tde/src/include/catalog/tde_principal_key.h b/contrib/pg_tde/src/include/catalog/tde_principal_key.h index a3294c73b02cb..5b0e41fff0505 100644 --- a/contrib/pg_tde/src/include/catalog/tde_principal_key.h +++ b/contrib/pg_tde/src/include/catalog/tde_principal_key.h @@ -7,6 +7,7 @@ #include "postgres.h" #include "catalog/tde_keyring.h" +#include "keyring/keyring_api.h" #ifndef FRONTEND #include "storage/lwlock.h" #endif @@ -24,7 +25,7 @@ typedef struct TDEPrincipalKeyInfo typedef struct TDEPrincipalKey { TDEPrincipalKeyInfo keyInfo; - unsigned char keyData[MAX_KEY_DATA_SIZE]; + unsigned char keyData[32]; uint32 keyLength; } TDEPrincipalKey; diff --git a/contrib/pg_tde/src/include/encryption/enc_aes.h b/contrib/pg_tde/src/include/encryption/enc_aes.h index 68b9416972fd6..d2d383faf20c1 100644 --- a/contrib/pg_tde/src/include/encryption/enc_aes.h +++ b/contrib/pg_tde/src/include/encryption/enc_aes.h @@ -6,8 +6,9 @@ #define ENC_AES_H #include +#include "pg_tde_guc.h" -extern void AesInit(void); +extern void AesInit(CipherOption Cipher); extern void AesEncrypt(const unsigned char *key, const unsigned char *iv, const unsigned char *in, int in_len, unsigned char *out); extern void AesDecrypt(const unsigned char *key, const unsigned char *iv, const unsigned char *in, int in_len, unsigned char *out); extern void AesGcmEncrypt(const unsigned char *key, const unsigned char *iv, int iv_len, const unsigned char *aad, int aad_len, const unsigned char *in, int in_len, unsigned char *out, unsigned char *tag, int tag_len); diff --git a/contrib/pg_tde/src/include/encryption/enc_tde.h b/contrib/pg_tde/src/include/encryption/enc_tde.h index 64299c85e33f5..d0bd4fcf16778 100644 --- a/contrib/pg_tde/src/include/encryption/enc_tde.h +++ b/contrib/pg_tde/src/include/encryption/enc_tde.h @@ -5,16 +5,20 @@ #ifndef ENC_TDE_H #define ENC_TDE_H -#define INTERNAL_KEY_LEN 16 +#include "access/pg_tde_keys_common.h" + +#define INTERNAL_KEY_LEN MAX_KEY_DATA_SIZE +#define INTERNAL_KEY_BLOCK_LEN 16 #define INTERNAL_KEY_IV_LEN 16 typedef struct InternalKey { + int key_size; uint8 key[INTERNAL_KEY_LEN]; uint8 base_iv[INTERNAL_KEY_IV_LEN]; } InternalKey; -extern void pg_tde_generate_internal_key(InternalKey *int_key); +extern void pg_tde_generate_internal_key(InternalKey *int_key, int size); extern void pg_tde_stream_crypt(const char *iv_prefix, uint32 start_offset, const char *data, diff --git a/contrib/pg_tde/src/include/keyring/keyring_api.h b/contrib/pg_tde/src/include/keyring/keyring_api.h index 88941b31c3a1c..1a43bd67c52bc 100644 --- a/contrib/pg_tde/src/include/keyring/keyring_api.h +++ b/contrib/pg_tde/src/include/keyring/keyring_api.h @@ -14,13 +14,10 @@ typedef enum ProviderType } ProviderType; #define TDE_KEY_NAME_LEN 256 -#define KEY_DATA_SIZE_128 16 /* 128 bit encryption */ -#define KEY_DATA_SIZE_256 32 /* 256 bit encryption, not yet supported */ -#define MAX_KEY_DATA_SIZE KEY_DATA_SIZE_256 /* maximum 256 bit encryption */ typedef struct KeyData { - unsigned char data[MAX_KEY_DATA_SIZE]; + unsigned char data[32]; unsigned len; } KeyData; diff --git a/contrib/pg_tde/src/include/pg_tde_guc.h b/contrib/pg_tde/src/include/pg_tde_guc.h index c4ce064402d6d..27542de0b88fd 100644 --- a/contrib/pg_tde/src/include/pg_tde_guc.h +++ b/contrib/pg_tde/src/include/pg_tde_guc.h @@ -10,6 +10,13 @@ extern bool AllowInheritGlobalProviders; extern bool EncryptXLog; extern bool EnforceEncryption; +extern int Cipher; + +typedef enum CipherOption +{ + TDE_CIPHER_AES_128, + TDE_CIPHER_AES_256, +} CipherOption; extern void TdeGucInit(void); diff --git a/contrib/pg_tde/src/keyring/keyring_api.c b/contrib/pg_tde/src/keyring/keyring_api.c index 11da816681f4d..a69f49868c115 100644 --- a/contrib/pg_tde/src/keyring/keyring_api.c +++ b/contrib/pg_tde/src/keyring/keyring_api.c @@ -6,6 +6,7 @@ #include "nodes/pg_list.h" #include "utils/memutils.h" +#include "access/pg_tde_keys_common.h" #include "keyring/keyring_api.h" #ifdef FRONTEND @@ -142,8 +143,7 @@ ValidateKey(KeyInfo *key) return false; } - /* For now we only support 128-bit keys */ - if (key->data.len != KEY_DATA_SIZE_128) + if (key->data.len != KEY_DATA_SIZE_128 && key->data.len != KEY_DATA_SIZE_256) { ereport(WARNING, errmsg("invalid key: unsupported key length \"%u\"", key->data.len)); diff --git a/contrib/pg_tde/src/keyring/keyring_vault.c b/contrib/pg_tde/src/keyring/keyring_vault.c index 06790de2e09d1..17fe5a623d39d 100644 --- a/contrib/pg_tde/src/keyring/keyring_vault.c +++ b/contrib/pg_tde/src/keyring/keyring_vault.c @@ -11,6 +11,7 @@ #include "mb/pg_wchar.h" #include "utils/builtins.h" +#include "access/pg_tde_keys_common.h" #include "keyring/keyring_api.h" #include "keyring/keyring_curl.h" #include "keyring/keyring_vault.h" diff --git a/contrib/pg_tde/src/pg_tde.c b/contrib/pg_tde/src/pg_tde.c index e6f32f099bbe0..14c4b14ceb0bb 100644 --- a/contrib/pg_tde/src/pg_tde.c +++ b/contrib/pg_tde/src/pg_tde.c @@ -70,7 +70,7 @@ tde_shmem_startup(void) PrincipalKeyShmemInit(); TDEXLogShmemInit(); TDEXLogSmgrInit(); - TDEXLogSmgrInitWrite(EncryptXLog); + TDEXLogSmgrInitWrite(EncryptXLog, TdeKeySize); LWLockRelease(AddinShmemInitLock); } @@ -93,8 +93,8 @@ _PG_init(void) check_percona_api_version(); pg_tde_init_data_dir(); - AesInit(); TdeGucInit(); + AesInit(Cipher); TdeEventCaptureInit(); InstallFileKeyring(); InstallVaultV2Keyring(); diff --git a/contrib/pg_tde/src/pg_tde_guc.c b/contrib/pg_tde/src/pg_tde_guc.c index 912385cc0df75..4b4f46ff0e0ee 100644 --- a/contrib/pg_tde/src/pg_tde_guc.c +++ b/contrib/pg_tde/src/pg_tde_guc.c @@ -4,6 +4,7 @@ #include "postgres.h" +#include "access/pg_tde_keys_common.h" #include "utils/guc.h" #include "pg_tde_guc.h" @@ -11,6 +12,30 @@ bool AllowInheritGlobalProviders = true; bool EncryptXLog = false; bool EnforceEncryption = false; +int Cipher = TDE_CIPHER_AES_128; +int TdeKeySize = KEY_DATA_SIZE_128; + +/* Custom GUC variable */ +static const struct config_enum_entry cipher_options[] = { + {"aes_128", TDE_CIPHER_AES_128, false}, + {"aes_256", TDE_CIPHER_AES_256, false}, +}; + +static void +assign_keys_size(int newval, void *extra) +{ + switch (newval) + { + case TDE_CIPHER_AES_128: + TdeKeySize = KEY_DATA_SIZE_128; + break; + case TDE_CIPHER_AES_256: + TdeKeySize = KEY_DATA_SIZE_256; + break; + default: + Assert(false); + } +} void TdeGucInit(void) @@ -51,4 +76,16 @@ TdeGucInit(void) NULL /* show_hook */ ); + DefineCustomEnumVariable("pg_tde.cipher", /* name */ + "TDE encryption algorithm.", /* short_desc */ + NULL, /* long_desc */ + &Cipher, /* value address */ + TDE_CIPHER_AES_128, /* boot value */ + cipher_options, /* options */ + PGC_SUSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + assign_keys_size, /* assign_hook */ + NULL /* show_hook */ + ); } diff --git a/contrib/pg_tde/src/smgr/pg_tde_smgr.c b/contrib/pg_tde/src/smgr/pg_tde_smgr.c index 1f55a57b6e937..2e05b75c62dd6 100644 --- a/contrib/pg_tde/src/smgr/pg_tde_smgr.c +++ b/contrib/pg_tde/src/smgr/pg_tde_smgr.c @@ -95,7 +95,7 @@ tde_smgr_create_key(const RelFileLocatorBackend *smgr_rlocator) { InternalKey *key = palloc_object(InternalKey); - pg_tde_generate_internal_key(key); + pg_tde_generate_internal_key(key, TdeKeySize); if (RelFileLocatorBackendIsTemp(*smgr_rlocator)) tde_smgr_save_temp_key(&smgr_rlocator->locator, key); @@ -113,7 +113,7 @@ tde_smgr_create_key_redo(const RelFileLocator *rlocator) { InternalKey key; - pg_tde_generate_internal_key(&key); + pg_tde_generate_internal_key(&key, TdeKeySize); pg_tde_save_smgr_key(*rlocator, &key); } @@ -228,7 +228,7 @@ tde_mdwritev(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, for (int i = 0; i < nblocks; ++i) { BlockNumber bn = blocknum + i; - unsigned char iv[16]; + unsigned char iv[INTERNAL_KEY_IV_LEN]; local_buffers[i] = &local_blocks[i * BLCKSZ]; @@ -285,7 +285,7 @@ tde_mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, else { unsigned char *local_blocks = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0); - unsigned char iv[16]; + unsigned char iv[INTERNAL_KEY_IV_LEN]; if (tdereln->encryption_status == RELATION_KEY_NOT_AVAILABLE) { @@ -329,7 +329,7 @@ tde_mdreadv(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, { bool allZero = true; BlockNumber bn = blocknum + i; - unsigned char iv[16]; + unsigned char iv[INTERNAL_KEY_IV_LEN]; /* * Detect unencrypted all-zero pages written by smgrzeroextend() by @@ -525,7 +525,7 @@ tde_smgr_delete_temp_key(const RelFileLocator *rel) static void CalcBlockIv(ForkNumber forknum, BlockNumber bn, const unsigned char *base_iv, unsigned char *iv) { - memset(iv, 0, 16); + memset(iv, 0, INTERNAL_KEY_IV_LEN); /* The init fork is copied to the main fork so we must use the same IV */ iv[7] = forknum == INIT_FORKNUM ? MAIN_FORKNUM : forknum; diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index e45d09f7151ea..8504f1d8b8e30 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -687,7 +687,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier, exit(1); } pg_tde_save_server_key(principalKey, false); - TDEXLogSmgrInitWrite(true); + TDEXLogSmgrInitWrite(true, 16); } #endif diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 3cebcb6adabe2..3e5fa01c93b9c 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -521,7 +521,7 @@ main(int argc, char *argv[]) * We are doing a write initialization only here and not at the startup because we * want to be sure that everything is checked and ready for writing at this point. */ - TDEXLogSmgrInitWrite(false); + TDEXLogSmgrInitWrite(false, 16); #endif /*