Skip to main content

Key Management Policy

Key Management Policy

Project / Organization: ALAI Holding ASBilkoDropBalkan PaymentAccounting AppSaaS Policy Number: POL-SEC-KM-001 Version: 1.0 Date: 2026-02-23 Author: Security ArchitectCTO Status: Draft Reviewers: CISO,DPO, CTO,Engineering DPO Next Review: 2027-02-23Lead Classification: Confidential — Restricted Distribution

Document History

Version Date Author Changes
0.1 2026-02-23 Security ArchitectCTO Initial draft — Drop key management policy for AWS KMS + Secrets ManagerBilko

1. Purpose & Scope

Purpose: This policy defines the lifecycle management requirements for all cryptographic keys and secrets used by ALAIBilko. HoldingIt AScovers for the Drop payment app, includingkey generation, distribution, storage, usage, rotation, revocation, and destruction.

Regulatory basis:

  • GDPR Art. 32 — appropriate technical measures for personal data
  • Personopplysningsloven (LOV-2018-06-15-38) § 28
  • IKT-forskriften (FOR-2003-05-21-630) §§ 5-6
  • DORA (EU) 2022/2554 Art. 9(4)(d) — encryption key management
  • Hvitvaskingsloven (LOV-2018-06-01-23) § 30 — KYC data protection at rest

Scope: All cryptographicBilko keysproduction and secretsstaging inenvironments. useAll across:personnel with access to Railway environment variables or Vaultwarden.

    Field-level

  • Production,encryption staging,scope: FIELD_ENCRYPTION_KEY and developmentFIELD_HMAC_KEY environmentsapply forto Drop
  • AWS infrastructure (KMS, Secrets Manager, S3, App Runner)
  • All employeeJMBG and contractorOIB workstationsfields handlingonly. ConfidentialPIB, orJIB, Restrictedand data
  • IBAN
  • Allare third-partyNOT integrationssubject whereto ALAIfield-level Holdingencryption ASper holdsADR-014 keys

Policy Owner: CISO§2 ([email protected])Tier Operational2 Owner:controls Security teamdisk-level encryption only).


2. Key Inventory

2.1 Complete Key Taxonomy

Classification
Key ID Key TypeAlgorithm Purpose DataStorage Rotation Period OwnerStorage
drop-national-id-keyJWT_PRIVATE_KEY KEKRSA 2048-bit private key AES-256-GCMJWT access token signing (RS256) FoedselsnummerRailway fieldsecret encryption(production) Restricted (L4)Annual Security teamAWS KMS (eu-north-1)CTO
drop-db-master-keyJWT_PUBLIC_KEY KEKRSA (Database)2048-bit public key AES-256JWT XTSaccess token verification PostgreSQLRailway TDEsecret — Phase 2(production) RestrictedAnnual (L4)with private) Security teamAWS KMS (eu-north-1)CTO
drop-kyc-keyREFRESH_TOKEN_SECRET KEK64-byte (Object)random hex AES-256-GCMRefresh token HMAC signing KYCRailway document encryption (S3 SSE-KMS)secret Restricted (L4)Annual Security teamAWS KMS (eu-north-1)CTO
drop-backup-keyFIELD_ENCRYPTION_KEY KEK32-byte random hex (Backup)AES-256) AES-256Field-level encryption of JMBG and OIB in Contacts table only DatabaseRailway backupsecret encryption+ Vaultwarden Restricted (L4)Annual Security teamAWS KMS (eu-west-1) — separate regionCTO
JWT_SECRETFIELD_HMAC_KEY Signing32-byte secretrandom hex Org-scoped HMAC-SHA-256SHA256 for jmbg_hash + oib_hash columns JWTRailway sessionsecret token+ signing (HS256)Vaultwarden ConfidentialAnnual (L3)with FIELD_ENCRYPTION_KEY) Security teamAWS Secrets ManagerCTO
TLS-EXTTLS CertificateECDSA P-256External HTTPS (getdrop.no)Cloudflare / Let's EncryptCloudflare managed
BankID-certTLS CertificateRSA-2048BankID Norway JWT signature verificationBankID Norge AS CABankID managed
SUMSUB-API-KEYAPI KeyHMAC-SHA-256Sumsub KYC API authenticationConfidential (L3)Security teamAWS Secrets Manager
DEK-*Data Encryption KeysAES-256-GCMPer-record envelope DEKsRestricted (L4)ApplicationGenerated by AWS KMS GenerateDataKey

