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

