From 96bd043f2f61e0378674216eb6e4c02ec5f4a963 Mon Sep 17 00:00:00 2001
From: Antonin Houska <ah@cybertec.at>
Date: Fri, 5 Jul 2019 16:24:02 +0200
Subject: [PATCH 17/17] User and development documentation.

---
 doc/src/sgml/config.sgml                   |  14 ++
 doc/src/sgml/encryption.sgml               |  95 ++++++++++++++
 doc/src/sgml/filelist.sgml                 |   1 +
 doc/src/sgml/installation.sgml             |  14 +-
 doc/src/sgml/postgres.sgml                 |   1 +
 doc/src/sgml/ref/allfiles.sgml             |   1 +
 doc/src/sgml/ref/initdb.sgml               |  11 ++
 doc/src/sgml/ref/pg_checksums.sgml         |  13 +-
 doc/src/sgml/ref/pg_ctl-ref.sgml           |  11 ++
 doc/src/sgml/ref/pg_keytool.sgml           | 121 +++++++++++++++++
 doc/src/sgml/ref/pg_resetwal.sgml          |  11 ++
 doc/src/sgml/ref/pg_rewind.sgml            |  11 ++
 doc/src/sgml/ref/pg_waldump.sgml           |  11 ++
 doc/src/sgml/ref/pgupgrade.sgml            |  31 +++++
 doc/src/sgml/reference.sgml                |   1 +
 src/backend/storage/file/README.encryption | 202 +++++++++++++++++++++++++++++
 16 files changed, 542 insertions(+), 7 deletions(-)
 create mode 100644 doc/src/sgml/encryption.sgml
 create mode 100644 doc/src/sgml/ref/pg_keytool.sgml
 create mode 100644 src/backend/storage/file/README.encryption

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index c91e3e1550..5d64736a03 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9004,6 +9004,20 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-data-encryption" xreflabel="data_encryption">
+      <term><varname>data_encryption</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>data_encryption</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Reports whether data encryption is enabled for this cluster.
+        See <xref linkend="encryption"/> for more information.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-data-directory-mode" xreflabel="data_directory_mode">
       <term><varname>data_directory_mode</varname> (<type>integer</type>)
       <indexterm>
