kms_v2.patch

application/x-patch

Filename: kms_v2.patch
Type: application/x-patch
Part: 0
Message: Re: [Proposal] Table-level Transparent Data Encryption (TDE) and Key Management Service (KMS)

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: unified
Series: patch v2
File+
configure 1 1
configure.in 1 1
src/backend/access/transam/xlog.c 31 0
src/backend/bootstrap/bootstrap.c 9 1
src/backend/crypto/kmgr.c 272 0
src/backend/crypto/Makefile 17 0
src/backend/Makefile 1 1
src/backend/postmaster/pgstat.c 9 0
src/backend/postmaster/postmaster.c 6 0
src/backend/tcop/postgres.c 8 0
src/backend/utils/misc/guc.c 35 1
src/backend/utils/misc/postgresql.conf.sample 5 0
src/bin/initdb/initdb.c 74 3
src/bin/pg_checksums/pg_checksums.c 1 0
src/bin/pg_controldata/pg_controldata.c 3 1
src/common/cipher.c 95 0
src/common/cipher_openssl.c 99 0
src/common/kmgr_utils.c 354 0
src/common/Makefile 3 0
src/include/access/xlog.h 3 0
src/include/catalog/pg_control.h 9 0
src/include/catalog/pg_proc.dat 13 0
src/include/common/cipher.h 70 0
src/include/common/cipher_openssl.h 39 0
src/include/common/kmgr_utils.h 64 0
src/include/crypto/kmgr.h 44 0
src/include/pg_config.h.in 3 0
src/include/pgstat.h 3 0
src/include/utils/guc_tables.h 1 0
diff --git a/configure b/configure
index 702adba839..2daa5bf0c2 100755
--- a/configure
+++ b/configure
@@ -12113,7 +12113,7 @@ done
   # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
   # doesn't have these OpenSSL 1.1.0 functions. So check for individual
   # functions.
-  for ac_func in OPENSSL_init_ssl BIO_get_data BIO_meth_new ASN1_STRING_get0_data
+  for ac_func in OPENSSL_init_ssl OPENSSL_init_crypto BIO_get_data BIO_meth_new ASN1_STRING_get0_data
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.in
index 8165f70039..79329d9f15 100644
--- a/configure.in
+++ b/configure.in
@@ -1194,7 +1194,7 @@ if test "$with_openssl" = yes ; then
   # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
   # doesn't have these OpenSSL 1.1.0 functions. So check for individual
   # functions.
-  AC_CHECK_FUNCS([OPENSSL_init_ssl BIO_get_data BIO_meth_new ASN1_STRING_get0_data])
+  AC_CHECK_FUNCS([OPENSSL_init_ssl OPENSSL_init_crypto BIO_get_data BIO_meth_new ASN1_STRING_get0_data])
   # OpenSSL versions before 1.1.0 required setting callback functions, for
   # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
   # function was removed.
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 9706a95848..4ace302038 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -21,7 +21,7 @@ SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
 	main nodes optimizer partitioning port postmaster \
 	regex replication rewrite \
 	statistics storage tcop tsearch utils $(top_builddir)/src/timezone \
-	jit
+	jit crypto
 
 include $(srcdir)/common.mk
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7f4f784c0e..5293137225 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -41,6 +41,7 @@
 #include "catalog/pg_database.h"
 #include "commands/tablespace.h"
 #include "common/controldata_utils.h"
+#include "crypto/kmgr.h"
 #include "miscadmin.h"
 #include "pg_trace.h"
 #include "pgstat.h"
@@ -77,6 +78,7 @@
 #include "utils/timestamp.h"
 
 extern uint32 bootstrap_data_checksum_version;
+extern uint32 bootstrap_data_encryption_cipher;
 
 /* Unsupported old recovery command file names (relative to $PGDATA) */
 #define RECOVERY_COMMAND_FILE	"recovery.conf"
@@ -4779,6 +4781,10 @@ ReadControlFile(void)
 	/* Make the initdb settings visible as GUC variables, too */
 	SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no",
 					PGC_INTERNAL, PGC_S_OVERRIDE);
+
+	SetConfigOption("data_encryption_cipher",
+					kmgr_cipher_string(GetDataEncryptionCipher()),
+					PGC_INTERNAL, PGC_S_OVERRIDE);
 }
 
 /*
@@ -4811,6 +4817,13 @@ GetMockAuthenticationNonce(void)
 	return ControlFile->mock_authentication_nonce;
 }
 
+WrappedEncKeyWithHmac *
+GetMasterEncryptionKey(void)
+{
+	Assert(ControlFile != NULL);
+	return &(ControlFile->master_dek);
+}
+
 /*
  * Are checksums enabled for data pages?
  */
@@ -4821,6 +4834,13 @@ DataChecksumsEnabled(void)
 	return (ControlFile->data_checksum_version > 0);
 }
 
+int
+GetDataEncryptionCipher(void)
+{
+	Assert(ControlFile != NULL);
+	return ControlFile->data_encryption_cipher;
+}
+
 /*
  * Returns a fake LSN for unlogged relations.
  *
@@ -5087,6 +5107,7 @@ BootStrapXLOG(void)
 	XLogPageHeader page;
 	XLogLongPageHeader longpage;
 	XLogRecord *record;
+	WrappedEncKeyWithHmac *masterkey;
 	char	   *recptr;
 	bool		use_existent;
 	uint64		sysidentifier;
@@ -5164,6 +5185,12 @@ BootStrapXLOG(void)
 	SetMultiXactIdLimit(checkPoint.oldestMulti, checkPoint.oldestMultiDB, true);
 	SetCommitTsLimit(InvalidTransactionId, InvalidTransactionId);
 
+	/*
+	 * Bootstrap key management module beforehand in order to encrypt the first
+	 * xlog record.
+	 */
+	masterkey = BootStrapKmgr(bootstrap_data_encryption_cipher);
+
 	/* Set up the XLOG page header */
 	page->xlp_magic = XLOG_PAGE_MAGIC;
 	page->xlp_info = XLP_LONG_HEADER;
@@ -5239,6 +5266,9 @@ BootStrapXLOG(void)
 	ControlFile->checkPoint = checkPoint.redo;
 	ControlFile->checkPointCopy = checkPoint;
 	ControlFile->unloggedLSN = FirstNormalUnloggedLSN;
+	if (masterkey)
+		memcpy(&(ControlFile->master_dek), masterkey,
+			   sizeof(WrappedEncKeyWithHmac));
 
 	/* Set important parameter values for use when replaying WAL */
 	ControlFile->MaxConnections = MaxConnections;
@@ -5250,6 +5280,7 @@ BootStrapXLOG(void)
 	ControlFile->wal_log_hints = wal_log_hints;
 	ControlFile->track_commit_timestamp = track_commit_timestamp;
 	ControlFile->data_checksum_version = bootstrap_data_checksum_version;
