From cc6a1b316952876a7212791a88661499db0af2e5 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Tue, 26 May 2020 13:48:17 +0900
Subject: [PATCH v11 6/7] Documentation update.

---
 doc/src/sgml/config.sgml              |  47 ++++
 doc/src/sgml/database-encryption.sgml | 381 ++++++++++++++++++++++++++
 doc/src/sgml/filelist.sgml            |   1 +
 doc/src/sgml/func.sgml                |  99 +++++++
 doc/src/sgml/installation.sgml        |   5 +-
 doc/src/sgml/postgres.sgml            |   1 +
 doc/src/sgml/ref/initdb.sgml          |  19 ++
 doc/src/sgml/ref/pgupgrade.sgml       |   7 +
 doc/src/sgml/storage.sgml             |   5 +
 9 files changed, 563 insertions(+), 2 deletions(-)
 create mode 100644 doc/src/sgml/database-encryption.sgml

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index a2694e548a..dad0b3aef7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7627,6 +7627,39 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
     </variablelist>
    </sect1>
 
+   <sect1 id="runtime-config-encryption">
+    <title>Encryption Key Management</title>
+
+    <variablelist>
+     <varlistentry id="guc-cluster-passphrase-command" xreflabel="cluster_passphrase_command">
+      <term><varname>cluster_passphrase_command</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>cluster_passphrase_command</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        This option specifies an external command to be invoked when a passphrase
+        for key management system needs to be obtained.
+       </para>
+       <para>
+        The command must print the passphrase to the standard output and exit
+        with code 0.  In the parameter value, <literal>%p</literal> is
+        replaced by a prompt string.  (Write <literal>%%</literal> for a
+        literal <literal>%</literal>.)  Note that the prompt string will
+        probably contain whitespace, so be sure to quote adequately.  A single
+        newline is stripped from the end of the output if present.  The passphrase
+        must be at least 64 bytes.
+       </para>
+       <para>
+        This parameter can only be set in the <filename>postgresql.conf</filename>
+        file or on the server command line.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </sect1>
+
    <sect1 id="runtime-config-client">
     <title>Client Connection Defaults</title>
 
@@ -9471,6 +9504,20 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </listitem>
      </varlistentry>
 
