Skip to main content

Key Management Policy

Key Management Policy

Project / Organization: BilkoALAI Holding ASBalkanDrop AccountingPayment SaaSApp Policy Number: POL-SEC-KM-001 Version: 1.0 Date: 2026-02-23 Author: CTOSecurity Architect Status: Draft Reviewers: DPO,CISO, EngineeringCTO, LeadDPO Next Review: 2027-02-23 Classification: Confidential — Restricted Distribution

Document History

Version Date Author Changes
0.1 2026-02-23 CTOSecurity Architect Initial draft — Drop key management policy for BilkoAWS KMS + Secrets Manager

1. Purpose & Scope

Purpose: This policy defines the lifecycle management requirements for all cryptographic keys and secrets used by Bilko.ALAI ItHolding coversAS keyfor the Drop payment app, including 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 Bilkocryptographic productionkeys and stagingsecrets environments.in use across:

  • Production, staging, and development environments for Drop
  • AWS infrastructure (KMS, Secrets Manager, S3, App Runner)
  • All personnelemployee withand accesscontractor toworkstations Railwayhandling environment variablesConfidential or Vaultwarden.

    Restricted data
  • All third-party integrations where ALAI Holding AS holds keys

Field-levelPolicy encryption scope:Owner: FIELD_ENCRYPTION_KEY and FIELD_HMAC_KEY apply to JMBG and OIB fields only. PIB, JIB, and IBAN are NOT subject to field-level encryption per ADR-014 §2CISO (Tier[email protected]) 2Operational controlsOwner: Security disk-level encryption only).team


2. Key Inventory

2.1 Complete Key Taxonomy

TypeData
Key ID KeyType Algorithm Purpose Storage Rotation PeriodClassification OwnerStorage
JWT_PRIVATE_KEYdrop-national-id-key RSA 2048-bit private keyKEK JWT access token signing (RS256)AES-256-GCM RailwayFoedselsnummer secretfield (production)encryption AnnualRestricted (L4) CTOSecurity teamAWS KMS (eu-north-1)
JWT_PUBLIC_KEYdrop-db-master-key RSAKEK 2048-bit public key(Database) JWTAES-256 access token verificationXTS RailwayPostgreSQL secretTDE (production)— Phase 2 AnnualRestricted (with private)L4) CTOSecurity teamAWS KMS (eu-north-1)
REFRESH_TOKEN_SECRETdrop-kyc-key 64-byteKEK random hex(Object) Refresh token HMAC signingAES-256-GCM RailwayKYC secretdocument encryption (S3 SSE-KMS) AnnualRestricted (L4) CTOSecurity teamAWS KMS (eu-north-1)
FIELD_ENCRYPTION_KEYdrop-backup-key 32-byte random hexKEK (AES-256)Backup) Field-level encryption of JMBG and OIB in Contacts table onlyAES-256 RailwayDatabase secretbackup + Vaultwardenencryption AnnualRestricted (L4) CTOSecurity teamAWS KMS (eu-west-1) — separate region
FIELD_HMAC_KEYJWT_SECRET 32-byteSigning random hexsecret Org-scoped HMAC-SHA256 for jmbg_hash + oib_hash columnsSHA-256 RailwayJWT secretsession +token Vaultwardensigning (HS256) AnnualConfidential (with FIELD_ENCRYPTION_KEY)L3) CTOSecurity teamAWS Secrets Manager
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 compromiseDB /credential quarterly reviewCTOrotation
SEF_API_KEYSENTRY_DSN APISentry keyerror stringmonitoring Serbia 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 compromiseCTOAnnual

3. Key Hierarchy