diff --git a/doc/src/sgml/encryption.sgml b/doc/src/sgml/encryption.sgml
new file mode 100644
index 0000000000..5913883bc0
--- /dev/null
+++ b/doc/src/sgml/encryption.sgml
@@ -0,0 +1,95 @@
+<!-- doc/src/sgml/encryption.sgml -->
+
+<chapter id="encryption">
+ <title>Transparent Cluster Encryption</title>
+
+ <para>
+  Cluster encryption can be used if <acronym>DBA</acronym> cannot or does not
+  want to rely on the filesystem in terms of data confidentiality. If this
+  feature is enabled, <productname>PostgreSQL</productname> encrypts data
+  (both relations and write-ahead-log) when writing it to disk, and decrypts
+  when reading it. The encryption is transparent, so applications see no
+  difference between encrypted and unencrypted cluster.
+ </para>
+
+ <para>
+  To create encrypted cluster, use the <option>-K</option> option to pass the
+  <xref linkend="app-initdb"/> utility path to the command that will retrieve
+  the encryption key. For example:
+<screen>
+<prompt>$</prompt> <userinput>initdb -D /usr/local/pgsql/data -K /usr/local/pgsql/fetch_key_cmd</userinput>
+</screen>
+  Here <filename>/usr/local/pgsql/fetch_key_cmd</filename> is an executable
+  file that writes the encryption key to its standard output and returns
+  zero. The simplest command looks like:
+
+<programlisting>
+echo &lt;your key&gt;
+</programlisting>
+ </para>
+
+ <para>
+   The encryption key is expected in hexadecimal format, two characters
+   (hexadecimal digits) per
+   byte. Since <productname>PostgreSQL</productname> currently uses key
+   of length 32 bytes (256 bits), the expected length of the key string
+   is 64 characters. For example:
+   <computeroutput>
+     882fb7c12e80280fd664c69d2d636913e86c381ba487c82f77653c0fac8ffc69
+   </computeroutput>
+ </para>
+
+ <para>
+   If the DBA wants to enter password, he can use
+   <xref linkend="app-pg-keytool"/> utility to derive ecryption key from
+   it. For example, if the <option>-K</option> option looks like in the
+   following example, the DBA will be asked for password interactively:
+
+<screen>
+<prompt>$</prompt> <userinput>initdb -K 'read -sp "Cluster encryption password: " PGENCRPWD; echo $PGENCRPWD
+| pg_keytool -D %D -w' -D data
+</userinput>
+</screen>
+
+ </para>
+
+ <para>
+   As long as the application is aware of the data directory, you can
+   put <literal>%D</literal> into the command and the application will replace
+   it with the actual directory path.
+ </para>
+
+ <para>
+   Likewise, the same command can be passed to <xref linkend="app-pg-ctl"/> in
+   order to start the cluster:
+
+<screen>
+<prompt>$</prompt> <userinput>pg_ctl -K 'read -sp "Cluster encryption password: " PGENCRPWD; echo $PGENCRPWD
+| pg_keytool -D %D -w' -D data start
+</userinput>
+</screen>
+ </para>
+
+ <para>
+  If encryption is enabled, <xref linkend="guc-full-page-writes"/> must be
+  turned on, otherwise the server refuses to start. This is because the
+  encryption introduces dependencies between data within a page, and thus
+  server crash during disk write can result in more serious damage of the page
+  than it would do without encryption. The whole page needs to be retrieved
+  from WAL in such a case to ensure reliable recovery.
+ </para>
+
+ <para>
+  Once the <productname>PostgreSQL</productname> server is running, client
+  applications should recognize no difference from an unencrypted cluster,
+  except that <xref linkend="guc-data-encryption"/> variable is set.
+ </para>
+
+ <para>
+  Since WAL is encrypted, any replication solution based on log shipping
+  (<xref linkend="warm-standby"/>) assume that all standby servers are
+  encrypted using the same key as their primary server. On the other hand,
+  <xref linkend="logical-replication"/> allows replication between encrypted
+  and unencrypted clusters, or between clusters encrypted with different keys.
+ </para>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 3da2365ea9..d21dabf03b 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -48,6 +48,7 @@
 <!ENTITY wal           SYSTEM "wal.sgml">
 <!ENTITY logical-replication    SYSTEM "logical-replication.sgml">
 <!ENTITY jit    SYSTEM "jit.sgml">
+<!ENTITY encryption    SYSTEM "encryption.sgml">
 
 <!-- programmer's guide -->
 <!ENTITY bgworker   SYSTEM "bgworker.sgml">
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 449386243b..a2daf33e6f 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -245,8 +245,9 @@ su - postgres
     <listitem>
      <para>
       You need <productname>OpenSSL</productname>, if you want to support
-      encrypted client connections.  <productname>OpenSSL</productname> is
-      also required for random number generation on platforms that do not
+      on-disk data encryption or encrypted client
+      connections.  <productname>OpenSSL</productname> is also required for
+      random number generation on platforms that do not
       have <filename>/dev/urandom</filename> (except Windows).  The minimum
       version required is 0.9.8.
      </para>
@@ -835,10 +836,11 @@ su - postgres
        </term>
        <listitem>
         <para>
-         Build with support for <acronym>SSL</acronym> (encrypted)
-         connections. This requires the <productname>OpenSSL</productname>
-         package to be installed.  <filename>configure</filename> will check
-         for the required header files and libraries to make sure that
+         Build with support for on-disk data encryption
+         or <acronym>SSL</acronym> (encrypted) connections. This requires
+         the <productname>OpenSSL</productname> package to be
+         installed.  <filename>configure</filename> will check for the
+         required header files and libraries to make sure that
          your <productname>OpenSSL</productname> installation is sufficient
          before proceeding.
         </para>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 3e115f1c76..b0d35600b2 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -163,6 +163,7 @@
   &wal;
   &logical-replication;
   &jit;
+  &encryption;
   &regress;
 
  </part>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 8d91f3529e..a60b02e23f 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -206,6 +206,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pgDump             SYSTEM "pg_dump.sgml">
 <!ENTITY pgDumpall          SYSTEM "pg_dumpall.sgml">
 <!ENTITY pgIsready          SYSTEM "pg_isready.sgml">