+	ControlFile->data_encryption_cipher = bootstrap_data_encryption_cipher;
 
 	/* some additional ControlFile fields are set in WriteControlFile() */
 
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index bfc629c753..95751a0c4e 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -28,6 +28,7 @@
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/link-canary.h"
+#include "crypto/kmgr.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -52,6 +53,9 @@
 
 uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
 
+/* No encryption */
+uint32		bootstrap_data_encryption_cipher = KMGR_ENCRYPTION_OFF;
+
 
 #define ALLOC(t, c) \
 	((t *) MemoryContextAllocZero(TopMemoryContext, (unsigned)(c) * sizeof(t)))
@@ -226,7 +230,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
 	/* If no -x argument, we are a CheckerProcess */
 	MyAuxProcType = CheckerProcess;
 
-	while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:X:-:")) != -1)
+	while ((flag = getopt(argc, argv, "B:c:d:D:e:Fkr:x:X:-:")) != -1)
 	{
 		switch (flag)
 		{
@@ -249,6 +253,10 @@ AuxiliaryProcessMain(int argc, char *argv[])
 					pfree(debugstr);
 				}
 				break;
+
+			case 'e':
+				bootstrap_data_encryption_cipher = kmgr_cipher_value(optarg);
+				break;
 			case 'F':
 				SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV);
 				break;
diff --git a/src/backend/crypto/Makefile b/src/backend/crypto/Makefile
new file mode 100644
index 0000000000..d7bfa17b39
--- /dev/null
+++ b/src/backend/crypto/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for cryptographic
+#
+# IDENTIFICATION
+#    src/backend/crypto/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/crypto
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = kmgr.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c
new file mode 100644
index 0000000000..697c387864
--- /dev/null
+++ b/src/backend/crypto/kmgr.c
@@ -0,0 +1,272 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 "common/sha2.h"
+#include "common/kmgr_utils.h"
+#include "crypto/kmgr.h"
+#include "storage/fd.h"
+#include "storage/shmem.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+
+/*
+ * Key encryption key.  This key is derived from the passphrase provided
+ * by user when startup.  This variable is set during verification
+ * of user given passphrase. After verification, the plain key data
+ * is set to this variable.
+ */
+static uint8 keyEncKey[KMGR_KEK_LEN];
+
+/*
+ * Mater encryption key.  Similar to key encryption key, this
+ * store the plain key data.
+ */
+static uint8 masterEncKey[KMGR_MAX_DEK_LEN];
+
+/* GUC variable */
+char *cluster_passphrase_command = NULL;
+
+int data_encryption_cipher;
+int EncryptionKeyLen;
+
+/*
+ * This function must be called ONCE on system install. We retrieve the KEK,
+ * generate the master key.
+ */
+WrappedEncKeyWithHmac *
+BootStrapKmgr(int bootstrap_data_encryption_cipher)
+{
+	WrappedEncKeyWithHmac *ret_mk;
+	char passphrase[KMGR_MAX_PASSPHRASE_LEN];
+	uint8 hmackey[KMGR_HMAC_KEY_LEN];
+	int	passlen;
+
+	if (bootstrap_data_encryption_cipher == KMGR_ENCRYPTION_OFF)
+		return NULL;
+
+#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
+
+	ret_mk = palloc0(sizeof(WrappedEncKeyWithHmac));
+
+	/*
+	 * Set data encryption cipher so that subsequent bootstrapping process
+	 * can proceed.
+	 */
+	SetConfigOption("data_encryption_cipher",
+					kmgr_cipher_string(bootstrap_data_encryption_cipher),
+					PGC_INTERNAL, PGC_S_OVERRIDE);
+
+	/* Get key encryption key from command */
+	passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+												  passphrase, KMGR_MAX_PASSPHRASE_LEN);
+	if (passlen < KMGR_MIN_PASSPHRASE_LEN)
+		ereport(ERROR,
+				(errmsg("passphrase must be more than %d bytes",
+						KMGR_MIN_PASSPHRASE_LEN)));
+
+	/* Get key encryption key and HMAC key from passphrase */
+	kmgr_derive_keys(passphrase, passlen, keyEncKey, hmackey);
+
+	/* Generate the master encryption key */
+	if (!pg_strong_random(masterEncKey, EncryptionKeyLen))
+		ereport(ERROR,
+				(errmsg("failed to generate cluster encryption key")));
+
+	/* Wrap the master key by KEK */
+	kmgr_wrap_key(keyEncKey, masterEncKey, EncryptionKeyLen, ret_mk->key);
+
+	/* Compute HMAC of the master key */
+	kmgr_compute_HMAC(hmackey, ret_mk->key, SizeOfWrappedDEK(),
+					  ret_mk->hmac);
+
+	/* return keys and HMACs generated during bootstrap */
+	return ret_mk;
+}
+
+/*
+ * Get encryption key passphrase and verify it, then get the un-wrapped
+ * master encryption key. This function is called by postmaster at startup time.
+ */
+void
+InitializeKmgr(void)
+{
+	WrappedEncKeyWithHmac *wrapped_mk;
+	char	passphrase[KMGR_MAX_PASSPHRASE_LEN];
+	int		passlen;
+
+	if (!DataEncryptionEnabled())
+		return;
+
+	/* Get cluster passphrase */
+	passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+												  passphrase, KMGR_MAX_PASSPHRASE_LEN);
+
+	/* Get two wrapped keys stored in control file */
+	wrapped_mk = GetMasterEncryptionKey();
+
+	/* Verify the correctness of given passphrase */
+	if (!kmgr_verify_passphrase(passphrase, passlen, wrapped_mk,
+								SizeOfWrappedDEK()))
+		ereport(ERROR,
+				(errmsg("cluster passphrase does not match expected passphrase")));
+
+	kmgr_derive_keys(passphrase, passlen, keyEncKey, NULL);
+
+	/* The passphrase is correct, unwrap the master key */
+	kmgr_unwrap_key(keyEncKey, wrapped_mk->key, SizeOfWrappedDEK(),
+					masterEncKey);
+}
+
+/* Return plain cluster encryption key */
+const char *
+KmgrGetMasterEncryptionKey(void)
+{
+	return DataEncryptionEnabled() ?
+		pstrdup((const char *) masterEncKey) : NULL;
+}
+
+extern
+void assign_data_encryption_cipher(int new_encryption_cipher,
+								   void *extra)
+{
+	switch (new_encryption_cipher)
+	{
+		case KMGR_ENCRYPTION_OFF :
+			EncryptionKeyLen = 0;
+			break;
+		case KMGR_ENCRYPTION_AES128:
+			EncryptionKeyLen = 16;
+			break;
+		case KMGR_ENCRYPTION_AES256:
+			EncryptionKeyLen = 32;
+			break;
+	}
+}
+
+/*
+ * SQL function to rotate the cluster encryption key. This function
+ * assumes that the cluster_passphrase_command is already reloaded
+ * to the new value.
+ */
+Datum
+pg_rotate_encryption_key(PG_FUNCTION_ARGS)
+{
+	WrappedEncKeyWithHmac new_masterkey;
+	WrappedEncKeyWithHmac *cur_masterkey;
+	char    passphrase[KMGR_MAX_PASSPHRASE_LEN];
+	uint8   new_kek[KMGR_KEK_LEN];
+	uint8   new_hmackey[KMGR_HMAC_KEY_LEN];
+	int     passlen;
+
+	passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+												  passphrase,
+												  KMGR_MAX_PASSPHRASE_LEN);
+	if (passlen < KMGR_MIN_PASSPHRASE_LEN)
+		ereport(ERROR,
+				(errmsg("passphrase must be more than %d bytes",
+						KMGR_MIN_PASSPHRASE_LEN)));
+
+	kmgr_derive_keys(passphrase, passlen, new_kek, new_hmackey);
+
+	/* Copy the current master encryption key */
+	memcpy(&(new_masterkey.key), masterEncKey, EncryptionKeyLen);
+
+	/*
+	 * Wrap and compute HMAC of the master key by the new key
+	 * encryption key.
+	 */
+	kmgr_wrap_key(new_kek, new_masterkey.key, EncryptionKeyLen,
+				  new_masterkey.key);
+	kmgr_compute_HMAC(new_hmackey, new_masterkey.key,
+					  SizeOfWrappedDEK(), new_masterkey.hmac);
+
+	/* Update control file */
+	LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
+	cur_masterkey = GetMasterEncryptionKey();
+	memcpy(cur_masterkey, &new_masterkey, sizeof(WrappedEncKeyWithHmac));
+	UpdateControlFile();
+	LWLockRelease(ControlFileLock);
+
+	PG_RETURN_BOOL(true);
+}
+
+Datum
+pg_kmgr_wrap(PG_FUNCTION_ARGS)
+{
+	bytea	*data = PG_GETARG_BYTEA_PP(0);
+	bytea	*res;
+	unsigned datalen;
+	unsigned reslen;
+	bool ret;
+
+	datalen = VARSIZE_ANY_EXHDR(data);
+
+	if (datalen % 8 != 0)
+		ereport(ERROR,
+				(errmsg("input data must be multiple of 8 bytes")));
+
+	reslen = VARHDRSZ + datalen + AES256_KEY_WRAP_VALUE_LEN;
+	res = palloc(reslen);
+
+	ret = kmgr_wrap_key(keyEncKey, (uint8 *) VARDATA_ANY(data), datalen,
+						(uint8 *) VARDATA(res));
+	if (!ret)
+		ereport(ERROR,
+				(errmsg("could not wrap the given secret")));
+
+	SET_VARSIZE(res, reslen);
+	PG_RETURN_BYTEA_P(res);
+}
+
+Datum
+pg_kmgr_unwrap(PG_FUNCTION_ARGS)
+{
+	bytea	*data = PG_GETARG_BYTEA_PP(0);
+	bytea	*res;
+	unsigned datalen;
+	unsigned reslen;
+	bool ret;
+
+	datalen = VARSIZE_ANY_EXHDR(data);
+
+	if (datalen % 8 != 0)
+		ereport(ERROR,
+				(errmsg("input data must be multiple of 8 bytes")));
+
+	reslen = VARHDRSZ + datalen - AES256_KEY_WRAP_VALUE_LEN;
+	res = palloc(reslen);
+
+	ret = kmgr_unwrap_key(keyEncKey, (uint8 *) VARDATA_ANY(data), datalen,
+						  (uint8 *) VARDATA(res));
+	if (!ret)
+		ereport(ERROR,
+				(errmsg("could not unwrap the given secret")));
+
+	SET_VARSIZE(res, reslen);
+	PG_RETURN_BYTEA_P(res);
+}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 51c486bebd..03d832aad0 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3949,6 +3949,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 7a92dac525..c67f18132e 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -100,6 +100,7 @@
 #include "common/file_perm.h"
 #include "common/ip.h"
 #include "common/string.h"
+#include "crypto/kmgr.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
 #include "libpq/libpq.h"
@@ -1335,6 +1336,11 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	autovac_init();
 
+	/*
+	 * Initialize cluster encryption key manager.
+	 */
+	InitializeKmgr();
+
 	/*
 	 * Load configuration files for client authentication.
 	 */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..4ff81743fc 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "crypto/kmgr.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -3883,6 +3884,13 @@ PostgresMain(int argc, char *argv[],
 	/* Early initialization */
 	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
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9f179a9129..7048ac87db 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -43,6 +43,7 @@
 #include "commands/vacuum.h"
 #include "commands/variable.h"
 #include "common/string.h"
+#include "crypto/kmgr.h"
 #include "funcapi.h"
 #include "jit/jit.h"
 #include "libpq/auth.h"
@@ -73,6 +74,7 @@
 #include "replication/walsender.h"
 #include "storage/bufmgr.h"
 #include "storage/dsm_impl.h"
+#include "storage/standby.h"
 #include "storage/fd.h"
 #include "storage/large_object.h"
 #include "storage/pg_shmem.h"
@@ -465,6 +467,13 @@ const struct config_enum_entry ssl_protocol_versions_info[] = {
 	{NULL, 0, false}
 };
 
+const struct config_enum_entry data_encryption_cipher_options[] = {
+	{"off",		KMGR_ENCRYPTION_OFF, false},
+	{"aes-128", KMGR_ENCRYPTION_AES128, false},
+	{"aes-256", KMGR_ENCRYPTION_AES256, false},
+	{NULL, 0, false}
+};
+
 static struct config_enum_entry shared_memory_options[] = {
 #ifndef WIN32
 	{"sysv", SHMEM_TYPE_SYSV, false},
@@ -717,6 +726,8 @@ const char *const config_group_names[] =
 	gettext_noop("Statistics / Monitoring"),
 	/* STATS_COLLECTOR */
 	gettext_noop("Statistics / Query and Index Statistics Collector"),
+	/* ENCRYPTION */
+	gettext_noop("Encryption"),
 	/* AUTOVACUUM */
 	gettext_noop("Autovacuum"),
 	/* CLIENT_CONN */
@@ -4193,7 +4204,7 @@ static struct config_string ConfigureNamesString[] =
 		{"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SSL,
 			gettext_noop("Sets the list of allowed SSL ciphers."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&SSLCipherSuites,
 #ifdef USE_OPENSSL
@@ -4240,6 +4251,16 @@ static struct config_string ConfigureNamesString[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"cluster_passphrase_command", PGC_SIGHUP, ENCRYPTION,
+			gettext_noop("Command to obtain passphrase for database encryption."),
+			NULL
+		},
+		&cluster_passphrase_command,
+		"",
+		NULL, NULL, NULL
+	},
+
 	{
 		{"application_name", PGC_USERSET, LOGGING_WHAT,
 			gettext_noop("Sets the application name to be reported in statistics and logs."),
@@ -4633,6 +4654,19 @@ static struct config_enum ConfigureNamesEnum[] =
 		check_ssl_max_protocol_version, NULL, NULL
 	},
 
+	{
+		{"data_encryption_cipher", PGC_INTERNAL, PRESET_OPTIONS,
+		 gettext_noop("Specify encryption algorithms to use."),
+		 NULL,
+		 GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
+		 GUC_SUPERUSER_ONLY
+		},
+		&data_encryption_cipher,
+		KMGR_ENCRYPTION_OFF,
+		data_encryption_cipher_options,
+		NULL, assign_data_encryption_cipher, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e1048c0047..0dd5ee946d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -614,6 +614,11 @@
 					# autovacuum, -1 means use
 					# vacuum_cost_limit
 
+#------------------------------------------------------------------------------
+# ENCRYPTION
+#------------------------------------------------------------------------------
+
+#cluster_passphrase_command = ''
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 7f1534aebb..5bed2c76ab 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -114,6 +114,13 @@ static const char *const auth_methods_local[] = {
 	NULL
 };
 
+static const char *const encryption_ciphers[] = {
+	"none",
+	"aes-128",
+	"aes-256",
+	NULL
+};
+
 /*
  * these values are passed in by makefile defines
  */
@@ -145,6 +152,8 @@ static bool data_checksums = false;
 static char *xlog_dir = NULL;
 static char *str_wal_segment_size_mb = NULL;
 static int	wal_segment_size_mb;
+static char *enc_cipher = NULL;
+static char *cluster_passphrase = NULL;
 
 
 /* internal vars */
@@ -1206,6 +1215,13 @@ setup_config(void)
 								  "password_encryption = scram-sha-256");
 	}
 
+	if (cluster_passphrase)
+	{
+		snprintf(repltok, sizeof(repltok), "cluster_passphrase_command = '%s'",
+				 escape_quotes(cluster_passphrase));
+		conflines = replace_token(conflines, "#cluster_passphrase_command = ''", repltok);
+	}
+
 	/*
 	 * If group access has been enabled for the cluster then it makes sense to
 	 * ensure that the log files also allow group access.  Otherwise a backup
@@ -1416,14 +1432,15 @@ bootstrap_template1(void)
 	unsetenv("PGCLIENTENCODING");
 
 	snprintf(cmd, sizeof(cmd),
-			 "\"%s\" --boot -x1 -X %u %s %s %s",
+			 "\"%s\" --boot -x1 -X %u %s %s %s %s %s",
 			 backend_exec,
 			 wal_segment_size_mb * (1024 * 1024),
 			 data_checksums ? "-k" : "",
+			 enc_cipher ? "-e" : "",
+			 enc_cipher ? enc_cipher : "",
 			 boot_options,
 			 debug ? "-d 5" : "");
 
-
 	PG_CMD_OPEN;
 
 	for (line = bki_lines; *line != NULL; line++)
@@ -2312,6 +2329,9 @@ usage(const char *progname)
 	printf(_("      --wal-segsize=SIZE    size of WAL segments, in megabytes\n"));
 	printf(_("\nLess commonly used options:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
+	printf(_("  -e  --enc-cipher=TYPE     set encryption cipher (AES-128/AES-256) for data encryption\n"));
+	printf(_("  -c  --cluster-passphrase-command=COMMAND\n"
+			 "                            set command to obtain passphrase for data encryption key\n"));
 	printf(_("  -k, --data-checksums      use data page checksums\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --no-clean            do not clean up after errors\n"));
@@ -2377,6 +2397,42 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost)
 	}
 }
 
+static void
+check_encryption_cipher(const char *cipher, const char *passphrase,
+						const char *const *valid_ciphers)
+{
+	const char *const *p;
+
+	if (!cipher && !passphrase)
+		return;
+
+#ifndef USE_OPENSSL
+	pg_log_error("cluster encryption is not supported because OpenSSL is not supported by this build");
+	exit(1);
+#endif
+
+	/* Check both options must be specified at the same time */
+	if (cipher && !passphrase)
+	{
+		pg_log_error("encryption passphrase command must be specified when encryption cipher is specified");
+		exit(1);
+	}
+
+	if (!cipher && passphrase)
+	{
+		pg_log_error("encryption cipher must be specified when encryption passphrase command is specified");
+		exit(1);
+	}
+
+	for (p = valid_ciphers; *p; p++)
+	{
+		if (strcasecmp(cipher, *p) == 0)
+			return;
+	}
+
+	pg_log_error("invalid encryption cipher \"%s\"\nencryption cipher options are AES-128 and AES-256", cipher);
+	exit(1);
+}
 
 void
 setup_pgdata(void)
@@ -2984,6 +3040,8 @@ main(int argc, char *argv[])
 		{"wal-segsize", required_argument, NULL, 12},
 		{"data-checksums", no_argument, NULL, 'k'},
 		{"allow-group-access", no_argument, NULL, 'g'},
+		{"enc-cipher", required_argument, NULL, 'e'},
+		{"cluster-passphrase-command", required_argument, NULL, 'c'},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -3025,7 +3083,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "c:dD:E:e:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3107,6 +3165,12 @@ main(int argc, char *argv[])
 			case 9:
 				pwfilename = pg_strdup(optarg);
 				break;
+			case 'e':
+				enc_cipher = pg_strdup(optarg);
+				break;
+			case 'c':
+				cluster_passphrase = pg_strdup(optarg);
+				break;
 			case 's':
 				show_setting = true;
 				break;
@@ -3185,6 +3249,8 @@ main(int argc, char *argv[])
 
 	check_need_password(authmethodlocal, authmethodhost);
 
+	check_encryption_cipher(enc_cipher, cluster_passphrase, encryption_ciphers);
+
 	/* set wal segment size */
 	if (str_wal_segment_size_mb == NULL)
 		wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024);
@@ -3244,6 +3310,11 @@ main(int argc, char *argv[])
 	else
 		printf(_("Data page checksums are disabled.\n"));
 
+	if (enc_cipher)
+		printf(_("Data encryption using %s is enabled.\n"), enc_cipher);
+	else
+		printf(_("Data encryption is disabled.\n"));
+
 	if (pwprompt || pwfilename)
 		get_su_pwd();
 
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 46ee1f1dc3..f84325fc17 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/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 19e21ab491..14d3c6757b 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -25,6 +25,7 @@
 #include "access/xlog_internal.h"
 #include "catalog/pg_control.h"
 #include "common/controldata_utils.h"
+#include "common/kmgr_utils.h"
 #include "common/logging.h"
 #include "getopt_long.h"
 #include "pg_getopt.h"
@@ -83,7 +84,6 @@ wal_level_str(WalLevel wal_level)
 	return _("unrecognized wal_level");
 }
 
-
 int
 main(int argc, char *argv[])
 {
@@ -333,5 +333,7 @@ main(int argc, char *argv[])
 		   ControlFile->data_checksum_version);
 	printf(_("Mock authentication nonce:            %s\n"),
 		   mock_auth_nonce_str);
+	printf(_("Data encryption cipher:               %s\n"),
+		   kmgr_cipher_string(ControlFile->data_encryption_cipher));
 	return 0;
 }
