PII-at-Rest Confidentiality
Status: draft · Owner: Chakshu · Companion to:2026-05-26-pii-chinese-walls-design.mdDecides: how PII is stored so that no single admin can extract it.
This is the confidentiality face of the pii-vault. The companion doc is the access face (tokenisation V, surrogates σ, blind index β, detok gated by OPA Φ) and defends T1/T2 + T3-reading-op-tables. This doc is what sits underneath: how the vault's plaintext system-of-record is stored against privileged insiders.
1. Property & adversaries
Target: no single party can (i) exfiltrate keys, (ii) bulk-extract plaintext, (iii) unilaterally change decryption policy.
A_db DB admin psql, reads all tables
A_app service operator deploys, reads service env + logs
A_infra host root memory, disk, process introspection on dev-2
A_kms one OpenBao custodian holds 1 of n Shamir sharescls(f) sensitivity lattice is inherited from the companion doc; this doc protects cls(f) ≥ pii.
2. Crypto structure
per record r, sensitive field set:
DEK_r random 256-bit data key
ct_r = AES-256-GCM(DEK_r, plaintext, nonce_r) nonce random per write
wDEK_r = Transit.wrap(KEK, DEK_r) KEK never leaves OpenBao
stored: { ct_r, wDEK_r, nonce_r, key_version } DEK_r itself never persisted
decrypt: app → Transit.unwrap(wDEK_r) → DEK_r → AES-GCM-open(ct_r)
KEK stays in OpenBao; DEK_r + plaintext transit app memory (the accepted ceiling, §4)OpenBao Transit (OSS - not the Enterprise Transform engine) provides wrap/unwrap with the KEK held inside Bao. OpenBao root/unseal is Shamir k-of-n.
3. Invariants
- C1 (envelope). For
cls(f) ≥ pii: plaintext persists only asAES-256-GCM(DEK_r);DEK_rpersists only asTransit.wrap(KEK, ·); KEK never leaves OpenBao. - C2 (no single admin). unseal, KEK rotation, and Transit policy change each require Shamir k-of-n ⇒ no lone custodian/admin can bring up, rotate, or re-scope decryption.
- C3 (path separation). Automated path = per-record
unwrap, rate-limited (≤ R) + field-scoped- audited. Bulk / export / break-glass = quorum-gated + high-priority audit. No automated bulk-decrypt path exists.
- C4 (separation of duties).
{Shamir custodians} ∩ {app operators} = ∅;{audit readers (compliance)} ∩ {operators} = ∅. One role can neither decrypt-and-hide nor approve-its-own break-glass. - C5 (accepted ceiling). Live-node skimming at rate
≤ Ris detected, not prevented (prevention needs a TEE - declined). Control = rate cap + audit-anomaly alert (§9b of companion doc).
4. Adversary → outcome
| Adversary | Bulk-extract? | Why |
|---|---|---|
A_db (psql) | no | tables hold ct + wDEK; no KEK, no DEK |
| A_app (one operator) | no | automated path rate-limited + audited; bulk path quorum-gated (C3) |
| A_kms (1 share) | no | k-of-n; one share insufficient (C2) |
| A_infra (root, now) | residual | unsealed KEK sits in OpenBao process memory ⇒ root could dump it. Shamir still blocks cold unseal. Closed in Phase B. |
| A_infra (root, Phase B) | no | KEK non-extractable in hardware HSM/KMS |
| any single admin | no (skim only) | can at most skim ≤ R/record on a live node (C5) |
5. Phasing
| Phase | Scope | Closes |
|---|---|---|
| A (now) | envelope via Transit; per-record DEK; verify Shamir k-of-n unseal; C3 path separation (rate-limit + audit automated, quorum break-glass); C4 custody | A_db, A_app, A_kms |
| B | move KEK behind hardware HSM / KMS (Transit backing key in HSM, or migrate) | A_infra residual |
| C | per-subject key hierarchy ⇒ crypto-shredding: erasure = destroy the subject's DEKs, not a row scrub (GDPR Art. 17 as a key-destruction op) | erasure cost + assurance |
6. Determinations (this doc)
- K1 - Envelope encryption with per-record DEKs; KEK in OpenBao Transit (KEK never leaves Bao). Not the Enterprise Transform engine.
- K2 - Shamir k-of-n on unseal + key-admin + policy change. Custodians diverse and disjoint from operators (C4).
- K3 - Two decryption paths only: automated per-record (rate-limited, scoped, audited) and quorum-gated bulk/break-glass. No automated bulk path.
- K4 - Accept C5 ceiling (live-node skim) until/unless a TEE is adopted; mitigate by rate cap + audit anomaly.
- K5 - HSM-backing (Phase B) is the committed path to close the A_infra residual; not deferred indefinitely.
7. Open questions
- DEK granularity: per-record vs per-subject vs per-(subject,field). Per-subject unlocks crypto-shredding (Phase C) but complicates shared-record cases (a claim references member + provider).
- Shamir
(n, k): how many custodians, threshold, and who holds shares - does separation of duties (C4) require ≥1 non-engineering custodian (DPO / counsel / board)? Governance, not code. - Automated-path rate cap
R: legitimate ceiling per service, and the anomaly threshold that pages compliance. - Crypto-agility: key_version is stored, but is rewrap-on-rotate lazy (on next read) or a batch job?
8. Not decided here
- TEE / confidential computing (would close C5) - explicitly out by choice.
- The access face (tokenisation, OPA gating) - lives in the companion doc.
- Snowflake/BI pseudonymisation - separate, deferred.