2.2 Secrets Inventory (AWS Secrets Manager)

Secret NamePurposeRotation
JWT_SECRETJWT token signingQuarterly
SUMSUB_SECRET_KEYSumsub KYC integrationAnnual or on compromise
BANKID_CLIENT_SECRETBankID OIDC client secretPer BankID schedule
DATABASE_URL PostgreSQL connection string with credentialsDatabase accessRailway secret On DBcompromise credential/ rotationquarterly reviewCTO
SENTRY_DSNSEF_API_KEY SentryAPI errorkey monitoringstring AnnualSerbia SEF e-invoice portal (per org)DB (encrypted) per orgPer SEF portal policyPer organization
FINA_CERTX.509 certificate + private keyHR-FISK e-invoice signing (FINA PKI)DB (encrypted) per orgPer FINA PKI (1-3 years)Per organization
SENTRY_DSNDSN stringError trackingRailway secret / env varOn compromiseCTO

3. Key Hierarchy

AWSgraph KMSTD
    ROOT["Root ofSecrets\n(CTO Trustpersonal (eu-north-1Vaultwarden primaryvault)"]
    RAILWAY["Railway Stockholm)Environment Secrets\n(production / staging / dev)"]
    ORG_SECRETS["Per-Organization Secrets\n(DB encrypted, L4 Restricted)\nSEF API keys, FINA certs"]
    APP["Application Runtime\n(keys loaded from env at startup)"]

    ROOT -->|"Provision"| +RAILWAY
    RAILWAY -->|"Load drop-national-id-at boot"| APP
    ROOT -->|"Rotation authority"| ORG_SECRETS
    ORG_SECRETS -->|"Decrypt on request"| APP

Principle: No key (AES-256-GCMmaterial is annualever rotation)committed |to +--source DEK-{user_id}-{timestamp}:code. Per-recordNo envelopekey DEKsis | +-- Encrypts: foedselsnummer fieldstored in usersplaintext tableoutside |Railway +--secrets Stored:or base64(encryptedDEK || iv || tag || ciphertext) | +-- drop-db-master-key (AES-256 XTS — annual rotation) | +-- Encrypts: PostgreSQL database at rest (Phase 2 — AWS RDS) | +-- drop-kyc-key (AES-256-GCM — annual rotation) | +-- Encrypts: KYC document objects in AWS S3 (SSE-KMS) | +-- AWS KMS Root of Trust (eu-west-1 — SEPARATE REGION) +-- drop-backup-key (AES-256 — annual rotation) +-- Encrypts: All database backup files AWS Secrets Manager (eu-north-1) +-- JWT_SECRET (HMAC-SHA-256 — quarterly rotation) | +-- Signs: Drop JWT session tokens (HS256 via jose ^6.1.3) +-- SUMSUB_SECRET_KEY (API key — annual) +-- BANKID_CLIENT_SECRET (OIDC client secret) +-- DATABASE_URL (connection string) Cloudflare / Let's Encrypt +-- TLS Certificate (ECDSA P-256 — 90-day automated rotation) +-- External HTTPS: getdrop.no BankID Norge AS CA +-- BankID Certificate (RSA-2048 — per BankID renewal schedule) +-- Used for: BankID JWT signature verification (Phase 2) Vaultwarden.


4. Key LifecycleGeneration Standards

4.1 Lifecycle Overview

