diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index a664ecf494..2197bd3a14 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1025,6 +1025,7 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 					true);
 
 	RelationOpenSmgr(rel);
+	PageEncryptInplace(page, MAIN_FORKNUM, lastblock);
 	PageSetChecksumInplace(page, lastblock);
 	smgrextend(rel->rd_smgr, MAIN_FORKNUM, lastblock, zerobuf.data, false);
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 39e33763df..498a18dd33 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -326,6 +326,7 @@ end_heap_rewrite(RewriteState state)
 						true);
 		RelationOpenSmgr(state->rs_new_rel);
 
+		PageEncryptInplace(state->rs_buffer, MAIN_FORKNUM, state->rs_blockno);
 		PageSetChecksumInplace(state->rs_buffer, state->rs_blockno);
 
 		smgrextend(state->rs_new_rel->rd_smgr, MAIN_FORKNUM, state->rs_blockno,
@@ -690,6 +691,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 			 */
 			RelationOpenSmgr(state->rs_new_rel);
 
+			PageEncryptInplace(page, MAIN_FORKNUM, state->rs_blockno);
 			PageSetChecksumInplace(page, state->rs_blockno);
 
 			smgrextend(state->rs_new_rel->rd_smgr, MAIN_FORKNUM,
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c4f22f1c69..f55f66110e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -175,6 +175,7 @@ btbuildempty(Relation index)
 	 * XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record.  Therefore, we need
 	 * this even when wal_level=minimal.
 	 */
+	PageEncryptInplace(metapage, INIT_FORKNUM, BTREE_METAPAGE);
 	PageSetChecksumInplace(metapage, BTREE_METAPAGE);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE,
 			  (char *) metapage, true);
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index 8730de25ed..729551d67b 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -665,6 +665,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
 				   true);
 	}
 
+	PageEncryptInplace(page, MAIN_FORKNUM, blkno);
 	PageSetChecksumInplace(page, blkno);
 
 	/*
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index e4508a2b92..4a0a24a5a7 100644
--- a/src/backend/access/spgist/spginsert.c
+++ b/src/backend/access/spgist/spginsert.c
@@ -168,6 +168,7 @@ spgbuildempty(Relation index)
 	 * of their existing content when the corresponding create records are
 	 * replayed.
 	 */
+	PageEncryptInplace(page, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO);
 	PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
 			  (char *) page, true);
@@ -177,6 +178,7 @@ spgbuildempty(Relation index)
 	/* Likewise for the root page. */
 	SpGistInitPage(page, SPGIST_LEAF);
 
+	PageEncryptInplace(page, INIT_FORKNUM, SPGIST_ROOT_BLKNO);
 	PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
 			  (char *) page, true);
@@ -186,6 +188,7 @@ spgbuildempty(Relation index)
 	/* Likewise for the null-tuples root page. */
 	SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
 
+	PageEncryptInplace(page, INIT_FORKNUM, SPGIST_NULL_BLKNO);
 	PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
 			  (char *) page, true);
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index a074dea53e..b95ed8eb0b 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -61,6 +61,7 @@
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/bufmgr.h"
+#include "storage/encryption.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/large_object.h"
@@ -939,6 +940,7 @@ static void ReadControlFile(void);
 static char *str_time(pg_time_t tnow);
 static void SetPromoteIsTriggered(void);
 static bool CheckForStandbyTrigger(void);
+static void XLogWritePages(char *from, Size nbytes, uint32 startoffset);
 
 #ifdef WAL_DEBUG
 static void xlog_outrec(StringInfo buf, XLogReaderState *record);
