DC-324(Feat): AES-256-GCM at-rest encryption for PII fields#273
Open
noxmwalsh wants to merge 15 commits into
Open
DC-324(Feat): AES-256-GCM at-rest encryption for PII fields#273noxmwalsh wants to merge 15 commits into
noxmwalsh wants to merge 15 commits into
Conversation
necampanini
reviewed
May 5, 2026
Collaborator
|
the ADR and algorithm pattern looks right to me. The only suggestion I have is for planned expiration of PII. I.e. does it need to hang around forever in the db, or can it be deleted after some known period of time? While that may be out of scope for this particular PR, I can say that the only thing better than encrypting PII at rest is not storing PII at all. |
necampanini
reviewed
May 5, 2026
noxmwalsh
commented
May 8, 2026
necampanini
approved these changes
May 8, 2026
jamesmblair
reviewed
May 8, 2026
jamesmblair
reviewed
May 8, 2026
jamesmblair
reviewed
May 8, 2026
jamesmblair
requested changes
May 8, 2026
Member
jamesmblair
left a comment
There was a problem hiding this comment.
Overall looks really good. Add a few minor comments.
jhernandez-cfa
approved these changes
May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔗 Jira ticket
DC-324
✍️ Description
Adds AES-256-GCM encryption at rest for reversible PII stored in the portal database, plus deterministic email lookup via
EmailHashso we do not rely on searchable plaintext email.Cryptography & configuration
PiiEncryptionsettings (ActiveKeyId, key ring with Base64 256-bit material) inappsettings.json; mirrored inappsettings.Development.example.jsonfor local setup.IPiiSymmetricEncryption/PiiAesGcmSymmetricEncryption: AES-GCM envelopes with per-write nonce, embedded key id, sentinel prefix (sep-pii:v1:), tamper detection via GCM tag.IEmailLookupHasher/EmailLookupHasher: HMAC-based normalized-email fingerprint for indexed equality lookups (aligns with existing identifier hashing patterns).PiiEncryptionSettingsValidator: Validates configured keys and coherence withPiiEncryptionGuard(production rejects placeholder dev keys, analogous to identifier hasher guard).Data model & migration
EncryptPiiAtRestColumnsAndEmailHash: encrypts relevant columns, addsEmailHash, adjusts constraints/indexes;DateOfBirthmoved to string storage suitable for ciphertext (migration uses dynamic SQL where needed).UserEncryptedFieldMappercentralizes encrypt/decrypt mapping for user entities.Repositories & flows
DatabaseUserRepository: reads/writes encrypted fields; lookup byEmailHashwith transitional handling for legacy plaintext rows; guards against duplicate emails when legacy plaintext still exists.DatabaseDocVerificationChallengeRepository: encrypts ID-proofing challenge payloads at rest.Startup
PiiPlaintextEncryptionBackfill: post-migration backfill of legacy plaintext → ciphertext and missingEmailHash(separate from operational key re-seal rotation — see ADR).Program.csafter migrations.Seeding
DataSeederupdated so bulk user adds respect existing emails via hash-aware checks and avoid silent duplicates next to legacy rows.Documentation
docs/adr/0014-pii-encryption-at-rest.md: algorithm, key ring, rotation vs backfill, email lookup, logging constraints (no plaintext PII in logs).Tests
PiiAesGcmSymmetricEncryptionTests,PiiPlaintextEncryptionBackfillTestsPiiEncryptionSettingsValidatorTests,PiiEncryptionGuardTestsDatabaseUserRepositoryTests, doc challenge repo tests,DataSeederTests,DatabaseSeederTests,PortalDbContextTestsTestPortalCryptography🔗 Links to related PRs
✅ Completion tasks
Deploy / ops notes
PiiEncryptionkey id(s) and key material per environment; do not use dev placeholders in production (PiiEncryptionGuard).PiiEncryptionbefore or with release.