flowchart LR
    GEN[Generation] -->|"AWS KMS CSPRNG"| DIST[Distribution]
    DIST -->|"Runtime API call\nnever in env files"| STORE[Storage]
    STORE -->|"KMS / Secrets Manager\naccess controlled"| USE[Usage]
    USE -->|"Scheduled"| ROT[Rotation]
    ROT -->|"New key active\noverlap period"| USE
    ROT -->|"Old key retired"| ARCH[Archive / Revoke]
    ARCH -->|"End of life"| DEST[Destruction]

    REVOKE[Emergency Revocation] -->|"Compromise detected"| DEST
    USE --> REVOKE

4.2 Generation

Entropy requirements:

  • All keys MUST be generated using AWS KMS or a CSPRNG
  • NEVER use user-supplied passphrases, timestamps, UUIDs, or predictable values as keys
  • NEVER generate keys in application code for Restricted data — use AWS KMS GenerateKey or GenerateDataKey

Approved key generation methods:

Key Type Generation Method ToolEntropy Requirements
SymmetricRSA (AES-256) KMS keysJWT) KMSopenssl CreateKeygenrsa 2048 API AWS2048-bit KMS (FIPS 140-2 Level 3 HSMs)minimum
EnvelopeSymmetric DEKs(AES-256) KMSopenssl GenerateDataKeyrand -hex 32 API AWS256 KMSbits (32 bytes)
JWTHMAC signing secretkey crypto.randomBytes(32).toString('hex')openssl rand -hex 32 Node.js256 CSPRNGbits
TLSRefresh certificatestoken secret ACMEopenssl protocolrand -hex 64 Cloudflare512 / Let's Encryptbits
BankIDAPI certificateskeys (external) BankIDGenerated Norgeby ASexternal CAportal (SEF/FINA) ExternalPer CAexternal system

Generation environment:Commands:

# Generate JWT key pair
openssl genrsa -out jwt_private.pem 2048
openssl rsa -in jwt_private.pem -pubout -out jwt_public.pem

# Generate AES-256 field encryption key
openssl rand -hex 32

# Generate HMAC key
openssl rand -hex 32

All generated keys must be imported to Railway and Vaultwarden within 1 hour. Local files deleted securely after import.


5. Key Storage

Production Keys (Railway)

  • All KMS-managedproduction keys stored as Railway environment variables
  • Railway EU West region — encrypted at rest by Railway (AES-256)
  • Access: CTO + one designated backup (CEO) only
  • Two-factor authentication mandatory for RestrictedRailway data:account
  • generated
  • Railway withinaccount AWSuses KMSALAI HSMsSSO / strong password (≥20 chars, in Vaultwarden)

Staging/Dev Keys

  • Separate Railway project (staging) different keys from production
  • Dev: .env.local files excluded from git via .gitignore
  • Dev keys may use weaker entropy but must still be valid format

Vaultwarden (Backup & Documentation)

  • URL: https://vault.basicconsulting.no
  • Stores: production key material neveras leavessecure AWSnotes (encrypted)
  • JWT_SECRET:Access: generatedCTO using+ crypto.randomBytes(32)CEO then(break-glass storedaccess)
  • Purpose: Recovery if Railway secrets are lost; rotation documentation

Per-Organization Secrets (SEF API Keys, FINA Certificates)

  • Stored in AWSPostgreSQL SecretsOrganizationSecret Managertable
  • AllValue keyencrypted generationwith eventsFIELD_ENCRYPTION_KEY loggedbefore instorage
  • AWS
  • Decrypted CloudTrailin-memory only when needed for API call
  • FINA private keys additionally protected with password (stored separately)

6. Key Rotation Procedures

4.36.1 DistributionAnnual Rotation (Standard)

Principles:Schedule: First Monday of each calendar year.

  • NEVER transmit keys in plaintext over any channel
  • NEVER include keys in source code, .env files committed to Git, or log output
  • NEVER send keys via email, Slack, or any messaging platform
  • Always fetch secrets at application runtime from AWS Secrets Manager or KMS API

DistributionFIELD_ENCRYPTION_KEY methods:

most
ScenarioMethodNotes
Application runtime secretsrotation (JWT_SECRET) AWSsensitive Secrets Managerrequires API at startupFatal error if JWT_SECRET missing
Envelope DEK generationAWS KMS GenerateDataKey per encryption operationKey material decrypted in memory only
Developer accessAWS IAM role + MFALeast privilege
CI/CD pipeline secretsGitHub Actions encrypted secrets (scoped)Rotated per project

4.4 Storage

Storage hierarchy for Drop:re-encryption):

Level1. 1Generate new AWS KMS HSMsFIELD_ENCRYPTION_KEY (FIPSopenssl 140-2rand Level-hex 3)32)
+--2. drop-national-id-Deploy a migration job that:
   a. Reads each encrypted field with old key
   (foedselsnummerb. encryption)Decrypts
   +--c. drop-db-master-Re-encrypts with new key
   (databased. TDEWrites — Phase 2)
  +-- drop-kyc-key (KYC document S3 encryption)
  +-- drop-backup-key (backup encryption — eu-west-1)

Level 2 — AWS Secrets Manager
  +-- JWT_SECRET (HMAC signing key)
  +-- SUMSUB_SECRET_KEY (API key)
  +-- BANKID_CLIENT_SECRET (OIDC secret)
  +-- DATABASE_URL (connection string)

Level 3 — Application memory (ephemeral only)
  +-- Decrypted DEK during active encryption/decryption
  +-- JWT_SECRET during token signing/verification
  NOTE: NEVER persist Level 3back to diskDB
3. Migration must be atomic per record (read old → write new in transaction)
4. Only after 100% migration: update Railway secret to new key
5. Delete old key from Vaultwarden (add to archive note with date)
6. Test: attempt decryption with both old (should fail) and new (should succeed) keys

ProhibitedJWT storagekey locations:pair rotation (zero-downtime):

    1. 
  • SourceGenerate codenew orRSA configkey filespair 2. Add new public key to JWKS endpoint alongside old (support both during rotation window) 3. Begin issuing new tokens signed with new private key 4. Wait for all old tokens to expire (15 minutes max) 5. Remove old public key from JWKS 6. Update JWT_PRIVATE_KEY and JWT_PUBLIC_KEY in GitRailway 7. Invalidate all refresh tokens (users will re-login)
  • 6.2 Emergency Rotation (On Compromise)

    If a key is suspected compromised:

    1. Immediately invalidate: all user sessions (clear RefreshToken table)
    2. ApplicationGenerate logsnew orkey errorwithin messages15 (Sentry, BetterStack)minutes
    3. UnencryptedUpdate databaseRailway columnssecret
    4. EmailDeploy attachmentsnew orapplication Slackinstance messages(Railway auto-deploys on env var change)
    5. BrowserDocument localStoragein orVaultwarden: sessionStorageold key, date of compromise, date of rotation
    6. ContainerAssess imagewhether layersbreach notification is required (see data-breach-response-plan.md)

6.3 FINA Certificate (HR-FISK) Rotation

FINA X.509 certificates for HR-FISK e-invoicing have a defined validity period (1-3 years per FINA PKI).

1. FINA certificate expiry alert fires 60 days before expiry
2. Organization admin is notified to renew via FINA portal
3. New certificate uploaded through Bilko settings → HR eRačun → Certificate
4. Old certificate archived (not deleted — needed to verify past submissions)
5. Test: submit a test e-invoice via HR-FISK test environment with new certificate

4.5

7. Usage

Key

Access control principles:

PrincipleImplementation
Least privilegeKMS key policies: encrypt-only for application, decrypt-only for compliance function
Separation of dutieskey-admin cannot perform decryption; application service cannot create keys
Key purpose bindingdrop-national-id-key used only for national ID encryption
Audit all accessEvery KMS operation logged in AWS CloudTrail
Time-bound accessIAM role assumption with time-bound session tokens

KMS key policy roles:

IAM RolePermissionsMFA Required
drop-key-adminCreateKey, ScheduleKeyDeletion, RotateKey — NO DecryptYES
drop-app-encryptkms:Encrypt, kms:GenerateDataKeyNo (service account)
drop-compliance-decryptkms:Decrypt for drop-national-id-key onlyYES
drop-key-auditorkms:ListKeys, kms:DescribeKey, CloudTrail readYES
drop-app-runnerkms:GenerateDataKey, kms:Decrypt (scoped per key)No (IAM role)