+      <varlistentry id="guc-key-management-enabled" xreflabel="key_management_enabled">
+      <term><varname>key_management_enabled</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary>Key management configuration parameter parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Reports whether encryption key management is enabled for this cluster.
+        See <xref linkend="app-initdb-cluster-passphrase-command"/> 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/database-encryption.sgml b/doc/src/sgml/database-encryption.sgml
new file mode 100644
index 0000000000..617f31bf28
--- /dev/null
+++ b/doc/src/sgml/database-encryption.sgml
@@ -0,0 +1,381 @@
+<!-- doc/src/sgml/database-encryption.sgml -->
+
+<chapter id="database-encryption">
+ <title>Database Encryption</title>
+
+ <indexterm zone="database-encryption">
+  <primary>Server Side Encryption</primary>
+ </indexterm>
+
+ <para>
+  The purpose of database encryption is to protect the confidential data
+  stored in a database from being revealed.  <productname>PostgreSQL</productname>
+  provides a way to encrypt database data with the encryption key managed
+  inside <productname>PostgreSQL</productname> when
+  <structname>key manager</structname> is enabled.  This section describes
+  how <productname>PostgreSQL</productname>  manages encryption keys and
+  how encryption process works.
+ </para>
+
+ <sect1 id="encryption-key-management">
+  <title>Encryption Key Management</title>
+
+  <para>
+   <productname>PostgreSQL</productname> supports internal
+   <firstterm>Encryption Key Management System</firstterm>, which is designed
+   to manage the life cycles of cryptographic keys within the
+   <productname>PostgreSQL</productname>.  This includes dealing with their
+   generation, storage, usage and rotation.
+  </para>
+
+  <para>
+   Encryption key management system is enabled when
+   <productname>PostgreSQL</productname> is built with
+   <literal>--with-openssl</literal> and
+   <xref linkend="app-initdb-cluster-passphrase-command"/> is specified during
+   <command>initdb</command>.  The cluster passphrase provided by
+   <option>--cluster-passphrase-command</option> option during
+   <command>initdb</command> and the one generated by
+   <xref linkend="guc-cluster-passphrase-command"/> in the
+   <filename>postgresql.conf</filename> must match, otherwise, the database
+   cluster will not start up. Please note that the cluster passphrase command
+   passed to <command>initdb</command> must return a passphrase equal or longer
+   than 64 bytes and less than 1024 bytes. For example.
+<programlisting>
+initdb -D dbname --cluster-passphrase-command="cat /path/to/passphrase-file"
+</programlisting>
+  </para>
+
+  <sect2 id="key-encryption-key">
+   <title>Key Encryption Key(<acronym>KEK</acronym>)</title>
+
+   <para>
+    During <command>initdb</command> process, the cluster passphrase provided
+    by <option>--cluster-passphrase-command</option> is derived into
+    <firstterm>Key Encryption Key (<acronym>KEK</acronym>)</firstterm>.
+    <acronym>KEK</acronym> encapsulates cryptographic keys managed inside
+    <productname>PostgreSQL</productname> described in
+    <xref linkend="key-derivations"/> using by a way of authenticated
+    encryption described in <xref linkend="authenticated-encryption-aes-hmac"/>
+    before storing the keys to a persistent storage. <acronym>KEK</acronym>
+    must be stored in a trusted key store, such as key vault software or
+    services, or a hardware security module.
+   </para>
+
+   <para>
+    When a <productname>PostgreSQL</productname> server with encryption key
+    management enabled is started, the <varname>cluster_passphrase_command</varname>
+    parameter in <filename>postgresql.conf</filename> will be evaluated and
+    the cluster passphrase will be derived into
+    <firstterm>Key Encryption Key (<acronym>KEK</acronym>)</firstterm> in similar
+    ways as initdb.
+   </para>
+
+   <para>
+    After that, the cryptographic keys will be retrieved from <filename>pg_cryptokeys</filename>
+    directory to be restored and integrity-checked by the key management
+    system using <acronym>KEK</acronym>.  If this process fails, it is likely
+    that the cluster passphrase supplied to the cluster is not the same as that
+    supplied to the <command>initdb</command> process.  The cluster will refuse
+    to start in this case and user has to manually correct the cluster passphrase.
+   </para>
+
+   <para>
+    <acronym>KEK</acronym> is not stored physically within the
+    <productname>PostgreSQL</productname> server as they are designed
+    to be derived from the correctly configured cluster passphrase.
+   </para>
+  </sect2>
+
+  <sect2 id="key-derivations">
+   <title>Key Derivations</title>
+
+   <para>
+    Encryption key management system can manage multiple cryptographic keys
+    that have different purposes and usages within <productname>PostgreSQL</productname>.
+    Currently, encryption key management system manages one cryptographic key
+    which is generated using <function>pg_strong_random()</function> during
+    <command>initdb</command> process.
+   </para>
+
+   <variablelist>
+    <varlistentry>
+     <term><literal>SQL Key</literal></term>
+     <listitem>
+      <para>
+       <literal>SQL Key</literal> is used to encrypt and decrypt data via
+       <function>pg_encrypt()</function> and <function>pg_decrypt()</function>
+       SQL functions.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </sect2>
+
+  <sect2 id="key-management-rotation">
+   <title>Key Rotation Process</title>
+
+   <para>
+    Encryption keys in general are not interminable, the longer the same key
+    is in use, the chance  of it being breached increases. Performing key
+    rotation on a regular basis help meet standardized security practices
+    such as <ulink url="https://www.pcisecuritystandards.org/">PCI-DSS</ulink>
+    and it is a good practice in security to limit the number of encrypted
+    bytes available for a specific key version. The key lifetimes are based
+    on key length, key strength, algorithm and total number of bytes
+    enciphered. The key management system provides a efficient method to
+    perform key rotation.
+   </para>
+
+   <para>
+    Please be aware that the phrase <literal>"key rotation"</literal> here
+    only refers to the rotation of <acronym>KEK</acronym>. The cryptographic
+    keys managed by encryption key management system are not rotated; they
+    will in fact be the same before and after a <literal>"key rotation"</literal>.
+    This can be justified because the actual keys are never stored anywhere
+    physically, presented to user or captured in logging. What is being
+    rotated here is the <acronym>KEK</acronym> who is responsible for
+    encapsulating and restoring cryptographic keys.
+   </para>
+
+   <para>
+    Since <acronym>KEK</acronym> is derived from a cluster passphrase, the
+    <literal>"key rotation"</literal> ultimately refers to the rotation of
+    cluster passphrase and deriving a new <acronym>KEK</acronym> from the
+    new cluster passphrase. The new <acronym>KEK</acronym> can then be used
+    to encapsulate all encryptions keys and store the new results in
+    <filename>pg_cryptokeys</filename> directory.
+   </para>
+
+   <para>
+    To complete the cluster passphrase rotation, user needs to follow the
+    steps below:
+   </para>
+   <itemizedlist>
+    <listitem>
+     <para>
+      Ensure the <productname>PostgreSQL</productname> server is running
+      correctly with KMS enabled.  Passphrase rotation cannot be completed
+      with the server shut down.
+     </para>
+    </listitem>
+    <listitem>
+     <para>
+      Update <xref linkend="guc-cluster-passphrase-command"/> parameter and
+      load such that the new command will return a new cluster passphrase.
+     </para>
+    </listitem>
+    <listitem>
+     <para>
+      In a session, execute <function>pg_rotate_cluster_passphrase()</function>
+      SQL function to initiate the rotation. The function returns true upon
+      successful key rotation and false if otherwise.
+<programlisting>
+=# SELECT pg_rotate_cluster_passphrase();
+pg_rotate_cluster_passphrase
+------------------------------
+ t
+(1 row)
+</programlisting>
+     </para>
+    </listitem>
+   </itemizedlist>
+
+   <para>
+    Upon successful cluster passphrase rotation, all managed cryptographic keys
+    will be re-encapsulated by the new <acronym>KEK</acronym> derived from the
+    new cluster passphrase. The new encapsulated  keys will be stored in
+    <filename>pg_cryptokeys</filename> directory.  Please note that the cryptographic
+    keys are the same as before; the rotation process only changes the
+    <acronym>KEK</acronym> that is used to encapsulate and verify the actual
+    cryptographic keys. This way, there is no need to decrypt all the encrypted
+    data with the old keys and re-encrypt them with the new.
+   </para>
+
+   <para>
+    In case of a crash during the cluster passphrase rotation process, the key
+    management system is able to recover to the previous sets of cryptographic
+    keys the next time server starts up. This is possible because the key rotation
+    and encapsulation process are done on a separate temporary key directory called
+    <filename>pg_cryptokeys_tmp</filename> and it will replace
+    <filename>pg_cryptokeys</filename> and be deleted only when everything is
+    successfully finished. If the server starts with pg_cryptokeys_tmp folder present,
+    it would indicate that previous attempt of cluster passphrase rotation was not
+    completed. In this case, the server will discard <filename>pg_cryptokeys_tmp</filename>
+    folder and load the keys in <filename>pg_cryptokeys</filename> as usual.
+   </para>
+  </sect2>
+ </sect1>
+
+ <sect1 id="encryption-sql-function">
+  <title>Encryption SQL Functions</title>
+  <para>
+   See <xref linkend="functions-encryption-table"/> for available encryption
+   functions.
+  </para>
+
+  <para>
+   These two functions can be used not only to encrypt data stored in a database
+   but also in conjunction with the cryptographic functions provided by
+   <filename>pgcrypto</filename> extension to perform column level encryption/decryption
+   without having to supply a clear text user secret or passphrase that is required
+   by many pgcrypto functions as input. Please refer to
+   <xref linkend="key-management-wrap-and-unwrap"/> for usage examples.
+  </para>
+
+  <sect2 id="authenticated-encryption-aes-hmac">
+   <title>Authenticated Encryption with AES-CBC and HMAC-SHA</title>
+
+   <para>
+    Authenticated Encryption with Associated Data (<acronym>AEAD</acronym>), is
+    a form of encryption. In addition to provide a way to protect confidential
+    data from being revealed, it provides a way to check its integrity and
+    authenticity of some associated data.
+   </para>
+
+   <para>
+    This section describes the authenticated encryption algorithm
+    <productname>PostgreSQL</productname> provides. This
+    <productname>PostgreSQL</productname> <acronym>AEAD</acronym> algorithm
+    is derived from the specification draft of
+    <ulink url="https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05">
+     Authenticated Encryption with AEAD-CBC and HMAC-SHA</ulink>. It follows
+    Encrypt-Then-MAC approach, basing on the composition of theAdvanced Encryption
+    Standard (<acronym>AES</acronym>) in the Cipher Block Chaining
+    (<acronym>CBC</acronym>) mode of operation for encryption with random
+    initialization vector(<acronym>IV</acronym>) and the <literal>HMAC-SHA</literal>
+    message authentication code (<acronym>MAC</acronym>).
+   </para>
+
+   <para>
+    <productname>PostgreSQL</productname> <acronym>AEAD</acronym> algorithm uses
+    two kinds of cryptographic keys:
+   </para>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><literal>Encryption Key</literal></term>
+      <listitem>
+       <para>
+        Encryption key is 256 bits long randomly generate key.  It is primarily used
+        to a key for encapsulate or restore data with <acronym>AES256</acronym>
+        algorithm.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term><literal>MAC Key</literal></term>
+      <listitem>
+       <para>
+        <acronym>MAC</acronym> key is 512 bits long randomly generated key.
+        <acronym>SHA512</acronym> is the algorithm used along with the
+        <acronym>MAC</acronym> key to compute a cryptographic hash for integrity
+        check purposes.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+
+   <para>
+    <productname>PostgreSQL</productname> <acronym>AEAD</acronym> algorithm is as
+    follows:
+
+    <orderedlist>
+     <listitem>
+      <simpara>Generate random <acronym>IV</acronym>.</simpara>
+     </listitem>
+     <listitem>
+      <simpara>Add padding to the plaintext following PKCS#7 described in
+      <ulink url="https://tools.ietf.org/html/rfc2315">RFC2315</ulink>.</simpara>
+     </listitem>
+     <listitem>
+      <simpara>Encrypt padded plain text with the <acronym>IV</acronym>
+       using <acronym>AES256</acronym> in <acronym>CBC</acronym>
+       mode.</simpara>
+     </listitem>
+     <listitem>
+      <simpara>Compute <acronym>HMAC</acronym> over the encrypted data.</simpara>
+     </listitem>
+     <listitem>
+      <simpara>Concatenate <acronym>HMAC</acronym>, <acronym>IV</acronym>
+       and encrypted ciphertext as the result of ciphertext.</simpara>
+     </listitem>
+    </orderedlist>
+   </para>
+
+   <para>
+    The length of the result ciphertext can be inferred from that of the plaintext
+    by following formula:
+<programlisting>
+ Ciphertext Length = 64 + 16 + 16 * (floor(input_size / 16) + 1)
+</programlisting>
+   </para>
+  </sect2>
+
+  <sect2 id="key-management-wrap-and-unwrap">
+   <title>Wrap and Unwrap User Secret</title>
+
+   <para>
+    This section describes a use case where combining <function>pg_encrypt()</function>
+    and <function>pg_decrypt()</function> with <xref linkend="pgcrypto"/>. User wraps
+    the user encryption secret with <function>pg_encrypt()</function> function and
+    passes the wrapped encryption secret to <function>pg_decrypt()</function> function
+    for the <structname>pgcrypto</structname> encryption functions. The wrapped secret
+    can be stored in the application server or somewhere secured and should be obtained
+    promptly for cryptographic operation with <structname>pgcrypto</structname>.
+   </para>
+
+   <para>
+    Here is an example that shows how to encrypt and decrypt data together with
+    these functions:
+   </para>
+
+   <para>
+    Wrap user secret key by <function>pg_encrypt()</function> function. In this example,
+    wrapped user secret is stored into <varname>key</varname> variable.
+<programlisting>
+=# SELECT pg_encrypt('my secret password') as key;
+                                                                                                             pg_encrypt
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ \x6ddff999f4b00206048e420839e9f0639d53537b7e66ee6d3609f5bbd01ad9c9244e487c3dc235fe0abd10d2bade46610ec381180b4d149725a8b8967edcbb5287a1ff05ae80fdb9f5bf7b5c1861632daf8677ef8c4448a0645b6c4b42a3e2f16396b11768148916331a65c42f40a2ed
+(1 row)
+=# \gset
+</programlisting>
+   </para>
+
+   <para>
+    Once wrapping the user secret key, user can encrypt and decrypt user data
+    using the wrapped user key together with the
+    <function>pg_decrypt()</function> function:
+   </para>
+
+   <para>
+<programlisting>
+ =# INSERT INTO tbl (col)
+        VALUES (pgp_sym_encrypt('secret data', pg_decrypt(:'key')));
+ INSERT 1
+
+ =# SELECT * FROM tbl;
+                                                                             col
+--------------------------------------------------------------------------------------------------------------------------------------------------------------
+ \xc30d04070302a199ee38bea0320b75d23c01577bb3ffb315d67eecbeca3e40e869cea65efbf0b470f805549af905f94d94c447fbfb8113f585fc86b30c0bd784b10c9857322dc00d556aa8de14
+(1 row)
+
+ =# SELECT pgp_sym_decrypt(col, pg_decrypt(:'key')) as col FROM tbl;
+     col
+--------------
+ secret data
+(1 row)
+</programlisting>
+   </para>
+
+   <para>
+    The data <literal>'secret data'</literal> is practically encrypted by the
+    user secret <literal>'my secret password'</literal> but using key wrapping
+    users don't need to know the actual user secret during operation.
+   </para>
+  </sect2>
+ </sect1>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 68179f71cd..52e353cafa 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 database-encryption SYSTEM "database-encryption.sgml">
 
 <!-- programmer's guide -->
 <!ENTITY bgworker   SYSTEM "bgworker.sgml">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7c06afd3ea..dabdb7c311 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -26679,4 +26679,103 @@ SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
 
   </sect1>
 
