Skip to content

security: authenticate key_base_iv in the WAL key file (M2, WAL keys)#19

Merged
jdatcmd merged 1 commit into
mainfrom
security/wal-key-base-iv-auth
Jul 16, 2026
Merged

security: authenticate key_base_iv in the WAL key file (M2, WAL keys)#19
jdatcmd merged 1 commit into
mainfrom
security/wal-key-base-iv-auth

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

M2 (WAL keys): key_base_iv unauthenticated in the WAL key file

Companion to #15, which authenticated key_base_iv in the relation key map file. This does the same for the WAL key file.

Problem

key_base_iv is the base IV for WAL data (AES-CTR). In the version-2 WAL key file format it was stored after the AEAD tag, outside the additional authenticated data, so the AAD (offsetof(WalKeyFileEntry, range_start)) covered only cipher and range_type. An actor with write access to the WAL key file could alter key_base_iv undetected and shift the WAL IV, which for a stream cipher is a confidentiality break.

Fix

  • Move key_base_iv ahead of range_start in WalKeyFileEntry so the AAD now authenticates it. The AAD expression is unchanged (offsetof(range_start)); it simply covers more now. range_start stays outside the AAD because it is zero at key creation and updated in place by the first WAL write.
  • Bump the WAL key file version 2 -> 3 (0x024B4557 -> 0x034B4557).

Migration (read compatibility preserved)

open_pg_tde_update_wal_keys_file already migrates an older file at startup. A new v2 dual-read reader (WalKeyFileEntryV2 + range_from_disk_entry_v2) decrypts a version-2 entry with the old AAD and re-wraps it as version 3, alongside the existing v1 reader. Migration re-wraps the same key bytes, so existing WAL stays decryptable.

Tests

  • t/wal_key_base_iv_migration.pl — a version-2 data directory fixture (WAL encryption enabled) migrates to version 3 on startup; the WAL key still decrypts, wal_keys is version 3 afterward, data written under v2 is readable, and new WAL can be written. PG18-only (fixture built on 18), mirroring security: authenticate key_base_iv in the key map file (M2, relation keys) #15.
  • t/wal_key_auth.pl — on a v3 cluster, flipping one byte of key_base_iv in the last WAL key entry makes the WAL key fail to decrypt, so the server refuses to start with Failed to decrypt key.

Validation

  • PG18: full meson test suite green (56/56, including both new tests); the migration test exercises the v2 reader end to end.
  • PG16: tamper test + WAL create/rotate tests pass (migration test skips, PG18 fixture).
  • Discrimination check: excluding key_base_iv from the AAD makes the tamper test's detection assertion fail (the server then only fails on unrelated WAL corruption, not authentication), confirming the test targets the fix.
  • Extension-only change (no core patch); pgindent / perltidy clean.

🤖 Generated with Claude Code

key_base_iv is the base IV for WAL data (AES-CTR). In the version-2 WAL
key file format it was stored after the AEAD tag, outside the additional
authenticated data, so the AAD covered only cipher and range_type. An
actor with write access to the WAL key file could alter key_base_iv
undetected and shift the WAL IV, which for a stream cipher is a
confidentiality break.

Move key_base_iv ahead of range_start in WalKeyFileEntry so the AAD
(offsetof(range_start)) now authenticates it, and bump the WAL key file
version 2 -> 3. range_start stays outside the AAD because it is zero at
key creation and updated in place by the first WAL write.

The version bump keeps read compatibility: open_pg_tde_update_wal_keys_file
migrates an older file at startup. A new v2 dual-read reader
(WalKeyFileEntryV2 + range_from_disk_entry_v2) decrypts a version-2 entry
with the old AAD and re-wraps it as version 3, alongside the existing v1
reader.

This mirrors the key map file fix (#15) for the WAL key file.

- t/wal_key_base_iv_migration.pl: a version-2 data directory fixture
  (WAL encryption enabled) migrates to version 3 on startup and the WAL
  key still decrypts; data written under v2 stays readable.
- t/wal_key_auth.pl: tampering key_base_iv in a v3 entry now fails the
  WAL key decryption, so the server refuses to start.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jdatcmd jdatcmd merged commit 165b578 into main Jul 16, 2026
25 of 37 checks passed
@jdatcmd jdatcmd deleted the security/wal-key-base-iv-auth branch July 16, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants