Encryption Model

How SikkerKey protects your secrets, credentials, and data.

Every secret in SikkerKey is encrypted with three independent layers. Your passwords are hashed, your sessions are protected, and nothing sensitive is stored in plaintext.

Three Layers of Secret Encryption

Each secret is wrapped in three layers of AES-256 encryption:

  1. Your secret is encrypted with its own unique data key. Every secret gets a fresh key, so compromising one encrypted blob tells you nothing about another.

  2. The data key is encrypted with your project's master key. Each project has its own independently generated master key, so access to one project reveals nothing about another.

  3. The project master key is stored encrypted. It can only be decrypted in server memory during request processing. The key that protects it never touches disk.

To read a single secret, all three layers must be unwrapped in sequence. The database contains only encrypted blobs at every level.

What this means in practice

  • Database breach: an attacker gets encrypted blobs at every layer. No plaintext secrets, no usable keys.
  • One secret compromised: other secrets in the same project remain protected. Each has its own data key.
  • One project compromised: other projects remain protected. Each has its own master key.
  • Blob swapping: encrypted values are bound to their specific secret ID. Moving an encrypted blob from one secret to another fails on decryption.

Key lifecycle

Key material is held in memory only for the duration of a single operation. After encryption or decryption completes, all keys are zeroed from memory before the response is sent.

Password Security

Account passwords are hashed with Argon2id, a memory-hard algorithm designed to resist GPU and ASIC attacks. SikkerKey enforces a minimum of 12 characters with at least one digit and one special character. Plaintext passwords are never stored or logged.

Two-Factor Authentication

TOTP secrets (the shared key your authenticator app uses) are encrypted at rest with AES-256-GCM. They are decrypted only during code verification.

Recovery codes are hashed with Argon2id (the same algorithm used for passwords). The plaintext codes are shown once during setup and never stored.

Sessions

Refresh tokens are generated as 32 cryptographically random bytes. Only the SHA-256 hash is stored in the database. The raw token lives in an HttpOnly, SameSite cookie. Even with full database access, an attacker cannot reconstruct the raw tokens needed to hijack a session.

Password Reset and Email Verification

Password reset tokens and email verification codes follow the same pattern: a random value is generated, hashed, and stored. The plaintext is delivered to the user (via email or displayed once). The database never contains the raw value.

Reset tokens expire after 1 hour. Verification codes expire after 10 minutes. Both are single-use.

File Attachments

Support ticket attachments are encrypted at rest with AES-256-GCM before being written to disk. Each file gets a fresh random initialization vector. Files are stored with UUID filenames, so the original filename is never visible on disk.

What a Database Dump Yields

DataWhat's storedWhat an attacker can do with it
SecretsEncrypted blobsNothing without the master keys
Project master keysEncryptedNothing without the server encryption key
PasswordsArgon2id hashesCannot reverse. No plaintext.
TOTP secretsAES-256 encryptedCannot decrypt without the server key
Session tokensSHA-256 hashesCannot reconstruct raw tokens
Machine keysPublic keys onlyCannot forge signatures
AttachmentsAES-256 encrypted on diskCannot decrypt without the server key

There is no plaintext secret material anywhere in the database or on disk.