+  <sect1 id="functions-encryption">
+   <title>Encryption Functions</title>
+
+   <sect2 id="functions-data-encryption">
+    <title>Data Encryption Function</title>
+    <para>
+     The functions shown in
+     <xref linkend="functions-encryption-table"/> are for encrypting
+     and decrypting data with the <literal>SQL key</literal> described in
+     <xref linkend="encryption-key-management"/>.
+    </para>
+
+    <table id="functions-encryption-table">
+     <title>Encryption <acronym>SQL</acronym> Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row>
+        <entry>Function</entry>
+        <entry>Return Type</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_encrypt</primary>
+         </indexterm>
+         <literal><function>pg_encrypt(<parameter>data</parameter> <type>text</type>)</function></literal>
+        </entry>
+        <entry>
+         <type>bytea</type>
+        </entry>
+        <entry>
+         Encrypt the given data with the internal SQL key
+        </entry>
+       </row>
+
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_unwrap</primary>
+         </indexterm>
+         <literal><function>pg_decrypt(<parameter>data</parameter> <type>bytea</type>)</function></literal>
+        </entry>
+        <entry>
+         <type>text</type>
+        </entry>
+        <entry>
+         Decrypt the given data with the internal SQL key
+        </entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+   </sect2>
+
+   <sect2 id="functions-key-management">
+    <title>Key Management Functions</title>
+    <para>
+     The function shown in
+     <xref linkend="functions-key-management-table"/> are for encryption
+     key management described in <xref linkend="encryption-key-management"/>.
+    </para>
+
+    <table id="functions-key-management-table">
+     <title>Encryption Key Management <acronym>SQL</acronym> Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row>
+        <entry>Function</entry>
+        <entry>Return Type</entry>
+        <entry>Description</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_rotate_cluster_passphrase</primary>
+         </indexterm>
+         <literal><function>pg_rotate_cluster_passphrase()</function></literal>
+        </entry>
+        <entry>
+         <type>boolean</type>
+        </entry>
+        <entry>
+         Rotate the cluster passphrase. See
+         <xref linkend="key-management-rotation"/> for details.
+        </entry>
+       </row>
+
+      </tbody>
+     </tgroup>
+    </table>
+   </sect2>
+  </sect1>
+
 </chapter>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 4f89b4e930..2bf6a297b6 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -979,8 +979,9 @@ build-postgresql:
        <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
