Credential vaults and envelope encryption
Envelope encryption with AES-256-GCM, a separate vault that holds credentials, and hash-chained audit logs that auditors can check.
What you will learn
- Keep account credentials in a separate vault and let the rest of the system hold only references.
- Encrypt with envelope encryption: a data key per record, wrapped by a root key in a key service.
- Make an audit log tamper-evident with a hash chain, and know its limits.
What needs protecting
The primer's accounts table stores what the service needs to reach each financial institution, with the credential hashed. That works for checking a credential, but an aggregator has to use the credential, or an access grant, to fetch data. So it must store it in a form it can recover, which means encryption, not hashing. Account numbers and personal details deserve the same care.
The aim is that most of the system never sees these values. The budget, notification and analytics services work with a reference such as vault-ref-3f9a…. Only a small, separate vault service can turn a reference back into the secret.
Envelope encryption
AWS's KMS documentation defines it: "Envelope encryption is the practice of encrypting plaintext data with a data key, and then encrypting the data key under another key."
- Generate a fresh data key for each record, a 256-bit AES key.
- Encrypt the secret with it using AES-GCM. NIST SP 800-38D recommends 96-bit IVs, and requires that an IV is never reused with the same key. GCM produces an authentication tag (128 bits is the maximum) that detects any change to the ciphertext.
- Encrypt the data key under a long-lived root key (AWS calls it a KMS key) and store the encrypted data key next to the ciphertext. Discard the plaintext data key.
To decrypt, ask the key service to decrypt the data key, then decrypt the record.
What this buys, per AWS:
- The data key needs no special storage. "You can safely store the encrypted data key alongside the encrypted data."
- Re-keying is cheap. To move to a new root key, re-encrypt only the small data keys, not every record.
- The root key stays put. KMS keys "never leave the AWS KMS FIPS 140-3 Security Level 3 validated hardware security modules unencrypted". AWS KMS itself uses AES-GCM with 256-bit keys.
- Access is controlled at one point. Only identities allowed to call the key service's decrypt operation, for example through an IAM policy, can recover anything.
A data key per record also keeps each key's use far below GCM's limits. With random IVs, SP 800-38D caps a single key at encryptions.
The visual uses your browser's real AES-256-GCM. A local key that cannot be exported stands in for the KMS key. Flip one bit of a ciphertext and decryption fails.
Tamper-evident audit logs
Record every decryption: who, when, which reference. To make silent edits detectable, chain the entries. Each entry stores
so changing an old entry breaks every hash after it.
There is a catch. Whoever controls the log can rewrite an entry and recompute every later hash, and the result is internally consistent again. Crosby and Wallach (USENIX Security 2009) make the point directly: "Tamper-evidence requires auditing. If the log is never examined, then tampering cannot be detected." The logger must regularly hand commitments, such as the latest head hash, to auditors who keep them. A rewritten log will no longer match a head an auditor already holds. Their paper also shows how a tree-based log makes those audits cheap.
Compliance names
Specific regimes bring specific duties. PCI DSS, for example, covers payment card data. Which ones apply depends on what you store and where you operate, so treat them as requirements to look up, not as features of the design.
Envelope encryption and an audit chain
Real AES-256-GCM in your browser: each record gets its own data key and 96-bit IV, and the data key is encrypted under a root key. Here the root key is a local key that cannot be exported, standing in for a KMS key.
- General App Microservices: 8 × App Compute Server
- Vault service: 4 × App Compute Server
- Key service (e.g. AWS KMS): 2 × Primary Relational DB
- Encrypted Vault DB (Ciphertext Only): 2 × Primary Relational DB
- Main App DB (vault references only): 3 × Primary Relational DB
- General App Microservices sends requests to Vault service
- Vault service sends requests to Key service (e.g. AWS KMS)
- Vault service sends requests to Encrypted Vault DB (Ciphertext Only)
- General App Microservices sends requests to Main App DB (vault references only)
Key takeaways
- An aggregator must use credentials, not just check them, so they are encrypted rather than hashed, and only a small vault service can decrypt them.
- AES-GCM with a fresh data key per record detects tampering; the encrypted data key is stored next to the data, and re-keying re-wraps only the small data keys.
- The root key never leaves the key service's hardware unencrypted, and access is controlled at its decrypt call.
- Chaining log entries with SHA-256 makes edits detectable, but whoever controls the log can rewrite the chain unless its head is published or checked elsewhere.
Sources
- System Design Primer: Design Mint.com, connecting accounts
- AWS KMS Developer Guide: Cryptography essentials (envelope encryption)
- NIST SP 800-38D: Galois/Counter Mode (GCM) and GMAC
- Crosby and Wallach, “Efficient Data Structures for Tamper-Evident Logging”, USENIX Security 2009
Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.