4.6 Rotation Schedule

Control loaded in viewRailwayAPIorg
Key RotationWho PeriodCan Access MethodOwnerAlert if OverdueHow
drop-national-id-keyJWT_PRIVATE_KEY AnnualApplication only (Railway env) AWSNever KMSexposed automaticvia keyAPI; rotation Securityat teamYes — 7 days overduestartup
drop-db-master-keyFIELD_ENCRYPTION_KEY AnnualApplication only AWSNever KMSlogged; automaticnever keyreturned rotation SecurityAPI teamYes — 7 days overdueresponse
drop-kyc-keyDATABASE_URL AnnualApplication + CTO AWSRailway KMSsecret; automaticCTO keycan rotation Securityin team Yes — 7 days overduedashboard
drop-backup-keySEF API keys AnnualApplication + org owner ManualDecrypted +only AWSfor KMSSEF rotation Securitycalls; team Yesowner can 7rotate daysvia overduesettings
JWT_SECRETFINA certificates QuarterlyApplication + org owner ManualDecrypted only generatefor new,HR-FISK deploy, rotateSecurity teamYes — 3 days overdue
TLS certificate (getdrop.no)90 daysAutomated (Cloudflare / Let's Encrypt)PlatformYes — 14 days before expiry
BankID certificatePer BankID scheduleManual — BankID renewal processSecurity teamCalendar reminder
SUMSUB_SECRET_KEYAnnual or on compromiseManual via Sumsub dashboardSecurity teamYes — 7 days overduesubmissions

JWT_SECRETAccess rotationlog: overlapAll procedure:

  1. Generate new JWT_SECRET value
  2. Deploy newRailway secret toviews AWS Secrets Manager
  3. Rolling deploy of Drop app (picks up new secret)
  4. Wait for all existing sessions to expire (max 24h — JWT expiry)
  5. All sessions issued with old secret are now invalid — users re-authenticate
  6. Delete old secret version from Secrets Manager

AWS KMS automatic rotation: AWS KMS generates new key material annually while retaining all prior versions for decryption of existing ciphertext.


4.7 Revocation Procedures

Trigger conditions for immediate revocation:

  • Known or suspected key compromise (key material logged, unauthorized access)
  • Employee departure with key management IAM role access
  • System compromise where key waslogged in use
  • Railway
  • Detectionaudit oftrail. unauthorizedAny decryptionaccess inoutside CloudTrailnormal logs
  • deployment
is

Emergency revocation procedure (target: < 1 hour):

Step 1: Alert Security Lead via #security-incident Slack channel
Step 2: Identify scope — which data was accessible with compromised key?
Step 3: AWS KMS: Disable compromised key (prevents new encrypt/decrypt)
Step 4: Generate replacement key via KMS CreateKey
Step 5: Re-encrypt all data protectedreviewed by compromised key (Restricted first)
Step 6: Update KMS key references in application configuration
Step 7: Deploy updated configuration
Step 8: Verify all systems using new key
Step 9: Audit CloudTrail: What decrypt operations used compromised key in last 30 days?
Step 10: Assess breach notification requirement (data-breach-response-plan.md §5)
         -> GDPR Art. 33 / Personopplysningsloven § 32: 72h to Datatilsynet if personal data affected
         -> Finanstilsynet notification if payment data affected
Step 11: Document in incident log
Step 12: Post-mortem within 48 hours

JWT_SECRET emergency rotation (session compromise):

Step 1: Generate new JWT_SECRET immediately
Step 2: Deploy to AWS Secrets Manager
Step 3: Rolling deploy Drop application — all in-flight sessions invalidated
Step 4: All users must re-authenticate
Step 5: Document in incident log

4.8 Destruction

When destruction is required:

  • Key rotated out and overlap period expired
  • Crypto-shredding: deleting user foedselsnummer by destroying envelope DEK (GDPR Art. 17)
  • System decommissioned

Destruction methods:

Key LocationDestruction MethodVerification
AWS KMS keyScheduleKeyDeletion (7-30 day waiting period)KMS + CloudTrail audit log
AWS Secrets ManagerDeleteSecret (7-30 day waiting period)Secrets Manager audit log
Envelope DEK in databaseDelete encrypted DEK fieldVerify decryption fails

Crypto-shredding for GDPR erasure (Art. 17): When a user requests account deletion:

  1. The envelope DEK for that user's foedselsnummer is deleted from the database
  2. The ciphertext (foedselsnummer) becomes permanently unreadable
  3. AML retention: transaction records retained 5 years per Hvitvaskingsloven § 30

5. Key Management System

Primary KMS: AWS KMS (eu-north-1 — Stockholm region) Backup KMS: AWS KMS (eu-west-1 — Ireland) for drop-backup-key only — separate region for disaster recovery Secrets management: AWS Secrets Manager (eu-north-1)


6. Access Controls for Key Operations

OperationRequired RolesApproval Process
Create new KMS keydrop-key-admin + CISO approvalAWS IAM + ticket
Rotate KMS key (manual)drop-key-admin (proposer) + CISO (approver)Dual approval
Emergency key disableSecurity Lead (any one)Single — immediate incident ticket
Schedule key deletiondrop-key-admin + CISODual approval — minimum 7-day waiting period
Grant new service accessSecurity Lead + system ownerIAM PR review + approval
Rotate JWT_SECRETSecurity LeadSingle — with deployment coordination

7. Foedselsnummer Field Encryption — Implementation Pattern

Key: drop-national-id-key (AWS KMS — separate from database master key) Stored: Only AES-256-GCM ciphertext — never plaintext foedselsnummer in database Source: src/drop-app/src/lib/encrypt.ts (Phase 2 implementation)

// Envelope encryption for foedselsnummer
// Key: drop-national-id-key (AWS KMS)
// Stored: base64(encryptedDEK || iv || tag || ciphertext)

async function encryptNationalId(fodselsnummer: string): Promise<string> {
    const iv = crypto.randomBytes(12);  // 96-bit random IV — never reused
    const dek = await kmsClient.generateDataKey({
        KeyId: process.env.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();
    dek.Plaintext.fill(0);  // Zero DEK plaintext from memory after use
    // Return: base64(encryptedDEK || iv || tag || ciphertext)
    return Buffer.concat([dek.CiphertextBlob, iv, tag, ciphertext]).toString('base64');
}

Access restriction: Only the Compliance function has IAM access to drop-compliance-decrypt role. Application code cannot decrypt foedselsnummer except during explicit compliance workflows.CTO.


8. AuditEscrow Logging& forRecovery

Key

FIELD_ENCRYPTION_KEY Operations

Escrow (Critical)

The FIELD_ENCRYPTION_KEY is the most critical key — loss means permanent loss of all L4 Restricted field data (tax IDs, IBAN).

AllEscrow key operations logged in AWS CloudTrail, including:procedure:

  • KeyFIELD_ENCRYPTION_KEY creationstored (actor,in keyVaultwarden ID,secure keynote type,accessible timestamp)to: CTO, CEO
  • Encryption/decryptionVaultwarden has its own backup (actor,see keysystem ID,infrastructure context, timestamp)docs)
  • Key material noted with: creation date, rotation (actor,date, old key version, new key version, timestamp)
  • Key disabling / deletion scheduling (actor, key ID, reason, timestamp)
  • Failed access attempts (actor, key ID, reason, timestamp)description

LogIf destination:FIELD_ENCRYPTION_KEY is lost and not recoverable: AWSAll CloudTrailencrypted field S3data (immutable,is append-only)permanently +unreadable. BetterStackThis aggregationis Loga retention:catastrophic 5data yearsloss (AMLevent. complianceContact legal Hvitvaskingslovencounsel §and 30)affected Alertsupervisory on:authorities.

Railway Account Recovery

  • DecryptRailway operationsroot byaccount: unexpected[email protected] IAM(password rolein Vaultwarden)
  • Failed2FA KMSrecovery accesscodes: attemptsVaultwarden >secure 3 in 5 minutesnote
  • Off-hoursDesignated backup access: CEO has view access to drop-national-id-key or drop-kyc-key
  • Any ScheduleKeyDeletion eventRailway (immediate alert)read-only)

9. ExceptionKey ProcessDestruction

ExceptionsWhen nota permittedkey for:is Restrictedretired (L4)superseded databy rotation):

  1. Remove from Railway environment variables
  2. Remove from active Vaultwarden entries
  3. Archive to Vaultwarden secure note: "Retired Keys" with date and reason
  4. Old FIELD_ENCRYPTION_KEY versions: retained for 3 months after rotation (foedselsnummer,in KYCcase documents)rollback needed), nothen exceptionspermanently deleted from Vaultwarden

10. Securion Sign-off Requirement

No changes to FIELD_ENCRYPTION_KEY, FIELD_HMAC_KEY, or field-level encryption.encryption implementation may be deployed to production without written approval from Parisa Tabriz (Securion).

Exception request process:Process:

  1. SubmitAll requestfield to:encryption [email protected]code changes must complete Securion security review
  2. Required:Review system,conducted dataagainst classification,checklist: key being excepted, business justification, risk assessment, compensating controls, duration (max 12 months)docs/security/FieldEncryptionSecurionChecklist.md
  3. Approval:Sign-off CISOevidence file required before mc.js done on any field encryption task
  4. LoggedEvidence file stored in: compliance register
  5. Review: Quarterly — exceptions docs/security/securion-approvals/YYYY-MM-DD-<task-id> 12 months require board-level approval.md

ActiveScope: exceptions:This requirement applies to:

  • Initial
  • SystemExceptionExpiryCompensating Controls
    JWT signing (HS256 shared secret)Shared-secret JWT instead of asymmetric RS256/EdDSAPhase 2 migrationJWT_SECRET in AWS Secrets Manager; 24h session expiry; server-side revocation table
    SQLite MVP (no column-level TDE)Filesystemfield encryption onlyPhase 2 migrationFull disk encryption; private network; no public DB access

    10. Emergency Procedures — Key Compromise

    Immediate response checklistimplementation (executeMC within#9966)
    1
  • Any hour of suspected compromise): □ Activate incident response — notify: Security Lead + CISO + CTO □ Identify scope: Which data was accessible with compromised key? □ AWS KMS: Disable compromised key immediately □ For JWT_SECRET compromise: rotate immediately (all sessions invalidated) □ Generate replacement key □ Re-encrypt all data protected by compromised key (Restricted data first) □ Audit CloudTrail: What was decrypted using compromised key in last 30 days? □ Assess breach notification requirement (data-breach-response-plan.md §5) -> GDPR Art. 33 / Personopplysningsloven § 32: 72hchanges to DatatilsynetFieldEncryption.kt, ->FieldHmac.kt, Finanstilsynet notification if payment data affected □ Document everything in incident log □ Post-mortem within 48 hours
  • Re-encryption priority:

    1. Foedselsnummer (drop-national-id-key) — Restricted (L4), highest priority
    2. KYC documents (drop-kyc-key) — Restricted (L4)FieldEncryptionRotationScript.kt
    3. Database backupmigration fileschanges (drop-backup-key)affecting jmbg, Restrictedoib, (L4)jmbg_hash, oib_hash columns
    4. DatabaseKey masterrotation procedures
    5. Addition of new encrypted fields
    6. Exemptions: Changes to non-cryptographic code (drop-db-master-key)UI masking, PhaseAPI 2response

    filtering) do not require Securion sign-off but must still undergo Proveo QA review.


    Approval

    Architect
    Role Name DateSignature SignatureDate
    Author SecurityCTO 2026-02-23
    CISOReviewer (DPO)
    CTOReviewer (Engineering Lead)
    DPO (GDPR relevance)Approver
    ManagementCEO