From 06b671fc04cd36cf77fef5c9bdfe6a654a64ef37 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Fri, 11 Apr 2025 01:17:03 +0200 Subject: [PATCH] PG-1460 Always generate a new WAL key on server start Since you can take a copy of a PostgreSQL data directory and start both the old and the new version you could get two versions where the same encrypted counter is used for CTR which would mean we could comapre them and potentially decrypt the data. For this reason we need to generate a new WAL key every time we start the server. --- .../pg_tde/src/access/pg_tde_xlog_encrypt.c | 20 +++++++++++++------ contrib/pg_tde/t/009_wal_encrypt.pl | 11 ++++++++++ contrib/pg_tde/t/expected/009_wal_encrypt.out | 8 ++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c b/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c index 16f6e9c531f..eaae1c87f24 100644 --- a/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c +++ b/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c @@ -192,19 +192,27 @@ TDEXLogSmgrInit(void) /* TODO: move to the separate func, it's not an SMGR init */ InternalKey *key = pg_tde_read_last_wal_key(); - /* TDOO: clean-up this mess */ - if ((!key && EncryptXLog) || (key && - ((key->rel_type & TDE_KEY_TYPE_WAL_ENCRYPTED && !EncryptXLog) || - (key->rel_type & TDE_KEY_TYPE_WAL_UNENCRYPTED && EncryptXLog)))) + /* + * Always generate a new key on starting PostgreSQL to protect against + * attacks on CTR ciphers based on comparing the WAL generated by two + * divergent copies of the same cluster. + */ + if (EncryptXLog) { pg_tde_create_wal_key(&EncryptionKey, &GLOBAL_SPACE_RLOCATOR(XLOG_TDE_OID), - (EncryptXLog ? TDE_KEY_TYPE_WAL_ENCRYPTED : TDE_KEY_TYPE_WAL_UNENCRYPTED)); + TDE_KEY_TYPE_WAL_ENCRYPTED); + } + else if (key && key->rel_type & TDE_KEY_TYPE_WAL_ENCRYPTED) + { + pg_tde_create_wal_key(&EncryptionKey, &GLOBAL_SPACE_RLOCATOR(XLOG_TDE_OID), + TDE_KEY_TYPE_WAL_UNENCRYPTED); + pfree(key); } else if (key) { EncryptionKey = *key; - pfree(key); pg_atomic_write_u64(&EncryptionState->enc_key_lsn, EncryptionKey.start_lsn); + pfree(key); } pg_tde_set_db_file_path(GLOBAL_SPACE_RLOCATOR(XLOG_TDE_OID).dbOid, EncryptionState->db_map_path); diff --git a/contrib/pg_tde/t/009_wal_encrypt.pl b/contrib/pg_tde/t/009_wal_encrypt.pl index 481f83d8541..88fece2e24d 100644 --- a/contrib/pg_tde/t/009_wal_encrypt.pl +++ b/contrib/pg_tde/t/009_wal_encrypt.pl @@ -86,6 +86,17 @@ PGTDE::append_to_file($stdout); $stdout = $node->safe_psql('postgres', 'INSERT INTO test_wal (k) VALUES (5), (6);', extra_params => ['-a']); PGTDE::append_to_file($stdout); +PGTDE::append_to_file("-- server restart with still wal encryption"); +$node->stop(); +$rt_value = $node->start(); +ok($rt_value == 1, "Restart Server"); + +$stdout = $node->safe_psql('postgres', "SHOW pg_tde.wal_encrypt;", extra_params => ['-a']); +PGTDE::append_to_file($stdout); + +$stdout = $node->safe_psql('postgres', 'INSERT INTO test_wal (k) VALUES (7), (8);', extra_params => ['-a']); +PGTDE::append_to_file($stdout); + $stdout = $node->safe_psql('postgres', "SELECT data FROM pg_logical_slot_get_changes('tde_slot', NULL, NULL);", extra_params => ['-a']); PGTDE::append_to_file($stdout); diff --git a/contrib/pg_tde/t/expected/009_wal_encrypt.out b/contrib/pg_tde/t/expected/009_wal_encrypt.out index 64fb19fa21b..7d7c5726891 100644 --- a/contrib/pg_tde/t/expected/009_wal_encrypt.out +++ b/contrib/pg_tde/t/expected/009_wal_encrypt.out @@ -21,6 +21,10 @@ ALTER SYSTEM SET pg_tde.wal_encrypt = on; SHOW pg_tde.wal_encrypt; on INSERT INTO test_wal (k) VALUES (5), (6); +-- server restart with still wal encryption +SHOW pg_tde.wal_encrypt; +on +INSERT INTO test_wal (k) VALUES (7), (8); SELECT data FROM pg_logical_slot_get_changes('tde_slot', NULL, NULL); BEGIN 739 COMMIT 739 @@ -36,6 +40,10 @@ BEGIN 742 table public.test_wal: INSERT: id[integer]:5 k[integer]:5 table public.test_wal: INSERT: id[integer]:6 k[integer]:6 COMMIT 742 +BEGIN 743 +table public.test_wal: INSERT: id[integer]:7 k[integer]:7 +table public.test_wal: INSERT: id[integer]:8 k[integer]:8 +COMMIT 743 SELECT pg_drop_replication_slot('tde_slot'); DROP EXTENSION pg_tde;