From 7947b5452c58a4743586180bbde489c35887b253 Mon Sep 17 00:00:00 2001
From: Antonin Houska <ah@cybertec.at>
Date: Fri, 5 Jul 2019 16:24:01 +0200
Subject: [PATCH 08/17] Enable XLOG encryption.

---
 src/backend/access/transam/xlog.c      |  90 +++++++++++++++++-
 src/backend/access/transam/xlogutils.c |  58 +++++++++++-
 src/backend/replication/walsender.c    | 164 ++++++++++++++++++++++++---------
 src/backend/storage/file/encryption.c  |  80 ++++++++++++++++
 src/backend/utils/misc/guc.c           |  16 +++-
 src/include/access/xlog.h              |  16 +++-
 src/include/storage/encryption.h       |  17 +++-
 7 files changed, 392 insertions(+), 49 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index cce7eca641..2f026f274e 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -2489,7 +2489,61 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
-			startoffset += XLogWritePages(from, npages, startoffset);
+			if (data_encrypted)
+			{
+				int			i,
+							nencrypted;
+				char	   *to;
+				uint32		encr_offset;
+
+				/*
+				 * Encrypt and write multiple pages at a time, in order to
+				 * reduce the number of syscalls.
+				 */
+				nencrypted = 0;
+				to = encrypt_buf_xlog;
+				encr_offset = startoffset;
+				for (i = 1; i <= npages; i++)
+				{
+					char		tweak[TWEAK_SIZE];
+					Size		nbytes;
+
+					XLogEncryptionTweak(tweak, ThisTimeLineID, openLogSegNo, encr_offset);
+
+					/*
+					 * We should not encrypt the unused space, in order to
+					 * avoid "reused key attack".
+					 */
+					if (i == npages && ispartialpage)
+						nbytes = WriteRqst.Write % XLOG_BLCKSZ;
+					else
+						nbytes = XLOG_BLCKSZ;
+
+					encrypt_block(from, to, nbytes, tweak, true);
+					nencrypted++;
+					from += XLOG_BLCKSZ;
+					to += XLOG_BLCKSZ;
+					encr_offset += XLOG_BLCKSZ;
+
+					/*
+					 * Write the encrypted data if the encryption buffer is
+					 * full or if the last page has been encrypted.
+					 */
+					if (nencrypted >= XLOG_ENCRYPT_BUF_PAGES || i >= npages)
+					{
+						startoffset += XLogWritePages(encrypt_buf_xlog,
+													  nencrypted,
+													  startoffset);
+
+						/* Prepare for the next round of page encryptions. */
+						nencrypted = 0;
+						to = encrypt_buf_xlog;
+						encr_offset = startoffset;
+					}
+				}
+			}
+			else
+				startoffset += XLogWritePages(from, npages, startoffset);
 
 			npages = 0;
 
@@ -3479,6 +3533,24 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno,
 			}
 			pgstat_report_wait_end();
 		}
+
+		/*
+		 * Since timeline is being changed and since encryption tweak contains
+		 * the timeline, we need to decrypt the buffer and encrypt it with the
+		 * new tweak. Do not encrypt the unused space, in order to avoid
+		 * "reused key attack".
+		 */
+		if (data_encrypted && nread > 0)
+		{
+			char		tweak[TWEAK_SIZE];
+
+			XLogEncryptionTweak(tweak, srcTLI, srcsegno, nbytes);
+			decrypt_block(buffer.data, buffer.data, nread, tweak, true);
+
+			XLogEncryptionTweak(tweak, ThisTimeLineID, destsegno, nbytes);
+			encrypt_block(buffer.data, buffer.data, nread, tweak, true);
+		}
+
 		errno = 0;
 		pgstat_report_wait_start(WAIT_EVENT_WAL_COPY_WRITE);
 		if ((int) write(fd, buffer.data, sizeof(buffer)) != (int) sizeof(buffer))
@@ -5273,6 +5345,14 @@ BootStrapXLOG(void)
 	use_existent = false;
 	openLogFile = XLogFileInit(1, &use_existent, false);
 