+<!ENTITY pgKeytool          SYSTEM "pg_keytool.sgml">
 <!ENTITY pgReceivewal       SYSTEM "pg_receivewal.sgml">
 <!ENTITY pgRecvlogical      SYSTEM "pg_recvlogical.sgml">
 <!ENTITY pgResetwal         SYSTEM "pg_resetwal.sgml">
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 7fc3152c6d..ce19151521 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -209,6 +209,17 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-data-encr-cmd" xreflabel="data encryption">
+      <term><option>-K</option></term>
+      <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        Encrypt the cluster data using a key retrieved from the command
+        specified here. See <xref linkend="encryption"/> for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-data-checksums" xreflabel="data checksums">
       <term><option>-k</option></term>
       <term><option>--data-checksums</option></term>
diff --git a/doc/src/sgml/ref/pg_checksums.sgml b/doc/src/sgml/ref/pg_checksums.sgml
index 33706d1d97..7a2cae5e57 100644
--- a/doc/src/sgml/ref/pg_checksums.sgml
+++ b/doc/src/sgml/ref/pg_checksums.sgml
@@ -106,7 +106,18 @@ PostgreSQL documentation
       <listitem>
        <para>
         Only validate checksums in the relation with filenode
-        <replaceable>filenode</replaceable>. 
+        <replaceable>filenode</replaceable>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-K</option></term>
+      <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        If the cluster is encrypted, run this command to retrieve the
+        encryption key. See <xref linkend="encryption"/> for details.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml
index e31275a04e..48966eb58e 100644
--- a/doc/src/sgml/ref/pg_ctl-ref.sgml
+++ b/doc/src/sgml/ref/pg_ctl-ref.sgml
@@ -293,6 +293,17 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-K</option></term>
+      <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        If the cluster is encrypted, run this command to retrieve the
+        encryption key. See <xref linkend="encryption"/> for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-l <replaceable class="parameter">filename</replaceable></option></term>
       <term><option>--log=<replaceable class="parameter">filename</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_keytool.sgml b/doc/src/sgml/ref/pg_keytool.sgml
new file mode 100644
index 0000000000..3c15910885
--- /dev/null
+++ b/doc/src/sgml/ref/pg_keytool.sgml
@@ -0,0 +1,121 @@
+<!--
+doc/src/sgml/ref/pg_keytool.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="app-pg-keytool">
+ <indexterm zone="app-pg-keytool">
+  <primary>pg_keysetup</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle><application>pg_keytool</application></refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>Application</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pg_keytool</refname>
+  <refpurpose>produce cluster encryption key, possibly deriving it from a
+  passphrase</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pg_keysetup</command>
+   <arg rep="repeat"><replaceable class="parameter">option</replaceable></arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+  <para>
+   <application>pg_keytool</application> reads encryption key or password from
+   the standard input. If it received password, it runs <firstterm>key
+   derivation function</firstterm> on it in order to derive the key. The
+   encryption key is written either to the standard output or sent to via
+   frontend/backend protocol to postmaster process which is being started up.
+  </para>
+
+  <para>
+    One common use case is that <application>pg_keytool</application> is used
+    within encryption key command, see the examples in
+    <xref linkend="encryption"/>. In this case it sends the key to the
+    standard output.
+  </para>
+
+  <para>
+    The other use case is that <productname>PostgreSQL</productname> is
+    started other way than using <xref linkend="app-pg-ctl"/>, typically
+    via <firstterm>systemd</firstterm> or a custom script. In this
+    case <application>pg_keytool</application> can be used to send the key to
+    specified TCP/IP port or to an Unix socket, on which the server
+    listens. This is the same port or socket to which applications will
+    eventually connect, but when the encrypted cluster starts up, it only
+    accepts the key.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Options</title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><option>-D <replaceable>directory</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies the directory where the database cluster is
+        stored. Different directory generally means that a different key will
+        be derived from the same password.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+       <term><option>-h <replaceable class="parameter">hostname</replaceable></option></term>
+       <term><option>--host=<replaceable class="parameter">hostname</replaceable></option></term>
+       <listitem>
+         <para>
+           Specifies the host name of the machine on which the server is
+           listening. If the value begins with a slash, it is used as the
+           directory for the Unix-domain socket.
+         </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry>
+       <term><option>-p <replaceable class="parameter">port</replaceable></option></term>
+       <listitem>
+         <para>
+           Specifies the TCP port or the local Unix-domain socket file
+           extension on which the server is listening for
+           connections. Defaults to the value of the <envar>PGPORT</envar>
+           environment variable or, if not set, to the port specified at
+           compile time, usually 5432.
+         </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry>
+       <term><option>-s</option></term>
+       <listitem>
+         <para>
+           Send the key to <productname>PostgreSQL</productname> server rather
+           than to standard output.
+         </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry>
+       <term><option>-w</option></term>
+       <listitem>
+         <para>
+           Expect password on standard input rather than encryption key.
+         </para>
+       </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index 8a9e22d050..613d14e1cc 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -100,6 +100,17 @@ PostgreSQL documentation
    </varlistentry>
 
    <varlistentry>
