Hello


Thanks a lot for the patch, I think in terms of functionality, the patch 
provides very straightforward functionalities regarding key management. In 
terms of documentation, I think the patch is still lacking some pieces of 
information that kind of prevent people from fully understanding how KMS works 
and how it can be used and why, (at least that is the impression I got from the 
zoom meeting recordings :p). I spent some time today revisiting the 
key-management documentation in the patch and rephrase and restructure it  
based on my current understanding of latest KMS design. I mentioned all 3 
application level keys that we have agreed and emphasize on explaining the SQL 
level encryption key because that is the key that can be used right now. Block 
and WAL levels keys we can add here more information once they are actually 
used in the TDE development. 



Please see below the KMS documentation that I have revised and I hope it will 
be more clear and easier for people to understand KMS. Feel free to make 
adjustments. Please note that we use the term "wrap" and "unwrap" a lot in our 
past discussions. Originally we used the terms within a context involving Key 
encryption keys (KEK). For example, "KMS wraps a master key with KEK". Later, 
we used the same term in a context involving encrypting user secret /password. 
For example, "KMS wraps a user secret with SQL key". In my opinion, both make 
sense but it may be confusing to people having the same term used differently. 
So in my revision below, the terms "wrap" and "unwrap" refer to encrypting or 
decrypting user secret / password as they are used in "pg_wrap() and 
pg_unwrap()". I use the terms "encapsulate" and "restore" when KEK is used to 
encrypt or decrypt a key.







Chapter 32: Encryption Key Management 

----------------------------------------------


PostgreSQL supports internal Encryption Key Management System, which is 
designed to manage the life cycles of cryptographic keys within the PostgreSQL 
system. This includes dealing with their generation, storage, usage and 
rotation.



Encryption Key Management is enabled when PostgreSQL is build with 
--with-openssl and cluster passphrase command is specified during initdb. The 
cluster passphrase provided by --cluster-passphrase-command option during 
initdb and the one generated by cluster_passphrase_command in the 
postgresql.conf must match, otherwise, the database cluster will not start up.



32.1 Key Generations and Derivations

------------------------------------------



When cluster_passphrase_command option is specified to the initdb, the process 
will derive the cluster passphrase into a Key Encryption Key (KEK) and a HMAC 
Key using key derivation protocol before the actual generation of application 
level cryptographic level keys.



-Key Encryption Key (KEK)

KEK is primarily used to encapsulate or restore a given application level 
cryptographic key



-HMAC Key

HMAC key is used to compute the HASH of a given application level cryptographic 
key for integrity check purposes



These 2 keys are not stored physically within the PostgreSQL cluster as they 
are designed to be derived from the correctly configured cluster passphrase.



Encryption Key Management System currently manages 3 application level 
cryptographic keys that have different purposes and usages within the 
PostgreSQL system and these are generated using pg_strong_random() after KEK 
and HMAC key derivation during initdb process.



The 3 keys are:



-SQL Level Key

SQL Level Key is used to wrap and unwrap a user secret / passphrase via 
pg_wrap() and pg_unwrap() SQL functions. These 2 functions are designed to be 
used in conjunction with the cryptographic functions provided by pgcrypto 
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 [Wrap and Unwrap User Secret section] for 
usage examples.



-Block Level Key

Block Level Key is primarily used to encrypt / decrypt buffers as part of the 
Transparent Data Encryption (TDE) feature



-WAL Level Key

WAL Level Key is primarily used to encrypt / decrypt WAL files as part of the 
Transparent Data Encryption (TDE) feature



The 3 application level keys above will be encapsulated and hashed using KEK 
and HMAC key mentioned above before they are physically stored to pg_cryptokeys 
directory within the cluster.



32.1. Key Initialization

-------------------------



When a PostgreSQL cluster with encryption key management enabled is started, 
the cluster_passphrase_command parameter in postgresql.conf will be evaluated 
and the cluster passphrase will be derived into KEK and HMAC Key in similar 
ways as initdb.



After that, the 3 encapsulated application level cryptographic keys will be 
retrieved from pg_cryptokeys directory to be restored and integrity-checked by 
the key management system using the derived KEK and HMAC key. 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.



32.2. Wrap and Unwrap User Secret

----------------------------------------

Encryption key management system provides pg_wrap() and pg_unwrap SQL functions 
(listed in Table 9.97) to perform wrap and unwrap operations on user secret 
with the SQL level encryption key. The SQL level encryption key is one of the 3 
application level keys generated during initdb process when cluster_passphrase 
is supplied.



When pg_wrap() and pg_unwrap() functions are invoked, SQL level encryption key 
will internally be used to perform the encryption and decryption operation with 
HMAC-based integrity check. From user's point of view, he or she is not aware 
of the actual SQL level encryption key used internally by both wrap functions 



One possible use case is to combine pg_wrap() and pg_unwrap() with pgcrypto. 
User wraps the user encryption secret with pg_wrap function and passes the 
wrapped encryption secret to pg_unwrap function for the pgcrypto encryption 
functions. The wrapped secret can be stored in the application server or 
somewhere secured and should be obtained promptly for cryptographic operation 
with pgcrypto.



Here is an example that shows how to encrypt and decrypt data together with 
wrap and unwrap functions:

=# SELECT pg_wrap('my secret passward');

                                                                              
pg_wrap

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