graphAWS TDKMS ROOT["Root Secrets\n(CTOof personalTrust Vaultwarden(eu-north-1 vault)"]primary RAILWAY["Railway EnvironmentStockholm)
    Secrets\n(production|
    +-- drop-national-id-key (AES-256-GCM — annual rotation)
    |       +-- DEK-{user_id}-{timestamp}: Per-record envelope DEKs
    |               +-- Encrypts: foedselsnummer field in users table
    |               +-- Stored: 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 / stagingLet's /Encrypt
    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"| RAILWAYTLS RAILWAYCertificate (ECDSA P-256 — 90-day automated rotation)
            +-->|"Load atExternal boot"|HTTPS: APPgetdrop.no

ROOTBankID Norge AS CA
    +-->|"Rotation authority"|BankID ORG_SECRETSCertificate ORG_SECRETS(RSA-2048 — per BankID renewal schedule)
            +-->|"Decrypt onUsed request"|for: APPBankID JWT signature verification (Phase 2)

Principle: No key material is ever committed to source code. No key is stored in plaintext outside Railway secrets or Vaultwarden.


4. Key Lifecycle

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

Standards

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 Entropy RequirementsTool
RSASymmetric (JWT)AES-256) KMS keys KMS opensslCreateKey genrsa 2048API 2048-bitAWS minimumKMS (FIPS 140-2 Level 3 HSMs)
SymmetricEnvelope (AES-256)DEKs KMS opensslGenerateDataKey rand -hex 32API 256AWS bits (32 bytes)KMS
HMACJWT keysigning secret openssl rand -hex 32crypto.randomBytes(32).toString('hex') 256Node.js bitsCSPRNG
RefreshTLS token secretcertificates opensslACME rand -hex 64protocol 512Cloudflare bits/ Let's Encrypt
APIBankID keys (external)certificates GeneratedBankID byNorge externalAS portal (SEF/FINA)CA PerExternal external systemCA

Commands:Generation environment:

# 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 productionKMS-managed keys storedfor asRestricted Railwaydata: environmentgenerated variables
  • within
  • RailwayAWS EUKMS West regionHSMs encrypted at rest by Railway (AES-256)
  • Access: CTO + one designated backup (CEO) only
  • Two-factor authentication mandatory for Railway account
  • Railway account uses ALAI SSO / 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 asnever secureleaves notes (encrypted)AWS
  • Access:JWT_SECRET: CTOgenerated +using CEOcrypto.randomBytes(32) (break-glassthen access)stored in AWS Secrets Manager
  • Purpose:All Recoverykey ifgeneration Railwayevents secrets are lost; rotation documentation

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

  • Storedlogged in PostgreSQLAWS OrganizationSecret table
  • Value encrypted with FIELD_ENCRYPTION_KEY before storage
  • Decrypted in-memory only when needed for API call
  • FINA private keys additionally protected with password (stored separately)CloudTrail

6.

4.3 Key Rotation Procedures

6.1 Annual Rotation (Standard)Distribution

Schedule:Principles:

First
    Monday
  • NEVER oftransmit eachkeys calendarin year.

    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

FIELD_ENCRYPTION_KEYDistribution rotationmethods:

sensitive
ScenarioMethodNotes
Application runtime secrets (mostJWT_SECRET) AWS Secrets requiresManager re-encryption):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:

1.Level Generate1 new FIELD_ENCRYPTION_KEYAWS KMS HSMs (opensslFIPS rand140-2 Level 3)
  +-hex- 32)
2. Deploy a migration job that:
   a. Reads each encrypted field with old drop-national-id-key b.(foedselsnummer Decryptsencryption)
  c.+-- Re-encrypts with new drop-db-master-key d.(database WritesTDE back— 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 3 to DB
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) keysdisk

JWTProhibited storage locations:

  • Source code or config files in Git
  • Application logs or error messages (Sentry, BetterStack)
  • Unencrypted database columns
  • Email attachments or Slack messages
  • Browser localStorage or sessionStorage
  • Container image layers

4.5 Usage

Access control principles:

PrincipleImplementation
Least privilegeKMS key pairpolicies: rotationencrypt-only (zero-downtime):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:











GenerateAddnewpublickeytoendpoint alongside oldduringrotationwindow)3. Begin issuing new tokens signed with new private key
4. Waittokenstoexpire






5. Remove old public key from JWKS
6. Update JWT_PRIVATE_KEY and JWT_PUBLIC_KEY in Railway
7. Invalidate all refresh tokensre-login)
IAM RolePermissionsMFA Required
1.drop-key-admin CreateKey, newScheduleKeyDeletion, RSARotateKey key pairNO 2.Decrypt YES
drop-app-encrypt kms:Encrypt, JWKSkms:GenerateDataKey No (supportservice bothaccount)
drop-compliance-decrypt kms:Decrypt for alldrop-national-id-key oldonly YES
drop-key-auditorkms:ListKeys, kms:DescribeKey, CloudTrail readYES
drop-app-runnerkms:GenerateDataKey, kms:Decrypt (15scoped minutesper max)key) No (usersIAM willrole)

6.2 Emergency4.6 Rotation (On Compromise)Schedule

If a key is suspected compromised:

  1. Immediately invalidate: all user sessions (clear RefreshToken table)
  2. Generate new key within 15 minutes
  3. Update Railway secret
  4. Deploy new application instance (Railway auto-deploys on env var change)
  5. Document in Vaultwarden: old key, date of compromise, date of rotation
  6. Assess whether breach 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

7. Key Access Control

at API indashboard for
Key WhoRotation Can AccessPeriod HowMethodOwnerAlert if Overdue
JWT_PRIVATE_KEYdrop-national-id-key Application only (Railway env)Annual NeverAWS exposedKMS viaautomatic API;key loadedrotation Security startupteamYes — 7 days overdue
FIELD_ENCRYPTION_KEYdrop-db-master-key Application onlyAnnual NeverAWS logged;KMS neverautomatic returnedkey inrotation Security responseteamYes — 7 days overdue
DATABASE_URLdrop-kyc-key Application + CTOAnnual RailwayAWS secret;KMS CTOautomatic cankey viewrotation Security Railwayteam Yes — 7 days overdue
SEF API keysdrop-backup-key ApplicationAnnualManual + orgAWS ownerKMS rotation DecryptedSecurity onlyteam Yes SEF API7 calls;days org owner can rotate via settingsoverdue
FINA certificatesJWT_SECRET Application + org ownerQuarterly DecryptedManual only forgenerate HR-FISKnew, submissionsdeploy, 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 overdue

