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:
-
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.
-
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.
-
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
| Data | What's stored | What an attacker can do with it |
|---|---|---|
| Secrets | Encrypted blobs | Nothing without the master keys |
| Project master keys | Encrypted | Nothing without the server encryption key |
| Passwords | Argon2id hashes | Cannot reverse. No plaintext. |
| TOTP secrets | AES-256 encrypted | Cannot decrypt without the server key |
| Session tokens | SHA-256 hashes | Cannot reconstruct raw tokens |
| Machine keys | Public keys only | Cannot forge signatures |
| Attachments | AES-256 encrypted on disk | Cannot decrypt without the server key |
There is no plaintext secret material anywhere in the database or on disk.