@@ -2529,41 +2531,33 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 		{
 			char	   *from;
 			Size		nbytes;
-			Size		nleft;
-			int			written;
 
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
-			nleft = nbytes;
-			do
-			{
-				errno = 0;
-				pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE);
-				written = pg_pwrite(openLogFile, from, nleft, startoffset);
-				pgstat_report_wait_end();
-				if (written <= 0)
-				{
-					char		xlogfname[MAXFNAMELEN];
-					int			save_errno;
 
-					if (errno == EINTR)
-						continue;
+			if (DataEncryptionEnabled())
+			{
+				int i;
 
-					save_errno = errno;
-					XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo,
-								 wal_segment_size);
-					errno = save_errno;
-					ereport(PANIC,
-							(errcode_for_file_access(),
-							 errmsg("could not write to log file %s "
-									"at offset %u, length %zu: %m",
-									xlogfname, startoffset, nleft)));
+				/* Encrypt xlog pages one by one */
+				for (i = 0; i < npages; i++)
+				{
+					char   *buftowrite;
+					Size	nwrite = Min(nbytes, XLOG_BLCKSZ);
+
+					buftowrite = EncryptXLog(from, nwrite, openLogSegNo,
+											 startoffset);
+					XLogWritePages(buftowrite, nwrite, startoffset);
+					startoffset += nwrite;
+					from += XLOG_BLCKSZ;
 				}
-				nleft -= written;
-				from += written;
-				startoffset += written;
-			} while (nleft > 0);
+			}
+			else
+			{
+				XLogWritePages(from, npages * (Size) XLOG_BLCKSZ, startoffset);
+				startoffset += npages * (Size) XLOG_BLCKSZ;
+			}
 
 			npages = 0;
 
@@ -2680,6 +2674,46 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	}
 }
 
+/*
+ * Write XLOG pages starting from 'startoffset'.
+ */
+static void
+XLogWritePages(char *from, Size nbytes, uint32 startoffset)
+{
+	Size		nleft;
+	int			written;
+
+	nleft = nbytes;
+	do
+	{
+		errno = 0;
+		pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE);
+		written = pg_pwrite(openLogFile, from, nleft, startoffset);
+		pgstat_report_wait_end();
+		if (written <= 0)
+		{
+			char		xlogfname[MAXFNAMELEN];
+			int			save_errno;
+
+			if (errno == EINTR)
+				continue;
+
+			save_errno = errno;
+			XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo,
+						 wal_segment_size);
+			errno = save_errno;
+			ereport(PANIC,
+					(errcode_for_file_access(),
+					 errmsg("could not write to log file %s "
+							"at offset %u, length %zu: %m",
+							xlogfname, startoffset, nleft)));
+		}
+		nleft -= written;
+		from += written;
+		startoffset += written;
+	} while (nleft > 0);
+}
+
 /*
  * Record the LSN for an asynchronous transaction commit/abort
  * and nudge the WALWriter if there is work for it to do.
@@ -12049,6 +12083,16 @@ retry:
 
 	xlogreader->seg.ws_tli = curFileTLI;
 
+	/*
+	 * Decrypt read record so that we can validate page both short header
+	 * and possibly long header.
+	 */
+	if (DataEncryptionEnabled())
+	{
+		DecryptXLog(readBuf, XLOG_BLCKSZ, readSegNo, readOff);
+		xlogreader->encrypted = false;
+	}
+
 	/*
 	 * Check the page header immediately, so that we can retry immediately if
 	 * it's not valid. This may seem unnecessary, because XLogReadRecord()
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index a63ad8cfd0..3ad57e1a85 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -29,6 +29,7 @@
 
 #ifndef FRONTEND
 #include "miscadmin.h"
+#include "storage/encryption.h"
 #include "pgstat.h"
 #include "utils/memutils.h"
 #endif
@@ -615,6 +616,16 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 		/* we can be sure to have enough WAL available, we scrolled back */
 		Assert(readLen == XLOG_BLCKSZ);
 
+#ifndef FRONTEND
+		if (state->encrypted)
+		{
+			uint32 off = XLogSegmentOffset(targetSegmentPtr, state->segcxt.ws_segsize);
+
+			DecryptXLog(state->readBuf, XLOG_BLCKSZ, targetSegNo, off);
+			state->encrypted = false;
+		}
+#endif
+
 		if (!XLogReaderValidatePageHeader(state, targetSegmentPtr,
 										  state->readBuf))
 			goto err;
@@ -650,6 +661,14 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
 			goto err;
 	}
 
