mirror of
https://github.com/postgres/postgres.git
synced 2025-05-30 00:02:11 -04:00
PG-1510 Use a unique IV per relation fork
The security of the encryption is reduced if we reuse the same initiation vector more than necessary so we make sure to use a unique IV per relation fork, with the exception of the initialization fork which is used by unlogged indexes when restarting the server after a crash. It is copied with low-level file system functions to the main fork on crash recovery so it needs to use the same IV as the main fork. The init fork issue is in no way more a security issue than to the extent that ideally we should pick a new IV when truncating unlogged tables on crash recovery but to fix this we would need to change the SMGR API and moving the copying of the intialization fork into that. And in the long term this might be what we want to do.
This commit is contained in:
parent
93ef4510b4
commit
19bef897e6
@ -114,6 +114,7 @@ tap_tests = [
|
||||
't/008_key_rotate_tablespace.pl',
|
||||
't/009_wal_encrypt.pl',
|
||||
't/010_change_key_provider.pl',
|
||||
't/011_unlogged_tables.pl',
|
||||
]
|
||||
|
||||
tests += {
|
||||
|
@ -26,7 +26,7 @@ typedef struct TDESMgrRelationData
|
||||
|
||||
typedef TDESMgrRelationData *TDESMgrRelation;
|
||||
|
||||
static void CalcBlockIv(BlockNumber bn, const unsigned char *base_iv, unsigned char *iv);
|
||||
static void CalcBlockIv(ForkNumber forknum, BlockNumber bn, const unsigned char *base_iv, unsigned char *iv);
|
||||
|
||||
static InternalKey *
|
||||
tde_smgr_get_key(SMgrRelation reln, RelFileLocator *old_locator, bool can_create)
|
||||
@ -102,7 +102,7 @@ tde_mdwritev(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
|
||||
|
||||
local_buffers[i] = &local_blocks_aligned[i * BLCKSZ];
|
||||
|
||||
CalcBlockIv(bn, int_key->base_iv, iv);
|
||||
CalcBlockIv(forknum, bn, int_key->base_iv, iv);
|
||||
|
||||
AesEncrypt(int_key->key, iv, ((unsigned char **) buffers)[i], BLCKSZ, local_buffers[i]);
|
||||
}
|
||||
@ -134,7 +134,7 @@ tde_mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
|
||||
|
||||
AesInit();
|
||||
|
||||
CalcBlockIv(blocknum, int_key->base_iv, iv);
|
||||
CalcBlockIv(forknum, blocknum, int_key->base_iv, iv);
|
||||
|
||||
AesEncrypt(int_key->key, iv, ((unsigned char *) buffer), BLCKSZ, local_blocks_aligned);
|
||||
|
||||
@ -184,7 +184,7 @@ tde_mdreadv(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
|
||||
if (allZero)
|
||||
continue;
|
||||
|
||||
CalcBlockIv(bn, int_key->base_iv, iv);
|
||||
CalcBlockIv(forknum, bn, int_key->base_iv, iv);
|
||||
|
||||
AesDecrypt(int_key->key, iv, ((unsigned char **) buffers)[i], BLCKSZ, ((unsigned char **) buffers)[i]);
|
||||
}
|
||||
@ -297,13 +297,17 @@ RegisterStorageMgr(void)
|
||||
|
||||
/*
|
||||
* The intialization vector of a block is its block number conmverted to a
|
||||
* 128 bit big endian number XOR the base IV of the relation file.
|
||||
* 128 bit big endian number plus the forknumber XOR the base IV of the
|
||||
* relation file.
|
||||
*/
|
||||
static void
|
||||
CalcBlockIv(BlockNumber bn, const unsigned char *base_iv, unsigned char *iv)
|
||||
CalcBlockIv(ForkNumber forknum, BlockNumber bn, const unsigned char *base_iv, unsigned char *iv)
|
||||
{
|
||||
memset(iv, 0, 16);
|
||||
|
||||
/* The init fork is copied to the main fork so we must use the same IV */
|
||||
iv[7] = forknum == INIT_FORKNUM ? MAIN_FORKNUM : forknum;
|
||||
|
||||
iv[12] = bn >> 24;
|
||||
iv[13] = bn >> 16;
|
||||
iv[14] = bn >> 8;
|
||||
|
69
contrib/pg_tde/t/011_unlogged_tables.pl
Normal file
69
contrib/pg_tde/t/011_unlogged_tables.pl
Normal file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Basename;
|
||||
use File::Compare;
|
||||
use File::Copy;
|
||||
use Test::More;
|
||||
use lib 't';
|
||||
use pgtde;
|
||||
|
||||
# Get file name and CREATE out file name and dirs WHERE requried
|
||||
PGTDE::setup_files_dir(basename($0));
|
||||
|
||||
# CREATE new PostgreSQL node and do initdb
|
||||
my $node = PGTDE->pgtde_init_pg();
|
||||
my $pgdata = $node->data_dir;
|
||||
|
||||
# UPDATE postgresql.conf to include/load pg_tde library
|
||||
open my $conf, '>>', "$pgdata/postgresql.conf";
|
||||
print $conf "shared_preload_libraries = 'pg_tde'\n";
|
||||
close $conf;
|
||||
|
||||
# Start server
|
||||
my $rt_value = $node->start;
|
||||
ok($rt_value == 1, "Start Server");
|
||||
|
||||
my ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'CREATE EXTENSION IF NOT EXISTS pg_tde;', extra_params => ['-a']);
|
||||
ok($cmdret == 0, "CREATE PGTDE EXTENSION");
|
||||
PGTDE::append_to_file($stdout);
|
||||
$stdout = $node->safe_psql('postgres', "SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/unlogged_tables.per');", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
$stdout = $node->safe_psql('postgres', "SELECT pg_tde_set_key_using_database_key_provider('test-key', 'file-vault');", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
|
||||
$stdout = $node->safe_psql('postgres', "CREATE UNLOGGED TABLE t (x int PRIMARY KEY) USING tde_heap;", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
|
||||
$stdout = $node->safe_psql('postgres', "INSERT INTO t SELECT generate_series(1, 4);", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
|
||||
$stdout = $node->safe_psql('postgres', "CHECKPOINT;", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
|
||||
PGTDE::append_to_file("-- kill -9");
|
||||
$node->kill9();
|
||||
|
||||
# Start server
|
||||
PGTDE::append_to_file("-- server start");
|
||||
$rt_value = $node->start;
|
||||
ok($rt_value == 1, "Start Server");
|
||||
|
||||
$stdout = $node->safe_psql('postgres', "TABLE t;", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
|
||||
$stdout = $node->safe_psql('postgres', "INSERT INTO t SELECT generate_series(1, 4);", extra_params => ['-a']);
|
||||
PGTDE::append_to_file($stdout);
|
||||
|
||||
# Stop the server
|
||||
$node->stop();
|
||||
|
||||
# compare the expected and out file
|
||||
my $compare = PGTDE->compare_results();
|
||||
|
||||
# Test/check if expected and result/out file match. If Yes, test passes.
|
||||
is($compare, 0, "Compare Files: $PGTDE::expected_filename_with_path and $PGTDE::out_filename_with_path files.");
|
||||
|
||||
# Done testing for this testcase file.
|
||||
done_testing();
|
12
contrib/pg_tde/t/expected/011_unlogged_tables.out
Normal file
12
contrib/pg_tde/t/expected/011_unlogged_tables.out
Normal file
@ -0,0 +1,12 @@
|
||||
CREATE EXTENSION IF NOT EXISTS pg_tde;
|
||||
SELECT pg_tde_add_database_key_provider_file('file-vault', '/tmp/unlogged_tables.per');
|
||||
1
|
||||
SELECT pg_tde_set_key_using_database_key_provider('test-key', 'file-vault');
|
||||
|
||||
CREATE UNLOGGED TABLE t (x int PRIMARY KEY) USING tde_heap;
|
||||
INSERT INTO t SELECT generate_series(1, 4);
|
||||
CHECKPOINT;
|
||||
-- kill -9
|
||||
-- server start
|
||||
TABLE t;
|
||||
INSERT INTO t SELECT generate_series(1, 4);
|
Loading…
x
Reference in New Issue
Block a user