+	if (data_encrypted)
+	{
+		char		tweak[TWEAK_SIZE];
+
+		XLogEncryptionTweak(tweak, ThisTimeLineID, 1, 0);
+		encrypt_block((char *) page, (char *) page, XLOG_BLCKSZ, tweak, true);
+	}
+
 	/* Write the first page with the initial record */
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_WAL_BOOTSTRAP_WRITE);
@@ -11724,6 +11804,14 @@ retry:
 	Assert(targetPageOff == readOff);
 	Assert(reqLen <= readLen);
 
+	if (data_encrypted)
+	{
+		char		tweak[TWEAK_SIZE];
+
+		XLogEncryptionTweak(tweak, curFileTLI, readSegNo, readOff);
+		decrypt_block(readBuf, readBuf, XLOG_BLCKSZ, tweak, true);
+	}
+
 	*readTLI = curFileTLI;
 
 	/*
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 10a663bae6..25139e7a86 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"
@@ -656,9 +657,11 @@ static void
 XLogRead(char *buf, int segsize, TimeLineID tli, XLogRecPtr startptr,
 		 Size count)
 {
-	char	   *p;
 	XLogRecPtr	recptr;
 	Size		nbytes;
+	char	   *decrypt_p;
+	uint32		decryptOff;
+	char	   *p;
 
 	/* state maintained across calls */
 	static int	sendFile = -1;
@@ -668,17 +671,46 @@ XLogRead(char *buf, int segsize, TimeLineID tli, XLogRecPtr startptr,
 
 	Assert(segsize == wal_segment_size);
 
+	/*
+	 * TODO Currently the function is only called with startptr at page
+	 * boundary. walsender.c:XLogRead() is more generic but it's a bit
+	 * different, so decryption also needs to be adopted in a little different
+	 * way (i.e. the decryption cannot be merged here mechanically). Manual
+	 * merge is not worth the effort right now because a separate patch to
+	 * consolidate XLogRead() functions is in the queue
+	 * (https://commitfest.postgresql.org/23/2098/). Let's implement the
+	 * decryption in a generic way when that patch is committed.
+	 */
+	Assert(startptr % XLOG_BLCKSZ == 0);
+
 	p = buf;
 	recptr = startptr;
 	nbytes = count;
 
+	decrypt_p = p;
+	decryptOff = XLogSegmentOffset(recptr, segsize);
+
 	while (nbytes > 0)
 	{
 		uint32		startoff;
 		int			segbytes;
 		int			readbytes;
 
-		startoff = XLogSegmentOffset(recptr, segsize);
+		if (recptr == startptr)
+			startoff = decryptOff;
+		else
+		{
+			startoff = XLogSegmentOffset(recptr, segsize);
+			if (startoff == 0)
+			{
+				/*
+				 * If segment boundary was reached, decryptOff should have
+				 * caught up, so we can (and should) sync it with startoff.
+				 */
+				Assert(decryptOff == segsize);
+				decryptOff = startoff;
+			}
+		}
 
 		/* Do we need to switch to a different xlog segment? */
 		if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo, segsize) ||
@@ -758,7 +790,29 @@ XLogRead(char *buf, int segsize, TimeLineID tli, XLogRecPtr startptr,
 		sendOff += readbytes;
 		nbytes -= readbytes;
 		p += readbytes;
+
+		/* Decrypt completed blocks */
+		if (data_encrypted)
+		{
+			while (decrypt_p + XLOG_BLCKSZ <= p)
+			{
+				char		tweak[TWEAK_SIZE];
+
+				XLogEncryptionTweak(tweak, tli, sendSegNo, decryptOff);
+				decrypt_block(decrypt_p, decrypt_p, XLOG_BLCKSZ, tweak,
+							  true);
+
+				decrypt_p += XLOG_BLCKSZ;
+				decryptOff += XLOG_BLCKSZ;
+			}
+		}
 	}
+
+	/*
+	 * TODO Currently the function is only called with count==XLOG_BLCKSZ. See
+	 * the comment on consolidation of XLogRead() above.
+	 */
+	Assert(decrypt_p == p);
 }
 
 /*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 92fa86fc9d..ae9c1418d1 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"
@@ -256,7 +257,9 @@ static void LagTrackerWrite(XLogRecPtr lsn, TimestampTz local_flush_time);
 static TimeOffset LagTrackerRead(int head, XLogRecPtr lsn, TimestampTz now);
 static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch);
 
-static void XLogRead(char *buf, XLogRecPtr startptr, Size count);
+static int	XLogReadBuffer(char *buf, int nbytes, int startoff);
+static void XLogRead(char *buf, XLogRecPtr startptr, Size count,
+		 bool decrypt);
 
 
 /* Initialize walsender process before entering the main command loop */