+#ifndef FRONTEND
+	if (state->encrypted)
+	{
+		DecryptXLog(state->readBuf, XLOG_BLCKSZ, targetSegNo, targetPageOff);
+		state->encrypted = false;
+	}
+#endif
+
 	/*
 	 * Now that we know we have the full header, validate it.
 	 */
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 32a3099c1f..463ec31445 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -25,6 +25,7 @@
 #include "access/xlogutils.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "storage/encryption.h"
 #include "storage/smgr.h"
 #include "utils/guc.h"
 #include "utils/hsearch.h"
@@ -946,6 +947,9 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
 				 &errinfo))
 		WALReadRaiseError(&errinfo);
 
+	if (DataEncryptionEnabled())
+		state->encrypted = true;
+
 	/* number of valid bytes in the buffer */
 	return count;
 }
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index d538f25726..e8f605030f 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -443,7 +443,7 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
 
 		smgrread(src, forkNum, blkno, buf.data);
 
-		if (!PageIsVerifiedExtended(page, blkno,
+		if (!PageIsVerifiedExtended(page, forkNum, blkno,
 									PIV_LOG_WARNING | PIV_REPORT_STAT))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c
index 9b3d026fc4..4990b42f4a 100644
--- a/src/backend/crypto/kmgr.c
+++ b/src/backend/crypto/kmgr.c
@@ -51,7 +51,7 @@ static KmgrShmemData *KmgrShmem;
 char   *cluster_passphrase_command = NULL;
 int		file_encryption_keylen = 0;
 
-CryptoKey	boostrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS];
+CryptoKey	bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS];
 
 extern char *bootstrap_old_key_datadir;
 extern int	bootstrap_file_encryption_keylen;
@@ -92,6 +92,7 @@ BootStrapKmgr(void)
 	else
 	{
 		char live_path[MAXPGPATH];
+		CryptoKey	keys[KMGR_MAX_INTERNAL_KEYS];
 
 		if (mkdir(LIVE_KMGR_DIR, pg_dir_create_mode) < 0)
 			ereport(ERROR,
@@ -99,7 +100,7 @@ BootStrapKmgr(void)
 					 errmsg("could not create cluster file encryption directory \"%s\": %m",
 							LIVE_KMGR_DIR)));
 
-		memset(boostrap_keys_wrap, 0, sizeof(boostrap_keys_wrap));
+		memset(bootstrap_keys_wrap, 0, sizeof(bootstrap_keys_wrap));
 		/* bzero keys on exit */
 		on_proc_exit(bzeroKmgrKeys, 0);
 	
@@ -124,12 +125,10 @@ BootStrapKmgr(void)
 		/* Wrap all data encryption keys by key encryption key */
 		for (int id = 0; id < KMGR_MAX_INTERNAL_KEYS; id++)
 		{
-			CryptoKey *key;
-
 			/* generate a data encryption key */
-			key = generate_crypto_key(bootstrap_file_encryption_keylen);
+			memcpy(&bootstrap_keys_wrap[id], generate_crypto_key(bootstrap_file_encryption_keylen), sizeof(CryptoKey));
 		
-			if (!kmgr_wrap_key(ctx, key, &(boostrap_keys_wrap[id])))
+			if (!kmgr_wrap_key(ctx, &bootstrap_keys_wrap[id], &(keys[id])))
 			{
 				pg_free_keywrap_ctx(ctx);
 				elog(ERROR, "failed to wrap data encryption key");
@@ -137,7 +136,7 @@ BootStrapKmgr(void)
 		}
 	
 		/* Save data encryption keys to the disk */
-		KmgrSaveCryptoKeys(LIVE_KMGR_DIR, boostrap_keys_wrap);
+		KmgrSaveCryptoKeys(LIVE_KMGR_DIR, keys);
 	
 		pg_free_keywrap_ctx(ctx);
 	}
