2.4 KiB
| id | title | status | assignee | created_date | updated_date | labels | dependencies | references | priority | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| TASK-124 | Encrypt all PII at rest in EncryptID database | Done | 2026-03-24 00:29 | 2026-03-24 00:29 |
|
|
high |
Description
Server-side AES-256-GCM encryption for all PII fields stored in PostgreSQL. Keys derived from JWT_SECRET via HKDF with dedicated salts (pii-v1 for encryption, pii-hash-v1 for HMAC). HMAC-SHA256 hash indexes for equality lookups on email and UP address fields.
Scope: 18 fields across 6 tables (users, guardians, identity_invites, space_invites, notifications, fund_claims). Username and display_name excluded (public identifiers, needed for ILIKE search).
Files:
src/encryptid/server-crypto.ts— NEW: encryptField(), decryptField(), hashForLookup()src/encryptid/schema.sql— 18 _enc/_hash columns + 4 indexessrc/encryptid/db.ts— async row mappers with decrypt fallback, dual-write on inserts/updates, hash-based lookupssrc/encryptid/server.ts— replaced unkeyed hashEmail() with HMAC hashForLookup()src/encryptid/migrations/encrypt-pii.ts— NEW: idempotent backfill script
Remaining: Drop plaintext columns after extended verification period.
Acceptance Criteria
- #1 All PII fields have corresponding _enc columns with AES-256-GCM ciphertext
- #2 HMAC-SHA256 hash indexes enable email and UP address lookups without plaintext
- #3 Row mappers decrypt transparently — callers receive plaintext
- #4 Wrong encryption key cannot decrypt (verified with test)
- #5 Same plaintext produces different ciphertext each time (random IV)
- #6 Backfill migration encrypts all existing rows (0 remaining unencrypted)
- #7 Legacy plaintext fallback works for pre-migration rows during transition
Final Summary
Deployed 2026-03-23. Commit 9695e95. Backfill completed: 1 user, 2 guardians, 8 identity invites, 2 fund claims encrypted. 19/19 verification tests passed (ciphertext format, decryption, HMAC determinism, wrong-key rejection, random IV uniqueness). Plaintext columns retained for rollback safety — drop in follow-up task after extended verification.