diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
new file mode 100644
index 2768c85..44e0c1e
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
*************** COPY postgres_log FROM '/full/path/to/lo
*** 7793,7798 ****
--- 7793,7833 ----
+
+ Encryption Key Management
+
+
+
+ cluster_passphrase_command (string)
+
+ cluster_passphrase_command configuration parameter
+
+
+
+
+ This option specifies an external command to be invoked when a
+ passphrase for key management system needs to be obtained.
+
+
+ The command must print the passphrase to the standard
+ output and have a zero exit code. In the parameter value,
+ %p is replaced by a prompt string. (Write
+ %% for a literal %.)
+ Note that the prompt string will probably contain whitespace,
+ so be sure to quote its use adequately. A single newline is
+ stripped from the end of the output if present. The passphrase
+ must be at least 64 bytes.
+
+
+ This parameter can only be set in the
+ postgresql.conf file or on the server
+ command line.
+
+
+
+
+
+
Client Connection Defaults
*************** dynamic_library_path = 'C:\tools\postgre
*** 9636,9641 ****
--- 9671,9692 ----
+
+
+ key_management_enabled (boolean)
+
+ Key management configuration parameter parameter
+
+
+
+
+ Reports whether encryption key management
+ is enabled for this cluster. See for more
+ information.
+
+
+ data_directory_mode (integer)
diff --git a/doc/src/sgml/database-encryption.sgml b/doc/src/sgml/database-encryption.sgml
new file mode 100644
index ...db84b0d
*** a/doc/src/sgml/database-encryption.sgml
--- b/doc/src/sgml/database-encryption.sgml
***************
*** 0 ****
--- 1,292 ----
+
+
+
+ Database Encryption
+
+
+ Server Side Encryption
+
+
+
+ The purpose of database encryption is to protect the confidential data
+ stored in a database from being revealed.
+
+
+
+ Encryption Key Management
+
+
+ PostgreSQL supports internal
+ Encryption Key Management System, which is designed
+ to manage the life cycles of cryptographic keys within the
+ PostgreSQL. This includes dealing with their
+ generation, storage, usage and rotation.
+
+
+
+ Encryption key management system is enabled when
+ PostgreSQL is built with
+ --with-openssl and
+ is specified during
+ initdb. The cluster passphrase provided by the
+ option during
+ initdb and the one generated by
+ in the
+ postgresql.conf must match, otherwise, the database
+ cluster will not start up. Note that the cluster passphrase command
+ passed to initdb must return a passphrase of at least
+ 64 bytes and less than 1024 bytes. For example.
+
+ initdb -D dbname --cluster-passphrase-command="cat /path/to/passphrase-file"
+
+
+
+
+ Encryption keys managed by PostgreSQL's
+ key management system are only used for the internal usage such as
+ transparent data encryption in a future release. These encryption keys
+ can never be taken out of database in plaintext form. Also, the
+ encryption key management system creates some internal encryption keys.
+ There is no interface to add and remove these keys.
+
+
+
+ Key Encryption Key(KEK)
+
+
+ During the initdb process, the cluster passphrase
+ provided by
+ is derived into a Key Encryption Key
+ (KEK). KEK
+ encapsulates cryptographic keys managed inside
+ PostgreSQL described in using by a way of authenticated encryption
+ described in before storing the keys
+ to a persistent storage. KEK must be stored in
+ a trusted key store, such as key vault software or services, or a
+ hardware security module.
+
+
+
+ When a PostgreSQL server
+ with enabled encryption key management is started, the
+ cluster_passphrase_command parameter in
+ postgresql.conf will be evaluated and the cluster
+ passphrase will be derived into KEK in similar
+ ways as initdb.
+
+
+
+ After that, the cryptographic keys will be retrieved from
+ pg_cryptokeys directory to be restored and
+ integrity-checked by the key management system using the KEK.
+ If this process fails, it is likely that the cluster passphrase supplied
+ to the cluster is not the same as that supplied to the
+ initdb process. The cluster will refuse to start in this
+ case and user has to manually correct the cluster passphrase.
+
+
+
+ KEK is not stored physically within the
+ PostgreSQL server as they are designed
+ to be derived from the correctly configured cluster passphrase.
+
+
+
+
+ Key Derivations
+
+
+ Encryption key management systems can manage multiple
+ cryptographic keys that have different purposes and usages within
+ PostgreSQL. Currently, the Postgres
+ encryption key management system manages no cryptographic keys.
+
+
+
+
+ Key Protection
+
+
+ Key management system persists cryptographic keys to the disk after wrapping
+ them by KEK. This section describes how key maangement
+ system wrap and unwrap key.
+
+
+
+ The key management system uses Encryption with Associated Data
+ (AEAD) to wrap cryptographic keys, which is
+ a form of encryption. In addition to providing a way to protect
+ confidential data from being revealed, it provides a way to check
+ the integrity and authenticity of some associated data. It uses
+ the Encrypt-Then-MAC approach, based on the Advanced Encryption
+ Standard (AES) in Cipher Block Chaining
+ (CBC) mode. It uses a random initialization
+ vector (IV) and a HMAC-SHA
+ message authentication code (MAC).
+
+
+
+ Key management system uses two kinds of cryptographic keys for key wrapping:
+
+
+
+
+
+ Encryption Key
+
+
+ Encryption key is 256 bits long randomly generate key. It is primarily used
+ as a key for encapsulate or restore data with AES256
+ algorithm.
+
+
+
+
+ MAC Key
+
+
+ MAC key is a 512-bit randomly generated key.
+ SHA512 is the algorithm used along with the
+ MAC key to compute a cryptographic hash for integrity
+ purposes.
+
+
+
+
+
+
+
+ Key management systems's key wrapping algorithm is as follows:
+
+
+
+ Generate random IV.
+
+
+ Add padding to the plaintext following PKCS#7 described in
+ RFC2315.
+
+
+ Encrypt padded plain text with the IV
+ using AES256 in CBC
+ mode.
+
+
+ Compute HMAC over the encrypted data.
+
+
+ Concatenate HMAC, IV
+ and encrypted ciphertext as the result of ciphertext.
+
+
+
+
+
+ The length of the result ciphertext can be inferred from that of the plaintext
+ by following formula:
+
+ Ciphertext Length = 64 + 16 + 16 * (floor(input_size / 16) + 1)
+
+
+
+
+
+ Key Rotation Process
+
+
+ Encryption keys in general are not interminable —
+ the longer the same key is in use, the greater the chance
+ of it being breached. Performing key rotation at regular
+ intervals helps meet standardized security practices such as PCI-DSS 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.
+
+
+
+ Please be aware that the phrase "key rotation" here
+ only refers to the rotation of KEK. The cryptographic
+ keys managed by encryption key management system are not rotated; they
+ will in fact be the same before and after a "key rotation".
+ 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 KEK who is responsible for
+ encapsulating and restoring cryptographic keys.
+
+
+
+ Since KEK is derived from a cluster passphrase, the
+ "key rotation" ultimately refers to the rotation of
+ cluster passphrase and deriving a new KEK from the
+ new cluster passphrase. The new KEK can then be used
+ to encapsulate all encryptions keys and store the new results in
+ pg_cryptokeys directory.
+
+
+
+ To complete the cluster passphrase rotation, user needs to follow the
+ steps below:
+
+
+
+
+ Ensure the PostgreSQL server is running
+ correctly with KMS enabled. Passphrase rotation cannot be completed
+ with the server shut down.
+
+
+
+
+ Update parameter and
+ load such that the new command will return a new cluster passphrase.
+
+
+
+
+ In a session, execute pg_rotate_cluster_passphrase()
+ SQL function to initiate the rotation. The function returns true upon
+ successful key rotation and false if otherwise.
+
+ =# SELECT pg_rotate_cluster_passphrase();
+ pg_rotate_cluster_passphrase
+ ------------------------------
+ t
+ (1 row)
+
+
+
+
+
+
+ Upon successful cluster passphrase rotation, all managed cryptographic
+ keys will be re-encapsulated by the new KEK
+ derived from the new cluster passphrase. The new encapsulated keys
+ will be stored in pg_cryptokeys directory.
+ Please note that the cryptographic keys are the same as before; the
+ rotation process only changes the KEK 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.
+
+
+
+ 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
+ pg_cryptokeys_tmp and it will replace
+ pg_cryptokeys 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 pg_cryptokeys_tmp folder and load the
+ keys in pg_cryptokeys as usual.
+
+
+
+
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 38e8aa0..b96f4ac
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 49,54 ****
--- 49,55 ----
+
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index e7cff98..e537e98
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT m.* FROM pg_statistic_ext join pg
*** 26770,26773 ****
--- 26770,26872 ----
+
+ Encryption Functions
+
+
+ Data Encryption Function
+
+ The functions shown in
+ are for encrypting
+ and decrypting data with the SQL key described in
+ .
+
+
+
+ Encryption SQL Functions
+
+
+
+ Function
+ Return Type
+ Description
+
+
+
+
+
+
+
+ pg_encrypt
+
+ pg_encrypt(datatext)
+
+
+ bytea
+
+
+ Encrypt the given data with the internal SQL key
+
+
+
+
+
+
+ pg_unwrap
+
+ pg_decrypt(databytea)
+
+
+ text
+
+
+ Decrypt the given data with the internal SQL key
+
+
+
+
+
+
+
+
+ Key Management Functions
+
+ The function shown in
+ are for encryption
+ key management described in .
+
+
+
+ Encryption Key Management SQL Functions
+
+
+
+ Function
+ Return Type
+ Description
+
+
+
+
+
+
+ pg_rotate_cluster_passphrase
+
+ pg_rotate_cluster_passphrase()
+
+
+ boolean
+
+
+ Rotate the cluster passphrase. See
+ for details.
+
+
+
+
+
+
+
+
+
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
new file mode 100644
index 3ac588d..a6e55ba
*** a/doc/src/sgml/installation.sgml
--- b/doc/src/sgml/installation.sgml
*************** build-postgresql:
*** 979,986 ****
Build with support for SSL (encrypted)
! connections. This requires the OpenSSL
! package to be installed. configure will check
for the required header files and libraries to make sure that
your OpenSSL installation is sufficient
before proceeding.
--- 979,987 ----
Build with support for SSL (encrypted)
! connections and key management. This requires the
! OpenSSL package to be installed.
! configure will check
for the required header files and libraries to make sure that
your OpenSSL installation is sufficient
before proceeding.
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
new file mode 100644
index 730d5fd..0ea7da6
*** a/doc/src/sgml/postgres.sgml
--- b/doc/src/sgml/postgres.sgml
*************** break is not needed in a wider output re
*** 171,176 ****
--- 171,177 ----
&wal;
&logical-replication;
&jit;
+ &database-encryption;
®ress;
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
new file mode 100644
index 385ac25..1d0678a
*** a/doc/src/sgml/ref/initdb.sgml
--- b/doc/src/sgml/ref/initdb.sgml
*************** PostgreSQL documentation
*** 163,168 ****
--- 163,187 ----
+
+
+
+
+ This option specifies an external command to be invoked when a passphrase
+ for key management system needs to be obtained.
+
+
+ The command must print the passphrase to the standard output and exit
+ with code 0. In the parameter value, %p is
+ replaced by a prompt string. (Write %% for a
+ 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.
+
+
+
+
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
new file mode 100644
index b59c569..29b0015
*** a/doc/src/sgml/ref/pgupgrade.sgml
--- b/doc/src/sgml/ref/pgupgrade.sgml
*************** psql --username=postgres --file=script.s
*** 823,828 ****
--- 823,835 ----
is down.
+
+ During the upgrade pg_upgrade 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
+ cluster_passphrase_command to both clusters. Otherwise
+ pg_upgrade fails due to mismatching the cluster passphrase.
+
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
new file mode 100644
index 3234adb..05e9b26
*** a/doc/src/sgml/storage.sgml
--- b/doc/src/sgml/storage.sgml
*************** Item
*** 78,83 ****
--- 78,88 ----
+ pg_cryptokeys
+ Subdirectory containing cryptographic keys
+
+
+ pg_dynshmemSubdirectory containing files used by the dynamic shared memory
subsystem
diff --git a/src/backend/Makefile b/src/backend/Makefile
new file mode 100644
index 9706a95..4ace302
*** a/src/backend/Makefile
--- b/src/backend/Makefile
*************** SUBDIRS = access bootstrap catalog parse
*** 21,27 ****
main nodes optimizer partitioning port postmaster \
regex replication rewrite \
statistics storage tcop tsearch utils $(top_builddir)/src/timezone \
! jit
include $(srcdir)/common.mk
--- 21,27 ----
main nodes optimizer partitioning port postmaster \
regex replication rewrite \
statistics storage tcop tsearch utils $(top_builddir)/src/timezone \
! jit crypto
include $(srcdir)/common.mk
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
new file mode 100644
index 52a67b1..d392911
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 44,49 ****
--- 44,50 ----
#include "commands/tablespace.h"
#include "common/controldata_utils.h"
#include "executor/instrument.h"
+ #include "crypto/kmgr.h"
#include "miscadmin.h"
#include "pg_trace.h"
#include "pgstat.h"
***************
*** 81,86 ****
--- 82,88 ----
#include "utils/timestamp.h"
extern uint32 bootstrap_data_checksum_version;
+ extern uint32 bootstrap_key_management_version;
/* Unsupported old recovery command file names (relative to $PGDATA) */
#define RECOVERY_COMMAND_FILE "recovery.conf"
*************** InitControlFile(uint64 sysidentifier)
*** 4607,4612 ****
--- 4609,4615 ----
ControlFile->wal_log_hints = wal_log_hints;
ControlFile->track_commit_timestamp = track_commit_timestamp;
ControlFile->data_checksum_version = bootstrap_data_checksum_version;
+ ControlFile->key_management_version = bootstrap_key_management_version;
}
static void
*************** ReadControlFile(void)
*** 4894,4899 ****
--- 4897,4905 ----
/* Make the initdb settings visible as GUC variables, too */
SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no",
PGC_INTERNAL, PGC_S_OVERRIDE);
+
+ SetConfigOption("key_management", KeyManagementEnabled() ? "yes" : "no",
+ PGC_INTERNAL, PGC_S_OVERRIDE);
}
/*
*************** DataChecksumsEnabled(void)
*** 4937,4942 ****
--- 4943,4958 ----
}
/*
+ * Are key management enabled?
+ */
+ bool
+ KeyManagementEnabled(void)
+ {
+ Assert(ControlFile != NULL);
+ return (ControlFile->key_management_version > 0);
+ }
+
+ /*
* Returns a fake LSN for unlogged relations.
*
* Each call generates an LSN that is greater than any previous value
*************** BootStrapXLOG(void)
*** 5343,5348 ****
--- 5359,5368 ----
/* some additional ControlFile fields are set in WriteControlFile() */
WriteControlFile();
+ /* Enable key manager if required */
+ if (ControlFile->key_management_version > 0)
+ BootStrapKmgr();
+
/* Bootstrap the commit log, too */
BootStrapCLOG();
BootStrapCommitTs();
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
new file mode 100644
index 76b2f50..8c46b20
*** a/src/backend/bootstrap/bootstrap.c
--- b/src/backend/bootstrap/bootstrap.c
***************
*** 28,33 ****
--- 28,34 ----
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "common/link-canary.h"
+ #include "crypto/kmgr.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
***************
*** 51,56 ****
--- 52,58 ----
#include "utils/relmapper.h"
uint32 bootstrap_data_checksum_version = 0; /* No checksum */
+ uint32 bootstrap_key_management_version = 0; /* disabled */
static void CheckerModeMain(void);
*************** AuxiliaryProcessMain(int argc, char *arg
*** 224,230 ****
/* If no -x argument, we are a CheckerProcess */
MyAuxProcType = CheckerProcess;
! while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:X:-:")) != -1)
{
switch (flag)
{
--- 226,232 ----
/* If no -x argument, we are a CheckerProcess */
MyAuxProcType = CheckerProcess;
! while ((flag = getopt(argc, argv, "B:c:d:D:eFkr:x:X:-:")) != -1)
{
switch (flag)
{
*************** AuxiliaryProcessMain(int argc, char *arg
*** 247,252 ****
--- 249,257 ----
pfree(debugstr);
}
break;
+ case 'e':
+ bootstrap_key_management_version = KMGR_VERSION;
+ break;
case 'F':
SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV);
break;
diff --git a/src/backend/crypto/Makefile b/src/backend/crypto/Makefile
new file mode 100644
index ...c273620
*** a/src/backend/crypto/Makefile
--- b/src/backend/crypto/Makefile
***************
*** 0 ****
--- 1,18 ----
+ #-------------------------------------------------------------------------
+ #
+ # Makefile
+ # Makefile for src/backend/crypto
+ #
+ # IDENTIFICATION
+ # src/backend/crypto/Makefile
+ #
+ #-------------------------------------------------------------------------
+
+ subdir = src/backend/crypto
+ top_builddir = ../../..
+ include $(top_builddir)/src/Makefile.global
+
+ OBJS = \
+ kmgr.o
+
+ include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c
new file mode 100644
index ...2a37c38
*** a/src/backend/crypto/kmgr.c
--- b/src/backend/crypto/kmgr.c
***************
*** 0 ****
--- 1,457 ----
+ /*-------------------------------------------------------------------------
+ *
+ * kmgr.c
+ * Key manager routines
+ *
+ * Key manager is enabled if user requests during initdb. During bootstrap,
+ * we generate internal keys, wrap them with KEK which is derived from the
+ * user-provided passphrase, and store them into each file located at KMGR_DIR.
+ * Once generated, these are not changed. During startup, we decrypt all
+ * internal keys and load them to the shared memory space. Internal keys on
+ * the shared memory are read-only. All wrapping and unwrapping key routines
+ * depends on openssl library for now.
+ *
+ * Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * IDENTIFICATION
+ * src/backend/crypto/kmgr.c
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+
+ #include
+ #include
+
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "pgstat.h"
+
+ #include "common/sha2.h"
+ #include "common/kmgr_utils.h"
+ #include "crypto/kmgr.h"
+ #include "storage/fd.h"
+ #include "storage/ipc.h"
+ #include "storage/shmem.h"
+ #include "utils/builtins.h"
+ #include "utils/guc.h"
+ #include "utils/memutils.h"
+
+ /* Struct stores internal keys in plaintext format */
+ typedef struct KmgrShmemData
+ {
+ /*
+ * Internal cryptographic keys. Keys are stored at its ID'th.
+ */
+ CryptoKey intlKeys[KMGR_MAX_INTERNAL_KEYS];
+ } KmgrShmemData;
+ static KmgrShmemData *KmgrShmem;
+
+ /*
+ * Key lengths in bytes of internal keys.
+ *
+ * No key supported for now.
+ */
+ static int internalKeyLengths[KMGR_MAX_INTERNAL_KEYS];
+
+ /* GUC variables */
+ bool key_management_enabled = false;;
+ char *cluster_passphrase_command = NULL;
+
+ static void KmgrSaveCryptoKeys(const char *dir, CryptoKey *keys);
+ static CryptoKey *generate_crypto_key(int len);
+ static void recoverIncompleteRotation(void);
+
+ /*
+ * This function must be called ONCE on system install.
+ */
+ void
+ BootStrapKmgr(void)
+ {
+ PgKeyWrapCtx *ctx;
+ CryptoKey keys_wrap[KMGR_MAX_INTERNAL_KEYS];
+ char passphrase[KMGR_MAX_PASSPHRASE_LEN];
+ uint8 kekenc[KMGR_ENC_KEY_LEN];
+ uint8 kekhmac[KMGR_MAC_KEY_LEN];
+ int passlen;
+
+ /*
+ * Requirement check. We need openssl library to enable key management
+ * because all encryption and decryption calls happen via openssl function
+ * calls.
+ */
+ #ifndef USE_OPENSSL
+ ereport(ERROR,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ (errmsg("cluster encryption is not supported because OpenSSL is not supported by this build"),
+ errhint("Compile with --with-openssl to use cluster encryption."))));
+ #endif
+
+ memset(keys_wrap, 0, sizeof(keys_wrap));
+
+ /* Get key encryption key from the passphrase command */
+ passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+ passphrase, KMGR_MAX_PASSPHRASE_LEN);
+ if (passlen < KMGR_MIN_PASSPHRASE_LEN)
+ ereport(ERROR,
+ (errmsg("passphrase must be more than %d bytes",
+ KMGR_MIN_PASSPHRASE_LEN)));
+
+ /* Get key encryption key and HMAC key from passphrase */
+ kmgr_derive_keys(passphrase, passlen, kekenc, kekhmac);
+
+ /* Create temporarily key wrap context */
+ ctx = pg_create_keywrap_ctx(kekenc, kekhmac);
+ if (!ctx)
+ elog(ERROR, "could not initialize encryption contect");
+
+ /* Wrap all internal keys by key encryption key */
+ for (int id = 0; id < KMGR_MAX_INTERNAL_KEYS; id++)
+ {
+ CryptoKey *key;
+
+ /* generate an internal key */
+ key = generate_crypto_key(internalKeyLengths[id]);
+
+ if (!kmgr_wrap_key(ctx, key, &(keys_wrap[id])))
+ {
+ pg_free_keywrap_ctx(ctx);
+ elog(ERROR, "failed to wrap cluster encryption key");
+ }
+ }
+
+ /* Save internal keys to the disk */
+ KmgrSaveCryptoKeys(KMGR_DIR, keys_wrap);
+
+ pg_free_keywrap_ctx(ctx);
+ }
+
+ /* Report shared-memory space needed by KmgrShmem */
+ Size
+ KmgrShmemSize(void)
+ {
+ if (!key_management_enabled)
+ return 0;
+
+ return MAXALIGN(sizeof(KmgrShmemData));
+ }
+
+ /* Allocate and initialize key manager memory */
+ void
+ KmgrShmemInit(void)
+ {
+ bool found;
+
+ if (!key_management_enabled)
+ return;
+
+ KmgrShmem = (KmgrShmemData *) ShmemInitStruct("Key manager",
+ KmgrShmemSize(), &found);
+
+ if (!found)
+ memset(KmgrShmem, 0, KmgrShmemSize());
+ }
+
+ /*
+ * Get encryption key passphrase and verify it, then get the internal keys.
+ * This function is called by postmaster at startup time.
+ */
+ void
+ InitializeKmgr(void)
+ {
+ CryptoKey *keys_wrap;
+ char passphrase[KMGR_MAX_PASSPHRASE_LEN];
+ int passlen;
+ int nkeys;
+
+ if (!key_management_enabled)
+ return;
+
+ elog(DEBUG1, "starting up key management system");
+
+ /* Recover the failure of the last passphrase rotation if necessary */
+ recoverIncompleteRotation();
+
+ /* Get the crypto keys from the file */
+ keys_wrap = kmgr_get_cryptokeys(KMGR_DIR, &nkeys);
+ Assert(nkeys == KMGR_MAX_INTERNAL_KEYS);
+
+ /* Get cluster passphrase */
+ passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+ passphrase, KMGR_MAX_PASSPHRASE_LEN);
+
+ /*
+ * Verify passphrase and prepare an internal key in plaintext on shared memory.
+ *
+ * XXX: do we need to prevent internal keys from being swapped out using
+ * mlock?
+ */
+ if (!kmgr_verify_passphrase(passphrase, passlen, keys_wrap, KmgrShmem->intlKeys,
+ KMGR_MAX_INTERNAL_KEYS))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cluster passphrase does not match expected passphrase")));
+ }
+
+ const CryptoKey *
+ KmgrGetKey(int id)
+ {
+ Assert(id < KMGR_MAX_INTERNAL_KEYS);
+
+ return (const CryptoKey *) &(KmgrShmem->intlKeys[id]);
+ }
+
+ /* Generate an empty CryptoKey */
+ static CryptoKey *
+ generate_crypto_key(int len)
+ {
+ CryptoKey *newkey;
+
+ Assert(len < KMGR_MAX_KEY_LEN);
+ newkey = (CryptoKey *) palloc0(sizeof(CryptoKey));
+
+ if (!pg_strong_random(newkey->key, len))
+ elog(ERROR, "failed to generate new crypto key");
+
+ newkey->klen = len;
+
+ return newkey;
+ }
+
+ /*
+ * Save the given crypto keys to the disk. We don't need CRC check for crypto
+ * keys because these keys have HMAC which is used for integrity check
+ * during unwrapping.
+ */
+ static void
+ KmgrSaveCryptoKeys(const char *dir, CryptoKey *keys)
+ {
+ elog(DEBUG2, "saving all cryptographic keys");
+
+ for (int i = 0; i < KMGR_MAX_INTERNAL_KEYS; i++)
+ {
+ int fd;
+ char path[MAXPGPATH];
+
+ CryptoKeyFilePath(path, dir, i);
+
+ if ((fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY)) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\": %m",
+ path)));
+
+ errno = 0;
+ pgstat_report_wait_start(WAIT_EVENT_KEY_FILE_WRITE);
+ if (write(fd, &(keys[i]), sizeof(CryptoKey)) != sizeof(CryptoKey))
+ {
+ /* if write didn't set errno, assume problem is no disk space */
+ if (errno == 0)
+ errno = ENOSPC;
+
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write file \"%s\": %m",
+ path)));
+ }
+ pgstat_report_wait_end();
+
+ pgstat_report_wait_start(WAIT_EVENT_KEY_FILE_SYNC);
+ if (pg_fsync(fd) != 0)
+ ereport(PANIC,
+ (errcode_for_file_access(),
+ errmsg("could not fsync file \"%s\": %m",
+ path)));
+ pgstat_report_wait_end();
+
+ if (close(fd) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close file \"%s\": %m",
+ path)));
+ }
+ }
+
+
+ /*
+ * Check the last passphrase rotation was completed. If not, we decide which wrapped
+ * keys will be used according to the status of temporary directory and its wrapped
+ * keys.
+ */
+ static void
+ recoverIncompleteRotation(void)
+ {
+ struct stat st;
+ struct stat st_tmp;
+ CryptoKey *keys;
+ int nkeys_tmp;
+
+ /* The cluster passphrase rotation was completed, nothing to do */
+ if (stat(KMGR_TMP_DIR, &st_tmp) != 0)
+ return;
+
+ /*
+ * If there is only temporary directory, it means that the previous
+ * rotation failed after wrapping the all internal keys by the new
+ * passphrase. Therefore we use the new cluster passphrase.
+ */
+ if (stat(KMGR_DIR, &st) != 0)
+ {
+ ereport(DEBUG1,
+ (errmsg("there is only temporary directory, use the newly wrapped keys")));
+
+ if (rename(KMGR_TMP_DIR, KMGR_DIR) != 0)
+ ereport(ERROR,
+ errmsg("could not rename directory \"%s\" to \"%s\": %m",
+ KMGR_TMP_DIR, KMGR_DIR));
+ ereport(LOG,
+ errmsg("cryptographic keys wrapped by new passphrase command are chosen"),
+ errdetail("last cluster passphrase rotation failed in the middle"));
+ return;
+ }
+
+ /*
+ * In case where both the original directory and temporary directory
+ * exist, there are two possibilities: (a) the all internal keys are
+ * wrapped by the new passphrase but rotation failed before removing the
+ * original directory, or (b) the rotation failed during wrapping internal
+ * keys by the new passphrase. In case of (a) we need to use the wrapped
+ * keys in the temporary directory as rotation is essentially completed,
+ * but in case of (b) we use the wrapped keys in the original directory.
+ *
+ * To check the possibility of (b) we validate the wrapped keys in the
+ * temporary directory by checking the number of wrapped keys. Since the
+ * wrapped key length is smaller than one disk sector, which is 512 bytes
+ * on common hardware, saving wrapped key is atomic write. So we can
+ * ensure that the all wrapped keys are valid if the number of wrapped
+ * keys in the temporary directory is KMGR_MAX_INTERNAL_KEYS.
+ */
+ keys = kmgr_get_cryptokeys(KMGR_TMP_DIR, &nkeys_tmp);
+
+ if (nkeys_tmp == KMGR_MAX_INTERNAL_KEYS)
+ {
+ /*
+ * This is case (a), the all wrapped keys in temporary directory are
+ * valid. Remove the original directory and rename.
+ */
+ ereport(DEBUG1,
+ (errmsg("last passphrase rotation failed before renaming direcotry name, use the newly wrapped keys")));
+
+ if (!rmtree(KMGR_DIR, true))
+ ereport(ERROR,
+ (errmsg("could not remove directory \"%s\"",
+ KMGR_DIR)));
+ if (rename(KMGR_TMP_DIR, KMGR_DIR) != 0)
+ ereport(ERROR,
+ errmsg("could not rename directory \"%s\" to \"%s\": %m",
+ KMGR_TMP_DIR, KMGR_DIR));
+
+ ereport(LOG,
+ errmsg("cryptographic keys wrapped by new passphrase command are chosen"),
+ errdetail("last cluster passphrase rotation failed in the middle"));
+ }
+ else
+ {
+ /*
+ * This is case (b), the last passphrase rotation failed during
+ * wrapping keys. Remove the keys in the temporary directory and use
+ * keys in the original keys.
+ */
+ ereport(DEBUG1,
+ (errmsg("last passphrase rotation failed during wrapping keys, use the old wrapped keys")));
+
+ if (!rmtree(KMGR_TMP_DIR, true))
+ ereport(ERROR,
+ (errmsg("could not remove directory \"%s\"",
+ KMGR_DIR)));
+ ereport(LOG,
+ errmsg("cryptographic keys wrapped by old passphrase command are chosen"),
+ errdetail("last cluster passphrase rotation failed in the middle"));
+ }
+
+ pfree(keys);
+ }
+
+ /*
+ * SQL function to rotate the cluster passphrase. This function assumes that
+ * the cluster_passphrase_command is already reloaded to the new value.
+ * All internal keys are wrapped by the new passphrase and saved to the disk.
+ * To update all crypto keys atomically we save the newly wrapped keys to the
+ * temporary directory, pg_cryptokeys_tmp, and remove the original directory,
+ * pg_cryptokeys, and rename it. These operation is performed without the help
+ * of WAL. In the case of failure during rotationpg_cryptokeys directory and
+ * pg_cryptokeys_tmp directory can be left in incomplete status. We recover
+ * the incomplete situation by checkIncompleteRotation.
+ */
+ Datum
+ pg_rotate_cluster_passphrase(PG_FUNCTION_ARGS)
+ {
+ PgKeyWrapCtx *ctx;
+ CryptoKey newkeys[KMGR_MAX_INTERNAL_KEYS];
+ char passphrase[KMGR_MAX_PASSPHRASE_LEN];
+ uint8 new_kekenc[KMGR_ENC_KEY_LEN];
+ uint8 new_kekhmac[KMGR_MAC_KEY_LEN];
+ int passlen;
+
+ if (!key_management_enabled)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not rotate cluster passphrase because key management is not supported")));
+
+ memset(newkeys, 0, sizeof(newkeys));
+
+ /* Recover the failure of the last passphrase rotation if necessary */
+ recoverIncompleteRotation();
+
+ passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command,
+ passphrase,
+ KMGR_MAX_PASSPHRASE_LEN);
+ if (passlen < KMGR_MIN_PASSPHRASE_LEN)
+ ereport(ERROR,
+ (errmsg("passphrase must be more than %d bytes",
+ KMGR_MIN_PASSPHRASE_LEN)));
+
+ /* Get new key encryption key and encryption context */
+ kmgr_derive_keys(passphrase, passlen, new_kekenc, new_kekhmac);
+ ctx = pg_create_keywrap_ctx(new_kekenc, new_kekhmac);
+ if (!ctx)
+ elog(ERROR, "could not initialize encryption contect");
+
+ for (int id = 0; id < KMGR_MAX_INTERNAL_KEYS; id++)
+ {
+ if (!kmgr_wrap_key(ctx, &(KmgrShmem->intlKeys[id]), &(newkeys[id])))
+ elog(ERROR, "failed to wrap key");
+ }
+
+ /* Create temporary directory */
+ if (MakePGDirectory(KMGR_TMP_DIR) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not create temporary directory \"%s\": %m",
+ KMGR_TMP_DIR)));
+ fsync_fname(KMGR_TMP_DIR, true);
+
+ /* Prevent concurrent key rotation */
+ LWLockAcquire(KmgrFileLock, LW_EXCLUSIVE);
+
+ /* Save the key wrapped by the new passphrase to the temporary directory */
+ KmgrSaveCryptoKeys(KMGR_TMP_DIR, newkeys);
+
+ /* Remove the original directory */
+ if (!rmtree(KMGR_DIR, true))
+ ereport(ERROR,
+ (errmsg("could not remove directory \"%s\"",
+ KMGR_DIR)));
+
+ /* Rename to the original directory */
+ if (rename(KMGR_TMP_DIR, KMGR_DIR) != 0)
+ ereport(ERROR,
+ (errmsg("could not rename directory \"%s\" to \"%s\": %m",
+ KMGR_TMP_DIR, KMGR_DIR)));
+ fsync_fname(KMGR_DIR, true);
+
+ LWLockRelease(KmgrFileLock);
+
+ pg_free_keywrap_ctx(ctx);
+ PG_RETURN_BOOL(true);
+ }
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
new file mode 100644
index 8b21ff4..744fa96
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 30,35 ****
--- 30,36 ----
#endif
#include
+ #include
#include
#include
#ifndef OPENSSL_NO_ECDH
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
new file mode 100644
index 822f0eb..a1faea7
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
*************** pgstat_get_wait_io(WaitEventIO w)
*** 4134,4139 ****
--- 4134,4148 ----
case WAIT_EVENT_DSM_FILL_ZERO_WRITE:
event_name = "DSMFillZeroWrite";
break;
+ case WAIT_EVENT_KEY_FILE_READ:
+ event_name = "KeyFileRead";
+ break;
+ case WAIT_EVENT_KEY_FILE_WRITE:
+ event_name = "KeyFileWrite";
+ break;
+ case WAIT_EVENT_KEY_FILE_SYNC:
+ event_name = "KeyFileSync";
+ break;
case WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ:
event_name = "LockFileAddToDataDirRead";
break;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
new file mode 100644
index 959e3b8..6ebab83
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 100,105 ****
--- 100,106 ----
#include "common/file_perm.h"
#include "common/ip.h"
#include "common/string.h"
+ #include "crypto/kmgr.h"
#include "lib/ilist.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
*************** PostmasterMain(int argc, char *argv[])
*** 1351,1356 ****
--- 1352,1362 ----
autovac_init();
/*
+ * Initialize key manager.
+ */
+ InitializeKmgr();
+
+ /*
* Load configuration files for client authentication.
*/
if (!load_hba())
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
new file mode 100644
index b89df01..6ad051d
*** a/src/backend/replication/basebackup.c
--- b/src/backend/replication/basebackup.c
***************
*** 18,23 ****
--- 18,24 ----
#include "access/xlog_internal.h" /* for pg_start/stop_backup */
#include "catalog/pg_type.h"
+ #include "common/kmgr_utils.h"
#include "common/file_perm.h"
#include "commands/progress.h"
#include "lib/stringinfo.h"
*************** struct exclude_list_item
*** 152,157 ****
--- 153,161 ----
*/
static const char *const excludeDirContents[] =
{
+ /* Skip temporary crypto key files */
+ KMGR_TMP_DIR,
+
/*
* Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped even
* when stats_temp_directory is set because PGSS_TEXT_FILE is always
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
new file mode 100644
index 96c2aaa..64fe10c
*** a/src/backend/storage/ipc/ipci.c
--- b/src/backend/storage/ipc/ipci.c
***************
*** 23,28 ****
--- 23,29 ----
#include "access/syncscan.h"
#include "access/twophase.h"
#include "commands/async.h"
+ #include "crypto/kmgr.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
*************** CreateSharedMemoryAndSemaphores(void)
*** 149,154 ****
--- 150,156 ----
size = add_size(size, BTreeShmemSize());
size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize());
+ size = add_size(size, KmgrShmemSize());
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
#endif
*************** CreateSharedMemoryAndSemaphores(void)
*** 267,272 ****
--- 269,275 ----
BTreeShmemInit();
SyncScanShmemInit();
AsyncShmemInit();
+ KmgrShmemInit();
#ifdef EXEC_BACKEND
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
new file mode 100644
index 774292f..a44805d
*** a/src/backend/storage/lmgr/lwlocknames.txt
--- b/src/backend/storage/lmgr/lwlocknames.txt
*************** XactTruncationLock 44
*** 53,55 ****
--- 53,56 ----
# 45 was XactTruncationLock until removal of BackendRandomLock
WrapLimitsVacuumLock 46
NotifyQueueTailLock 47
+ KmgrFileLock 48
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
new file mode 100644
index 411cfad..7572f18
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 42,47 ****
--- 42,48 ----
#include "catalog/pg_type.h"
#include "commands/async.h"
#include "commands/prepare.h"
+ #include "crypto/kmgr.h"
#include "executor/spi.h"
#include "jit/jit.h"
#include "libpq/libpq.h"
*************** PostgresMain(int argc, char *argv[],
*** 3902,3907 ****
--- 3903,3915 ----
BaseInit();
/*
+ * Initialize kmgr for cluster encryption. Since kmgr needs to attach to
+ * shared memory the initialization must be called after BaseInit().
+ */
+ if (!IsUnderPostmaster)
+ InitializeKmgr();
+
+ /*
* Create a per-backend PGPROC struct in shared memory, except in the
* EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
* this before we can use LWLocks (and in the EXEC_BACKEND case we already
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
new file mode 100644
index a62d64e..411f068
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 47,52 ****
--- 47,53 ----
#include "commands/vacuum.h"
#include "commands/variable.h"
#include "common/string.h"
+ #include "crypto/kmgr.h"
#include "funcapi.h"
#include "jit/jit.h"
#include "libpq/auth.h"
*************** const char *const config_group_names[] =
*** 745,750 ****
--- 746,753 ----
gettext_noop("Statistics / Monitoring"),
/* STATS_COLLECTOR */
gettext_noop("Statistics / Query and Index Statistics Collector"),
+ /* ENCRYPTION */
+ gettext_noop("Encryption"),
/* AUTOVACUUM */
gettext_noop("Autovacuum"),
/* CLIENT_CONN */
*************** static struct config_bool ConfigureNames
*** 2036,2041 ****
--- 2039,2055 ----
NULL, NULL, NULL
},
+ {
+ {"key_management", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Show whether key management is enabled for this cluster."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &key_management_enabled,
+ false,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
*************** static struct config_string ConfigureNam
*** 4392,4397 ****
--- 4406,4421 ----
"",
NULL, NULL, NULL
},
+
+ {
+ {"cluster_passphrase_command", PGC_SIGHUP, ENCRYPTION,
+ gettext_noop("Command to obtain passphrase for database encryption."),
+ NULL
+ },
+ &cluster_passphrase_command,
+ "",
+ NULL, NULL, NULL
+ },
{
{"application_name", PGC_USERSET, LOGGING_WHAT,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
new file mode 100644
index 9cb571f..fc30641
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 633,638 ****
--- 633,643 ----
# autovacuum, -1 means use
# vacuum_cost_limit
+ #------------------------------------------------------------------------------
+ # ENCRYPTION
+ #------------------------------------------------------------------------------
+
+ #cluster_passphrase_command = ''
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
new file mode 100644
index ee3bfa8..f088151
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
*************** static bool data_checksums = false;
*** 146,151 ****
--- 146,152 ----
static char *xlog_dir = NULL;
static char *str_wal_segment_size_mb = NULL;
static int wal_segment_size_mb;
+ static char *cluster_passphrase = NULL;
/* internal vars */
*************** static const char *const subdirs[] = {
*** 203,208 ****
--- 204,210 ----
"global",
"pg_wal/archive_status",
"pg_commit_ts",
+ "pg_cryptokeys",
"pg_dynshmem",
"pg_notify",
"pg_serial",
*************** setup_config(void)
*** 1185,1190 ****
--- 1187,1199 ----
"password_encryption = md5");
}
+ if (cluster_passphrase)
+ {
+ snprintf(repltok, sizeof(repltok), "cluster_passphrase_command = '%s'",
+ escape_quotes(cluster_passphrase));
+ conflines = replace_token(conflines, "#cluster_passphrase_command = ''", repltok);
+ }
+
/*
* If group access has been enabled for the cluster then it makes sense to
* ensure that the log files also allow group access. Otherwise a backup
*************** bootstrap_template1(void)
*** 1395,1408 ****
unsetenv("PGCLIENTENCODING");
snprintf(cmd, sizeof(cmd),
! "\"%s\" --boot -x1 -X %u %s %s %s",
backend_exec,
wal_segment_size_mb * (1024 * 1024),
data_checksums ? "-k" : "",
boot_options,
debug ? "-d 5" : "");
-
PG_CMD_OPEN;
for (line = bki_lines; *line != NULL; line++)
--- 1404,1417 ----
unsetenv("PGCLIENTENCODING");
snprintf(cmd, sizeof(cmd),
! "\"%s\" --boot -x1 -X %u %s %s %s %s",
backend_exec,
wal_segment_size_mb * (1024 * 1024),
data_checksums ? "-k" : "",
+ cluster_passphrase ? "-e" : "",
boot_options,
debug ? "-d 5" : "");
PG_CMD_OPEN;
for (line = bki_lines; *line != NULL; line++)
*************** usage(const char *progname)
*** 2290,2295 ****
--- 2299,2306 ----
printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n"));
printf(_("\nLess commonly used options:\n"));
printf(_(" -d, --debug generate lots of debugging output\n"));
+ printf(_(" -c --cluster-passphrase-command=COMMAND\n"
+ " set command to obtain passphrase for key management\n"));
printf(_(" -k, --data-checksums use data page checksums\n"));
printf(_(" -L DIRECTORY where to find the input files\n"));
printf(_(" -n, --no-clean do not clean up after errors\n"));
*************** main(int argc, char *argv[])
*** 2958,2963 ****
--- 2969,2975 ----
{"wal-segsize", required_argument, NULL, 12},
{"data-checksums", no_argument, NULL, 'k'},
{"allow-group-access", no_argument, NULL, 'g'},
+ {"cluster-passphrase-command", required_argument, NULL, 'c'},
{NULL, 0, NULL, 0}
};
*************** main(int argc, char *argv[])
*** 2999,3005 ****
/* process command-line options */
! while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1)
{
switch (c)
{
--- 3011,3017 ----
/* process command-line options */
! while ((c = getopt_long(argc, argv, "c:dD:E:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1)
{
switch (c)
{
*************** main(int argc, char *argv[])
*** 3081,3086 ****
--- 3093,3101 ----
case 9:
pwfilename = pg_strdup(optarg);
break;
+ case 'c':
+ cluster_passphrase = pg_strdup(optarg);
+ break;
case 's':
show_setting = true;
break;
*************** main(int argc, char *argv[])
*** 3151,3156 ****
--- 3166,3179 ----
exit(1);
}
+ #ifndef USE_OPENSSL
+ if (cluster_passphrase)
+ {
+ pg_log_error("cluster encryption is not supported because OpenSSL is not supported by this build");
+ exit(1);
+ }
+ #endif
+
check_authmethod_unspecified(&authmethodlocal);
check_authmethod_unspecified(&authmethodhost);
*************** main(int argc, char *argv[])
*** 3218,3223 ****
--- 3241,3251 ----
else
printf(_("Data page checksums are disabled.\n"));
+ if (cluster_passphrase)
+ printf(_("Key management system is enabled.\n"));
+ else
+ printf(_("Key management system is disabled.\n"));
+
if (pwprompt || pwfilename)
get_su_pwd();
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
new file mode 100644
index 3e00ac0..39ba7f0
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
***************
*** 25,30 ****
--- 25,31 ----
#include "access/xlog_internal.h"
#include "catalog/pg_control.h"
#include "common/controldata_utils.h"
+ #include "common/kmgr_utils.h"
#include "common/logging.h"
#include "getopt_long.h"
#include "pg_getopt.h"
*************** main(int argc, char *argv[])
*** 334,338 ****
--- 335,341 ----
ControlFile->data_checksum_version);
printf(_("Mock authentication nonce: %s\n"),
mock_auth_nonce_str);
+ printf(_("Key management version: %u\n"),
+ ControlFile->key_management_version);
return 0;
}
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
new file mode 100644
index cb6ef19..a9d200e
*** a/src/bin/pg_resetwal/pg_resetwal.c
--- b/src/bin/pg_resetwal/pg_resetwal.c
*************** PrintControlValues(bool guessed)
*** 804,809 ****
--- 804,811 ----
(ControlFile.float8ByVal ? _("by value") : _("by reference")));
printf(_("Data page checksum version: %u\n"),
ControlFile.data_checksum_version);
+ printf(_("Key management version: %u\n"),
+ ControlFile.key_management_version);
}
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
new file mode 100644
index 1abc257..3084ae7
*** a/src/bin/pg_rewind/filemap.c
--- b/src/bin/pg_rewind/filemap.c
*************** static const char *excludeDirContents[]
*** 74,79 ****
--- 74,87 ----
"pg_notify",
/*
+ * Skip cryptographic keys. It's generally not good idea to copy the
+ * cryptographic keys from source database because these might use
+ * different cluster passphrase.
+ */
+ "pg_cryptokeys", /* defined as KMGR_DIR */
+ "pg_cryptokeys_tmp", /* defined as KMGR_TMP_DIR */
+
+ /*
* Old contents are loaded for possible debugging but are not required for
* normal operation, see SerialInit().
*/
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
new file mode 100644
index 39bcaa8..e5794ff
*** a/src/bin/pg_upgrade/controldata.c
--- b/src/bin/pg_upgrade/controldata.c
***************
*** 9,18 ****
--- 9,24 ----
#include "postgres_fe.h"
+ #include
#include
#include "pg_upgrade.h"
+ #include "access/xlog_internal.h"
+ #include "common/controldata_utils.h"
+ #include "common/file_utils.h"
+ #include "common/kmgr_utils.h"
+
/*
* get_control_data()
*
*************** get_control_data(ClusterInfo *cluster, b
*** 59,64 ****
--- 65,71 ----
bool got_date_is_int = false;
bool got_data_checksum_version = false;
bool got_cluster_state = false;
+ bool got_key_management_enabled = false;
char *lc_collate = NULL;
char *lc_ctype = NULL;
char *lc_monetary = NULL;
*************** get_control_data(ClusterInfo *cluster, b
*** 202,207 ****
--- 209,221 ----
got_data_checksum_version = true;
}
+ /* Only in <= 14 */
+ if (GET_MAJOR_VERSION(cluster->major_version) <= 1400)
+ {
+ cluster->controldata.key_management_enabled = false;
+ got_key_management_enabled = true;
+ }
+
/* we have the result of cmd in "output". so parse it line by line now */
while (fgets(bufin, sizeof(bufin), output))
{
*************** get_control_data(ClusterInfo *cluster, b
*** 485,490 ****
--- 499,516 ----
cluster->controldata.data_checksum_version = str2uint(p);
got_data_checksum_version = true;
}
+ else if ((p = strstr(bufin, "Key management:")) != NULL)
+ {
+ p = strchr(p, ':');
+
+ if (p == NULL || strlen(p) <= 1)
+ pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+ p++; /* remove ':' char */
+ /* used later for contrib check */
+ cluster->controldata.key_management_enabled = strstr(p, "on") != NULL;
+ got_key_management_enabled = true;
+ }
}
pclose(output);
*************** get_control_data(ClusterInfo *cluster, b
*** 539,545 ****
!got_index || !got_toast ||
(!got_large_object &&
cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
! !got_date_is_int || !got_data_checksum_version)
{
if (cluster == &old_cluster)
pg_log(PG_REPORT,
--- 565,572 ----
!got_index || !got_toast ||
(!got_large_object &&
cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
! !got_date_is_int || !got_data_checksum_version ||
! !got_key_management_enabled)
{
if (cluster == &old_cluster)
pg_log(PG_REPORT,
*************** get_control_data(ClusterInfo *cluster, b
*** 605,610 ****
--- 632,641 ----
if (!got_data_checksum_version)
pg_log(PG_REPORT, " data checksum version\n");
+ /* value added in Postgres 12 */
+ if (!got_key_management_enabled)
+ pg_log(PG_REPORT, " key management enabled\n");
+
pg_fatal("Cannot continue without required control information, terminating\n");
}
}
*************** check_control_data(ControlData *oldctrl,
*** 669,674 ****
--- 700,713 ----
pg_fatal("old cluster uses data checksums but the new one does not\n");
else if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
pg_fatal("old and new cluster pg_controldata checksum versions do not match\n");
+
+ /*
+ * We cannot upgrade if the old cluster enables the key management but
+ * the new one doesn't support because the old one might already have
+ * data encrypted by the master encryption key.
+ */
+ if (oldctrl->key_management_enabled && !newctrl->key_management_enabled)
+ pg_fatal("old cluster uses key management but the new one does not\n");
}
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
new file mode 100644
index cc8a675..282359f
*** a/src/bin/pg_upgrade/file.c
--- b/src/bin/pg_upgrade/file.c
***************
*** 11,16 ****
--- 11,17 ----
#include
#include
+ #include
#ifdef HAVE_COPYFILE_H
#include
#endif
***************
*** 21,26 ****
--- 22,28 ----
#include "access/visibilitymap.h"
#include "common/file_perm.h"
+ #include "common/file_utils.h"
#include "pg_upgrade.h"
#include "storage/bufpage.h"
#include "storage/checksum.h"
*************** check_hard_link(void)
*** 372,374 ****
--- 374,451 ----
unlink(new_link_file);
}
+
+ /*
+ * Copy cryptographic keys from the old cluster to the new cluster.
+ */
+ void
+ copy_master_encryption_key(ClusterInfo *old_cluster, ClusterInfo * new_cluster)
+ {
+ DIR *dir;
+ struct dirent *de;
+ char path[MAXPGPATH];
+
+ /* We copy the crypto keys only if both clusters enable the key management */
+ if (!old_cluster->controldata.key_management_enabled ||
+ !new_cluster->controldata.key_management_enabled)
+ return;
+
+ prep_status("Copying master encryption key");
+
+ snprintf(path, MAXPGPATH, "%s/%s", old_cluster->pgdata, KMGR_DIR);
+
+ if ((dir = opendir(path)) == NULL)
+ pg_fatal("could not open directory \"%s\": %m", path);
+
+ while ((de = readdir(dir)) != NULL)
+ {
+ if (strlen(de->d_name) == 4 &&
+ strspn(de->d_name, "0123456789ABCDEF") == 4)
+ {
+ CryptoKey key;
+ char src_path[MAXPGPATH];
+ char dst_path[MAXPGPATH];
+ uint32 id;
+ int src_fd;
+ int dst_fd;
+ int len;
+
+ id = strtoul(de->d_name, NULL, 16);
+
+ snprintf(src_path, MAXPGPATH, "%s/%s/%04X",
+ old_cluster->pgdata, KMGR_DIR, id);
+ snprintf(dst_path, MAXPGPATH, "%s/%s/%04X",
+ new_cluster->pgdata, KMGR_DIR, id);
+
+ if ((src_fd = open(src_path, O_RDONLY | PG_BINARY, 0)) < 0)
+ pg_fatal("could not open file \"%s\": %m", src_path);
+
+ if ((dst_fd = open(dst_path, O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
+ pg_file_create_mode)) < 0)
+ pg_fatal("could not open file \"%s\": %m", dst_path);
+
+ /* Read the source key */
+ len = read(src_fd, &key, sizeof(CryptoKey));
+ if (len != sizeof(CryptoKey))
+ {
+ if (len < 0)
+ pg_fatal("could not read file \"%s\": %m", src_path);
+ else
+ pg_fatal("could not read file \"%s\": read %d of %zu",
+ src_path, len, sizeof(CryptoKey));
+ }
+
+ /* Write to the dest key */
+ len = write(dst_fd, &key, sizeof(CryptoKey));
+ if (len != sizeof(CryptoKey))
+ pg_fatal("could not write fie \"%s\"", dst_path);
+
+ close(src_fd);
+ close(dst_fd);
+ }
+ }
+
+ closedir(dir);
+
+ check_ok();
+ }
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
new file mode 100644
index 1bc86e4..d1bacdf
*** a/src/bin/pg_upgrade/pg_upgrade.c
--- b/src/bin/pg_upgrade/pg_upgrade.c
*************** main(int argc, char **argv)
*** 158,163 ****
--- 158,170 ----
old_cluster.pgdata, new_cluster.pgdata);
/*
+ * Copy the internal encryption keys from the old cluster to the new one.
+ * This is necessary because the data in the old cluster might be
+ * encrypted with the old master encryption key.
+ */
+ copy_master_encryption_key(&old_cluster, &new_cluster);
+
+ /*
* Assuming OIDs are only used in system tables, there is no need to
* restore the OID counter because we have not transferred any OIDs from
* the old system, but we do it anyway just in case. We do it late here
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
new file mode 100644
index 8b90cef..32ab236
*** a/src/bin/pg_upgrade/pg_upgrade.h
--- b/src/bin/pg_upgrade/pg_upgrade.h
***************
*** 11,16 ****
--- 11,17 ----
#include
#include "libpq-fe.h"
+ #include "common/kmgr_utils.h"
/* Use port in the private/dynamic port number range */
#define DEF_PGUPORT 50432
*************** typedef struct
*** 219,224 ****
--- 220,226 ----
bool date_is_int;
bool float8_pass_by_value;
bool data_checksum_version;
+ bool key_management_enabled;
} ControlData;
/*
*************** void rewriteVisibilityMap(const char *f
*** 375,380 ****
--- 377,384 ----
const char *schemaName, const char *relName);
void check_file_clone(void);
void check_hard_link(void);
+ void copy_master_encryption_key(ClusterInfo *old_cluster,
+ ClusterInfo * new_cluster);
/* fopen_priv() is no longer different from fopen() */
#define fopen_priv(path, mode) fopen(path, mode)
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 25c55bd..cfb34fd
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** OBJS_COMMON = \
*** 49,54 ****
--- 49,55 ----
archive.o \
base64.o \
checksum_helper.o \
+ cipher.o \
config_info.o \
controldata_utils.o \
d2s.o \
*************** OBJS_COMMON = \
*** 61,66 ****
--- 62,68 ----
ip.o \
jsonapi.o \
keywords.o \
+ kmgr_utils.o \
kwlookup.o \
link-canary.o \
md5.o \
*************** OBJS_COMMON = \
*** 81,86 ****
--- 83,89 ----
ifeq ($(with_openssl),yes)
OBJS_COMMON += \
+ cipher_openssl.o \
protocol_openssl.o \
sha2_openssl.o
else
diff --git a/src/common/cipher.c b/src/common/cipher.c
new file mode 100644
index ...908afe8
*** a/src/common/cipher.c
--- b/src/common/cipher.c
***************
*** 0 ****
--- 1,87 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cipher.c
+ * Shared frontend/backend for cryptographic functions
+ *
+ * Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * IDENTIFICATION
+ * src/common/cipher.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+
+ #include "common/cipher.h"
+ #ifdef USE_OPENSSL
+ #include "common/cipher_openssl.h"
+ #endif
+
+ /*
+ * Return a newly created cipher context. 'cipher' specifies cipher algorithm
+ * by identifer like PG_CIPHER_XXX.
+ */
+ PgCipherCtx *
+ pg_cipher_ctx_create(int cipher, uint8 *key, int klen)
+ {
+ PgCipherCtx *ctx = NULL;
+
+ if (cipher >= PG_MAX_CIPHER_ID)
+ return NULL;
+
+ #ifdef USE_OPENSSL
+ ctx = (PgCipherCtx *) palloc0(sizeof(PgCipherCtx));
+
+ ctx->encctx = ossl_cipher_ctx_create(cipher, key, klen, true);
+ ctx->decctx = ossl_cipher_ctx_create(cipher, key, klen, false);
+ #endif
+
+ return ctx;
+ }
+
+ void
+ pg_cipher_ctx_free(PgCipherCtx *ctx)
+ {
+ #ifdef USE_OPENSSL
+ ossl_cipher_ctx_free(ctx->encctx);
+ ossl_cipher_ctx_free(ctx->decctx);
+ #endif
+ }
+
+ bool
+ pg_cipher_encrypt(PgCipherCtx *ctx, const uint8 *in, int inlen,
+ uint8 *out, int *outlen, const uint8 *iv)
+ {
+ bool r = false;
+ #ifdef USE_OPENSSL
+ r = ossl_cipher_encrypt(ctx->encctx, in, inlen, out, outlen, iv);
+ #endif
+ return r;
+ }
+
+ bool
+ pg_cipher_decrypt(PgCipherCtx *ctx, const uint8 *in, int inlen,
+ uint8 *out, int *outlen, const uint8 *iv)
+ {
+ bool r = false;
+ #ifdef USE_OPENSSL
+ r = ossl_cipher_decrypt(ctx->decctx, in, inlen, out, outlen, iv);
+ #endif
+ return r;
+ }
+
+ bool
+ pg_HMAC_SHA512(const uint8 *key, const uint8 *in, int inlen,
+ uint8 *out)
+ {
+ bool r = false;
+ #ifdef USE_OPENSSL
+ r = ossl_HMAC_SHA512(key, in, inlen, out);
+ #endif
+ return r;
+ }
diff --git a/src/common/cipher_openssl.c b/src/common/cipher_openssl.c
new file mode 100644
index ...e8bc241
*** a/src/common/cipher_openssl.c
--- b/src/common/cipher_openssl.c
***************
*** 0 ****
--- 1,181 ----
+ /*-------------------------------------------------------------------------
+ * cipher_openssl.c
+ * Cryptographic function using OpenSSL
+ *
+ * This contains the common low-level functions needed in both frontend and
+ * backend, for implement the database encryption.
+ *
+ * Portions Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * IDENTIFICATION
+ * src/common/cipher_openssl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+
+ #include "common/sha2.h"
+ #include "common/cipher_openssl.h"
+
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ /*
+ * prototype for the EVP functions that return an algorithm, e.g.
+ * EVP_aes_128_cbc().
+ */
+ typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void);
+
+ static bool ossl_initialized = false;
+
+ static ossl_EVP_cipher_func get_evp_aes_cbc(int klen);
+
+ static ossl_EVP_cipher_func
+ get_evp_aes_cbc(int klen)
+ {
+ switch (klen)
+ {
+ case PG_AES128_KEY_LEN:
+ return EVP_aes_128_cbc;
+ case PG_AES192_KEY_LEN:
+ return EVP_aes_192_cbc;
+ case PG_AES256_KEY_LEN:
+ return EVP_aes_256_cbc;
+ default:
+ return NULL;
+ }
+ }
+
+ /*
+ * Initialize and return an EVP_CIPHER_CTX. Return NULL if the given
+ * cipher algorithm is not supported or on failure..
+ */
+ EVP_CIPHER_CTX *
+ ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
+ {
+ EVP_CIPHER_CTX *ctx;
+ ossl_EVP_cipher_func func;
+ int ret;
+
+ if (!ossl_initialized)
+ {
+ #ifdef HAVE_OPENSSL_INIT_SSL
+ OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
+ #else
+ OPENSSL_config(NULL);
+ SSL_library_init();
+ SSL_load_error_strings();
+ #endif
+ ossl_initialized = true;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ switch (cipher)
+ {
+ case PG_CIPHER_AES_CBC:
+ func = get_evp_aes_cbc(klen);
+ if (!func)
+ goto failed;
+ break;
+ default:
+ goto failed;
+ }
+
+
+ if (enc)
+ ret = EVP_EncryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
+ else
+ ret = EVP_DecryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
+
+ if (!ret)
+ goto failed;
+
+ if (!EVP_CIPHER_CTX_set_key_length(ctx, PG_AES256_KEY_LEN))
+ goto failed;
+
+ /*
+ * Always enable padding. We don't need to check the return value as
+ * EVP_CIPHER_CTX_set_padding always returns 1.
+ */
+ EVP_CIPHER_CTX_set_padding(ctx, 1);
+
+ return ctx;
+
+ failed:
+ EVP_CIPHER_CTX_free(ctx);
+ return NULL;
+ }
+
+ void
+ ossl_cipher_ctx_free(EVP_CIPHER_CTX *ctx)
+ {
+ return EVP_CIPHER_CTX_free(ctx);
+ }
+
+ bool
+ ossl_cipher_encrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv)
+ {
+ int len;
+ int enclen;
+
+ if (!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv))
+ return false;
+
+ if (!EVP_EncryptUpdate(ctx, out, &len, in, inlen))
+ return false;
+
+ enclen = len;
+
+ if (!EVP_EncryptFinal_ex(ctx, (uint8 *) ((char *) out + enclen),
+ &len))
+ return false;
+
+ *outlen = enclen + len;
+
+ return true;
+ }
+
+ bool
+ ossl_cipher_decrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv)
+ {
+ int declen;
+ int len;
+
+ if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv))
+ return false;
+
+ if (!EVP_DecryptUpdate(ctx, out, &len, in, inlen))
+ return false;
+
+ declen = len;
+
+ if (!EVP_DecryptFinal_ex(ctx, (uint8 *) ((char *) out + declen),
+ &len))
+ return false;
+
+ *outlen = declen + len;
+
+ return true;
+ }
+
+ bool
+ ossl_HMAC_SHA512(const uint8 *key, const uint8 *in, int inlen,
+ uint8 *out)
+ {
+ return HMAC(EVP_sha512(), key, PG_SHA512_DIGEST_LENGTH,
+ in, (uint32) inlen, out, NULL);
+ }
diff --git a/src/common/kmgr_utils.c b/src/common/kmgr_utils.c
new file mode 100644
index ...42ab462
*** a/src/common/kmgr_utils.c
--- b/src/common/kmgr_utils.c
***************
*** 0 ****
--- 1,523 ----
+ /*-------------------------------------------------------------------------
+ *
+ * kmgr_utils.c
+ * Shared frontend/backend for cryptographic key management
+ *
+ * Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * IDENTIFICATION
+ * src/common/kmgr_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+
+ #include
+ #include
+
+ #ifdef FRONTEND
+ #include "common/logging.h"
+ #endif
+ #include "common/file_perm.h"
+ #include "common/kmgr_utils.h"
+ #include "common/sha2.h"
+ #include "crypto/kmgr.h"
+ #include "utils/elog.h"
+ #include "storage/fd.h"
+
+ #ifndef FRONTEND
+ #include "pgstat.h"
+ #include "storage/fd.h"
+ #endif
+
+ #define KMGR_PROMPT_MSG "Enter database encryption pass phrase: "
+
+ #ifdef FRONTEND
+ static FILE *open_pipe_stream(const char *command);
+ static int close_pipe_stream(FILE *file);
+ #endif
+
+ static void read_one_keyfile(const char *dataDir, uint32 id, CryptoKey *key_p);
+
+ /* Return a key wrap context initialized with the given keys */
+ PgKeyWrapCtx *
+ pg_create_keywrap_ctx(uint8 key[KMGR_ENC_KEY_LEN], uint8 mackey[KMGR_MAC_KEY_LEN])
+ {
+ PgKeyWrapCtx *ctx;
+
+ ctx = (PgKeyWrapCtx *) palloc0(sizeof(PgKeyWrapCtx));
+
+ /* Create and initialize a cipher context */
+ ctx->cipherctx = pg_cipher_ctx_create(PG_CIPHER_AES_CBC, key, KMGR_ENC_KEY_LEN);
+ if (ctx->cipherctx == NULL)
+ return NULL;
+
+ /* Set encryption key and MAC key */
+ memcpy(ctx->key, key, KMGR_ENC_KEY_LEN);
+ memcpy(ctx->mackey, mackey, KMGR_MAC_KEY_LEN);
+
+ return ctx;
+ }
+
+ /* Free the key wrap context */
+ void
+ pg_free_keywrap_ctx(PgKeyWrapCtx *ctx)
+ {
+ if (!ctx)
+ return;
+
+ Assert(ctx->cipherctx);
+
+ pg_cipher_ctx_free(ctx->cipherctx);
+
+ #ifndef FRONTEND
+ pfree(ctx);
+ #else
+ pg_free(ctx);
+ #endif
+ }
+
+ /*
+ * Encrypt the given data. Return true and set encrypted data to 'out' if
+ * success. Otherwise return false. The caller must allocate sufficient space
+ * for cipher data calculated by using KmgrSizeOfCipherText(). Please note that
+ * this function modifies 'out' data even on failure case.
+ */
+ bool
+ kmgr_wrap_key(PgKeyWrapCtx *ctx, CryptoKey *in, CryptoKey *out)
+ {
+ uint8 *hmac;
+ uint8 *iv;
+ uint8 *enc;
+ int enclen;
+
+ Assert(ctx && in && out);
+
+ hmac = out->key;
+ iv = hmac + KMGR_HMAC_LEN;
+ enc = iv + PG_AES_IV_SIZE;
+
+ /* Generate IV */
+ if (!pg_strong_random(iv, PG_AES_IV_SIZE))
+ return false;
+
+ if (!pg_cipher_encrypt(ctx->cipherctx, in->key, in->klen, enc, &enclen, iv))
+ return false;
+
+ if (!pg_HMAC_SHA512(ctx->mackey, enc, enclen, hmac))
+ return false;
+
+ out->klen = KmgrSizeOfCipherText(in->klen);;
+ Assert(out->klen == KMGR_HMAC_LEN + PG_AES_IV_SIZE + enclen);
+
+ return true;
+ }
+
+ /*
+ * Decrypt the given Data. Return true and set plain text data to `out` if
+ * success. Otherwise return false. The caller must allocate sufficient space
+ * for cipher data calculated by using KmgrSizeOfPlainText(). Please note that
+ * this function modifies 'out' data even on failure case.
+ */
+ bool
+ kmgr_unwrap_key(PgKeyWrapCtx *ctx, CryptoKey *in, CryptoKey *out)
+ {
+ uint8 hmac[KMGR_HMAC_LEN];
+ uint8 *expected_hmac;
+ uint8 *iv;
+ uint8 *enc;
+ int enclen;
+
+ Assert(ctx && in && out);
+
+ expected_hmac = in->key;
+ iv = expected_hmac + KMGR_HMAC_LEN;
+ enc = iv + PG_AES_IV_SIZE;
+ enclen = in->klen - (enc - in->key);
+
+ /* Verify the correctness of HMAC */
+ if (!pg_HMAC_SHA512(ctx->mackey, enc, enclen, hmac))
+ return false;
+
+ if (memcmp(hmac, expected_hmac, KMGR_HMAC_LEN) != 0)
+ return false;
+
+ /* Decrypt encrypted data */
+ if (!pg_cipher_decrypt(ctx->cipherctx, enc, enclen, out->key, &(out->klen), iv))
+ return false;
+
+ return true;
+ }
+
+ /*
+ * Verify the correctness of the given passphrase by unwrapping the given keys.
+ * If the given passphrase is correct we set unwrapped keys to keys_out and return
+ * true. Otherwise return false. Please note that this function changes the
+ * contents of keys_out even on failure. Both keys_in and keys_out must be the
+ * same length, nkey.
+ */
+ bool
+ kmgr_verify_passphrase(char *passphrase, int passlen,
+ CryptoKey *keys_in, CryptoKey *keys_out, int nkeys)
+ {
+ PgKeyWrapCtx *tmpctx;
+ uint8 user_enckey[KMGR_ENC_KEY_LEN];
+ uint8 user_hmackey[KMGR_MAC_KEY_LEN];
+
+ /*
+ * Create temporary wrap context with encryption key and HMAC key extracted
+ * from the passphrase.
+ */
+ kmgr_derive_keys(passphrase, passlen, user_enckey, user_hmackey);
+ tmpctx = pg_create_keywrap_ctx(user_enckey, user_hmackey);
+
+ for (int i = 0; i < nkeys; i++)
+ {
+
+ if (!kmgr_unwrap_key(tmpctx, &(keys_in[i]), &(keys_out[i])))
+ {
+ /* The passphrase is not correct */
+ pg_free_keywrap_ctx(tmpctx);
+ return false;
+ }
+ }
+
+ /* The passphrase is correct, free the cipher context */
+ pg_free_keywrap_ctx(tmpctx);
+
+ return true;
+ }
+
+ /* Generate encryption key and mac key from given passphrase */
+ void
+ kmgr_derive_keys(char *passphrase, Size passlen,
+ uint8 enckey[KMGR_ENC_KEY_LEN],
+ uint8 mackey[KMGR_MAC_KEY_LEN])
+ {
+ pg_sha256_ctx ctx1;
+ pg_sha512_ctx ctx2;
+
+ StaticAssertStmt(KMGR_ENC_KEY_LEN == PG_AES256_KEY_LEN,
+ "derived encryption key size does not match AES256 key size");
+ StaticAssertStmt(KMGR_MAC_KEY_LEN == PG_HMAC_SHA512_KEY_LEN,
+ "derived mac key size does not match HMAC-SHA512 key size");
+
+ /* Generate encryption key from passphrase */
+ pg_sha256_init(&ctx1);
+ pg_sha256_update(&ctx1, (const uint8 *) passphrase, passlen);
+ pg_sha256_final(&ctx1, enckey);
+
+ /* Generate mac key from passphrase */
+ pg_sha512_init(&ctx2);
+ pg_sha512_update(&ctx2, (const uint8 *) passphrase, passlen);
+ pg_sha512_final(&ctx2, mackey);
+ }
+
+ /*
+ * Run cluster passphrase command.
+ *
+ * prompt will be substituted for %p.
+ *
+ * The result will be put in buffer buf, which is of size size.
+ * The return value is the length of the actual result.
+ */
+ int
+ kmgr_run_cluster_passphrase_command(char *passphrase_command, char *buf,
+ int size)
+ {
+ char command[MAXPGPATH];
+ char *p;
+ char *dp;
+ char *endp;
+ FILE *fh;
+ int pclose_rc;
+ size_t len = 0;
+
+ Assert(size > 0);
+ buf[0] = '\0';
+
+ dp = command;
+ endp = command + MAXPGPATH - 1;
+ *endp = '\0';
+
+ for (p = passphrase_command; *p; p++)
+ {
+ if (p[0] == '%')
+ {
+ switch (p[1])
+ {
+ case 'p':
+ strlcpy(dp, KMGR_PROMPT_MSG, strlen(KMGR_PROMPT_MSG)+1);
+ dp += strlen(KMGR_PROMPT_MSG);
+ p++;
+ break;
+ case '%':
+ p++;
+ if (dp < endp)
+ *dp++ = *p;
+ break;
+ default:
+ if (dp < endp)
+ *dp++ = *p;
+ break;
+ }
+ }
+ else
+ {
+ if (dp < endp)
+ *dp++ = *p;
+ }
+ }
+ *dp = '\0';
+
+ #ifdef FRONTEND
+ fh = open_pipe_stream(command);
+ if (fh == NULL)
+ {
+ pg_log_fatal("could not execute command \"%s\": %m",
+ command);
+ exit(EXIT_FAILURE);
+ }
+ #else
+ fh = OpenPipeStream(command, "r");
+ if (fh == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not execute command \"%s\": %m",
+ command)));
+ #endif
+
+ if ((len = fread(buf, sizeof(char), size, fh)) < size)
+ {
+ if (ferror(fh))
+ {
+ #ifdef FRONTEND
+ pg_log_fatal("could not read from command \"%s\": %m",
+ command);
+ exit(EXIT_FAILURE);
+ #else
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read from command \"%s\": %m",
+ command)));
+ #endif
+ }
+ }
+
+ #ifdef FRONTEND
+ pclose_rc = close_pipe_stream(fh);
+ #else
+ pclose_rc = ClosePipeStream(fh);
+ #endif
+
+ if (pclose_rc == -1)
+ {
+ #ifdef FRONTEND
+ pg_log_fatal("could not close pipe to external command: %m");
+ exit(EXIT_FAILURE);
+ #else
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close pipe to external command: %m")));
+ #endif
+ }
+ else if (pclose_rc != 0)
+ {
+ #ifdef FRONTEND
+ pg_log_fatal("command \"%s\" failed", command);
+ exit(EXIT_FAILURE);
+ #else
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("command \"%s\" failed",
+ command),
+ errdetail_internal("%s", wait_result_to_str(pclose_rc))));
+ #endif
+ }
+
+ return len;
+ }
+
+ #ifdef FRONTEND
+ static FILE *
+ open_pipe_stream(const char *command)
+ {
+ FILE *res;
+
+ #ifdef WIN32
+ size_t cmdlen = strlen(command);
+ char *buf;
+ int save_errno;
+
+ buf = malloc(cmdlen + 2 + 1);
+ if (buf == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ buf[0] = '"';
+ mempcy(&buf[1], command, cmdlen);
+ buf[cmdlen + 1] = '"';
+ buf[cmdlen + 2] = '\0';
+
+ res = _popen(buf, "r");
+
+ save_errno = errno;
+ free(buf);
+ errno = save_errno;
+ #else
+ res = popen(command, "r");
+ #endif /* WIN32 */
+ return res;
+ }
+
+ static int
+ close_pipe_stream(FILE *file)
+ {
+ #ifdef WIN32
+ return _pclose(file);
+ #else
+ return pclose(file);
+ #endif /* WIN32 */
+ }
+ #endif /* FRONTEND */
+
+ CryptoKey *
+ kmgr_get_cryptokeys(const char *path, int *nkeys)
+ {
+ struct dirent *de;
+ DIR *dir;
+ CryptoKey *keys;
+
+ #ifndef FRONTEND
+ if ((dir = AllocateDir(path)) == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ path)));
+ #else
+ if ((dir = opendir(path)) == NULL)
+ pg_log_fatal("could not open directory \"%s\": %m", path);
+ #endif
+
+ keys = (CryptoKey *) palloc0(sizeof(CryptoKey) * KMGR_MAX_INTERNAL_KEYS);
+ *nkeys = 0;
+
+ #ifndef FRONTEND
+ while ((de = ReadDir(dir, KMGR_DIR)) != NULL)
+ #else
+ while ((de = readdir(dir)) != NULL)
+ #endif
+ {
+ if (strlen(de->d_name) == 4 &&
+ strspn(de->d_name, "0123456789ABCDEF") == 4)
+ {
+ uint32 id;
+
+ id = strtoul(de->d_name, NULL, 16);
+
+ if (id < 0 || id >= KMGR_MAX_INTERNAL_KEYS)
+ {
+ #ifndef FRONTEND
+ elog(ERROR, "invalid cryptographic key identifier %u", id);
+ #else
+ pg_log_fatal("invalid cryptographic key identifier %u", id);
+ #endif
+ }
+
+ if (*nkeys >= KMGR_MAX_INTERNAL_KEYS)
+ {
+ #ifndef FRONTEND
+ elog(ERROR, "too many cryptographic kes");
+ #else
+ pg_log_fatal("too many cryptographic keys");
+ #endif
+ }
+
+ read_one_keyfile(path, id, &(keys[id]));
+ (*nkeys)++;
+ }
+ }
+
+ #ifndef FRONTEND
+ FreeDir(dir);
+ #else
+ closedir(dir);
+ #endif
+
+ return keys;
+ }
+
+ static void
+ read_one_keyfile(const char *cryptoKeyDir, uint32 id, CryptoKey *key_p)
+ {
+ char path[MAXPGPATH];
+ int fd;
+ int r;
+
+ CryptoKeyFilePath(path, cryptoKeyDir, id);
+
+ #ifndef FRONTEND
+ if ((fd = OpenTransientFile(path, O_RDONLY | PG_BINARY)) == -1)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open file \"%s\" for reading: %m",
+ path)));
+ #else
+ if ((fd = open(path, O_RDONLY | PG_BINARY, 0)) == -1)
+ pg_log_fatal("could not open file \"%s\" for reading: %m",
+ path);
+ #endif
+
+ #ifndef FRONTEND
+ pgstat_report_wait_start(WAIT_EVENT_KEY_FILE_READ);
+ #endif
+
+ /* Get key bytes */
+ r = read(fd, key_p, sizeof(CryptoKey));
+ if (r != sizeof(CryptoKey))
+ {
+ if (r < 0)
+ {
+ #ifndef FRONTEND
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", path)));
+ #else
+ pg_log_fatal("could not read file \"%s\": %m", path);
+ #endif
+ }
+ else
+ {
+ #ifndef FRONTEND
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg("could not read file \"%s\": read %d of %zu",
+ path, r, sizeof(CryptoKey))));
+ #else
+ pg_log_fatal("could not read file \"%s\": read %d of %zu",
+ path, r, sizeof(CryptoKey));
+ #endif
+ }
+ }
+
+ #ifndef FRONTEND
+ pgstat_report_wait_end();
+ #endif
+
+ #ifndef FRONTEND
+ if (CloseTransientFile(fd) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not close file \"%s\": %m",
+ path)));
+ #else
+ if (close(fd) != 0)
+ pg_log_fatal("could not close file \"%s\": %m", path);
+ #endif
+ }
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
new file mode 100644
index 221af87..33f28c7
*** a/src/include/access/xlog.h
--- b/src/include/access/xlog.h
*************** extern void UpdateControlFile(void);
*** 319,324 ****
--- 319,325 ----
extern uint64 GetSystemIdentifier(void);
extern char *GetMockAuthenticationNonce(void);
extern bool DataChecksumsEnabled(void);
+ extern bool KeyManagementEnabled(void);
extern XLogRecPtr GetFakeLSNForUnloggedRel(void);
extern Size XLOGShmemSize(void);
extern void XLOGShmemInit(void);
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
new file mode 100644
index 06bed90..c7df6d6
*** a/src/include/catalog/pg_control.h
--- b/src/include/catalog/pg_control.h
*************** typedef struct ControlFileData
*** 226,231 ****
--- 226,234 ----
*/
char mock_authentication_nonce[MOCK_AUTH_NONCE_LEN];
+ /* Key management cipher. Zero if no version */
+ uint32 key_management_version;
+
/* CRC of all above ... MUST BE LAST! */
pg_crc32c crc;
} ControlFileData;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
new file mode 100644
index 22340ba..b1523e1
*** a/src/include/catalog/pg_proc.dat
--- b/src/include/catalog/pg_proc.dat
***************
*** 10992,10995 ****
--- 10992,11001 ----
proname => 'is_normalized', prorettype => 'bool', proargtypes => 'text text',
prosrc => 'unicode_is_normalized' },
+ # function for key managements
+ { oid => '8200', descr => 'rotate cluter passphrase',
+ proname => 'pg_rotate_cluster_passphrase',
+ provolatile => 'v', prorettype => 'bool',
+ proargtypes => '', prosrc => 'pg_rotate_cluster_passphrase' },
+
]
diff --git a/src/include/common/cipher.h b/src/include/common/cipher.h
new file mode 100644
index ...f782791
*** a/src/include/common/cipher.h
--- b/src/include/common/cipher.h
***************
*** 0 ****
--- 1,78 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cipher.h
+ * Declarations for cryptographic functions
+ *
+ * Portions Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * src/include/common/cipher.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef CIPHER_H
+ #define CIPHER_H
+
+ #ifdef USE_OPENSSL
+ #include
+ #include
+ #include
+ #endif
+
+ /*
+ * Supported symmetric encryption algorithm. These identifiers are passed
+ * to pg_cipher_ctx_create() function, and then actual encryption
+ * implementations need to initialize their context of the given encryption
+ * algorithm.
+ */
+ #define PG_CIPHER_AES_CBC 0
+ #define PG_MAX_CIPHER_ID 1
+
+ /* AES128/192/256 various length definitions */
+ #define PG_AES128_KEY_LEN (128 / 8)
+ #define PG_AES192_KEY_LEN (192 / 8)
+ #define PG_AES256_KEY_LEN (256 / 8)
+
+ /*
+ * The encrypted data is a series of blocks of size. Initialization
+ * vector(IV) is the same size of cipher block.
+ */
+ #define PG_AES_BLOCK_SIZE 16
+ #define PG_AES_IV_SIZE (PG_AES_BLOCK_SIZE)
+
+ /* HMAC key and HMAC length. We use HMAC-SHA256 */
+ #define PG_HMAC_SHA512_KEY_LEN 64
+ #define PG_HMAC_SHA512_LEN 64
+
+ #ifdef USE_OPENSSL
+ typedef EVP_CIPHER_CTX cipher_private_ctx;
+ #else
+ typedef void cipher_private_ctx;
+ #endif
+
+ /*
+ * This struct has two implementation-private context for
+ * encryption and decryption. The caller must create the encryption
+ * context using by pg_cipher_ctx_create() and pass the context to
+ * pg_cipher_encrypt() or pg_cipher_decrypt().
+ */
+ typedef struct PgCipherCtx
+ {
+ cipher_private_ctx *encctx;
+ cipher_private_ctx *decctx;
+ } PgCipherCtx;
+
+ extern PgCipherCtx *pg_cipher_ctx_create(int cipher, uint8 *key, int klen);
+ extern void pg_cipher_ctx_free(PgCipherCtx *ctx);
+ extern bool pg_cipher_encrypt(PgCipherCtx *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+ extern bool pg_cipher_decrypt(PgCipherCtx *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+ extern bool pg_HMAC_SHA512(const uint8 *key,
+ const uint8 *in, int inlen,
+ uint8 *out);
+
+ #endif /* CIPHER_H */
diff --git a/src/include/common/cipher_openssl.h b/src/include/common/cipher_openssl.h
new file mode 100644
index ...0fd1308
*** a/src/include/common/cipher_openssl.h
--- b/src/include/common/cipher_openssl.h
***************
*** 0 ****
--- 1,37 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cipher_openssl.h
+ * Declarations for helper functions using OpenSSL
+ *
+ * Portions Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * src/include/common/cipher_openssl.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef CIPHER_OPENSSL_H
+ #define CIPHER_OPENSSL_H
+
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+
+ #include "common/cipher.h"
+
+ extern EVP_CIPHER_CTX *ossl_cipher_ctx_create(int cipher, uint8 *key, int klen,
+ bool enc);
+ extern void ossl_cipher_ctx_free(EVP_CIPHER_CTX *ctx);
+ extern bool ossl_cipher_encrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+ extern bool ossl_cipher_decrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+ extern bool ossl_HMAC_SHA512(const uint8 *key,
+ const uint8 *in, int inlen,
+ uint8 *out);
+ #endif
diff --git a/src/include/common/kmgr_utils.h b/src/include/common/kmgr_utils.h
new file mode 100644
index ...1dc8f43
*** a/src/include/common/kmgr_utils.h
--- b/src/include/common/kmgr_utils.h
***************
*** 0 ****
--- 1,105 ----
+ /*-------------------------------------------------------------------------
+ *
+ * kmgr_utils.h
+ * Declarations for utility function for key management
+ *
+ * Portions Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * src/include/common/kmgr_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef KMGR_UTILS_H
+ #define KMGR_UTILS_H
+
+ #include "common/cipher.h"
+
+ /* Current version number */
+ #define KMGR_VERSION 1
+
+ /*
+ * Directory where cryptographic keys reside within PGDATA. KMGR_DIR_TMP
+ * is used during cluster passphrase rotation.
+ */
+ #define KMGR_DIR "pg_cryptokeys"
+ #define KMGR_TMP_DIR "pg_cryptokeys_tmp"
+
+ /*
+ * Identifiers of internal keys. When adding a new internal key, we
+ * also need to add its key length to internalKeyLengths.
+ */
+ /* #define KMGR_XXX_KEY_ID 0 */
+ #define KMGR_MAX_INTERNAL_KEYS 0
+
+ /* Encryption key and MAC key used for key wrapping */
+ #define KMGR_ENC_KEY_LEN PG_AES256_KEY_LEN
+ #define KMGR_MAC_KEY_LEN PG_HMAC_SHA512_KEY_LEN
+ #define KMGR_HMAC_LEN PG_HMAC_SHA512_LEN
+
+ /* Key wrapping key consists of encryption key and mac key */
+ #define KMGR_KEY_LEN (PG_AEAD_ENC_KEY_LEN + PG_AEAD_MAC_KEY_LEN)
+
+ /* Allowed length of cluster passphrase */
+ #define KMGR_MIN_PASSPHRASE_LEN 64
+ #define KMGR_MAX_PASSPHRASE_LEN 1024
+
+ /* Maximum length of key the key manager can store */
+ #define KMGR_MAX_KEY_LEN 128
+ #define KMGR_MAX_WRAPPED_KEY_LEN KmgrSizeOfCipherText(KMGR_MAX_KEY_LEN)
+
+ /*
+ * Size of encrypted key size with padding. We use PKCS#7 padding,
+ * described in RFC 5652.
+ */
+ #define SizeOfDataWithPadding(klen) \
+ ((int)(klen) + (PG_AES_BLOCK_SIZE - ((int)(klen) % PG_AES_BLOCK_SIZE)))
+
+ /* Macros to compute the size of cipher text and plain text */
+ #define KmgrSizeOfCipherText(len) \
+ (KMGR_MAC_KEY_LEN + PG_AES_IV_SIZE + SizeOfDataWithPadding((int)(len)))
+ #define KmgrSizeOfPlainText(klen) \
+ ((int)(klen) - (KMGR_MAC_KEY_LEN + PG_AES_IV_SIZE))
+
+ /* CryptoKey file name is keys id */
+ #define CryptoKeyFilePath(path, dir, id) \
+ snprintf((path), MAXPGPATH, "%s/%04X", (dir), (id))
+
+ /*
+ * Cryptographic key data structure. This structure is used for
+ * both on-disk (raw key) and on-memory (wrapped key).
+ */
+ typedef struct CryptoKey
+ {
+ int klen;
+ uint8 key[KMGR_MAX_WRAPPED_KEY_LEN];
+ } CryptoKey;
+
+ /* Key wrapping cipher context */
+ typedef struct PgKeyWrapCtx
+ {
+ uint8 key[KMGR_ENC_KEY_LEN];
+ uint8 mackey[KMGR_MAC_KEY_LEN];
+ PgCipherCtx *cipherctx;
+ } PgKeyWrapCtx;
+
+ extern PgKeyWrapCtx *pg_create_keywrap_ctx(uint8 key[KMGR_ENC_KEY_LEN],
+ uint8 mackey[KMGR_MAC_KEY_LEN]);
+ extern void pg_free_keywrap_ctx(PgKeyWrapCtx *ctx);
+ extern bool kmgr_wrap_key(PgKeyWrapCtx *ctx, CryptoKey *in, CryptoKey *out);
+ extern bool kmgr_unwrap_key(PgKeyWrapCtx *ctx, CryptoKey *in, CryptoKey *out);
+
+
+
+ extern void kmgr_derive_keys(char *passphrase, Size passlen,
+ uint8 enckey[KMGR_ENC_KEY_LEN],
+ uint8 mackey[KMGR_MAC_KEY_LEN]);
+ extern bool kmgr_verify_passphrase(char *passphrase, int passlen,
+ CryptoKey *keys_in, CryptoKey *keys_out,
+ int nkey);
+ extern bool kmgr_wrap_key(PgKeyWrapCtx *ctx, CryptoKey *in, CryptoKey *out);
+ extern bool kmgr_unwrap_key(PgKeyWrapCtx *ctx, CryptoKey *in, CryptoKey *out);
+ extern int kmgr_run_cluster_passphrase_command(char *passphrase_command,
+ char *buf, int size);
+ extern CryptoKey *kmgr_get_cryptokeys(const char *path, int *nkeys);
+
+ #endif /* KMGR_UTILS_H */
diff --git a/src/include/crypto/kmgr.h b/src/include/crypto/kmgr.h
new file mode 100644
index ...783f06d
*** a/src/include/crypto/kmgr.h
--- b/src/include/crypto/kmgr.h
***************
*** 0 ****
--- 1,29 ----
+ /*-------------------------------------------------------------------------
+ *
+ * kmgr.h
+ *
+ * Portions Copyright (c) 2020, tỷ lệ kèo bóng đá
+ *
+ * src/include/crypto/kmgr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef KMGR_H
+ #define KMGR_H
+
+ #include "common/cipher.h"
+ #include "common/kmgr_utils.h"
+ #include "storage/relfilenode.h"
+ #include "storage/bufpage.h"
+
+ /* GUC parameters */
+ extern bool key_management_enabled;
+ extern char *cluster_passphrase_command;
+
+ extern Size KmgrShmemSize(void);
+ extern void KmgrShmemInit(void);
+ extern void BootStrapKmgr(void);
+ extern void InitializeKmgr(void);
+ extern const CryptoKey *KmgrGetKey(int id);
+
+ #endif /* KMGR_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
new file mode 100644
index fb270df..d50a7c9
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 385,390 ****
--- 385,393 ----
/* Define to 1 if you have the `OPENSSL_init_ssl' function. */
#undef HAVE_OPENSSL_INIT_SSL
+ /* Define to 1 if you have the `OPENSSL_init_crypto' function. */
+ #undef HAVE_OPENSSL_INIT_CRYPTO
+
/* Define to 1 if you have the header file. */
#undef HAVE_OSSP_UUID_H
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
new file mode 100644
index a821ff4..6549260
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
*************** typedef enum
*** 998,1003 ****
--- 998,1006 ----
WAIT_EVENT_DATA_FILE_TRUNCATE,
WAIT_EVENT_DATA_FILE_WRITE,
WAIT_EVENT_DSM_FILL_ZERO_WRITE,
+ WAIT_EVENT_KEY_FILE_READ,
+ WAIT_EVENT_KEY_FILE_WRITE,
+ WAIT_EVENT_KEY_FILE_SYNC,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_SYNC,
WAIT_EVENT_LOCK_FILE_ADDTODATADIR_WRITE,
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
new file mode 100644
index 04431d0..a0e582a
*** a/src/include/utils/guc_tables.h
--- b/src/include/utils/guc_tables.h
*************** enum config_group
*** 89,94 ****
--- 89,95 ----
STATS,
STATS_MONITORING,
STATS_COLLECTOR,
+ ENCRYPTION,
AUTOVACUUM,
CLIENT_CONN,
CLIENT_CONN_STATEMENT,
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644
index efb206a..5276c41
*** a/src/test/Makefile
--- b/src/test/Makefile
*************** endif
*** 29,35 ****
endif
ifeq ($(with_openssl),yes)
ifneq (,$(filter ssl,$(PG_TEST_EXTRA)))
! SUBDIRS += ssl
endif
endif
--- 29,35 ----
endif
ifeq ($(with_openssl),yes)
ifneq (,$(filter ssl,$(PG_TEST_EXTRA)))
! SUBDIRS += ssl crypto
endif
endif