@@ -256,7 +255,7 @@ KmgrGetKey(int id)
 	Assert(id < KMGR_MAX_INTERNAL_KEYS);
 
 	return (const CryptoKey *) (IsBootstrapProcessingMode() ?
-			&(boostrap_keys_wrap[id]) : &(KmgrShmem->intlKeys[id]));
+			&(bootstrap_keys_wrap[id]) : &(KmgrShmem->intlKeys[id]));
 }
 
 /* Generate an empty CryptoKey */
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 2eb19ad293..84c4bdee09 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -77,6 +77,7 @@
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
 #include "storage/condition_variable.h"
+#include "storage/encryption.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
@@ -858,6 +859,10 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
 	XLByteToSeg(targetPagePtr, segno, state->segcxt.ws_segsize);
 	CheckXLogRemoved(segno, state->seg.ws_tli);
 
+	/* Decrypt xlog page if needed */
+	if (DataEncryptionEnabled())
+		state->encrypted = true;
+
 	return count;
 }
 
diff --git a/src/backend/storage/Makefile b/src/backend/storage/Makefile
index 8376cdfca2..3f99e317e1 100644
--- a/src/backend/storage/Makefile
+++ b/src/backend/storage/Makefile
@@ -8,6 +8,6 @@ subdir = src/backend/storage
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS     = buffer file freespace ipc large_object lmgr page smgr sync
+SUBDIRS     = buffer encryption file freespace ipc large_object lmgr page smgr sync
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index ad0d1a9abc..3003090058 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -918,7 +918,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			}
 
 			/* check for garbage data */
-			if (!PageIsVerifiedExtended((Page) bufBlock, blockNum,
+			if (!PageIsVerifiedExtended((Page) bufBlock, forkNum, blockNum,
 										PIV_LOG_WARNING | PIV_REPORT_STAT))
 			{
 				if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages)
@@ -2793,12 +2793,15 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 	 */
 	bufBlock = BufHdrGetBlock(buf);
 
+	bufToWrite = PageEncryptCopy((Page) bufBlock, buf->tag.forkNum,
+								  buf->tag.blockNum);
+
 	/*
 	 * Update page checksum if desired.  Since we have only shared lock on the
 	 * buffer, other processes might be updating hint bits in it, so we must
 	 * copy the page to private storage if we do checksumming.
 	 */
-	bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum);
+	bufToWrite = PageSetChecksumCopy((Page) bufToWrite, buf->tag.blockNum);
 
 	if (track_io_timing)
 		INSTR_TIME_SET_CURRENT(io_start);
@@ -3275,6 +3278,9 @@ FlushRelationBuffers(Relation rel)
 
 				localpage = (char *) LocalBufHdrGetBlock(bufHdr);
 
+				PageEncryptInplace(localpage, bufHdr->tag.forkNum,
+									bufHdr->tag.blockNum);
+
 				/* Setup error traceback support for ereport() */
 				errcallback.callback = local_buffer_write_error_callback;
 				errcallback.arg = (void *) bufHdr;
diff --git a/src/backend/storage/encryption/Makefile b/src/backend/storage/encryption/Makefile
new file mode 100644
index 0000000000..b9c722104c
--- /dev/null
+++ b/src/backend/storage/encryption/Makefile
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for storage/encryption
+#
+# IDENTIFICATION
+#    src/backend/storage/encryption/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/storage/encryption/
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = \
+	bufenc.o \
+	walenc.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/encryption/bufenc.c b/src/backend/storage/encryption/bufenc.c
new file mode 100644
index 0000000000..ed0242d857
--- /dev/null
+++ b/src/backend/storage/encryption/bufenc.c
@@ -0,0 +1,93 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufenc.c
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/encryption/bufenc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "storage/bufpage.h"
+#include "storage/encryption.h"
+#include "storage/fd.h"
+#include "crypto/kmgr.h"
+
+static uint8 buf_encryption_iv[PG_AES_IV_SIZE];
+static void set_buffer_encryption_iv(Page page, BlockNumber blocknum);
+
+void
+EncryptBufferBlock(BlockNumber blocknum, Page page)
+{
+	PgCipherCtx *relkeyctx;
+	const CryptoKey *relkey;
+	uint8 *key;
+	int	outlen;
+
+	relkey = KmgrGetKey(KMGR_REL_KEY_ID);
+	key = (uint8 *) pstrdup((const char *) relkey->key);
+	relkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, relkey->klen / 8);
+
+	set_buffer_encryption_iv(page, blocknum);
+	pg_cipher_encrypt(relkeyctx,
+					  (uint8*)(page + PageEncryptOffset),
+					  SizeOfPageEncryption,
+					  (uint8*)(page + PageEncryptOffset),
+					  &outlen,
+					  buf_encryption_iv);
+
+	Assert(SizeOfPageEncryption == outlen);
+}
+
+void
+DecryptBufferBlock(BlockNumber blocknum, Page page)
+{
+	PgCipherCtx *relkeyctx;
+	const CryptoKey *relkey;
+	uint8 *key;
+	int	outlen;
+
+	relkey = KmgrGetKey(KMGR_REL_KEY_ID);
+	key = (uint8 *) pstrdup((const char *) relkey->key);
+	relkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, relkey->klen / 8);
+
+	set_buffer_encryption_iv(page, blocknum);
+	pg_cipher_decrypt(relkeyctx,
+					  (uint8*)(page + PageEncryptOffset),
+					  SizeOfPageEncryption,
+					  (uint8*)(page + PageEncryptOffset),
+					  &outlen,
+					  buf_encryption_iv);
+
+	Assert(SizeOfPageEncryption == outlen);
+}
+
+/*
+ * Nonce for buffer encryption consists of page lsn, block number
+ * and counter. The counter is a counter value for CTR cipher mode.
+ */
+static void
+set_buffer_encryption_iv(Page page, BlockNumber blocknum)
+{
+	uint8   *p = buf_encryption_iv;
+
+	MemSet(buf_encryption_iv, 0, PG_AES_IV_SIZE);
+
+	/* page lsn (8 byte) */
+	memcpy(p, &((PageHeader) page)->pd_lsn, sizeof(PageXLogRecPtr));
+	p += sizeof(PageXLogRecPtr);
+
+	/* block number (4 byte) */
+	memcpy(p, &blocknum, sizeof(BlockNumber));
+	p += sizeof(BlockNumber);
+
+	/* Space for counter (4 byte) */
+	memset(p, 0, ENC_BUFFER_AES_COUNTER_SIZE);
+	p += ENC_BUFFER_AES_COUNTER_SIZE;
+}
+
diff --git a/src/backend/storage/encryption/walenc.c b/src/backend/storage/encryption/walenc.c
new file mode 100644
index 0000000000..97cecab558
--- /dev/null
+++ b/src/backend/storage/encryption/walenc.c
@@ -0,0 +1,110 @@
+/*-------------------------------------------------------------------------
+ *
+ * walenc.c
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/encryption/walenc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xlog.h"
+#include "access/xlog_internal.h"
+#include "storage/encryption.h"
+#include "crypto/kmgr.h"
+
+static uint8 wal_encryption_iv[PG_AES_IV_SIZE];
+static char wal_encryption_buf[XLOG_BLCKSZ];
+
+static void
+set_wal_encryption_iv(XLogSegNo segment, uint32 offset)
+{
+	uint8 *p = wal_encryption_iv;
+	uint32 pageno = offset / XLOG_BLCKSZ;
+
+	/* Space for counter (4 byte) */
+	memset(p, 0, ENC_WAL_AES_COUNTER_SIZE);
+	p += ENC_WAL_AES_COUNTER_SIZE;
+
+	/* Segment number (8 byte) */
+	memcpy(p, &segment, sizeof(XLogSegNo));
+	p += sizeof(XLogSegNo);
+
+	/* Page number within a WAL segment (4 byte) */
+	memcpy(p, &pageno, sizeof(uint32));
+}
+
+/*
+ * Copy the contents of WAL page and encrypt it. Returns the copied and
+ * encrypted WAL page.
+ */
+char *
+EncryptXLog(char *page, Size nbytes, XLogSegNo segno, uint32 offset)
+{
+#if 0
+	const CryptoKey *walkey;
+	PgCipherCtx *walkeyctx = NULL;
+	uint8	    *key;
+	int			 outlen;
+
+	Assert(nbytes <= XLOG_BLCKSZ);
+
+	walkey = KmgrGetKey(KMGR_WAL_KEY_ID);
+	key = (uint8 *) pstrdup((const char *) walkey->key);
+	walkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, walkey->klen / 8);
+
+	set_wal_encryption_iv(segno, offset);
+
+	/* Copy to work buffer */
+	memcpy(wal_encryption_buf, page, XLOG_BLCKSZ);
+
+	pg_cipher_encrypt(walkeyctx,
+					  (uint8*)(wal_encryption_buf + XLogEncryptionOffset),
+					  nbytes - XLogEncryptionOffset,
+					  (uint8*)(wal_encryption_buf + XLogEncryptionOffset),
+					  &outlen,
+					  wal_encryption_iv);
+
+	Assert(outlen == (nbytes - XLogEncryptionOffset));
+	return wal_encryption_buf;
+#endif
+	return page;
+}
+
+/*
+ * Decrypt a WAL page and return. Unlike EncryptXLog, this function encrypt
+ * the given buffer directly.
+ */
+void
+DecryptXLog(char *page, Size nbytes, XLogSegNo segno, uint32 offset)
+{
+#if 0
+	const CryptoKey *walkey;
+	PgCipherCtx *walkeyctx = NULL;
+	uint8	    *key;
+	int			 outlen;
+
+	Assert(nbytes <= XLOG_BLCKSZ);
+
+	walkey = KmgrGetKey(KMGR_WAL_KEY_ID);
+	key = (uint8 *) pstrdup((const char *) walkey->key);
+	walkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, walkey->klen / 8);
+
+	set_wal_encryption_iv(segno, offset);
+
+	pg_cipher_decrypt(walkeyctx,
+					  (uint8*)(page + XLogEncryptionOffset),
+					  nbytes - XLogEncryptionOffset,
+					  (uint8*)(page + XLogEncryptionOffset),
+					  &outlen,
+					  wal_encryption_iv);
+
+	Assert(outlen == (nbytes - XLogEncryptionOffset));
+#endif
+}
+
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index ddf18079e2..5ad4d35c44 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -19,6 +19,7 @@
 #include "access/xlog.h"
 #include "pgstat.h"
 #include "storage/checksum.h"