@@ -787,7 +790,7 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
 		count = flushptr - targetPagePtr;	/* part of the page available */
 
 	/* now actually read the data, we know it's there */
-	XLogRead(cur_page, targetPagePtr, XLOG_BLCKSZ);
+	XLogRead(cur_page, targetPagePtr, XLOG_BLCKSZ, true);
 
 	return count;
 }
@@ -2352,19 +2355,53 @@ WalSndKill(int code, Datum arg)
 	SpinLockRelease(&walsnd->mutex);
 }
 
+static int
+XLogReadBuffer(char *buf, int nbytes, int startoff)
+{
+	int			readbytes;
+
+	/* Need to seek in the file? */
+	if (sendOff != startoff)
+	{
+		if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not seek in log segment %s to offset %u: %m",
+							XLogFileNameP(curFileTimeLine, sendSegNo),
+							startoff)));
+		sendOff = startoff;
+	}
+
+	pgstat_report_wait_start(WAIT_EVENT_WAL_READ);
+	readbytes = read(sendFile, buf, nbytes);
+	pgstat_report_wait_end();
+	if (readbytes <= 0)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read from log segment %s, offset %u, length %lu: %m",
+						XLogFileNameP(curFileTimeLine, sendSegNo),
+						sendOff, (unsigned long) nbytes)));
+	}
+
+	return readbytes;
+}
+
 /*
- * Read 'count' bytes from WAL into 'buf', starting at location 'startptr'
+ * Read 'count' bytes from WAL into 'buf', starting at location
+ * 'startptr'. Decrypt the data if it's encrypted and if caller wants it
+ * decrypted.
  *
- * XXX probably this should be improved to suck data directly from the
- * WAL buffers when possible.
+ * XXX probably this should be improved to fetch data directly from the WAL
+ * buffers when possible.
  *
  * Will open, and keep open, one WAL segment stored in the global file
- * descriptor sendFile. This means if XLogRead is used once, there will
- * always be one descriptor left open until the process ends, but never
- * more than one.
+ * descriptor sendFile. This means if XLogRead is used once, there will always
+ * be one descriptor left open until the process ends, but never more than
+ * one.
  */
 static void
-XLogRead(char *buf, XLogRecPtr startptr, Size count)
+XLogRead(char *buf, XLogRecPtr startptr, Size count, bool decrypt)
 {
 	char	   *p;
 	XLogRecPtr	recptr;
@@ -2454,42 +2491,87 @@ retry:
 			sendOff = 0;
 		}
 
-		/* Need to seek in the file? */
-		if (sendOff != startoff)
+		/* Caller should not request decryption of unencrypted data. */
+		Assert(!(decrypt && !data_encrypted));
+
+		if (data_encrypted && decrypt)
 		{
-			if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0)
-				ereport(ERROR,
-						(errcode_for_file_access(),
-						 errmsg("could not seek in log segment %s to offset %u: %m",
-								XLogFileNameP(curFileTimeLine, sendSegNo),
-								startoff)));
-			sendOff = startoff;
-		}
+			int			pageoff = startoff % XLOG_BLCKSZ;
+			uint32		pagebase = startoff - pageoff;
+			int			bufbytes,
+						bufend,
+						i;
+			char		tweak[TWEAK_SIZE];
 
-		/* How many bytes are within this segment? */
-		if (nbytes > (wal_segment_size - startoff))
-			segbytes = wal_segment_size - startoff;
-		else
-			segbytes = nbytes;
+			/*
+			 * Only accept as much data as what can fit into the buffer.
+			 */
+			if (nbytes > (ENCRYPT_BUF_XLOG_SIZE - pageoff))
+				bufbytes = ENCRYPT_BUF_XLOG_SIZE - pageoff;
+			else
+				bufbytes = nbytes;
+			bufend = pageoff + bufbytes;
 
