Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions documentation/docs/concepts/threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ running server:
confidentiality, not authentication. `open_pg_tde` does not currently detect
deliberate modification of ciphertext on disk. Authenticated page encryption
is a separate, larger initiative; see the design note on authenticated pages.
- **Unlogged table forks.** An unlogged table keeps an init fork that
PostgreSQL copies over the main fork to reset the table after a crash. That
reset is a raw file copy of encrypted bytes performed by the server outside
the storage hooks, so the init fork and the main fork must share the same XTS
tweak at each block position for the copy to decrypt afterward. As a result,
an attacker with the data files can tell which main-fork blocks are
byte-identical to the init-fork template. This is limited to unlogged tables,
reveals only block equality and not the contents of either fork, and does not
affect regular (logged) tables.
- **Column-level protection from the server.** All encryption is at the storage
layer, so the server necessarily decrypts data to operate on it.
`open_pg_tde` does not provide client-side or column-level encryption that
Expand Down Expand Up @@ -84,4 +93,5 @@ running server:
| Privileged user on a running server | No |
| Catalog metadata and statistics | No |
| Tamper detection (integrity) | No |
| Block equality between an unlogged table's init and main forks | No |
| Encryption key exposure via the catalog | Not applicable (keys are not in the catalog) |
13 changes: 12 additions & 1 deletion src/encryption/enc_tde.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,18 @@ CalcBlockIv(ForkNumber forknum, BlockNumber bn, const unsigned char *base_iv, un
{
memset(iv, 0, TDE_BLOCK_IV_LEN);

/* The init fork is copied to the main fork so we must use the same IV */
/*
* The init fork is copied to the main fork so we must use the same IV.
*
* PostgreSQL resets an unlogged relation after a crash by raw-copying the
* init fork file over the main fork (copy_file() in reinit.c), which the
* storage hooks never see. For that copy of already-encrypted bytes to
* decrypt as the main fork, the init fork must be encrypted with the main
* fork's tweak. This is intentional. The tradeoff is that the two forks
* share the XTS tweak at each block, so their ciphertext reveals which
* blocks are byte-identical; that limitation is documented in the threat
* model (unlogged table forks).
*/
iv[7] = forknum == INIT_FORKNUM ? MAIN_FORKNUM : forknum;

iv[12] = bn >> 24;
Expand Down
Loading