+#include "storage/encryption.h"
 #include "utils/memdebug.h"
 #include "utils/memutils.h"
 
@@ -26,7 +27,6 @@
 /* GUC variable */
 bool		ignore_checksum_failure = false;
 
-
 /* ----------------------------------------------------------------
  *						Page support functions
  * ----------------------------------------------------------------
@@ -85,7 +85,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
  * to pgstat.
  */
 bool
-PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
+PageIsVerifiedExtended(Page page, ForkNumber forknum, BlockNumber blkno, int flags)
 {
 	PageHeader	p = (PageHeader) page;
 	size_t	   *pagebytes;
@@ -108,6 +108,8 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 				checksum_failure = true;
 		}
 
+		PageDecryptInplace(page, forknum, blkno);
+
 		/*
 		 * The following checks don't prove the header is correct, only that
 		 * it looks sane enough to allow into the buffer pool. Later usage of
@@ -1427,3 +1429,47 @@ PageSetChecksumInplace(Page page, BlockNumber blkno)
 
 	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno);
 }
+
+char*
+PageEncryptCopy(Page page, ForkNumber forknum, BlockNumber blkno)
+{
+	static char *pageCopy = NULL;
+
+	if (PageIsNew(page) || !DataEncryptionEnabled() || !EncryptForkNum(forknum))
+		return (char *) page;
+
+	/*
+	 * We allocate the copy space once and use it over on each subsequent
+	 * call.  The point of palloc'ing here, rather than having a static char
+	 * array, is first to ensure adequate alignment for the checksumming code
+	 * and second to avoid wasting space in processes that never call this.
+	 */
+	if (pageCopy == NULL)
+		pageCopy = MemoryContextAlloc(TopMemoryContext, BLCKSZ);
+
+	memcpy(pageCopy, (char *) page, BLCKSZ);
+	EncryptBufferBlock(blkno, pageCopy);
+	return pageCopy;
+}
+
+void
+PageEncryptInplace(Page page, ForkNumber forknum, BlockNumber blkno)
+{
+	Assert(forknum <= MAX_FORKNUM && blkno != InvalidBlockNumber);
+
+	if (PageIsNew(page) || !DataEncryptionEnabled() || !EncryptForkNum(forknum))
+		return;
+
+	EncryptBufferBlock(blkno, page);
+}
+
+void
+PageDecryptInplace(Page page, ForkNumber forknum, BlockNumber blkno)
+{
+	Assert(forknum <= MAX_FORKNUM && blkno != InvalidBlockNumber);
+
+	if (PageIsNew(page) || !DataEncryptionEnabled() || !EncryptForkNum(forknum))
+		return;
+
+	DecryptBufferBlock(blkno, page);
+}
diff --git a/src/common/cipher_openssl.c b/src/common/cipher_openssl.c
index 1ab5a390e7..8a822c3901 100644
--- a/src/common/cipher_openssl.c
+++ b/src/common/cipher_openssl.c
@@ -34,6 +34,7 @@
 typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void);
 
 static ossl_EVP_cipher_func get_evp_aes_cbc(int klen);