+     <term><option>-K</option></term>
+     <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+     <listitem>
+       <para>
+         If the cluster is encrypted, run this command to retrieve the
+         encryption key. See <xref linkend="encryption"/> for details.
+       </para>
+     </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><option>-n</option></term>
     <term><option>--dry-run</option></term>
     <listitem>
diff --git a/doc/src/sgml/ref/pg_rewind.sgml b/doc/src/sgml/ref/pg_rewind.sgml
index 4d91eeb0ff..0e4f79b652 100644
--- a/doc/src/sgml/ref/pg_rewind.sgml
+++ b/doc/src/sgml/ref/pg_rewind.sgml
@@ -140,6 +140,17 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-K</option></term>
+      <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        If the cluster is encrypted, run this command to retrieve the
+        encryption key. See <xref linkend="encryption"/> for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>--source-pgdata=<replaceable class="parameter">directory</replaceable></option></term>
       <listitem>
        <para>
diff --git a/doc/src/sgml/ref/pg_waldump.sgml b/doc/src/sgml/ref/pg_waldump.sgml
index 329c10e430..ca1e4e1468 100644
--- a/doc/src/sgml/ref/pg_waldump.sgml
+++ b/doc/src/sgml/ref/pg_waldump.sgml
@@ -101,6 +101,17 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-K</option></term>
+      <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        If the cluster is encrypted, run this command to retrieve the
+        encryption key. See <xref linkend="encryption"/> for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-n <replaceable>limit</replaceable></option></term>
       <term><option>--limit=<replaceable>limit</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 82886760f1..0c04990d51 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -120,6 +120,37 @@
      </varlistentry>
 
      <varlistentry>
+      <term><option>-K</option></term>
+      <term><option>--encryption-key-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        If the clusters are encrypted, run this command to retrieve the
+        encryption key. This is the same command that was passed to
+        <xref linkend="app-initdb"/> for cluster creation. For the upgrade to
+        be successful, both old and new clusters must use the same key.
+        Therefore, unless the command contains the <literal>%D</literal>
+        string, it does not matter which cluster the command comes from.
+       </para>
+
+       <note>
+         <para>
+           If you are using <xref linkend="app-pg-keytool"/> to derive the
+           encryption key (see <xref linkend="app-initdb-data-encr-cmd"/>),
+           make sure the <option>-D</option> points to
+           the <emphasis>old</emphasis> cluster. If it pointed to the new
+           one then the new cluster would derive a different key from the same
+           password and thus <xref linkend="pgupgrade"/> would fail to start
+           the new cluster.
+         </para>
+       </note>
+
+       <para>
+         See <xref linkend="encryption"/> for details on the command.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-k</option></term>
       <term><option>--link</option></term>
       <listitem><para>use hard links instead of copying files to the new
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index cef09dd38b..5eed489f27 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -279,6 +279,7 @@
    &pgChecksums;
    &pgControldata;
    &pgCtl;
+   &pgKeytool;
    &pgResetwal;
    &pgRewind;
    &pgtestfsync;