-		pgstat_report_wait_start(WAIT_EVENT_WAL_READ);
-		readbytes = read(sendFile, p, segbytes);
-		pgstat_report_wait_end();
-		if (readbytes < 0)
-		{
-			ereport(ERROR,
-					(errcode_for_file_access(),
-					 errmsg("could not read from log segment %s, offset %u, length %zu: %m",
-							XLogFileNameP(curFileTimeLine, sendSegNo),
-							sendOff, (Size) segbytes)));
+			/*
+			 * Read the data, including the leading part of the page which
+			 * caller is not interested in. The tweak we passed to
+			 * encrypt_block() for encryption was for the beginning of the
+			 * block, so it'd be hard to start decryption anywhere else.
+			 */
+			readbytes = 0;
+			while (readbytes < bufend)
+				readbytes += XLogReadBuffer(encrypt_buf_xlog + readbytes,
+											bufend - readbytes,
+											pagebase + readbytes);
+
+			/*
+			 * Decrypt the data one page at a time (the tweak is only valid
+			 * for particular page).
+			 */
+			for (i = 0; i < readbytes; i += XLOG_BLCKSZ)
+			{
+				Size		nencrypt;
+
+				XLogEncryptionTweak(tweak,
+									curFileTimeLine,
+									sendSegNo,
+									pagebase + i);
+
+				/*
+				 * If the last page is not complete, only decrypt the used
+				 * part.
+				 */
+				if ((bufend - i) < XLOG_BLCKSZ)
+					nencrypt = bufend - i;
+				else
+					nencrypt = XLOG_BLCKSZ;
+
+				decrypt_block(encrypt_buf_xlog + i,
+							  encrypt_buf_xlog + i,
+							  nencrypt,
+							  tweak,
+							  true);
+			}
+
+			/*
+			 * Caller does not care that we possibly had to read pageoff bytes
+			 * in addition (because we cannot decrypt trailing part of the
+			 * page alone). This overhead must not affect the accounting.
+			 */
+			readbytes = bufbytes;
+
+			/* Copy the data to the output buffer. */
+			memcpy(p, encrypt_buf_xlog + pageoff, bufbytes);
 		}
-		else if (readbytes == 0)
+		else
 		{
-			ereport(ERROR,
-					(errcode(ERRCODE_DATA_CORRUPTED),
-					 errmsg("could not read from log segment %s, offset %u: read %d of %zu",
-							XLogFileNameP(curFileTimeLine, sendSegNo),
-							sendOff, readbytes, (Size) segbytes)));
+			/* How many bytes are within this segment? */
+			if (nbytes > (wal_segment_size - startoff))
+				segbytes = wal_segment_size - startoff;
+			else
+				segbytes = nbytes;
+
+			readbytes = XLogReadBuffer(p, segbytes, startoff);
 		}
 
 		/* Update state for read */
@@ -2768,7 +2850,7 @@ XLogSendPhysical(void)
 	 * calls.
 	 */
 	enlargeStringInfo(&output_message, nbytes);
-	XLogRead(&output_message.data[output_message.len], startptr, nbytes);
+	XLogRead(&output_message.data[output_message.len], startptr, nbytes, false);
 	output_message.len += nbytes;
 	output_message.data[output_message.len] = '\0';
 
diff --git a/src/backend/storage/file/encryption.c b/src/backend/storage/file/encryption.c
index 1eda334762..971b6928e4 100644
--- a/src/backend/storage/file/encryption.c
+++ b/src/backend/storage/file/encryption.c
@@ -63,6 +63,8 @@ char encryption_verification[ENCRYPTION_SAMPLE_SIZE];
 
 bool	encryption_setup_done = false;
 
+char	   *encrypt_buf_xlog = NULL;
+
 #ifdef USE_ENCRYPTION
 static void init_encryption_context(EVP_CIPHER_CTX **ctx_p, bool stream);
 static void evp_error(void);