+static ossl_EVP_cipher_func get_evp_aes_ctr(int klen);
 
 static ossl_EVP_cipher_func
 get_evp_aes_cbc(int klen)
@@ -51,6 +52,22 @@ get_evp_aes_cbc(int klen)
 	}
 }
 
+static ossl_EVP_cipher_func
+get_evp_aes_ctr(int klen)
+{
+	switch (klen)
+	{
+		case PG_AES128_KEY_LEN:
+			return EVP_aes_128_ctr;
+		case PG_AES192_KEY_LEN:
+			return EVP_aes_192_ctr;
+		case PG_AES256_KEY_LEN:
+			return EVP_aes_256_ctr;
+		default:
+			return NULL;
+	}
+}
+
 /*
  * Initialize and return an EVP_CIPHER_CTX. Return NULL if the given
  * cipher algorithm is not supported or on failure..
@@ -68,13 +85,16 @@ ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
 	{
 		case PG_CIPHER_AES_CBC:
 			func = get_evp_aes_cbc(klen);
-			if (!func)
-				goto failed;
+			break;
+		case PG_CIPHER_AES_CTR:
+			func = get_evp_aes_ctr(klen);
 			break;
 		default:
 			goto failed;
 	}
 
+	if (!func)
+		goto failed;
 
 	if (enc)
 		ret = EVP_EncryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
@@ -84,7 +104,7 @@ ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
 	if (!ret)
 		goto failed;
 
-	if (!EVP_CIPHER_CTX_set_key_length(ctx, PG_AES256_KEY_LEN))
+	if (!EVP_CIPHER_CTX_set_key_length(ctx, klen))
 		goto failed;
 
 	/*
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 4146753d47..488ae2987f 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -50,6 +50,7 @@ typedef struct XLogPageHeaderData
 } XLogPageHeaderData;
 
 #define SizeOfXLogShortPHD	MAXALIGN(sizeof(XLogPageHeaderData))
+#define XLogEncryptionOffset	SizeOfXLogShortPHD
 
 typedef XLogPageHeaderData *XLogPageHeader;
 
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 0b6d00dd7d..f268e396d2 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -209,6 +209,7 @@ struct XLogReaderState
 	 */
 	char	   *readBuf;
 	uint32		readLen;
+	bool		encrypted;	/* readBuf is encrypted? */
 
 	/* last read XLOG position for data currently in readBuf */
 	WALSegmentContext segcxt;