diff --git a/src/backend/storage/file/README.encryption b/src/backend/storage/file/README.encryption
new file mode 100644
index 0000000000..5ffd587cf5
--- /dev/null
+++ b/src/backend/storage/file/README.encryption
@@ -0,0 +1,202 @@
+src/backend/storage/file/README.encryption
+
+Transparent Cluster Encryption
+==============================
+
+When creating a new cluster (instance), user can choose to have his data
+encrypted on disk. If this feature is active, data is encrypted before it's
+written to disk and decrypted after it has been read, however the data is
+unencrypted in memory (data-at-rest encryption). The following characteristics
+should be considered by anyone who is interested in the feature:
+
+	1. The encryption is transparent from application's point of view.
+
+	2. A single key is used to encrypt the whole cluster.
+
+The full instance encryption feature helps to ensure data confidentiality,
+especially when user cannot rely on confidentiality on filesystem level. On
+the other hand, it does not ensure data integrity, i.e. it does not help to
+detect whether an adversary wrote his cipher data to the disk. The block
+cipher methods generally do not protect data integrity, and it'd probably be
+hard anyway because we encrypt postgres data pages (typically of size 8 kB)
+separate from each other. If the attacker only changes part of the page, this
+can be detected if data checksums feature is enabled. And specifically for
+XLOG, each XLOG record has its own checksum.
+
+Since the data is stored on a disk, we naturally base our approach on "Disk
+encryption theory" [1]. For each kind of file we use the AES cipher in the
+appropriate mode of operation. The AES cipher itself encrypts / decrypts
+individual blocks ("encryption blocks") of 16 bytes (128 bits), while the mode
+of operation defines the rules how to apply the cipher to data which spans
+multiple encryption blocks.
+
+Relations
+---------
+
+AES cipher in CBC mode [2] is used to encrypt relation files, one relation
+block (page) at a time. The important characteristic of this mode is that, if
+a single encryption block changes, the next run of the encryption produces an
+encrypted page where not only that block is different, but also all the
+following blocks. Furthermore, as the postgres page starts with LSN, even if
+only the last encryption block of the page changes, the whole cipher text of
+the page will be different after the next encryption.
+
+This propagation of changes across encryption blocks makes it harder for
+adversary to deduce what happens in the database. Without the propagation he
+might be able to see data changes with higher granularity. For example, if
+only the beginning of index page changes, it gives some information on the
+keys inserted.
+
+On the other hand, the same change propagation can cause problems if only a
+part of a new version of encrypted page is written to disk (torn page
+write). If such a page is decrypted during crash recovery, part of the plain
+text can become garbage. This can affect even those parts of the page that
+haven't been modified recently, and therefore XLOG records to fix those parts
+are not available. Therefore, if the cluster encryption is enabled,
+full_page_writes configuration variable must be set too, otherwise the server
+refuses to start. With this setting, the damage described here can be fixed by
+retrieving the full-page-image (FPI) from XLOG and applying it.
+
+However, even if full_page_writes is set, postgres can still avoid writing FPI
+XLOG record if only hint bit(s) changed on a page and if wal_log_hints
+configuration variable is off. The problem with encryption is that even a
+single bit change is propagated to all the following encryption blocks during
+the next encryption of the page. Again, if only part of the new cipher text is
+written, the remaining portion of the page becomes garbage after decryption,
+and we might not be able to fix it during recovery unless we have the FPI in
+the XLOG. So if encryption is enabled, postgres behaves as if wal_log_hints
+was always set.
+
+XLOG
+----
+
+The specific problem of XLOG is that record must not be changed once it has
+been flushed to disk. However if we used a block cipher, and if a new XLOG
+record started in the same encryption block in which the previous record ends,
+that encryption block would become completely different after the next run of
+encryption of the containing page. Torn write of such a block (e.g. if memory
+page boundary crosses the encryption block) is likely to make decryption
+produce garbage, which will also appear in the already-flushed record.
+
+Therefore we encrypt XLOG using a stream cipher, or rather block cipher in
+stream mode of operation. Stream cipher uses XOR operation to combine a "key
+stream" with the input stream, and it does not matter if the length of the
+input stream is aligned to any value. In particular, the CTR mode [3] was
+chosen because it allows for both read and write operations to be
+parallelized.
+
+The XLOG is encrypted / decrypted one XLOG page (typically 8 kB) at a time, so
+rather than a single stream, the encrypted XLOG is actually a sequence of
+per-page streams.
+
+Since stream ciphers are susceptible to "reused key attack" [4], we must
+ensure that the unused part of the last XLOG page (filled with zeroes) is
+never encrypted.
+
+Temporary files
+---------------
+
+BufFileWrite() and BufFileRead() functions (see buffile.c) hide the encryption
+/ decryption from caller. The encryption / decryption processes one buffer at
+a time so that the buffers can be retrieved independent from each other.
+
+If the encryption is enabled, the following requirements need to be taken into
+account:
+
+1. The file buffer cannot be positioned at arbitrary offset of the file. If
+the encryption routine starts at some position of the file, decryption must
+not start elsewhere because there's no way to determine which initialization
+vector was used internally for the corresponding encryption blocks (of 16
+bytes) during encryption. It makes sense to position the buffer at file offset
+that is whole multiple of buffer size.
+
+2. In general, the useful (written) data does not fill whole multiple of
+encryption blocks, but we must write the whole blocks for decryption to
+succeed. This implies that we need to fill the unused part of the last block
+with zeroes and also remember the amount of useful bytes in the segment file.
+(In fact we align the segment file size to file buffers instead of encryption
+blocks, which probably makes the implementation a bit simpler.)
+
+Stream cipher might seem like a solution of the padding problem, but we cannot
+use it here because parts of the temporary file can be rewritten. That would
+expose the temporary file to "reused key attack" [4].
+
+Auxiliary files
+---------------
+
+To store other kinds of data encrypted than the ones above, developers are
+advised to use BufFileWriteTransient() and BufFileReadTransient() functions
+(also located in buffile.c). These are especially useful if some data
+structure should be written to an encrypted file and user does not want to
+care whether the next write position is at encryption block boundary.
+
+Wherever the write starts, BufFileWriteTransient() first reads the whole
+encryption block that contains the start positions, then decrypts it, adds the
+new data to it, encrypts the whole block and writes it back to the
+file. Likewise, BufFileReadTransient() ensures that only the whole encryption
+blocks are read from disk and decrypted, whether the next read position is at
+block boundary or not.
+
+Serialization of data changes during logical decoding (reorderbuffer.c) is the
+typical use case for this API.
+
+Initialization vector (IV), encryption tweak
+--------------------------------------------
+
+Besides the input data and the key, both block and stream cipher used for the
+postgres cluster encryption require an initialization vector. It should make
+analysis of the encrypted data more difficult. Also according to [1], the same
+data should be encrypted differently if located elsewhere on the disk. Term
+"encryption tweak" is more common in the context of disk encryption.
+
+When encrypting relations, each encryption unit (i.e. page) has an unique
+tweak, which consists of RelFileNode structure, buffer number and fork
+number. As a consequence, we need to decrypt page using the existing tweak and
+encrypt it with a new tweak if copying it from one relation to
+another. Typically this happens when a new database is being created from
+template database.
+
+To avoid "reencryption" during pg_upgrade, all the OIDs contained in the
+RelFileNode structure (tablespace OID, database OID and relation file OID)
+must be preserved. If pg_upgrade had to reencrypt files, it could not be used
+with the --link option.
+
+XLOG encryption tweak consists of timeline, segment number and offset at which
+the XLOG page starts in the segment. The "reencryption" takes place when XLOG
+page is copied from one timeline to another, typicially at the end of
+recovery.
+
+As for temporary files, PID of the owning backend, file / fileset number and
+block number (where block is of the same size as a relation page) within the
+file provide sufficient uniqueness, so we use these to generate the tweak.
+
+Auxiliary files can be closed and reopened by another backend, so there's no
+PID strictly associated with them. Therefore we generate the tweak by hashing
+the file path, and appending block number to the hash.
+
+Replication
+-----------
+
+During streaming replication, the walsender process sends the XLOG encrypted
+and walreceiver just writes it. Decryption is performed before the slave
+cluster tries to apply the changes. That implies that the same encryption key
+must be used both master and slave. If it should be possible someday to stream
+the XLOG unencrypted. One implication is that pg_basebackup should then also
+receive the relation data unencrypted.
+
+As for logical replication, the only change introduced by this feature is that
+the XLOG has to be decrypted before the contained data changes can be
+decoded. The data changes are transferred to the subscribing database /
+cluster unencrypted, so both master and slave can use different encryption
+keys.
+
+References
+----------
+
+[1] https://en.wikipedia.org/wiki/Disk_encryption_theory
+
+[2] https://en.wikipedia.org/wiki/Disk_encryption_theory#Cipher-block_chaining_(CBC)
+
+[3] https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)
+
+[4] https://en.wikipedia.org/wiki/Stream_cipher_attacks#Reused_key_attack
-- 
2.13.7

