0002-Add-key-management-module-for-cluster-encryption.patch
application/octet-stream
Filename: 0002-Add-key-management-module-for-cluster-encryption.patch
Type: application/octet-stream
Part: 4
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0002
Subject: Add key management module for cluster encryption.
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 2 | 0 |
| src/backend/postmaster/pgstat.c | 9 | 0 |
| src/backend/postmaster/postmaster.c | 6 | 0 |
| src/backend/storage/encryption/kmgr.c | 517 | 0 |
| src/backend/storage/ipc/ipci.c | 3 | 0 |
| src/backend/tcop/postgres.c | 8 | 0 |
| src/bin/pg_checksums/pg_checksums.c | 1 | 0 |
| src/include/pgstat.h | 3 | 0 |
| src/include/storage/kmgr.h | 68 | 0 |
From 6f142dc888ea98cdb5d20833114ad754115be83b Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Thu, 15 Aug 2019 13:12:03 +0900
Subject: [PATCH 2/5] Add key management module for cluster encryption.
---
src/backend/access/transam/xlog.c | 2 +
src/backend/postmaster/pgstat.c | 9 +
src/backend/postmaster/postmaster.c | 6 +
src/backend/storage/encryption/kmgr.c | 517 ++++++++++++++++++++++++++++++++++
src/backend/storage/ipc/ipci.c | 3 +
src/backend/tcop/postgres.c | 8 +
src/bin/pg_checksums/pg_checksums.c | 1 +
src/include/pgstat.h | 3 +
src/include/storage/kmgr.h | 68 +++++
9 files changed, 617 insertions(+)
create mode 100644 src/backend/storage/encryption/kmgr.c
create mode 100644 src/include/storage/kmgr.h
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f553523..5cc2a40 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -57,6 +57,7 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/ipc.h"
+#include "storage/kmgr.h"
#include "storage/large_object.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
@@ -5264,6 +5265,7 @@ BootStrapXLOG(void)
BootStrapCommitTs();
BootStrapSUBTRANS();
BootStrapMultiXact();
+ BootStrapKmgr(bootstrap_data_encryption_cipher);
pfree(buffer);
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 2bb14cd..daf19e4 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3956,6 +3956,15 @@ pgstat_get_wait_io(WaitEventIO w)
case WAIT_EVENT_DSM_FILL_ZERO_WRITE:
event_name = "DSMFillZeroWrite";
break;
+ case WAIT_EVENT_KMGR_FILE_READ:
+ event_name = "KmgrFileRead";
+ break;
+ case WAIT_EVENT_KMGR_FILE_SYNC:
+ event_name = "KmgrFileSync";
+ break;
+ case WAIT_EVENT_KMGR_FILE_WRITE:
+ event_name = "KmgrFileWrite";
+ break;
case WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ:
event_name = "LockFileAddToDataDirRead";
break;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3339804..1aeda3c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -119,6 +119,7 @@
#include "replication/walsender.h"
#include "storage/fd.h"
#include "storage/ipc.h"
+#include "storage/kmgr.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@@ -1313,6 +1314,11 @@ PostmasterMain(int argc, char *argv[])
autovac_init();
/*
+ * Initialize cluster encryption key manager.
+ */
+ InitializeKmgr();
+
+ /*
* Load configuration files for client authentication.
*/
if (!load_hba())
diff --git a/src/backend/storage/encryption/kmgr.c b/src/backend/storage/encryption/kmgr.c
new file mode 100644
index 0000000..c44af9e
--- /dev/null
+++ b/src/backend/storage/encryption/kmgr.c
@@ -0,0 +1,517 @@
+/*-------------------------------------------------------------------------
+ *
+ * kmgr.c
+ * Encryption key management module.
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/storage/encryption/kmgr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <unistd.h>
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "access/xlog.h"
+#include "storage/encryption.h"
+#include "storage/fd.h"
+#include "storage/kmgr.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/inval.h"
+#include "utils/syscache.h"
+
+/* Kmgr file name */
+#define KMGR_FILENAME "global/pg_kmgr"
+
+/* Info for key derivation of TDEK and WDEK */
+#define TDE_TDEK_INFO "TDEK"
+#define TDE_WDEK_INFO "WDEK"
+
+typedef unsigned char keydata_t;
+
+/*
+ * Struct for key manager meta data written in KMGR_FILENAME. Kmgr file stores
+ * information that is used for KEK derivation, verification and wrapped MDEK.
+ * The file is written once when bootstrapping and is read when postmaster
+ * startup.
+ */
+typedef struct KmgrFileData
+{
+ /* salt used for KEK derivation */
+ keydata_t kek_salt[TDE_KEK_DEVIRATION_SALT_SIZE];
+
+ /* HMAC for KEK */
+ keydata_t kek_hmac[TDE_KEK_HMAC_SIZE];
+
+ /* MDEK in encrypted state, NULL-terminated */
+ keydata_t mdek[TDE_MDEK_WRAPPED_SIZE];
+
+ /* CRC of all above ... MUST BE LAST! */
+ pg_crc32c crc;
+} KmgrFileData;
+
+/*
+ * Shared memory struct for encryption key information. All keys are stored
+ * in non-encrypted state and never be modified during running. Therefore
+ * processes can use them without locking.
+ */
+typedef struct KmgrCtlData
+{
+ /* Master data encryption key */
+ keydata_t mdek[TDE_MDEK_SIZE];
+
+ /* Table data encryption key */
+ keydata_t tdek[ENC_MAX_ENCRYPTION_KEY_SIZE];
+
+ /* WAL data encryption key */
+ keydata_t wdek[ENC_MAX_ENCRYPTION_KEY_SIZE];
+} KmgrCtlData;
+static KmgrCtlData *KmgrCtl = NULL;
+
+/* GUC variable */
+char *cluster_passphrase_command = NULL;
+
+static int run_cluster_passphrase_command(const char *prompt,
+ char *buf, int size);
+static KmgrFileData *read_kmgr_file(void);
+static void write_kmgr_file(KmgrFileData *filedata);
+static void get_kek_and_hmackey_from_passphrase(char *passphrase, char passlen,
+ keydata_t salt[TDE_KEK_DEVIRATION_SALT_SIZE],
+ keydata_t kek[TDE_KEK_SIZE],
+ keydata_t hmackey[TDE_KEK_HMAC_KEY_SIZE]);
+static bool verify_passphrase(KmgrFileData *kmgfile,
+ char passphrase[TDE_MAX_PASSPHRASE_LEN],
+ int passlen, keydata_t kek[TDE_KEK_SIZE]);
+static void derive_encryption_key(char *id, keydata_t key[ENC_MAX_ENCRYPTION_KEY_SIZE]);
+
+/*
+ * This func must be called ONCE on system install. we derive KEK,
+ * generate MDEK and salt, compute hmac, write kmgr file etc.
+ */
+void
+BootStrapKmgr(int bootstrap_data_encryption_cipher)
+{
+ const char *prompt = "Enter database encryption pass phrase:";
+ KmgrFileData kmgrfile;
+ char passphrase[TDE_MAX_PASSPHRASE_LEN];
+ keydata_t kek_salt[TDE_KEK_DEVIRATION_SALT_SIZE];
+ keydata_t kek_hmac[TDE_KEK_HMAC_SIZE];
+ keydata_t mdek[TDE_MDEK_SIZE];
+ keydata_t encmdek[TDE_MDEK_WRAPPED_SIZE];
+ keydata_t kek[TDE_KEK_SIZE];
+ keydata_t hmackey[TDE_KEK_HMAC_KEY_SIZE];
+ int encmdek_size;
+ int len;
+ int ret;
+
+#ifndef USE_OPENSSL
+ ereport(ERROR,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ (errmsg("cluster encryption is not supported because OpenSSL is not supported by this build"),
+ errhint("Compile with --with-openssl to use cluster encryption."))));
+#endif
+
+ if (bootstrap_data_encryption_cipher == 0)
+ return;
+
+ /*
+ * Set data encryption cipher so that subsequent bootstrapping process
+ * can proceed.
+ */
+ SetConfigOption("data_encryption_cipher",
+ EncryptionCipherString(bootstrap_data_encryption_cipher),
+ PGC_INTERNAL, PGC_S_OVERRIDE);
+
+ /* Get encryption key passphrase */
+ len = run_cluster_passphrase_command(prompt,
+ passphrase,
+ TDE_MAX_PASSPHRASE_LEN);
+
+ /* Generate salt for KEK derivation */
+ ret = pg_strong_random(kek_salt, TDE_KEK_DEVIRATION_SALT_SIZE);
+ if (!ret)
+ ereport(ERROR,
+ (errmsg("failed to generate random salt for key encryption key")));
+
+ /* Get KEK and HMAC key */
+ get_kek_and_hmackey_from_passphrase(passphrase, len, kek_salt, kek, hmackey);
+
+ /* Generate salt for KEK */
+ ret = pg_strong_random(mdek, TDE_MDEK_SIZE);
+ if (!ret)
+ ereport(ERROR,
+ (errmsg("failed to generate the master encryption key")));
+
+ /* Encrypt MDEK with KEK */
+ pg_wrap_key(kek, TDE_KEK_SIZE, mdek, TDE_MDEK_SIZE, encmdek, &encmdek_size);
+
+ if (encmdek_size != TDE_MDEK_WRAPPED_SIZE)
+ elog(ERROR, "wrapped MDEK key size is invalid, got %d expected %d",
+ encmdek_size, TDE_MDEK_WRAPPED_SIZE);
+
+ /* Compute HMAC */
+ pg_compute_hmac(hmackey, TDE_KEK_HMAC_SIZE, encmdek, TDE_MDEK_WRAPPED_SIZE,
+ kek_hmac);
+
+ /* Fill out the kmgr file contents */
+ memcpy(kmgrfile.kek_salt, kek_salt, TDE_KEK_DEVIRATION_SALT_SIZE);
+ memcpy(kmgrfile.kek_hmac, kek_hmac, TDE_KEK_HMAC_SIZE);
+ memcpy(kmgrfile.mdek, encmdek, TDE_MDEK_WRAPPED_SIZE);
+
+ /* write kmgr file to the disk */
+ write_kmgr_file(&kmgrfile);
+
+ /* Set keys */
+ memcpy(KmgrCtl->mdek, mdek, TDE_MDEK_SIZE);
+ derive_encryption_key(TDE_TDEK_INFO, KmgrCtl->tdek);
+ derive_encryption_key(TDE_WDEK_INFO, KmgrCtl->wdek);
+}
+
+Size
+KmgrShmemSize(void)
+{
+ return sizeof(KmgrCtlData);
+}
+
+void
+KmgrShmemInit(void)
+{
+ bool found;
+
+ KmgrCtl = (KmgrCtlData *) ShmemInitStruct("KeyManagementData",
+ KmgrShmemSize(), &found);
+
+ if (!found)
+ MemSet(KmgrCtl, 0, KmgrShmemSize());
+}
+
+/*
+ * Run cluster_passphrase_command
+ *
+ * prompt will be substituted for %p.
+ *
+ * The result will be put in buffer buf, which is of size size. The return
+ * value is the length of the actual result.
+ */
+static int
+run_cluster_passphrase_command(const char *prompt, char *buf, int size)
+{
+ StringInfoData command;
+ char *p;
+ FILE *fh;
+ int pclose_rc;
+ size_t len = 0;
+
+ Assert(prompt);
+ Assert(size > 0);
+ buf[0] = '\0';
+
+ initStringInfo(&command);
+
+ for (p = cluster_passphrase_command; *p; p++)
+ {
+ if (p[0] == '%')
+ {
+ switch (p[1])
+ {
+ case 'p':
+ appendStringInfoString(&command, prompt);
+ p++;
+ break;
+ case '%':
+ appendStringInfoChar(&command, '%');
+ p++;
+ break;
+ default:
+ appendStringInfoChar(&command, p[0]);
+ }
+ }
+ else
+ appendStringInfoChar(&command, p[0]);
+ }
+
+ fh = OpenPipeStream(command.data, "r");
+ if (fh == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not execute command \"%s\": %m",
+ command.data)));
+
+ if (!fgets(buf, size, fh))
+ {
+ if (ferror(fh))
+ {
+ pfree(command.data);
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read from command \"%s\": %m",
+ command.data)));
+ }
+ }
+
+ pclose_rc = ClosePipeStream(fh);
+ if (pclose_rc == -1)
+ {
+ pfree(command.data);
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close pipe to external command: %m")));
+ }
+ else if (pclose_rc != 0)
+ {
+ pfree(command.data);
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("command \"%s\" failed",
+ command.data),
+ errdetail_internal("%s", wait_result_to_str(pclose_rc))));
+ }
+
+ /* strip trailing newline */
+ len = strlen(buf);
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[--len] = '\0';
+
+ pfree(command.data);
+
+ return len;
+}
+
+/*
+ * Get encryption key passphrase and verify it, then get the un-encrypted
+ * MDEK. This function is called by postmaster at startup time.
+ */
+void
+InitializeKmgr(void)
+{
+ const char *prompt = "Enter database encryption pass phrase:";
+ char passphrase[TDE_MAX_PASSPHRASE_LEN];
+ keydata_t kek[TDE_KEK_SIZE];
+ keydata_t mdek[TDE_MDEK_SIZE];
+ KmgrFileData *kmgrfile;
+ int len;
+ int mdek_size;
+
+ if (!DataEncryptionEnabled())
+ return;
+
+ /* Get contents of kmgr file */
+ kmgrfile = read_kmgr_file();
+
+ /* Get encryption key passphrase */
+ len = run_cluster_passphrase_command(prompt,
+ passphrase,
+ TDE_MAX_PASSPHRASE_LEN);
+
+ /* Verify the given passphrase */
+ if (!verify_passphrase(kmgrfile, passphrase, len, kek))
+ ereport(ERROR,
+ (errmsg("cluster passphrase does not match expected passphrase")));
+
+ /* Unwrap MDEK with KEK */
+ pg_unwrap_key(kek, TDE_KEK_SIZE, kmgrfile->mdek, TDE_MDEK_WRAPPED_SIZE,
+ mdek, &mdek_size);
+
+ if (mdek_size != TDE_MDEK_SIZE)
+ elog(ERROR, "unwrapped MDEK key size is invalid, got %d expected %d",
+ mdek_size, TDE_MDEK_SIZE);
+
+ /* Set MDEK, TDEK and WDEK to the shared memory struct */
+ memcpy(KmgrCtl->mdek, mdek, TDE_MDEK_SIZE);
+ derive_encryption_key(TDE_TDEK_INFO, KmgrCtl->tdek);
+ derive_encryption_key(TDE_WDEK_INFO, KmgrCtl->wdek);
+}
+
+/*
+ * Read kmgr file, and return palloc'd file data.
+ */
+static KmgrFileData *
+read_kmgr_file(void)
+{
+ KmgrFileData *kmgrfile;
+ pg_crc32c crc;
+ int read_len;
+ int fd;
+
+ fd = BasicOpenFile(KMGR_FILENAME, O_RDONLY | PG_BINARY);
+
+ if (fd < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\": %m", KMGR_FILENAME)));
+
+ /* Read data */
+ kmgrfile = (KmgrFileData *) palloc(sizeof(KmgrFileData));
+
+ pgstat_report_wait_start(WAIT_EVENT_KMGR_FILE_READ);
+ if ((read_len = read(fd, kmgrfile, sizeof(KmgrFileData))) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ (errmsg("could not read from file \"%s\": %m", KMGR_FILENAME))));
+ pgstat_report_wait_end();
+
+ if (close(fd))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close file \"%s\": %m", KMGR_FILENAME)));
+
+ /* Verify CRC */
+ INIT_CRC32C(crc);
+ COMP_CRC32C(crc, (char *) kmgrfile, offsetof(KmgrFileData, crc));
+ FIN_CRC32C(crc);
+
+ if (!EQ_CRC32C(crc, kmgrfile->crc))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg("calculated CRC checksum does not match value stored in file \"%s\"",
+ KMGR_FILENAME)));
+
+ return kmgrfile;
+}
+
+/*
+ * Write kmgr file. This function is used only when bootstrapping.
+ */
+static void
+write_kmgr_file(KmgrFileData *filedata)
+{
+ int fd;
+
+ INIT_CRC32C(filedata->crc);
+ COMP_CRC32C(filedata->crc, filedata, offsetof(KmgrFileData, crc));
+ FIN_CRC32C(filedata->crc);
+
+ fd = BasicOpenFile(KMGR_FILENAME, PG_BINARY | O_CREAT | O_RDWR);
+
+ if (fd < 0)
+ {
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\": %m",
+ KMGR_FILENAME)));
+ return;
+ }
+
+ pgstat_report_wait_start(WAIT_EVENT_KMGR_FILE_WRITE);
+ if (write(fd, filedata, sizeof(KmgrFileData)) != sizeof(KmgrFileData))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write kmgr file \"%s\": %m",
+ KMGR_FILENAME)));
+ pgstat_report_wait_end();
+
+ pgstat_report_wait_start(WAIT_EVENT_KMGR_FILE_SYNC);
+ if (pg_fsync(fd) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ (errmsg("could not sync file \"%s\": %m",
+ KMGR_FILENAME))));
+ pgstat_report_wait_end();
+
+ if (close(fd))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close file \"%s\": %m", KMGR_FILENAME)));
+
+}
+
+/*
+ * Derive key from passphrase and extract KEK and HMAC key from the derived key.
+ */
+static void
+get_kek_and_hmackey_from_passphrase(char *passphrase, char passlen,
+ keydata_t salt[TDE_KEK_DEVIRATION_SALT_SIZE],
+ keydata_t kek[TDE_KEK_SIZE],
+ keydata_t hmackey[TDE_KEK_HMAC_KEY_SIZE])
+{
+ keydata_t enckey_and_hmackey[TDE_KEK_DERIVED_KEY_SIZE];
+
+ /* Derive key from passphrase, or error */
+ pg_derive_key_passphrase(passphrase, passlen,
+ salt, TDE_KEK_DEVIRATION_SALT_SIZE,
+ TDE_KEK_DEVIRATION_ITER_COUNT,
+ TDE_KEK_SIZE + TDE_KEK_HMAC_KEY_SIZE,
+ enckey_and_hmackey);
+
+ /* Extract KEK and HMAC key from the derived key */
+ memcpy(kek, enckey_and_hmackey, TDE_KEK_SIZE);
+ memcpy(hmackey, enckey_and_hmackey + TDE_KEK_SIZE, TDE_KEK_HMAC_KEY_SIZE);
+}
+
+/*
+ * Verify correctness of the given passphrase. We compute HMAC of encrypted
+ * MDEK written in the kmgr file, and compare it to the HMAC written in the kmgr
+ * file. Return true and set KEK if the passphrase is verified, otherwise return
+ * false.
+ */
+static bool
+verify_passphrase(KmgrFileData *kmgrfile, char passphrase[TDE_MAX_PASSPHRASE_LEN],
+ int passlen, keydata_t kek[TDE_KEK_SIZE])
+{
+ keydata_t hmac[TDE_KEK_HMAC_SIZE];
+ keydata_t kek_tmp[TDE_KEK_SIZE];
+ keydata_t hmackey_tmp[TDE_KEK_HMAC_KEY_SIZE];
+
+ get_kek_and_hmackey_from_passphrase(passphrase, passlen,
+ kmgrfile->kek_salt, kek_tmp,
+ hmackey_tmp);
+
+ /*
+ * Compute HMAC of encrypted MDEK in kmgr file with the HMAC key derived
+ * from passphrase.
+ */
+ pg_compute_hmac(hmackey_tmp, TDE_KEK_HMAC_KEY_SIZE, kmgrfile->mdek,
+ TDE_MDEK_WRAPPED_SIZE, hmac);
+
+ /* Compare two HMACs */
+ if (memcmp(kmgrfile->kek_hmac, hmac, TDE_KEK_HMAC_SIZE) != 0)
+ return false;
+
+ /* user-provided passphrase is correct, set KEK */
+ memcpy(kek, kek_tmp, TDE_KEK_SIZE);
+
+ return true;
+}
+
+/* Return table data encryption key (TDEK) */
+const char *
+GetTableEncryptionKey(void)
+{
+ Assert(KmgrCtl != NULL);
+
+ return (char *) KmgrCtl->tdek;
+}
+
+/* Return WAL data encryption key (WDEK) */
+const char *
+GetWALEncryptionKey(void)
+{
+ Assert(KmgrCtl != NULL);
+
+ return (const char *) KmgrCtl->wdek;
+}
+
+/*
+ * Derive new encryption key with info from MDEK.
+ */
+static void
+derive_encryption_key(char *info, keydata_t key[ENC_MAX_ENCRYPTION_KEY_SIZE])
+{
+ char key_len = EncryptionKeySize;
+
+ /* derive new TDEK that length is EncryptionKeySize from MDEK */
+ pg_derive_key(KmgrCtl->mdek, TDE_MDEK_SIZE, (keydata_t *) info,
+ key, key_len);
+}
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index d7d7335..f8b3e76 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -36,6 +36,7 @@
#include "storage/bufmgr.h"
#include "storage/dsm.h"
#include "storage/ipc.h"
+#include "storage/kmgr.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "storage/predicate.h"
@@ -147,6 +148,7 @@ CreateSharedMemoryAndSemaphores(int port)
size = add_size(size, BTreeShmemSize());
size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize());
+ size = add_size(size, KmgrShmemSize());
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
#endif
@@ -263,6 +265,7 @@ CreateSharedMemoryAndSemaphores(int port)
BTreeShmemInit();
SyncScanShmemInit();
AsyncShmemInit();
+ KmgrShmemInit();
#ifdef EXEC_BACKEND
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e8d8e6f..b225918 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -63,6 +63,7 @@
#include "replication/walsender.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
+#include "storage/kmgr.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
@@ -3859,6 +3860,13 @@ PostgresMain(int argc, char *argv[],
BaseInit();
/*
+ * Initialize kmgr for cluster encryption. Since kmgr needs to attach to
+ * shared memory the initialization must be called after BaseInit().
+ */
+ if (!IsUnderPostmaster)
+ InitializeKmgr();
+
+ /*
* Create a per-backend PGPROC struct in shared memory, except in the
* EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
* this before we can use LWLocks (and in the EXEC_BACKEND case we already
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 8c00ec9..4d311ed 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -100,6 +100,7 @@ static const char *const skip[] = {
"pg_control",
"pg_filenode.map",
"pg_internal.init",
+ "pg_kmgr",
"PG_VERSION",
#ifdef EXEC_BACKEND
"config_exec_params",
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0a3ad3a..8ec5ecf 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -896,6 +896,9 @@ typedef enum
WAIT_EVENT_DATA_FILE_TRUNCATE,
WAIT_EVENT_DATA_FILE_WRITE,
WAIT_EVENT_DSM_FILL_ZERO_WRITE,
+ WAIT_EVENT_KMGR_FILE_READ,
+ WAIT_EVENT_KMGR_FILE_SYNC,
+ WAIT_EVENT_KMGR_FILE_WRITE,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_SYNC,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_WRITE,
diff --git a/src/include/storage/kmgr.h b/src/include/storage/kmgr.h
new file mode 100644
index 0000000..b62a0ae
--- /dev/null
+++ b/src/include/storage/kmgr.h
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * kmgr.h
+ * Key management module for transparent data encryption
+ *
+ * Portions Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * src/include/storage/kmgr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef KMGR_H
+#define KMGR_H
+
+#include "storage/relfilenode.h"
+#include "storage/bufpage.h"
+
+/* Size of key encryption key (KEK), which is always AES-256 key */
+#define TDE_KEK_SIZE 32
+
+/*
+ * Size of HMAC key for KEK is the same as the length of hash, we use
+ * SHA-256.
+ */
+#define TDE_KEK_HMAC_KEY_SIZE 32
+
+/* SHA-256 results 256 bits HMAC */
+#define TDE_KEK_HMAC_SIZE 32
+
+/*
+ * Size of the derived key from passphrase. It consists of KEK and HMAC key.
+ */
+#define TDE_KEK_DERIVED_KEY_SIZE (TDE_KEK_SIZE + TDE_KEK_HMAC_KEY_SIZE)
+
+/*
+ * Iteration count of password based key derivation. NIST recommends that
+ * minimum iteration count is 1000.
+ */
+#define TDE_KEK_DEVIRATION_ITER_COUNT 100000
+
+/*
+ * Size of salt for password based key derivation. NIST recommended that
+ * salt size is at least 16
+ */
+#define TDE_KEK_DEVIRATION_SALT_SIZE 64
+
+/* Key wrapping appends the initial 64 bit value */
+#define TDE_KEY_WRAP_VALUE_SIZE 8
+
+/* Size of master data encryption key (MDEK), which is always AES-256 key */
+#define TDE_MDEK_SIZE 32
+
+/* Wrapped key size is n+1 value */
+#define TDE_MDEK_WRAPPED_SIZE (TDE_MDEK_SIZE + TDE_KEY_WRAP_VALUE_SIZE)
+
+#define TDE_MAX_PASSPHRASE_LEN MAXPGPATH
+
+/* GUC variable */
+extern char *cluster_passphrase_command;
+
+extern void BootStrapKmgr(int bootstrap_data_encryption_cipher);
+extern Size KmgrShmemSize(void);
+extern void KmgrShmemInit(void);
+extern void InitializeKmgr(void);
+extern const char *GetTableEncryptionKey(void);
+extern const char *GetWALEncryptionKey(void);
+
+#endif /* KMGR_H */
--
2.10.5