\xb2c89f76f04f95d029f179e0fc3df4ed7254127b5562a9e27d42d1cd037c942dea65ce7c0750c520fa4f4e90481c9eb7e1e42a068248c262c1a6f25c6eab64303b1154ccc9a14361223641aab4a7aabe

(1 row)

Once wrapping the user key, user can encrypt and decrypt user data using the 
wrapped user key together with the key unwrap functions:

=# INSERT INTO tbl

        VALUES (pgp_sym_encrypt('secret data',

                                 
pg_unwrap('\xb2c89f76f04f95d029f179e0fc3df4ed7254127b5562a9e27d42d1cd037c942dea65ce7c0750c520fa4f4e90481c9eb7e1e42a068248c262c1a6f25c6eab64303b1154ccc9a14361223641aab4a7aabe')));

INSERT 1

=# SELECT * FROM tbl;

                                                                             col

--------------------------------------------------------------------------------------------------------------------------------------------------------------

\xc30d04070302a199ee38bea0320b75d23c01577bb3ffb315d67eecbeca3e40e869cea65efbf0b470f805549af905f94d94c447fbfb8113f585fc86b30c0bd784b10c9857322dc00d556aa8de14

(1 row)

=# SELECT pgp_sym_decrypt(col,

                           
pg_unwrap('\xb2c89f76f04f95d029f179e0fc3df4ed7254127b5562a9e27d42d1cd037c942dea65ce7c0750c520fa4f4e90481c9eb7e1e42a068248c262c1a6f25c6eab64303b1154ccc9a14361223641aab4a7aabe'))
 as col

    FROM tbl;

     col

--------------

secret data

(1 row)

The data 'secret data' is practically encrypted by the user secret 'my secret 
passward' but using wrap and unwrap functions user don't need to know the 
actual user secret during operation.





32.3. Key Rotation Process

------------------------------



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 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 lifetimse are based on key length, key 
strength, algorithm and total number of bytes enciphered. The key management 
systems provides a efficient method to perform key rotation.



Please be aware that the phrase "key rotation" here only refers to the rotation 
of KEK and HMAC keys. The 3 application level encryption keys (SQL, Block and 
WAL levels) 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 and HMAC keys who are responsible for encapsulating and 
restoring the actual application level encryption keys.



Since both KEK and HMAC keys are derived from a cluster passphrase, the "key 
rotation" ultimately refers to the rotation of cluster passphrase and deriving 
a new KEK and HMAC keys from the new cluster passphrase. The new set of KEK and 
HMAC keys can then be used to encapsulate all 3 application level encryptions 
keys and store the new results in pg_cryptokeys directory.



To rotate the cluster passphrase, user firstly needs to update 
cluster_passphrase_command in the postgresql.conf and then execute 
pg_rotate_cluster_passphrase() SQL function to initiate the rotation.




Cary Huang

-------------

HighGo Software Inc. (Canada)

mailto:cary.hu...@highgo.ca

http://www.highgo.ca





---- On Mon, 30 Mar 2020 21:30:19 -0700 Masahiko Sawada 
<mailto:masahiko.saw...@2ndquadrant.com> wrote ----


On Tue, 31 Mar 2020 at 09:36, Cary Huang <mailto:cary.hu...@highgo.ca> wrote: 
> 
> Hi 
> I had a look on kms_v9 patch and have some comments 
> 
> --> pg_upgrade.c 
> keys are copied correctly, but as pg_upgrade progresses further, it will try 
> to start the new_cluster from "issue_warnings_and_set_wal_level()" function, 
> which is called after key copy. The new cluster will fail to start due to the 
> mismatch between cluster_passphrase_command and the newly copied keys. This 
> causes pg_upgrade to always finish with failure. We could move 
> "copy_master_encryption_key()" to be called after 
> "issue_warnings_and_set_wal_level()" and this will make pg_upgrade to finish 
> with success, but user will still have to manually correct the 
> "cluster_passphrase_command" param on the new cluster in order for it to 
> start up correctly. Should pg_upgrade also take care of copying 
> "cluster_passphrase_command" param from old to new cluster after it has 
> copied the encryption keys so users don't have to do this step? If the 
> expectation is for users to manually correct "cluster_passphrase_command" 
> param after successful pg_upgrade and key copy, then there should be a 
> message to remind the users to do so. 
 
I think both the old cluster and the new cluster must be initialized 
with the same passphrase at initdb. Specifying the different 
passphrase command to the new cluster at initdb and changing it after 
pg_upgrade doesn't make sense. Also I don't think we need to copy 
cluster_passphrase_command same as other GUC parameters. 
 
I've changed the patch so that pg_upgrade copies the crypto keys only 
if both new and old cluster enable the key management. User must 
specify the same passphrase command to both old and new cluster, which 
is not cumbersome, I think. I also added the description about this to 
the doc. 
 
> 
> -->Kmgr.c 
> + /* 
> + * 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("both directories %s and %s exist, use the newly wrapped keys", 
> + KMGR_DIR, KMGR_TMP_DIR))); 
> 
> I think the error message should say "there is only temporary directory 
> exist" instead of "both directories exist" 
 
You're right. Fixed. 
 
I've attached the new version patch. 
 
Regards, 
 
-- 
Masahiko Sawada http://www.2ndQuadrant.com/ 
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Reply via email to