Data Encryption Policy
Data Encryption Policy
Project / Organization: ALAI Holding AS — Drop Payment App Policy Number: POL-SEC-ENC-001 Version: 1.0 Date: 2026-02-23 Author: Security Architect Status: Draft Reviewers: CISO, CTO, DPO Next Review: 2027-02-23 Classification: Confidential
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | 2026-02-23 | Security Architect | 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 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 ALAI Holding AS for Drop
- All employees, contractors, and third parties with access to Drop systems
- All data classified as Internal, Confidential, or Restricted (see compliance-framework.md §7)
- All cloud environments (AWS App Runner, S3) and developer workstations
Exceptions: Public-facing static content (HTML, CSS, JS, public 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; provides confidentiality + integrity |
| Fødselsnummer (national ID) field encryption | AES | 256-bit | GCM | Separate key from database master; application layer |
| KYC document encryption | AES | 256-bit | GCM | Stored in S3 with SSE-KMS |
| 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 |
|---|---|---|---|
| TLS key exchange | ECDHE | P-256 | Cloudflare + AWS App Runner |
| BankID JWT verification | RSA | 2048-bit (BankID certificate) | BankID Norway certificate authority |
| Code signing | Ed25519 | 256-bit | CI/CD artifact signing |
| AWS KMS key wrapping | RSA-OAEP | 4096-bit | AWS-managed |
Hashing & Password Storage
| Use Case | Algorithm | Parameters | Notes |
|---|---|---|---|
| Password hashing | bcrypt | cost factor 12 | bcryptjs ^3.0.3 — pure JS; SHA-256 legacy support REMOVED (fix C4) |
| JWT token hashing (session lookup) | SHA-256 | — | Session table stores SHA-256(JWT) for revocation check |
| HMAC (webhook signatures) | HMAC-SHA-256 | — | For API integrations |
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 | Status |
|---|---|---|
| MD5 | Collision attacks (2004+) | Prohibited |
| SHA-1 | Collision attacks (2017+) | Prohibited |
| DES / 3DES | Key size insufficient | Prohibited |
| RC4 | Statistical biases | Prohibited |
| ECB mode (any cipher) | Leaks data patterns | Prohibited |
| RSA < 2048-bit | Insufficient key strength | Prohibited |
| SHA-256 for password hashing | Not a password hash — no salt/stretching | REMOVED (security fix C4, 2026-02-13) |
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 |
|---|---|---|---|
| SQLite (current MVP) | OS-level full-disk encryption | Platform (AWS) | All classifications |
| PostgreSQL — Phase 2 (primary) | AES-256 TDE via AWS RDS | AWS KMS — drop-db-master-key |
All classifications |
| PostgreSQL — fødselsnummer field | AES-256-GCM application layer | AWS KMS — drop-national-id-key (SEPARATE) |
Restricted (L4) |
| PostgreSQL — KYC documents | AES-256-GCM application layer | AWS KMS — drop-kyc-key |
Restricted (L4) |
Implementation pattern — fødselsnummer field encryption:
// Envelope encryption for fødselsnummer
// 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 encryptNationalId(fodselsnummer: string): Promise<string> {
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.Plaintext, iv);
const ciphertext = Buffer.concat([cipher.update(fodselsnummer, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
// Return: base64(encryptedDEK || iv || tag || ciphertext)
return Buffer.concat([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 — KYC documents | SSE-KMS (AES-256) | AWS KMS — drop-kyc-key |
Server-side encryption, per-object |
| AWS S3 — backups | SSE-KMS (AES-256) | AWS KMS — drop-backup-key (separate region) |
Separate key from primary |
| Developer workstations | OS-level full disk encryption | Platform key (FileVault on macOS) | Required for all development machines |
3.3 Backup Encryption
All database backups encrypted before transfer off primary system.
Key: drop-backup-key (AWS KMS — eu-west-1 region, separate from primary eu-north-1)
Rotation: Annual
Retention: 30 days rolling
Backup verification: Monthly restore test — see beredskapsplan.md
4. Encryption in Transit
4.1 TLS Configuration Standards
External-facing services (Cloudflare + AWS App Runner):
- Minimum TLS version: TLS 1.3 (enforced at Cloudflare edge)
- TLS 1.2: Not supported (Cloudflare configuration)
- HTTP → HTTPS: Automatic redirect (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)
HSTS configuration (source: next.config.ts):
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Prohibited TLS configurations:
- TLS 1.0 and TLS 1.1 — prohibited (Cloudflare blocks)
- SSL 2.0 / SSL 3.0 — prohibited
- RC4 cipher suites — prohibited
- NULL cipher suites — prohibited
4.2 Certificate Management
| Certificate Type | Validity | Authority | Rotation | Monitoring |
|---|---|---|---|---|
| External TLS (getdrop.no) | 90 days | Let's Encrypt (via Cloudflare) | Automated | Alert 14 days before expiry |
| BankID integration certificate | Per BankID schedule | BankID Norge AS CA | Manual — BankID renewal process | Calendar reminder |
| AWS internal certificates | AWS-managed | AWS Certificate Manager | AWS-managed | AWS CloudWatch alerts |
4.3 API Communication
All Drop API traffic:
Client → Cloudflare: TLS 1.3 (edge termination)
Cloudflare → AWS App Runner: TLS 1.3 (re-encrypted)
Open Banking API calls (AISP/PISP — Phase 2):
Drop API → Bank API: TLS 1.3 + OAuth 2.0 / eIDAS certificate
httpOnly cookie security:
secure: true (HTTPS-only 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 |
|---|---|---|---|
national_id_encrypted |
users |
Restricted (L4) | drop-national-id-key (AWS KMS) |
kyc_document_ref |
users / kyc_records |
Restricted (L4) | drop-kyc-key (AWS KMS) |
bankid_sub |
users |
Confidential (L3) | Database TDE |
token_hash |
sessions |
Confidential (L3) | Database TDE (SHA-256 hash — not raw token) |
5.2 JWT Token Security
Algorithm: HS256 (HMAC-SHA-256)
Library: jose ^6.1.3
Configuration:
// Source: 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 — throws fatal error if missing (dev fallback uses process.cwd() hash).
Phase 2 upgrade path: Migrate to RS256 (RSA) or EdDSA (Ed25519) for better key rotation support and multi-service verification.
5.3 Tokenization
Payment 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 activated in future: PCI-compliant tokenization via partner (Marqeta/Lithic) — Drop stores only last_four + token_ref.
6. Key Management
Full policy: key-management-policy.md
Summary
| Key Type | KMS | Rotation | Owner |
|---|---|---|---|
| Fødselsnummer encryption key | AWS KMS (drop-national-id-key) |
Annual | Security team |
| Database master key | AWS KMS (drop-db-master-key) |
Annual | Security team |
| KYC document key | AWS KMS (drop-kyc-key) |
Annual | Security team |
| Backup encryption key | AWS KMS — separate region | Annual | Security team |
| JWT signing secret | AWS Secrets Manager (JWT_SECRET) |
Quarterly | Security team |
| BankID certificates | BankID Norge AS CA | Per BankID schedule | BankID Norge AS |
| TLS certificates (external) | Cloudflare / Let's Encrypt | 90 days (automated) | Platform |
7. Cryptographic Inventory
| System | Algorithm | Key Size | Mode | Key Location | Notes |
|---|---|---|---|---|---|
| SQLite (current) | OS disk encryption | Platform | XTS | Platform | MVP only |
| PostgreSQL TDE (Phase 2) | AES-256 | 256-bit | XTS | AWS KMS drop-db-master-key |
Phase 2 |
| Fødselsnummer field encryption | AES-256-GCM | 256-bit | GCM | AWS KMS drop-national-id-key |
Phase 2 — separate key |
| External TLS | ECDSA P-256 | 256-bit | — | Cloudflare / Let's Encrypt | 90-day cert |
| JWT signing | HMAC-SHA-256 | 256-bit | — | AWS Secrets Manager | Rotation: quarterly |
| Password hashing | bcrypt | cost=12 | — | N/A | bcryptjs library |
| Session token lookup | SHA-256 | 256-bit | — | N/A | Hash stored in sessions table |
| S3 backup encryption | AES-256 SSE-KMS | 256-bit | — | AWS KMS drop-backup-key |
Separate region |
8. Algorithm Deprecation Schedule
| Algorithm | Current Usage | Deprecation Date | Migration Target | Status |
|---|---|---|---|---|
| SHA-256 for passwords | REMOVED (fix C4, 2026-02-13) | Removed | bcrypt(12) | DONE |
| bcrypt → Argon2id | All user passwords | 2027-Q1 | Argon2id (m=65536,t=3,p=4) | Planned |
| HS256 JWT → RS256/EdDSA | JWT signing | Phase 2 | Ed25519 (EdDSA) | Planned |
| SQLite TDE → PostgreSQL TDE | Database | 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: [email protected]
- Required information: system, data classification, algorithm being excepted, business justification, risk assessment, compensating controls, proposed exception duration (max 12 months)
- Approval required from: CISO
- Exceptions logged in: compliance register (internal)
- Review: Quarterly — exceptions exceeding 12 months require board-level approval
Active exceptions:
| System | Exception | Expiry | Compensating Controls |
|---|---|---|---|
| JWT signing (HS256) | Shared-secret JWT instead of asymmetric keys | Phase 2 | JWT_SECRET in AWS Secrets Manager; short expiry (24h); session revocation table |
| 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 |