feat(backend): field-level encryption for PII data (Claim & VerificationSession)#212
Merged
Cedarich merged 2 commits intoPulsefy:mainfrom Mar 29, 2026
Merged
Conversation
…icationSession) Implements AES-256-GCM/CBC encryption for sensitive recipient metadata before persistence, per Privacy by Design principles (issue Pulsefy#209). - Add EncryptionService with encrypt/decrypt (AES-256-GCM, random IV) and encryptDeterministic/decryptDeterministic (AES-256-CBC, fixed IV) driven by ENCRYPTION_MASTER_KEY env variable - Add EncryptionModule exported for use across feature modules - Encrypt Claim.recipientRef on create; decrypt on findOne/findAll/disburse - Encrypt VerificationSession.identifier (deterministic, for rate-limit queries) and VerificationSession.code (non-deterministic) on write; decrypt on read before sending OTP and code comparison - Update ClaimsModule and VerificationModule to import EncryptionModule - Add identity-mock EncryptionService to existing test suites so all prior assertions continue to pass without modification - Add comprehensive unit tests for EncryptionService (encrypt/decrypt round-trips, determinism, tamper detection, edge cases) - Document ENCRYPTION_MASTER_KEY in .env.example with generation guide
|
@BigBen-7 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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.
Summary
Closes #209
Implements Privacy by Design field-level encryption for sensitive PII stored in PostgreSQL, using Node's built-in
cryptomodule (no external dependencies added).EncryptionService(src/common/encryption/) — two encryption strategies:encrypt/decrypt— AES-256-GCM with random IV (non-deterministic, authenticated). Used forClaim.recipientRefandVerificationSession.code.encryptDeterministic/decryptDeterministic— AES-256-CBC with HMAC-derived fixed IV. Used forVerificationSession.identifierso equality-based DB lookups (rate-limitingcountquery) continue to work.EncryptionModule— NestJS module that provides and exportsEncryptionService; imported intoClaimsModuleandVerificationModule.ClaimsService— encryptsrecipientRefoncreate(); decrypts onfindOne(),findAll(), and before passing to the on-chain adapter indisburse().VerificationFlowService— encryptsidentifier(deterministic) andcodeonstart()andresend(); decryptsidentifierbefore sending OTP, decryptscodebefore comparing incomplete().ENCRYPTION_MASTER_KEYenv var (documented in.env.example); warns at startup if unset.EncryptionServiceso all prior assertions remain green.Test plan
EncryptionServiceunit tests pass (encryption.service.spec.ts— 10/10)0 errors, warnings only— all pre-existing)ClaimsServiceandVerificationFlowServicetest mocks updated with identity-mock encryption so prior assertions are unaffectedlint → test → build(all steps pass oncepnpm installrunsprisma generate)