+         connections and key management. 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.
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index c41ce9499b..a96196c85d 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -170,6 +170,7 @@ break is not needed in a wider output rendering.
   &wal;
   &logical-replication;
   &jit;
+  &database-encryption;
   &regress;
 
  </part>
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index a04a180165..d5e8862516 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -165,6 +165,25 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-cluster-passphrase-command" xreflabel="cluster passphrase command">
+      <term><option>--cluster-passphrase-command=<replaceable class="parameter">command</replaceable></option></term>
+      <listitem>
+       <para>
+        This option specifies an external command to be invoked when a passphrase
+        for key management system needs to be obtained.
+       </para>
+       <para>
+        The command must print the passphrase to the standard output and exit
+        with code 0.  In the parameter value, <literal>%p</literal> is
+        replaced by a prompt string.  (Write <literal>%%</literal> for a
+        literal <literal>%</literal>.)  Note that the prompt string will
+        probably contain whitespace, so be sure to quote adequately.  A single
+        newline is stripped from the end of the output if present.  The passphrase
+        must be at least 64 bytes.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 905167690b..61a386a2ff 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -823,6 +823,13 @@ psql --username=postgres --file=script.sql postgres
    is down.
   </para>
 
+  <para>
+   During the upgrade <command>pg_upgrade</command> copies the all internal keys
+   to the new cluster. If you want to upgrade from the old cluster that enables
+   the key management to the new cluster that also enables, you must use the same
+   <varname>cluster_passphrase_command</varname> to both clusters. Otherwise
+   <command>pg_upgrade</command> fails due to mismatching the cluster passphrase.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index ea08d0b614..3d1f02ef83 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -77,6 +77,11 @@ Item
  <entry>Subdirectory containing transaction commit timestamp data</entry>
 </row>
 
+<row>
+ <entry><filename>pg_cryptokeys</filename></entry>
+ <entry>Subdirectory containing cryptographic keys</entry>
+</row>
+
 <row>
  <entry><filename>pg_dynshmem</filename></entry>
  <entry>Subdirectory containing files used by the dynamic shared memory
-- 
2.23.0