AccessJWT_SECRET log:rotation overlap procedure:

  1. Generate new JWT_SECRET value
  2. Deploy new secret to 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 was in use
  • Detection of unauthorized decryption in CloudTrail logs

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 protected 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 Railwayusers secretmust viewsre-authenticate
loggedStep 5: Document in Railwayincident 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:

Any
Key LocationDestruction MethodVerification
AWS KMS keyScheduleKeyDeletion (7-30 day waiting period)KMS + CloudTrail audit trail.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 outsideto normaldrop-compliance-decrypt deploymentrole. isApplication reviewedcode bycannot CTO.decrypt foedselsnummer except during explicit compliance workflows.


8. EscrowAudit &Logging Recoveryfor Key Operations

FIELD_ENCRYPTION_KEY Escrow (Critical)

The FIELD_ENCRYPTION_KEY is the most criticalAll key operations losslogged meansin permanentAWS lossCloudTrail, of all L4 Restricted field data (tax IDs, IBAN).

Escrow procedure:including:

  • FIELD_ENCRYPTION_KEYKey storedcreation in(actor, Vaultwardenkey secureID, notekey accessibletype, to: CTO, CEOtimestamp)
  • Vaultwarden has its own backupEncryption/decryption (seeactor, systemkey infrastructureID, docs)context, timestamp)
  • Key material noted with: creation date, rotation date,(actor, descriptionold key version, new key version, timestamp)
  • Key disabling / deletion scheduling (actor, key ID, reason, timestamp)
  • Failed access attempts (actor, key ID, reason, timestamp)

IfLog FIELD_ENCRYPTION_KEY is lost and not recoverable:destination: AllAWS encryptedCloudTrail field dataS3 is(immutable, permanentlyappend-only) unreadable.+ ThisBetterStack isaggregation aLog catastrophicretention: data5 lossyears event.(AML Contactcompliance legal counselHvitvaskingsloven and§ affected30) supervisoryAlert authorities.on:

Railway Account Recovery

  • RailwayDecrypt rootoperations account:by [email protected]unexpected (passwordIAM role
  • Failed KMS access attempts > 3 in Vaultwarden)5 minutes
  • 2FA recovery codes: Vaultwarden secure note
  • Designated backup access: CEO has viewOff-hours access to Railwaydrop-national-id-key or drop-kyc-key
  • Any ScheduleKeyDeletion event (read-only)immediate alert)

9. KeyException DestructionProcess

WhenExceptions anot keypermitted isfor: retiredRestricted (supersededL4) bydata rotation):(foedselsnummer, KYC documents) — no exceptions to field-level encryption.

Exception request process:

  1. RemoveSubmit fromrequest Railwayto: environment variables[email protected]
  2. RemoveRequired: fromsystem, activedata Vaultwardenclassification, entrieskey being excepted, business justification, risk assessment, compensating controls, duration (max 12 months)
  3. ArchiveApproval: to Vaultwarden secure note: "Retired Keys" with date and reasonCISO
  4. OldLogged FIELD_ENCRYPTION_KEYin: versions:compliance retainedregister
  5. for
  6. Review: 3Quarterly — exceptions > 12 months afterrequire rotationboard-level (in case rollback needed), then permanently deleted from Vaultwardenapproval

Active exceptions:

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)Filesystem encryption onlyPhase 2 migrationFull disk encryption; private network; no public DB access

10. SecurionEmergency Sign-offProcedures Requirement— Key Compromise

No

Immediate changesresponse checklist (execute within 1 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: 72h to FIELD_ENCRYPTION_KEY,Datatilsynet
   FIELD_HMAC_KEY,-> orFinanstilsynet field-levelnotification encryptionif implementationpayment maydata beaffected
deployed toDocument productioneverything withoutin writtenincident approvallog
from ParisaPost-mortem Tabrizwithin (Securion).

48 hours

Process:Re-encryption priority:

  1. AllFoedselsnummer field(drop-national-id-key) encryption codeRestricted changes(L4), musthighest complete Securion security reviewpriority
  2. ReviewKYC conducted against checklist: docs/security/FieldEncryptionSecurionChecklist.md
  3. Sign-off evidence file required before mc.js done on any field encryption task
  4. Evidence file stored in: docs/security/securion-approvals/YYYY-MM-DD-<task-id>.md

Scope: This requirement applies to:

  • Initial field encryption implementationdocuments (MCdrop-kyc-key) #9966)
  • AnyRestricted changes to FieldEncryption.kt, FieldHmac.kt, FieldEncryptionRotationScript.kt(L4)
  • Database migrationbackup changesfiles affecting(drop-backup-key) jmbg, oib,Restricted jmbg_hash, oib_hash columns(L4)
  • KeyDatabase rotationmaster procedures(drop-db-master-key) — Phase 2
  • Addition of new encrypted fields

Exemptions: Changes to non-cryptographic code (UI masking, API response filtering) do not require Securion sign-off but must still undergo Proveo QA review.


Approval

Security
Role Name SignatureDate DateSignature
Author CTO Architect 2026-02-23
Reviewer (DPO)CISO
Reviewer (Engineering Lead)CTO
ApproverDPO (GDPR relevance) CEO
Management