@@ -165,6 +167,21 @@ setup_encryption(void)
 	init_encryption_context(&ctx_encrypt_stream, true);
 	init_encryption_context(&ctx_decrypt_stream, true);
 
+	/*
+	 * We need multiple pages here, so allocate the memory dynamically instead
+	 * of using PGAlignedBlock. That also ensures it'll be MAXALIGNed, which
+	 * is useful because the buffer will be used for I/O.
+	 *
+	 * Use TopMemoryContext because on server side this code is run by
+	 * postmaster and postmaster context gets freed after fork().
+	 */
+#ifndef FRONTEND
+	encrypt_buf_xlog = (char *) MemoryContextAlloc(TopMemoryContext,
+												   ENCRYPT_BUF_XLOG_SIZE);
+#else
+	encrypt_buf_xlog = (char *) palloc(ENCRYPT_BUF_XLOG_SIZE);
+#endif
+
 	encryption_setup_done = true;
 #else  /* !USE_ENCRYPTION */
 #ifndef FRONTEND
@@ -264,6 +281,50 @@ encrypt_block(const char *input, char *output, Size size, char *tweak,
 #endif							/* USE_ENCRYPTION */
 }
 
+/*
+ * Decrypts one block of data with a specified tweak value. May only be called
+ * when encryption_enabled is true.
+ *
+ * Input and output buffer may point to the same location.
+ *
+ * For detailed comments see encrypt_block().
+ */
+void
+decrypt_block(const char *input, char *output, Size size, char *tweak,
+			  bool stream)
+{
+#ifdef USE_ENCRYPTION
+	int			out_size;
+	EVP_CIPHER_CTX *ctx;
+
+	Assert(data_encrypted);
+	Assert((size >= ENCRYPTION_BLOCK && size % ENCRYPTION_BLOCK == 0) ||
+		   stream);
+
+	if (!stream && IsAllZero(input, size))
+	{
+		memset(output, 0, size);
+		return;
+	}
+
+	ctx = !stream ? ctx_decrypt : ctx_decrypt_stream;
+
+	/* The remaining initialization. */
+	if (EVP_DecryptInit_ex(ctx, NULL, NULL, encryption_key,
+						   (unsigned char *) tweak) != 1)
+		evp_error();
+
+	/* Do the actual encryption. */
+	if (EVP_DecryptUpdate(ctx, (unsigned char *) output,
+						  &out_size, (unsigned char *) input, size) != 1)
+		evp_error();
+
+	Assert(out_size == size);
+#else
+	/* data_encrypted should not be set */
+	Assert(false);
+#endif							/* USE_ENCRYPTION */
+}
 
 #ifdef USE_ENCRYPTION
 /*
@@ -345,3 +406,22 @@ evp_error(void)
 #endif							/* FRONTEND */
 }
 #endif							/* USE_ENCRYPTION */
+
+/*
+ * Xlog is encrypted page at a time. Each xlog page gets a unique tweak via
+ * timeline, segment and offset.
+ *
+ * The function is located here rather than some of the xlog*.c modules so
+ * that front-end applications can easily use it too.
+ */
+void
+XLogEncryptionTweak(char *tweak, TimeLineID timeline, XLogSegNo segment,
+					uint32 offset)
+{
+	memset(tweak, 0, TWEAK_SIZE);
+	memcpy(tweak, &timeline, sizeof(timeline));
+	tweak += sizeof(timeline);
+	memcpy(tweak, &segment, sizeof(XLogSegNo));
+	tweak += sizeof(XLogSegNo);
+	memcpy(tweak, &offset, sizeof(offset));
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2b7fc2fb0f..440120635e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -173,6 +173,7 @@ static void assign_session_replication_role(int newval, void *extra);
 static bool check_temp_buffers(int *newval, void **extra, GucSource source);
 static bool check_bonjour(bool *newval, void **extra, GucSource source);
 static bool check_ssl(bool *newval, void **extra, GucSource source);
+static bool check_full_page_writes(bool *newval, void **extra, GucSource source);
 static bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
 static bool check_log_stats(bool *newval, void **extra, GucSource source);
 static bool check_canonical_path(char **newval, void **extra, GucSource source);
@@ -1173,7 +1174,7 @@ static struct config_bool ConfigureNamesBool[] =
 		},
 		&fullPageWrites,
 		true,
