Recipe

Recipe: PII storage + encryption design

How Meridian encrypts, stores, and rotates personally identifiable information so that a database breach yields ciphertext only.

Threat model

  • Attacker obtains full database dump (rows, columns, indexes).
  • Attacker obtains application-server memory for <60 seconds.
  • Insider with DB read access attempts lateral movement.

Key hierarchy

KEK (AES-256-GCM) — stored in HSM / KMS, never leaves secure enclave.

DEK (AES-256-GCM) — per-row data encryption key, wrapped by KEK, rotated every 30 days.

Row key — HKDF-SHA512(DEK, row UUID), unique per row, never stored.

Encryption flow

  1. Generate random 256-bit DEK per customer tenant on creation.
  2. Wrap DEK with KEK via AES-KWP (NIST SP 800-38F). Store wrapped DEK in encryption_keys table.
  3. For each PII column, derive row key = HKDF-SHA512(DEK, row_uuid). Encrypt plaintext with AES-256-GCM, 96-bit random IV. Store ciphertext || IV || tag.
  4. Index searchable fields with blind HMAC-SHA256(DEK, normalized plaintext). Store HMAC in separate indexed column.

Rotation procedure

Every 30 days a cron job creates a new DEK per tenant, re-wraps all row keys, and re-encrypts active PII rows in a background transaction. Old DEK is retained for 7 days to decrypt audit logs, then destroyed.

Access control

Application servers never hold the KEK. Decryption requests flow through an internal sidecar that authenticates via mTLS and enforces rate limits. All decryption events are logged to an append-only audit store.

Compliance mapping

This design satisfies GDPR Art. 32 (encryption at rest), SOC 2 CC6.1 (logical access), and PCI DSS 3.4 (render PAN unreadable).