diff --git a/src/common/Makefile b/src/common/Makefile
index 44ca68fa6c..dc157b51aa 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -48,6 +48,7 @@ LIBS += $(PTHREAD_LIBS)
 
 OBJS_COMMON = \
 	base64.o \
+	cipher.o \
 	config_info.o \
 	controldata_utils.o \
 	d2s.o \
@@ -57,6 +58,7 @@ OBJS_COMMON = \
 	file_perm.o \
 	ip.o \
 	keywords.o \
+	kmgr_utils.o \
 	kwlookup.o \
 	link-canary.o \
 	md5.o \
@@ -76,6 +78,7 @@ OBJS_COMMON = \
 
 ifeq ($(with_openssl),yes)
 OBJS_COMMON += \
+	cipher_openssl.o \
 	protocol_openssl.o \
 	sha2_openssl.o
 else
diff --git a/src/common/cipher.c b/src/common/cipher.c
new file mode 100644
index 0000000000..8b8a76af2e
--- /dev/null
+++ b/src/common/cipher.c
@@ -0,0 +1,95 @@
+/*-------------------------------------------------------------------------
+ *
+ * cipher.c
+ *	  Shared frontend/backend for cryptographic functions
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/common/cipher.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/cipher.h"
+#include "common/cipher_openssl.h"
+
+pg_cipher_ctx *
+pg_cipher_ctx_create(void)
+{
+#ifdef USE_OPENSSL
+	return ossl_cipher_ctx_create();
+#endif
+	return NULL;
+}
+
+void
+pg_cipher_setup(void)
+{
+#ifdef USE_OPENSSL
+	ossl_cipher_setup();
+#endif
+}
+
+bool
+pg_aes256_ctr_wrap_init(pg_cipher_ctx *ctx)
+{
+#ifdef USE_OPENSSL
+	return ossl_aes256_ctr_wrap_init(ctx);
+#endif
+	return false;
+}
+
+bool
+pg_aes256_ctr_unwrap_init(pg_cipher_ctx *ctx)
+{
+#ifdef USE_OPENSSL
+	return ossl_aes256_ctr_unwrap_init(ctx);
+#endif
+	return false;
+}
+
+bool
+pg_cipher_encrypt(pg_cipher_ctx *ctx, const uint8 *key,
+				   const uint8 *input, int input_size,
+				   const uint8 *iv, uint8 *dest, int *dest_size)
+
+{
+	bool r = true;
+#ifdef USE_OPENSSL
+	r = ossl_cipher_encrypt(ctx, key, input, input_size, iv,
+							dest, dest_size);
+#endif
+	return r;
+}
+
+bool
+pg_cipher_decrypt(pg_cipher_ctx *ctx, const uint8 *key,
+				   const uint8 *input, int input_size,
+				   const uint8 *iv, uint8 *dest, int *dest_size)
+{
+	bool r = true;
+#ifdef USE_OPENSSL
+	r = ossl_cipher_decrypt(ctx, key, input, input_size, iv,
+							dest, dest_size);
+#endif
+	return r;
+}
+
+bool
+pg_compute_HMAC(const uint8 *key, const uint8 *data,
+				 int data_size, uint8 *result, int *result_size)
+{
+	bool r = true;
+#ifdef USE_OPENSSL
+	r = ossl_compute_HMAC(key, data, data_size, result,
+						  result_size);
+#endif
+	return r;
+}
diff --git a/src/common/cipher_openssl.c b/src/common/cipher_openssl.c
new file mode 100644
index 0000000000..519653c7d5
--- /dev/null
+++ b/src/common/cipher_openssl.c
@@ -0,0 +1,99 @@
+/*-------------------------------------------------------------------------
+ * cipher_openssl.c
+ *		Cryptographic function using OpenSSL
+ *
+ * This contains the common low-level functions needed in both frontend and
+ * backend, for implement the database encryption.
+ *
+ * Portions Copyright (c) 2017-2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/common/cipher_openssl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/cipher_openssl.h"
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+
+pg_cipher_ctx *
+ossl_cipher_ctx_create(void)
+{
+	return EVP_CIPHER_CTX_new();
+}
+
+bool
+ossl_cipher_setup(void)
+{
+#ifdef HAVE_OPENSSL_INIT_CRYPTO
+	/* Setup OpenSSL */
+	if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
+		return false;
+	return true;
+#endif
+	return false;
+}
+
+bool
+ossl_aes256_ctr_wrap_init(pg_cipher_ctx *ctx)
+{
+	EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+	if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, NULL, NULL))
+		return false;
+
+	EVP_CIPHER_CTX_set_key_length(ctx, AES256_KEY_LEN);
+	return true;
+}
+
+bool
+ossl_aes256_ctr_unwrap_init(pg_cipher_ctx *ctx)
+{
+	EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+	if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, NULL, NULL))
+		return false;
+
+	EVP_CIPHER_CTX_set_key_length(ctx, AES256_KEY_LEN);
+	return true;
+}
+
+bool
+ossl_cipher_encrypt(pg_cipher_ctx *ctx, const uint8 *key,
+					const uint8 *input, int input_size,
+					const uint8 *iv, uint8 *dest,
+					int *dest_size)
+{
+	if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
+		return false;
+
+	return EVP_EncryptUpdate(ctx, dest, dest_size, input, input_size);
+}
+
+bool
+ossl_cipher_decrypt(pg_cipher_ctx *ctx, const uint8 *key,
+					const uint8 *input, int input_size,
+					const uint8 *iv, uint8 *dest,
+					int *dest_size)
+{
+	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
+		return false;
+
+	return EVP_DecryptUpdate(ctx, dest, dest_size, input, input_size);
+}
+
+bool
+ossl_compute_HMAC(const uint8 *key, const uint8 *data,
+				  int data_size, uint8 *result,
+				  int *result_size)
+{
+	return HMAC(EVP_sha256(), key, AES256_KEY_LEN, data,
+				(uint32) data_size, result, (uint32 *) result_size);
+}
diff --git a/src/common/kmgr_utils.c b/src/common/kmgr_utils.c
new file mode 100644
index 0000000000..a4ce4235ed
--- /dev/null
+++ b/src/common/kmgr_utils.c
@@ -0,0 +1,354 @@
+/*-------------------------------------------------------------------------
+ *
+ * kmgr_utils.c
+ *	  Shared frontend/backend for cryptographic key management
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/common/kmgr_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#ifdef FRONTEND
+#include "common/logging.h"
+#endif
+#include "common/kmgr_utils.h"
+#include "common/sha2.h"
+#include "utils/elog.h"
+#include "storage/fd.h"
+
+#define KMGR_PROMPT_MSG "Enter database encryption pass phrase:"
+
+static pg_cipher_ctx *wrapctx = NULL;
+static pg_cipher_ctx *unwrapctx = NULL;
+static bool keywrap_initialized = false;
+
+#ifdef FRONTEND
+static FILE *open_pipe_stream(const char *command);
+static int close_pipe_stream(FILE *file);
+#endif
+
+static void
+initialize_keywrap_ctx(void)
+{
+	wrapctx = pg_cipher_ctx_create();
+	if (wrapctx == NULL)
+		goto err;
+
+	unwrapctx = pg_cipher_ctx_create();
+	if (unwrapctx == NULL)
+		goto err;
+
+	if (!pg_aes256_ctr_wrap_init(wrapctx))
+		goto err;
+
+	if (!pg_aes256_ctr_wrap_init(unwrapctx))
+		goto err;
+
+	keywrap_initialized = true;
+	return;
+
+err:
+#ifdef FRONTEND
+	pg_log_fatal("could not initialize cipher context");
+	exit(EXIT_FAILURE);
+#else
+	ereport(ERROR,
+			(errcode(ERRCODE_INTERNAL_ERROR),
+			 errmsg("could not initialize cipher context")));
+#endif
+}
+
+/*
+ * Hash the given passphrase and extract it into KEK and HMAC
+ * key.
+ */
+void
+kmgr_derive_keys(char *passphrase, Size passlen,
+				 uint8 kek[KMGR_KEK_LEN],
+				 uint8 hmackey[KMGR_HMAC_KEY_LEN])
+{
+	uint8 keys[PG_SHA512_DIGEST_LENGTH];
+	pg_sha512_ctx ctx;
+
+	pg_sha512_init(&ctx);
+	pg_sha512_update(&ctx, (const uint8 *) passphrase, passlen);
+	pg_sha512_final(&ctx, keys);
+
+	/*
+	 * SHA-512 results 64 bytes. We extract it into two keys for
+	 * each 32 bytes.
+	 */
+	if (kek)
+		memcpy(kek, keys, KMGR_KEK_LEN);
+	if (hmackey)
+		memcpy(hmackey, keys + KMGR_KEK_LEN, KMGR_HMAC_KEY_LEN);
+}
+
+/*
+ * Verify the correctness of the given passphrase. We compute HMACs of the
+ * wrapped key using the HMAC key retrieved from the user provided passphrase.
+ * And then we compare it with the HMAC stored alongside the controlfile. Return
+ * true if both HMACs are matched, meaning the given passphrase is correct.
+ * Otherwise return false.
+ */
+bool
+kmgr_verify_passphrase(char *passphrase, int passlen,
+					   WrappedEncKeyWithHmac *kh, int keylen)
+{
+	uint8 user_kek[KMGR_KEK_LEN];
+	uint8 user_hmackey[KMGR_HMAC_KEY_LEN];
+	uint8 result_hmac[KMGR_HMAC_LEN];
+
+	kmgr_derive_keys(passphrase, passlen, user_kek, user_hmackey);
+
+	/* Verify both HMAC */
+	kmgr_compute_HMAC(user_hmackey, kh->key, keylen, result_hmac);
+
+	if (memcmp(result_hmac, kh->hmac, KMGR_HMAC_LEN) != 0)
+		return false;
+
+	return true;
+}
+
+/*
+ * 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.
+ */
+int
+kmgr_run_cluster_passphrase_command(char *passphrase_command, char *buf,
+									int size)
+{
+	char		command[MAXPGPATH];
+	char	   *p;
+	char		*dp;
+	char		*endp;
+	FILE	   *fh;
+	int			pclose_rc;
+	size_t		len = 0;
+
+	Assert(size > 0);
+	buf[0] = '\0';
+
+	dp = command;
+	endp = command + MAXPGPATH - 1;
+	*endp = '\0';
+
+	for (p = passphrase_command; *p; p++)
+	{
+		if (p[0] == '%')
+		{
+			switch (p[1])
+			{
+				case 'p':
+					StrNCpy(dp, KMGR_PROMPT_MSG, strlen(KMGR_PROMPT_MSG));
+					dp += strlen(KMGR_PROMPT_MSG);
+					p++;
+					break;
+				case '%':
+					p++;
+					if (dp < endp)
+						*dp++ = *p;
+					break;
+				default:
+					if (dp < endp)
+						*dp++ = *p;
+					break;
+			}
+		}
+		else
+		{
+			if (dp < endp)
+				*dp++ = *p;
+		}
+	}
+	*dp = '\0';
+
+#ifdef FRONTEND
+	fh = open_pipe_stream(command);
+	if (fh == NULL)
+	{
+		pg_log_fatal("could not execute command \"%s\": %m",
+					 command);
+		exit(EXIT_FAILURE);
+	}
+#else
+	fh = OpenPipeStream(command, "r");
+	if (fh == NULL)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not execute command \"%s\": %m",
+						command)));
+#endif
+
+	if ((len = fread(buf, sizeof(char), size, fh)) < size)
+	{
+		if (ferror(fh))
+		{
+#ifdef FRONTEND
+			pg_log_fatal("could not read from command \"%s\": %m",
+						 command);
+			exit(EXIT_FAILURE);
+#else
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not read from command \"%s\": %m",
+							command)));
+#endif
+		}
+	}
+
+#ifdef FRONTEND
+	pclose_rc = close_pipe_stream(fh);
+#else
+	pclose_rc = ClosePipeStream(fh);
+#endif
+
+	if (pclose_rc == -1)
+	{
+#ifdef FRONTEND
+		pg_log_fatal("could not close pipe to external command: %m");
+		exit(EXIT_FAILURE);
+#else
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not close pipe to external command: %m")));
+#endif
+	}
+	else if (pclose_rc != 0)
+	{
+#ifdef FRONTEND
+		pg_log_fatal("command \"%s\" failed", command);
+		exit(EXIT_FAILURE);
+#else
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("command \"%s\" failed",
+						command),
+				 errdetail_internal("%s", wait_result_to_str(pclose_rc))));
+#endif
+	}
+
+	return len;
+}
+
+bool
+kmgr_wrap_key(uint8 *key, const uint8 *in, int insize, uint8 *out)
+{
+	int outsize;
+
+	if (!keywrap_initialized)
+		initialize_keywrap_ctx();
+
+	return pg_cipher_encrypt(wrapctx, key, in , insize,
+							 NULL, out, &outsize);
+}
+
+bool
+kmgr_unwrap_key(uint8 *key, const uint8 *in, int insize, uint8 *out)
+{
+	int outsize;
+
+	if (!keywrap_initialized)
+		initialize_keywrap_ctx();
+
+	return pg_cipher_decrypt(unwrapctx, key, in, insize,
+							 NULL, out, &outsize);
+}
+
+bool
+kmgr_compute_HMAC(uint8 *key, const uint8 *data, int size,
+				  uint8 *result)
+{
+	int resultsize;
+
+	return pg_compute_HMAC(key, data, size, result, &resultsize);
+}
+
+/* Convert cipher name string to integer value */
+int
+kmgr_cipher_value(const char *name)
+{
+	if (strcasecmp(name, "aes-128") == 0)
+		return KMGR_ENCRYPTION_AES128;
+
+	if (strcasecmp(name, "aes-256") == 0)
+		return KMGR_ENCRYPTION_AES256;
+
+	return KMGR_ENCRYPTION_OFF;
+}
+
+/* Convert integer value to cipher name string */
+char *
+kmgr_cipher_string(int value)
+{
+	switch (value)
+	{
+		case KMGR_ENCRYPTION_OFF :
+			return "off";
+		case KMGR_ENCRYPTION_AES128:
+			return "aes-128";
+		case KMGR_ENCRYPTION_AES256:
+			return "aes-256";
+		default:
+			break;
+	}
+
+	return "unknown";
+}
+
+#ifdef FRONTEND
+static FILE *
+open_pipe_stream(const char *command)
+{
+	FILE	   *res;
+
+#ifdef WIN32
+	size_t		cmdlen = strlen(command);
+	char	   *buf;
+	int			save_errno;
+
+	buf = malloc(cmdlen + 2 + 1);
+	if (buf == NULL)
+	{
+		errno = ENOMEM;
+		return NULL;
+	}
+	buf[0] = '"';
+	mempcy(&buf[1], command, cmdlen);
+	buf[cmdlen + 1] = '"';
+	buf[cmdlen + 2] = '\0';
+
+	res = _popen(buf, "r");
+
+	save_errno = errno;
+	free(buf);
+	errno = save_errno;
+#else
+	res = popen(command, "r");
+#endif /* WIN32 */
+	return res;
+}
+
+static int
+close_pipe_stream(FILE *file)
+{
+#ifdef WIN32
+	return _pclose(file);
+#else
+	return pclose(file);
+#endif /* WIN32 */
+}
+#endif /* FRONTEND */
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 98b033fc20..c035db2ccd 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -15,6 +15,7 @@
 #include "access/xlogdefs.h"
 #include "access/xloginsert.h"
 #include "access/xlogreader.h"
