WonderFunds stores financial data. That alone makes cryptography not optional, but mandatory. This article lays out the specific design decisions: why we chose certain methods, which alternatives we rejected, and where the limits of our threat model lie.
Why AES-256-GCM?
The threat scenario at WonderFunds is data-at-rest: someone gains access to the database. Not the running application server, not network traffic, but the stored data.
For this scenario, we need symmetric encryption. Both sides of the operation (encryption and decryption) run on the same server. There's no second communication partner who would need a public key. Asymmetric methods like RSA or a hybrid scheme would be overhead with no security benefit.
AES-256-GCM delivers two properties in a single pass:
- Confidentiality: The data is unreadable without the key.
- Integrity: The GCM mode produces an authentication tag. Any tampering, even a single flipped bit, causes decryption to fail. Not with wrong data, but with an error.
The alternative would be AES-256-CBC plus a separate HMAC. Works, but GCM handles both atomically. Less code, fewer implementation pitfalls.
Per-User KEK Derivation
Not all users share the same key. WonderFunds derives an individual Key Encryption Key (KEK) for each user:
KEK = SHA-256(masterKey || userId)
The master key is an environment variable with at least 128 bits of entropy (32 hex characters), normalized to 256 bits via SHA-256. The user ID is a UUID.
Each user has their own key. If a single KEK is compromised, only that one user is affected. Everyone else stays protected.
Why SHA-256 instead of HKDF or PBKDF2?
PBKDF2 and Argon2 are key-stretching algorithms: they make derivation intentionally slow to harden brute-force attacks on weak inputs. Our input isn't weak, though. The master key has 256 bits of entropy. Brute-force is not a realistic scenario at this key length.
HKDF would be the formally more correct choice for key derivation. In practice, SHA-256(masterKey || userId) produces an equivalent result with high-entropy input. The decision favored simplicity: fewer dependencies, fewer configuration parameters, less attack surface in the implementation.
This is a deliberate trade-off: formal correctness vs. implementation simplicity. With high-entropy input, it's defensible.
The Data Token as a Pseudonymous Identifier
Each user receives a data token at registration: 32 cryptographically random bytes, stored as a 64-character hexadecimal string. 256 bits of entropy.



