PII Encryption
Relevant source files
The following files were used as context for generating this wiki page:
The PII Encryption system in the Subito project provides a standardized mechanism for protecting Personally Identifiable Information (PII) at rest. It specifically targets sensitive partner and customer data, such as bank details and identification numbers, ensuring that even if the database is compromised, the sensitive values remain unreadable without the proper encryption key.
This system utilizes industry-standard AES-256-GCM encryption. In production environments, the system strictly enforces the presence of a secure encryption key to prevent accidental storage of sensitive data in plaintext.
Sources: apps/api/src/lib/pii-crypto.ts:40-45, packages/db/drizzle/0000_normal_the_order.sql:98-105
Cryptographic Architecture
The encryption logic is centralized in the pii-crypto library within the API application. It employs the aes-256-gcm algorithm for authenticated encryption, providing both confidentiality and data integrity.
Key Derivation and Management
The system derives a 32-byte (256-bit) encryption key from the PII_ENCRYPTION_KEY environment variable. The derivation process follows two paths based on the format of the provided key:
- Direct Hex: If the key is a 64-character hex string, it is converted directly to a 32-byte Buffer.
- Passphrase Derivation: If the key is any other string (minimum 8 characters), it uses
scryptSyncwith a hardcoded salt (subito-pii-salt) to stretch the passphrase into a 32-byte key. This is primarily intended for development environments.
Sources: apps/api/src/lib/pii-crypto.ts:13-25
Encrypted Data Format
The resulting encrypted string follows a specific serialized format to allow the decryption function to identify and process the data correctly:
pii1:[base64url(IV + AuthTag + Ciphertext)]
| Component | Description | Length (Bytes) |
|---|---|---|
| Prefix | pii1: identifier to distinguish encrypted from plaintext | 5 chars |
| IV | Initialization Vector (randomly generated for every encryption) | 12 |
| AuthTag | GCM Authentication Tag for integrity verification | 16 |
| Ciphertext | The actual encrypted PII data | Variable |
Sources: apps/api/src/lib/pii-crypto.ts:8-10, apps/api/src/lib/pii-crypto.ts:53-54
Data Flow
The following diagram illustrates the lifecycle of PII data from plaintext input to encrypted storage and back.
The flow ensures backward compatibility by returning plaintext if the prefix is missing or if the environment is not configured for encryption.
Sources: apps/api/src/lib/pii-crypto.ts:32-55, apps/api/src/lib/pii-crypto.ts:57-86
Database Implementation
PII encryption is applied to specific columns within the database schema, primarily those associated with partners and payment methods. Although the database schema defines these as varchar or text, the application-level logic ensures values are encrypted before insertion.
Target PII Columns
Based on the database schema and seed data, the following entities contain fields designated for PII protection:
| Table | Column | Description |
|---|---|---|
partners | aadhar_number | Government identification number |
partners | pan_number | Tax identification number |
partners | bank_account_number | Partner's payout account |
saved_payment_methods | token | Gateway-specific payment tokens |
saved_payment_methods | upi_id | User UPI addresses |
Sources: packages/db/src/schema/payments.ts:133-145, packages/db/drizzle/0000_normal_the_order.sql:98-105
Sequence Diagram: Storing Partner PII
This diagram shows how the API interacts with the encryption library before persisting data to the PostgreSQL database via Drizzle ORM.
Configuration and Security
The system behavior changes significantly based on the environment variables.
| Variable | Requirement | Behavior |
|---|---|---|
PII_ENCRYPTION_KEY | Required in Prod | If missing in production, encryptPii throws a hard error to prevent data leaks. |
NODE_ENV | Optional | Determines if encryption is mandatory. |
Implementation Snippet: Key Derivation
function getKey32(): Buffer | null {
const raw = process.env.PII_ENCRYPTION_KEY
if (!raw || raw.length < 8) {
return null
}
if (/^[0-9a-fA-F]{64}$/.test(raw)) {
return Buffer.from(raw, "hex")
}
return scryptSync(raw, "subito-pii-salt", 32)
}
Sources: apps/api/src/lib/pii-crypto.ts:13-25
Summary
The PII Encryption system serves as a critical security layer for the Subito platform. By utilizing AES-256-GCM with random IVs and integrity tags, it ensures that sensitive user and partner data remains protected. The implementation facilitates a smooth transition between development (where encryption can be optional) and production (where it is strictly enforced).