diff --git a/src/include/common/cipher.h b/src/include/common/cipher.h
index e3fb1d04b5..c8148714fd 100644
--- a/src/include/common/cipher.h
+++ b/src/include/common/cipher.h
@@ -25,7 +25,8 @@
  * algorithm.
  */
 #define PG_CIPHER_AES_CBC			0
-#define PG_MAX_CIPHER_ID			1
+#define PG_CIPHER_AES_CTR			1
+#define PG_MAX_CIPHER_ID			2
 
 /* AES128/192/256 various length definitions */
 #define PG_AES128_KEY_LEN			(128 / 8)
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index d0a52f8e08..67be7e50af 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -15,6 +15,7 @@
 #define BUFPAGE_H
 
 #include "access/xlogdefs.h"
+#include "common/relpath.h"
 #include "storage/block.h"
 #include "storage/item.h"
 #include "storage/off.h"
@@ -165,6 +166,9 @@ typedef struct PageHeaderData
 
 typedef PageHeaderData *PageHeader;
 
+#define PageEncryptOffset		offsetof(PageHeaderData, pd_linp)
+#define SizeOfPageEncryption	(BLCKSZ - PageEncryptOffset)
+
 /*
  * pd_flags contains the following flag bits.  Undefined bits are initialized
  * to zero and may be used in the future.
@@ -418,8 +422,8 @@ do { \
 						((overwrite) ? PAI_OVERWRITE : 0) | \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
-#define PageIsVerified(page, blkno) \
-	PageIsVerifiedExtended(page, blkno, \
+#define PageIsVerified(page, forknum, blkno) \
+	PageIsVerifiedExtended(page, forknum, blkno, \
 						   PIV_LOG_WARNING | PIV_REPORT_STAT)
 
 /*
@@ -433,7 +437,7 @@ StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
 				 "BLCKSZ has to be a multiple of sizeof(size_t)");
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
-extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
+extern bool PageIsVerifiedExtended(Page page, ForkNumber forknum, BlockNumber blkno, int flags);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
 extern Page PageGetTempPage(Page page);
@@ -452,5 +456,8 @@ extern bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum,
 									Item newtup, Size newsize);
 extern char *PageSetChecksumCopy(Page page, BlockNumber blkno);
 extern void PageSetChecksumInplace(Page page, BlockNumber blkno);
+extern char *PageEncryptCopy(Page page, ForkNumber forknum, BlockNumber blkno);
+extern void PageEncryptInplace(Page page, ForkNumber forknum, BlockNumber blkno);
+extern void PageDecryptInplace(Page page, ForkNumber forknum, BlockNumber blkno);
 
 #endif							/* BUFPAGE_H */
diff --git a/src/include/storage/encryption.h b/src/include/storage/encryption.h
new file mode 100644
index 0000000000..a61c47ea83
--- /dev/null
+++ b/src/include/storage/encryption.h
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * encryption.h
+ *	  Cluster encryption functions.
+ *
+ * Portions Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * src/include/storage/encryption.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ENCRYPTION_H
+#define ENCRYPTION_H
+
+#include "access/xlogdefs.h"
+#include "common/cipher.h"
+#include "crypto/kmgr.h"
+#include "storage/bufpage.h"
+
+#define DataEncryptionEnabled() true
+
+/* Cluster encryption encrypts only main fork */
+#define EncryptForkNum(forknum) \
+	((forknum) == MAIN_FORKNUM || (forknum) == INIT_FORKNUM)
+
+/*
+ * The size in byte for counter of AES-CTR mode in nonce.
+ */
+#define ENC_BUFFER_AES_COUNTER_SIZE 4
+#define ENC_WAL_AES_COUNTER_SIZE 4
+
+/* bufenc.c */
+extern void DecryptBufferBlock(BlockNumber blocknum, Page page);
+extern void EncryptBufferBlock(BlockNumber blocknum, Page page);
+
+/* walenc.c */
+extern char *EncryptXLog(char *page, Size nbytes, XLogSegNo segno,
+						uint32 offset);
+extern void DecryptXLog(char *page, Size nbytes, XLogSegNo segno,
+						uint32 offset);
+
+#endif							/* ENCRYPTION_H */
