The end LSN of the current buffer to decript was exclusive while the end
LSN of the key was inclusive which had led to confusion and an
off-by-one bug.
Also add a simple test case for the WAL encryption using the logical
decoding's test plugin.
We already had protection against decrypting relation keys with the wrong
principal key but to properly protect us against new relation keys being
encrypted with the wrong principal key we need to also verify that the
principal key was correct when we fetch the principal key from the key
provider. We do so by signing the principal key info header of the key map
file using AES-128-GCM.
This way we cannot get a jumbled mess of relation keys encrypted with
multiple different principal keys.
While this does not merge the two similar functions it removes a lot
of cruft from pg_tde_read_one_map_entry2() making the functions more
similar and the code easier to read.
Same as last commit but for the WAL encryption.
Rewrote the calculation in a way gcc's vectorizer likes, as verified
with Godbolt. The code generated by clang is ok and branch free but it
fails to properly vectorize both before and after.
We might as well increase the entropy by adding a random base value to
the IVs used used when encrypting relations. But since for now the pair
of key + IV is generated and used together it adds little extra security
over what we already have.
We add the IV with XOR since that is a cheap and easy operation which
uases no extra collissions.
If we look up something about a relation in the command start event
trigger we need to hold onto that lock until the end of the command
and not release it so no other commd is able to change the data we
looked at between now and when the DDL command itself actually locks
the relation.
The oids worked as-is, in general any reserved catalog oid which is
frefers to an object of another type will work as a special magical
oid. Here we decide to use tablespce oids for special database
values and a database oid, 1, for the relation value. But in the
latter case anything would have worked since we are guarnteed to
have no collissions due to the database oid not being and actual
database.
The old solution which use a mix worked too but this is more
consistent.
The previous 8192 * 100 initial allocation was arbitrary and bigger than
necessary. We pick something bassically as arbitrary but now a multiple of
the actual page size of DSAs, 4 KB.
This comment is pretty strange since we actually do verify, badly, that
there are no encrypted tables.
Additionally we merge two small functions to make the code easier to
understand.
There is no reason to waste memory when WAL encryption is not enabled
sd long as we do not support changing the WAL encryption status while
running. And if we add support for this we can revist this code.
All callers call pg_tde_free_key_map_entry() with MAP_ENTRY_VALID so
we can move it down to the place where it is used instead of passing
it as an argument.
Make sure that we lock the key file before adding a WAL key. It should
be harmless since this happens very early in the start but if we ever
add the ability to rotate WAL keys while running this will become an
issue.
Additionally we add asserts to make sure we hold the right lock level
when we open the key map file.
Instead of using ugly hacks like extern and defining kmip_ereoprt() in
a separate file we just move all code which requires the KMIP headers to
a separate file which does not use any PostgreSQL features.
Since we only ever returned false in one odd internal error, i.e.
when something is wrong with the file system, we may as well remove
the returned boolean flag and jsut always raise an error when the
rotation fails.
To protect us against people decrypting relation keys using the wrong
principal key we change to encrypting intenral keys using AES-128-GCM
which verifies that the data was encrypted with the same key as we
decrypt it with. Additionally we make sure to add some of the plain
test data when claculating the AEAD tag.
To imrpove security plus prepare for implementing AES-GCM we save a
unique initialization vector per entry in the key map. This required
refactoring the API a bit but that is a nice thing for GCM too.
Since partitioned tables, and indexes, lack storage the "is encrypted"
property is not relevant to them because encryption is done at the SMGR
level. Therefore we should either throw an error or return NULL, and
here we choose to return NULL to make the function easier to work with.
Since only WAL encryption use the cached context it should not be part
of every internal key. Instead store it in the WAL decryption key cache
and the backend global state for WAL encryption.
The userId field of the principal key info has never been used since
it was introduced in commit 210c95cf00cacc3304321950279201406708d09d
so we can safely remove it.