rspace-online/backlog/tasks/task-124 - Encrypt-all-PII-...

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
security
encryptid
database
src/encryptid/server-crypto.ts
src/encryptid/migrations/encrypt-pii.ts
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 indexes
  • src/encryptid/db.ts — async row mappers with decrypt fallback, dual-write on inserts/updates, hash-based lookups
  • src/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.