-		NULL, NULL, NULL
+		check_full_page_writes, NULL, NULL
 	},
 
 	{
@@ -11105,6 +11106,19 @@ check_ssl(bool *newval, void **extra, GucSource source)
 }
 
 static bool
+check_full_page_writes(bool *newval, void **extra, GucSource source)
+{
+	if (!(*newval) && data_encrypted)
+	{
+		GUC_check_errdetail("Cannot disable parameter when the cluster is encrypted.");
+
+		return false;
+	}
+
+	return true;
+}
+
+static bool
 check_stage_log_stats(bool *newval, void **extra, GucSource source)
 {
 	if (*newval && log_statement_stats)
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d519252aad..2ad441623d 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -19,6 +19,7 @@
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
 #include "storage/fd.h"
+#include "storage/encryption.h"
 
 
 /* Sync methods */
@@ -186,10 +187,19 @@ extern PGDLLIMPORT int wal_level;
  * Normally, we don't WAL-log hint bit updates, but if checksums are enabled,
  * we have to protect them against torn page writes.  When you only set
  * individual bits on a page, it's still consistent no matter what combination
- * of the bits make it to disk, but the checksum wouldn't match.  Also WAL-log
- * them if forced by wal_log_hints=on.
+ * of the bits make it to disk, but the checksum wouldn't match.
+ *
+ * Regardless checksums, if encryption is enabled, hint bit change followed by
+ * a torn page write can result in such a situation that decryption produces
+ * page whose contents (following the hint bit) is garbage. This is because
+ * the block cipher we use propagates changes in the lower addresses of plain
+ * data to higher addresses of the cipher data. We need full-page image also
+ * to recover from this state.
+ *
+ * Also WAL-log the hint bits if forced by wal_log_hints=on.
  */
-#define XLogHintBitIsNeeded() (DataChecksumsEnabled() || wal_log_hints)
+#define XLogHintBitIsNeeded() (DataChecksumsEnabled() || data_encrypted || \
+							   wal_log_hints)
 
 /* Do we need to WAL-log information required only for Hot Standby and logical replication? */
 #define XLogStandbyInfoActive() (wal_level >= WAL_LEVEL_REPLICA)
diff --git a/src/include/storage/encryption.h b/src/include/storage/encryption.h
index 72bcae0972..c6ad9b43bd 100644
--- a/src/include/storage/encryption.h
+++ b/src/include/storage/encryption.h
@@ -116,6 +116,16 @@ extern char encryption_verification[];
 /* Do we have encryption_key and the encryption library initialized? */
 extern bool	encryption_setup_done;
 
+/*
+ * XLOG encryption/decryption buffer. This buffer spans multiple pages, in
+ * order to reduce the number of syscalls when doing I/O.
+ *
+ * XXX Fine tune the buffer size.
+ */
+extern char *encrypt_buf_xlog;
+#define	XLOG_ENCRYPT_BUF_PAGES	8
+#define ENCRYPT_BUF_XLOG_SIZE	(XLOG_ENCRYPT_BUF_PAGES * XLOG_BLCKSZ)
+
 #ifndef FRONTEND
 extern Size EncryptionShmemSize(void);
 extern void EncryptionShmemInit(void);
@@ -127,7 +137,12 @@ extern void read_encryption_key(read_encryption_key_cb read_char);
 extern void setup_encryption(void);
 extern void sample_encryption(char *buf);
 extern void encrypt_block(const char *input, char *output, Size size,
-							char *tweak, bool stream);
+			  char *tweak, bool stream);
+extern void decrypt_block(const char *input, char *output, Size size,
+			  char *tweak, bool stream);
 extern void encryption_error(bool fatal, char *message);
 
+extern void XLogEncryptionTweak(char *tweak, TimeLineID timeline,
+					XLogSegNo segment, uint32 offset);
+
 #endif							/* ENCRYPTION_H */
-- 
2.13.7

