From b536b4f9a6efc7dd546c79b56d7c634f3c372964 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Wed, 30 Oct 2019 16:18:30 +0900
Subject: [PATCH v2 3/5] Buffer encryption

---
 contrib/bloom/blinsert.c              |  1 +
 src/backend/access/hash/hashpage.c    |  1 +
 src/backend/access/heap/rewriteheap.c |  4 +++
 src/backend/access/nbtree/nbtree.c    |  1 +
 src/backend/access/nbtree/nbtsort.c   |  1 +
 src/backend/access/spgist/spginsert.c |  3 ++
 src/backend/catalog/storage.c         |  2 +-
 src/backend/storage/buffer/bufmgr.c   | 10 ++++--
 src/backend/storage/page/bufpage.c    | 50 ++++++++++++++++++++++++++-
 src/include/storage/bufpage.h         |  9 ++++-
 src/include/storage/encryption.h      |  9 +++++
 11 files changed, 86 insertions(+), 5 deletions(-)

diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index 30d17f501d..21183c3beb 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -177,6 +177,7 @@ blbuildempty(Relation index)
 	 * XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record.  Therefore, we need
 	 * this even when wal_level=minimal.
 	 */
+	PageEncryptInplace(metapage, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO);
 	PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
 	smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
 			  (char *) metapage, true);
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 838ee68c86..93d8876fa6 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1028,6 +1028,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 d41dbcf5f7..695e6106cc 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -338,6 +338,8 @@ 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,
@@ -709,6 +711,8 @@ 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 4cfd5289ad..61bc879a3e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -169,6 +169,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 ab19692006..5bbec955a8 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -679,6 +679,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 b40bd440cf..6dde15cd01 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/catalog/storage.c b/src/backend/catalog/storage.c
index 625af8d49a..1a16427776 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -370,7 +370,7 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
 
 		smgrread(src, forkNum, blkno, buf.data);
 
-		if (!PageIsVerified(page, blkno))
+		if (!PageIsVerified(page, forkNum, blkno))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg("invalid page in block %u of relation %s",
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 7ad10736d5..225333f80a 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -906,7 +906,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			}
 
 			/* check for garbage data */
-			if (!PageIsVerified((Page) bufBlock, blockNum))
+			if (!PageIsVerified((Page) bufBlock, forkNum, blockNum))
 			{
 				if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages)
 				{
@@ -2743,12 +2743,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);
@@ -3225,6 +3228,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/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 6b49810e37..64dbddacb6 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"
 
@@ -79,7 +80,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
  * will clean up such a page and make it usable.
  */
 bool
-PageIsVerified(Page page, BlockNumber blkno)
+PageIsVerified(Page page, ForkNumber forknum, BlockNumber blkno)
 {
 	PageHeader	p = (PageHeader) page;
 	size_t	   *pagebytes;
@@ -102,6 +103,8 @@ PageIsVerified(Page page, BlockNumber blkno)
 				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
@@ -1203,3 +1206,48 @@ 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/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 4ef6d8ddd4..f6b20dc658 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.
@@ -419,7 +423,7 @@ do { \
 						((is_heap) ? PAI_IS_HEAP : 0))
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
-extern bool PageIsVerified(Page page, BlockNumber blkno);
+extern bool PageIsVerified(Page page, ForkNumber forknum, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
 										OffsetNumber offsetNumber, int flags);
 extern Page PageGetTempPage(Page page);
@@ -438,5 +442,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
index a7dcd9282f..a8007fecb1 100644
--- a/src/include/storage/encryption.h
+++ b/src/include/storage/encryption.h
@@ -20,6 +20,10 @@
 #define DataEncryptionEnabled() \
 	(data_encryption_cipher > TDE_ENCRYPTION_OFF)
 
+/* Cluster encryption encrypts only main fork */
+#define EncryptForkNum(forknum) \
+	((forknum) == MAIN_FORKNUM || (forknum) == INIT_FORKNUM)
+
 /*
  * The encrypted data is a series of blocks of size ENCRYPTION_BLOCK.
  * Initialization vector(IV) is the same size of cipher block.
@@ -32,6 +36,11 @@
  */
 #define ENC_MAX_ENCRYPTION_KEY_SIZE	32
 
+/*
+ * The size in byte for counter of AES-CTR mode in nonce.
+ */
+#define ENC_BUFFER_AES_COUNTER_SIZE 4
+
 /* bufenc.c */
 extern void DecryptBufferBlock(BlockNumber blocknum, Page page);
 extern void EncryptBufferBlock(BlockNumber blocknum, Page page);
-- 
2.23.0