+#include "crypto/kmgr.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
@@ -291,8 +292,10 @@ extern TimestampTz GetCurrentChunkReplayStartTime(void);
 
 extern void UpdateControlFile(void);
 extern uint64 GetSystemIdentifier(void);
+extern WrappedEncKeyWithHmac *GetMasterEncryptionKey(void);
 extern char *GetMockAuthenticationNonce(void);
 extern bool DataChecksumsEnabled(void);
+extern int	GetDataEncryptionCipher(void);
 extern XLogRecPtr GetFakeLSNForUnloggedRel(void);
 extern Size XLOGShmemSize(void);
 extern void XLOGShmemInit(void);
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index de5670e538..a8a1a46a17 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -17,6 +17,7 @@
 
 #include "access/transam.h"
 #include "access/xlogdefs.h"
+#include "crypto/kmgr.h"
 #include "pgtime.h"				/* for pg_time_t */
 #include "port/pg_crc32c.h"
 
@@ -219,6 +220,9 @@ typedef struct ControlFileData
 	/* Are data pages protected by checksums? Zero if no checksum version */
 	uint32		data_checksum_version;
 
+	/* Are data pages and WAL encrypted? Zero if encryption is disabled */
+	uint32		data_encryption_cipher;
+
 	/*
 	 * Random nonce, used in authentication requests that need to proceed
 	 * based on values that are cluster-unique, like a SASL exchange that
@@ -226,6 +230,11 @@ typedef struct ControlFileData
 	 */
 	char		mock_authentication_nonce[MOCK_AUTH_NONCE_LEN];
 
