Data Encryption Policy
Data Encryption Policy
Project / Organization:
{{ORG_NAME}}ALAI Holding AS — Drop Payment App Policy Number: POL-SEC-{{NUMBER}}ENC-001 Version:{{VERSION}}1.0 Date:{{DATE}}2026-02-23 Author:{{AUTHOR}}Security Architect Status: Draft| In Review | ApprovedReviewers:{{REVIEWERS}}CISO, CTO, DPO Next Review:{{REVIEW_DATE}}2027-02-23 Classification: Confidential
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | Initial draft — Drop encryption standards |
1. Purpose & Scope
Purpose: This policy defines the minimum encryption standards for data at rest, in transit, and at the application level across all systems operated by {{ORG_NAME}}.ALAI Holding AS for the Drop payment app.
Regulatory basis:
- GDPR Art. 32 — appropriate technical measures
- Personopplysningsloven (LOV-2018-06-15-38) § 28
- IKT-forskriften (FOR-2003-05-21-630) §§ 5-6
- DORA (EU) 2022/2554 Art. 9(4)(d)
- Hvitvaskingsloven (LOV-2018-06-01-23) § 30 — AES-256 for KYC data at rest
Scope: This policy applies to:
- All systems, applications, and infrastructure operated by
{{ORG_NAME}}ALAI Holding AS for Drop - All employees, contractors, and third parties with access to
{{ORG_NAME}}Drop systems - All data classified as Internal, Confidential, or Restricted (see compliance-framework.md §
5)7) - All cloud
environments,environmentson-premises(AWSsystems,App Runner, S3) and developer workstations
Exceptions: Public-facing static content (HTML, CSS, JS files,JS, public images)exchange rate data) does not require encryption at rest beyond transport layer security.
2. Encryption Standards & Approved Algorithms
2.1 Approved Algorithms
Symmetric Encryption
| Use Case | Algorithm | Key Size | Mode | Notes |
|---|---|---|---|---|
| Data at rest (general) | AES | 256-bit | GCM | Authenticated encryption — | preferred;
| AES | 256-bit | GCM | ||
| KYC document encryption | AES | 256-bit | GCM | Stored |
| Database backups | AES | 256-bit | GCM | Separate backup KMS key in separate AWS region |
| Log encryption | AES | 256-bit | GCM | Rotation every 90 days |
Asymmetric Encryption
| Use Case | Algorithm | Key Size | Notes |
|---|---|---|---|
| ECDHE | P-256 |
||
| RSA | |||
| Code signing | Ed25519 | 256-bit | |
| RSA-OAEP | 4096-bit |
Hashing & Password Storage
| Use Case | Algorithm | Parameters | Notes |
|---|---|---|---|
| Password hashing | |||
| bcrypt | cost factor |
bcryptjs |
|
| SHA-256 | — | ||
| HMAC ( |
HMAC-SHA-256 | — | For |
Source: src/drop-app/src/lib/utils-server.ts:8-16 (bcrypt); src/drop-app/src/lib/auth.ts (JWT)
2.2 Prohibited Algorithms (NEVER USE)
| Algorithm | Reason | |
|---|---|---|
| MD5 | Collision attacks (2004+) | |
| SHA-1 | Collision attacks (2017+) | |
| DES / 3DES | Key size insufficient | |
| RC4 | Statistical biases | |
| ECB mode (any cipher) | Leaks data patterns | |
| RSA < 2048-bit | Insufficient key strength | |
Critical fix (C4): SHA-256 legacy password support was completely removed in the 2026-02-13 hardening. verifyPassword() now only accepts bcrypt hashes. Source: src/drop-app/src/lib/utils-server.ts:14-19.
3. Encryption at Rest
3.1 Database Encryption
| Database | Method | Key Management | Classification Coverage |
|---|---|---|---|
| All classifications | |||
| PostgreSQL — Phase 2 ( |
AES-256 TDE via AWS RDS | AWS KMS — drop-db-master-key |
All classifications |
| PostgreSQL — fødselsnummer field | AES-256-GCM |
AWS KMS — drop-national-id-key (SEPARATE) |
Restricted (L4) |
| PostgreSQL — KYC documents | AES-256-GCM application layer | drop-kyc-key |
|
Implementation pattern (field-level):— fødselsnummer field encryption:
// Envelope encryption for PIIfødselsnummer
fields// Key: drop-national-id-key (AWS KMS — separate from db master)
// Stored: Only AES-256-GCM ciphertext — never plaintext in DB
// Access: Compliance function only, via role-based KMS key policy
async function encryptField(plaintext:encryptNationalId(fodselsnummer: string): Promise<string> {
// 1. Generate random DEK
const dek = crypto.randomBytes(32);
// 2. Encrypt DEK with KEK from KMS
const encryptedDek = await kmsClient.encrypt({ KeyId: KEK_ARN, Plaintext: dek });
// 3. Encrypt data with DEK
const iv = crypto.randomBytes(12); // 96-bit random IV
const dek = await kmsClient.generateDataKey({ KeyId: NATIONAL_ID_KEY_ARN, KeySpec: 'AES_256' });
const cipher = crypto.createCipheriv('aes-256-gcm', dek,dek.Plaintext, iv);
const ciphertext = Buffer.concat([cipher.update(plaintext)fodselsnummer, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
// 4. Return: base64(encryptedDekencryptedDEK || iv || tag || ciphertext)
return Buffer.concat([encryptedDek,dek.CiphertextBlob, iv, tag, ciphertext]).toString('base64');
}
Card data note (critical fix C1): Cards feature is gated behind feature flags (all default false). No full PAN or CVV is ever stored. Only last_four and token_ref are stored. Card issuance will use a PCI-compliant partner (Marqeta/Lithic). Source: src/drop-app/src/lib/feature-flags.ts.
3.2 File / Object Storage Encryption
| Storage | Method | Key Management | Notes | |
|---|---|---|---|---|
| AWS S3 |
SSE-KMS (AES-256) | drop-kyc-key |
Server-side encryption, per-object | |
| SSE-KMS (AES- |
AWS KMS — drop-backup-key (separate region) |
Separate | ||
| Developer |
OS-level full disk encryption | Platform key (FileVault |
Required for all | development |
3.3 Backup Encryption
All database backups MUST be encrypted before transfer off primary system.
pg_dumpKey: |drop-backup-key gzip | gpg --encrypt --recipient {{BACKUP_KEY_ID}} > backup.sql.gz.gpg
Backup encryption key: Stored in separate(AWS KMS region— eu-west-1 region, separate from primary Backupeu-north-1)
key rotation:Rotation: Annual
Retention: 30 days rolling
Backup keyverification: escrow:Monthly Storedrestore withtest {{ESCROW_LOCATION}}— see beredskapsplan.md
4. Encryption in Transit
4.1 TLS Configuration Standards
MinimumExternal-facing TLSservices version:(Cloudflare + AWS App Runner):
External-facing services:Minimum TLS1.2version:(TLS 1.3preferred)(enforced at Cloudflare edge)Internal service-to-service:TLS 1.32: Not supported (enforced)Cloudflare configuration)LegacyHTTPsystem→support:HTTPS:DocumentedAutomaticexceptionredirectrequired(HTTP 301)
Approved cipher suites (TLS 1.3):
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256 (minimum — prefer 256)
ApprovedHSTS cipher suitesconfiguration (TLSsource: 1.2 — for backwards compatibility only)next.config.ts):
ECDHE-ECDSA-AES256-GCM-SHA384Strict-Transport-Security: ECDHE-RSA-AES256-GCM-SHA384max-age=63072000; ECDHE-ECDSA-CHACHA20-POLY1305includeSubDomains; ECDHE-RSA-CHACHA20-POLY1305preload
Prohibited TLS configurations:
- TLS 1.0 and TLS 1.1 — prohibited (Cloudflare blocks)
- SSL 2.0 / SSL 3.0 — prohibited
- RC4 cipher suites — prohibited
Export-grade cipher suites — prohibited- NULL
cipher suites — prohibited Anonymous DHcipher suites — prohibited
4.2 Certificate Management
| Certificate Type | Validity | Authority | Rotation | Monitoring |
|---|---|---|---|---|
| External TLS ( |
90 days | Let's Encrypt |
Automated |
Alert |
| Manual — BankID renewal process | Calendar reminder | |||
Certificate monitoring: {{CERT_MONITORING_TOOL}} — alert channel: #security-alerts
4.3 mTLS for Internal ServiceAPI Communication
All service-to-serviceDrop API traffic:
Client → Cloudflare: TLS 1.3 (edge termination)
Cloudflare → AWS App Runner: TLS 1.3 (re-encrypted)
Open Banking API calls within(AISP/PISP the— clusterPhase MUST2):
useDrop mTLS.API Implementation:→ {{ISTIOBank API: TLS 1.3 + OAuth 2.0 / LINKERDeIDAS /certificate
cert-managerhttpOnly /cookie custom}}security:
Certificatesecure: generation: SPIFFE/SVID standard
Rotation: Every {{ROTATION_PERIOD}} — automated via sidecar
Verification: Server verifies client cert against trusted CA
Service identity: SPIFFE URItrue (e.g.,HTTPS-only spiffe://{{TRUST_DOMAIN}}/ns/{{NS}}/sa/{{SERVICE_ACCOUNT}})transport)
sameSite: 'strict' (CSRF protection)
Source: src/drop-app/src/lib/auth.ts:48-54
5. Application-Level Encryption
5.1 Field-Level Encryption
When required: All fields classified as Restricted (L4) must be encrypted at the application layer, in addition to database-level encryption.
Fields requiring field-level encryption:
| Field | Table | Classification | Key |
|---|---|---|---|
|
users |
Restricted (L4) | (AWS KMS) |
|
/ kyc_records |
(AWS KMS) |
|
|
|
Database TDE |
|
|
|
Database TDE (SHA-256 hash — not raw token) |
5.2 EnvelopeJWT EncryptionToken Security
Algorithm: HS256 (HMAC-SHA-256)
Library: jose ^6.1.3
Configuration:
Master// KeySource: (KEK)src/drop-app/src/lib/auth.ts
const token = await new SignJWT({ userId, role })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('24h')
.sign(new TextEncoder().encode(process.env.JWT_SECRET));
Production requirement: JWT_SECRET env var is required — storedthrows infatal KMS,error neverif loaded into application memory
↓missing (KMSdev encryptfallback operation)uses Dataprocess.cwd() Encryptionhash).
Phase 2 upgrade path: Migrate to RS256 (DEK)RSA) —or generated per record/session, encrypted by KEK
↓EdDSA (applicationEd25519) encryptfor operationbetter —key AES-256-GCM)rotation Datasupport —and encryptedmulti-service at rest, decrypted only on read
DEK lifetime:
- Per-session DEKs: lifetime of session
- Per-record DEKs: same as record (stored encrypted alongside data)
- Bulk DEKs: rotate every {{ROTATION_PERIOD}}
verification.
5.3 Tokenization
WhatPayment data: Drop uses a pass-through PSD2 model — no card numbers stored. Cards feature gated behind feature flags (all default false). When cards feature is tokenized:activated Paymentin cardfuture: data, bank account numbers
Implementation: {{PAYMENT_PROVIDER}} handlesPCI-compliant tokenization via partner (Marqeta/Lithic) — weDrop store tokensstores only Format-preservinglast_four encryption:+ {{YES/NO}} — {{TOOL}} for {{USE_CASE}}token_ref.
6. Key Management
Full policy: key-management-policy.md
Summary
| Key Type | KMS | Rotation | Owner |
|---|---|---|---|
drop-national-id-key) |
Annual | Security team | |
drop-db-master-key) |
|||
drop-kyc-key) |
|||
| AWS KMS — separate region | Annual | Security team | |
| JWT signing |
JWT_SECRET) |
Quarterly | Security team |
| TLS certificates (external) | Cloudflare / Let's Encrypt | 90 days (automated) | Platform |
7. Cryptographic Inventory
| System | Algorithm | Key Size | Mode | Key Location | ||
|---|---|---|---|---|---|---|
| SQLite (current) | OS disk encryption | Platform | XTS | Platform | MVP only | |
| PostgreSQL TDE (Phase 2) | AES-256 | 256-bit | XTS | drop-db-master-key |
||
| AES-256-GCM | 256-bit | GCM | drop-national-id-key |
|||
| External TLS | ECDSA P-256 | 256-bit | ||||
| HMAC-SHA-256 | 256-bit | — | ||||
| cost=12 | — | N/A | bcryptjs library |
|||
| Session token lookup | SHA-256 |
256-bit | — | Hash stored in sessions table | ||
| S3 backup encryption | AES-256 SSE-KMS | AWS KMS drop-backup-key |
Separate region |
8. Algorithm Deprecation Schedule
| Algorithm | Current Usage | Deprecation Date | Migration Target | Status | |
|---|---|---|---|---|---|
| bcrypt |
Argon2id (m=65536,t=3,p=4) | ||||
| SQLite TDE → PostgreSQL TDE | Phase 2 | PostgreSQL + AWS KMS | Planned |
9. Exception Process
Exceptions are not permitted for: Restricted (L4) data (fødselsnummer, KYC documents) — no exceptions.
Exception request process:
- Submit exception request to:
{{SECURITY_TEAM_EMAIL}}[email protected] - Required information:
System andsystem, dataclassificationclassification,affectedAlgorithm/standardalgorithm beingexceptedexcepted,frombusiness Businessjustification,justificationrisk Riskassessment,assessmentcompensating Compensatingcontrols,controlsProposedproposed exception duration (max 12 months)
- Approval required from: CISO
- Exceptions logged in:
{{EXCEPTION_REGISTER_LOCATION}}compliance register (internal) - Review: Quarterly — exceptions
that cannot be remediated withinexceeding 12 months require board-level approval
Active exceptions:
| System | Exception | Expiry | Compensating Controls |
|---|---|---|---|
| SQLite (MVP) | Filesystem encryption only, no column-level TDE | Phase 2 migration | Full disk encryption; private network; no public DB access |
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | Security Architect | 2026-02-23 | |
| CISO | |||
| CTO | |||
| DPO (GDPR relevance) | |||
| Management |