+	/*
+	 * Key information for data encryption.
+	 */
+	WrappedEncKeyWithHmac master_dek;
+
 	/* CRC of all above ... MUST BE LAST! */
 	pg_crc32c	crc;
 } ControlFileData;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fcf2a1214c..d4a744af6c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10748,4 +10748,17 @@
   proname => 'pg_partition_root', prorettype => 'regclass',
   proargtypes => 'regclass', prosrc => 'pg_partition_root' },
 
+# function for key managements
+{ oid => '8200', descr => 'rotate cluter encryption key',
+  proname => 'pg_rotate_encryption_key',
+  provolatile => 'v', prorettype => 'bool',
+  proargtypes => '', prosrc => 'pg_rotate_encryption_key' },
+{ oid => '8201', descr => 'wrap the given secret',
+  proname => 'pg_kmgr_wrap',
+  provolatile => 'v', prorettype => 'bytea',
+  proargtypes => 'bytea', prosrc => 'pg_kmgr_wrap' },
+{ oid => '8202', descr => 'unwrap the given secret',
+  proname => 'pg_kmgr_unwrap',
+  provolatile => 'v', prorettype => 'bytea',
+  proargtypes => 'bytea', prosrc => 'pg_kmgr_unwrap' },
 ]
diff --git a/src/include/common/cipher.h b/src/include/common/cipher.h
new file mode 100644
index 0000000000..c61cb34b1c
--- /dev/null
+++ b/src/include/common/cipher.h
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+ *
+ * cipher.h
+ *		Declarations for cryptographic functions
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/cipher.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CIPHER_H
+#define CIPHER_H
+
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#endif
+
+/* Key lengths for AES */
+#define AES128_KEY_LEN		16
+#define AES256_KEY_LEN		32
+
+/*
+ * The encrypted data is a series of blocks of size ENCRYPTION_BLOCK.
+ * Initialization vector(IV) is the same size of cipher block.
+ */
+#define AES_BLOCK_SIZE 16
+#define AES_IV_SIZE		(AES_BLOCK_SIZE)
+
+/*
+ * Key wrapping appends the initial 8 bytes value. Therefore
+ * wrapped key size gets larger than original one.
+ */
+#define AES256_KEY_WRAP_VALUE_LEN		8
+#define AES256_MAX_WRAPPED_KEY_LEN		(AES256_KEY_LEN + AES256_KEY_WRAP_VALUE_LEN)
+
+/* Size of HMAC key is the same as the length of hash, we use SHA-256 */
+#define SHA256_HMAC_KEY_LEN		32
+
+/* SHA-256 results 256 bits HMAC */
+#define SHA256_HMAC_LEN	32
+
+
+#ifdef USE_OPENSSL
+typedef EVP_CIPHER_CTX pg_cipher_ctx;
+#else
+typedef void pg_cipher_ctx;
+#endif
+
+extern pg_cipher_ctx *pg_cipher_ctx_create(void);
+extern void pg_cipher_setup(void);
+extern bool pg_aes256_ctr_wrap_init(pg_cipher_ctx *ctx);
+extern bool pg_aes256_ctr_unwrap_init(pg_cipher_ctx *ctx);
+
+extern bool pg_cipher_encrypt(pg_cipher_ctx *ctx, const uint8 *key,
+							   const uint8 *input, int input_size,
+							   const uint8 *iv, uint8 *dest,
+							   int *dest_size);
+extern bool pg_cipher_decrypt(pg_cipher_ctx *ctx, const uint8 *key,
+							   const uint8 *input, int input_size,
+							   const uint8 *iv, uint8 *dest,
+							   int *dest_size);
+extern bool pg_compute_HMAC(const uint8 *key, const uint8 *data,
+							 int data_size, uint8 *result,
+							 int *result_size);
+
+#endif /* CIPHER_H */
diff --git a/src/include/common/cipher_openssl.h b/src/include/common/cipher_openssl.h
new file mode 100644
index 0000000000..97d8ae766b
--- /dev/null
+++ b/src/include/common/cipher_openssl.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * cipher_openssl.h
+ *		Declarations for helper functions using OpenSSL
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/cipher_openssl.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CIPHER_OPENSSL_H
+#define CIPHER_OPENSSL_H
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/cipher.h"
+
+extern pg_cipher_ctx *ossl_cipher_ctx_create(void);
+extern bool ossl_cipher_setup(void);
+extern bool ossl_aes256_ctr_wrap_init(pg_cipher_ctx *ctx);
+extern bool ossl_aes256_ctr_unwrap_init(pg_cipher_ctx *ctx);
+extern bool ossl_cipher_encrypt(pg_cipher_ctx *ctx, const uint8 *key,
+								const uint8 *input, int input_size,
+								const uint8 *iv, uint8 *dest,
+								int *dest_size);
+extern bool ossl_cipher_decrypt(pg_cipher_ctx *ctx, const uint8 *key,
+								const uint8 *input, int input_size,
+								const uint8 *iv, uint8 *dest,
+								int *dest_size);
+extern bool ossl_compute_HMAC(const uint8 *key, const uint8 *data,
+							  int data_size, uint8 *result,
+							  int *result_size);
+#endif
diff --git a/src/include/common/kmgr_utils.h b/src/include/common/kmgr_utils.h
new file mode 100644
index 0000000000..600f3b1b1b
--- /dev/null
+++ b/src/include/common/kmgr_utils.h
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * kmgr_utils.h
+ *		Declarations for utility function for cryptographic key management
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/kmgr_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef KMGR_UTILS_H
+#define KMGR_UTILS_H
+
+#include "common/cipher.h"
+
+/* Key encryption key is always AES-256 key */
+#define KMGR_KEK_LEN		AES256_KEY_LEN
+
+/* Master data encryption key supports AES-128 and AES-256 */
+#define KMGR_MAX_DEK_LEN	AES256_KEY_LEN
+
+/* HMAC and HMAC key */
+#define KMGR_HMAC_KEY_LEN	SHA256_HMAC_KEY_LEN
+#define KMGR_HMAC_LEN		SHA256_HMAC_LEN
+
+#define KMGR_MAX_PASSPHRASE_LEN	1024
+#define KMGR_MIN_PASSPHRASE_LEN 8
+
+/* Value of data_encryption_cipher */
+enum
+{
+	KMGR_ENCRYPTION_OFF = 0,
+	KMGR_ENCRYPTION_AES128,
+	KMGR_ENCRYPTION_AES256
+};
+
+/*
+ * Struct for keys that needs to be verified using its HMAC.
+ */
+typedef struct WrappedEncKeyWithHmac
+{
+	uint8 key[AES256_MAX_WRAPPED_KEY_LEN];
+	uint8 hmac[KMGR_HMAC_LEN];
+} WrappedEncKeyWithHmac;
+
+extern void kmgr_derive_keys(char *passphrase, Size passlen,
+							 uint8 kek[KMGR_KEK_LEN],
+							 uint8 hmackey[KMGR_HMAC_KEY_LEN]);
+extern bool kmgr_verify_passphrase(char *passphrase, int passlen,
+								   WrappedEncKeyWithHmac *kh, int keylen);
+extern bool kmgr_wrap_key(uint8 *key, const uint8 *in, int insize,
+						  uint8 *out);
+extern bool kmgr_unwrap_key(uint8 *key, const uint8 *in, int insize,
+							uint8 *out);
+extern bool kmgr_compute_HMAC(uint8 *key, const uint8 *data, int size,
+							  uint8 *result);
+extern int kmgr_run_cluster_passphrase_command(char *passphrase_command,
+											   char *buf, int size);
+extern int kmgr_cipher_value(const char *name);
+extern char * kmgr_cipher_string(int value);
+
+#endif /* KMGR_UTILS_H */
diff --git a/src/include/crypto/kmgr.h b/src/include/crypto/kmgr.h
new file mode 100644
index 0000000000..fc41131f69
--- /dev/null
+++ b/src/include/crypto/kmgr.h
@@ -0,0 +1,44 @@
+/*-------------------------------------------------------------------------
+ *
+ * kmgr.h
+ *	  Key management module for transparent data encryption
+ *
+ * Portions Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * src/include/crypto/kmgr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef KMGR_H
+#define KMGR_H
+
+#include "common/cipher.h"
+#include "common/kmgr_utils.h"
+#include "storage/relfilenode.h"
+#include "storage/bufpage.h"
+
+#define KMGR_MAX_PASSPHRASE_LEN		1024
+#define KMGR_PROMPT_MSG "Enter database encryption pass phrase:"
+
+#define DataEncryptionEnabled() \
+	(data_encryption_cipher > KMGR_ENCRYPTION_OFF)
+
+#define SizeOfWrappedDEK() \
+	(EncryptionKeyLen + AES256_KEY_WRAP_VALUE_LEN)
+
+/* GUC parameter */
+extern PGDLLIMPORT int data_encryption_cipher;
+
+/* Encryption keys (TDEK and WDEK) length */
+extern int EncryptionKeyLen;
+
+/* GUC variable */
+extern char *cluster_passphrase_command;
+
+extern WrappedEncKeyWithHmac *BootStrapKmgr(int bootstrap_data_encryption_cipher);
+extern void InitializeKmgr(void);
+extern const char *KmgrGetMasterEncryptionKey(void);
+extern void assign_data_encryption_cipher(int new_encryption_cipher,
+										  void *extra);
+
+#endif /* KMGR_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 6f485f73cd..bb35bf782c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -415,6 +415,9 @@
 /* Define to 1 if you have the `OPENSSL_init_ssl' function. */
 #undef HAVE_OPENSSL_INIT_SSL
 
+/* Define to 1 if you have the `OPENSSL_init_crypto' function. */
+#undef HAVE_OPENSSL_INIT_CRYPTO
+
 /* Define to 1 if you have the <ossp/uuid.h> header file. */
 #undef HAVE_OSSP_UUID_H
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index aecb6013f0..3d1c5a3bd1 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -895,6 +895,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/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 454c2df487..c0c53b1e13 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -89,6 +89,7 @@ enum config_group
 	STATS,
 	STATS_MONITORING,
 	STATS_COLLECTOR,
+	ENCRYPTION,
 	AUTOVACUUM,
 	CLIENT_CONN,
 	CLIENT_CONN_STATEMENT,