Deadman Secrets Security Model
How zero-knowledge encryption, threshold key splitting, and hardware-isolated delivery keep your secrets unreadable — even to us.
Security Overview
Deadman Secrets is designed around a single uncompromising principle: the server should never be able to decrypt your secrets, even if fully compromised.
This is achieved through a combination of:
- Client-side encryption — your vault key never leaves your device in plaintext
- AWS Nitro Enclave isolation — the cryptographic operations that enable delivery happen inside a hardware-attested enclave that the host OS cannot inspect
- Shamir's (2-of-3) threshold scheme — delivery requires co-operation; no single party holds a complete key
- KMS attestation gating — the enclave's KMS key policy rejects any process that cannot present a valid PCR0 attestation from the expected enclave image
The result is a system where even an Anthropic-style subpoena of our infrastructure, a full database dump, or a compromised EKS node cannot expose user secret values.
Zero-Knowledge Design
"Zero-knowledge" in this context means the Deadman Secrets servers hold zero information that would allow them to decrypt your secrets.
What the server stores
- Encrypted secret blobs: AES-256-GCM ciphertext + IV + authentication tag. Useless without the vault key.
- Wrapped vault key: Your vault key, itself encrypted with a key derived from your password. The server cannot derive this key.
- Auth hash: An Argon2id hash of your password (using a separate auth salt). Used only to verify login credentials, never to decrypt anything.
- Email (blind-indexed): Your email is stored as an HMAC-SHA256 digest under a server-side KMS-managed HMAC key. Used for lookups only; not reversible without the KMS key.
- Shamir shares (per-beneficiary): One share (out of three) of each secret's decryption key per beneficiary. Useless without a second share.
What the server never stores
- Your master password (in any form)
- Your vault key in plaintext
- Plaintext secret values
- Per-file decryption keys in plaintext
- Beneficiary Shamir shares (those are sent directly to the beneficiary's email)
System Diagram
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT (browser / mobile / extension) │
│ │
│ password ──[Argon2id + authSalt]──► authHash ──► API /login │
│ password ──[Argon2id + masterSalt]► derivedKey │
│ derivedKey + wrappedVaultKey ──[AES-256-GCM decrypt]──► │
│ vaultKey (in-memory only) │
│ secret ──[AES-256-GCM + vaultKey]──► ciphertext ──► API PUT │
└──────────────────────────────────┬──────────────────────────────┘
│ HTTPS + RFC 9421 HTTP Sig
┌──────────▼──────────────┐
│ API SERVER (EKS) │
│ • Stores ciphertext │
│ • Verifies JWT + sig │
│ • Checks deadlines │
│ • Forwards share ops │
│ to Enclave via vsock │
└──────────┬───────────────┘
│ vsock (AF_VSOCK)
┌──────────▼───────────────────────────┐
│ AWS NITRO ENCLAVE (cmd/enclave) │
│ • No network, no persistent storage │
│ • Attested EIF image (PCR0 pinned) │
│ • Calls KMS (attestation required) │
│ • Performs SSS split/combine │
│ • Wraps/unwraps share keys │
└──────────┬───────────────────────────┘
│ KMS API (TLS, attestation doc)
┌──────────▼───────────────────────────┐
│ AWS KMS (CMK) │
│ Key policy: allow only if │
│ PCR0 == expected enclave hash │
└──────────────────────────────────────┘
Key Derivation (Argon2id)
Deadman Secrets uses Argon2id (the winner of the Password Hashing Competition) for all password-based key derivation. Two separate derivations occur for each user:
Authentication hash
Parameters: m=65536 (64 MiB memory), t=3 iterations, p=1 parallelism,
output length 32 bytes, random 16-byte salt (authSalt) generated at registration.
The resulting hash is sent to the server as the login credential. The server stores it with bcrypt (cost factor 12) as a second layer. This means even a full database dump cannot be used to log in without breaking Argon2id first.
Vault key derivation
Parameters: same as above, with a separate random 16-byte masterKeySalt.
Output: a 32-byte AES-256 key used to unwrap the vault key.
The vault key is itself a random 32-byte AES-256 key generated at registration. It is wrapped (encrypted) with the derived key and stored on the server. The vault key exists in plaintext only in browser/device memory during an active session.
Encryption (AES-256-GCM)
All secret values are encrypted with AES-256-GCM (Galois/Counter Mode), which provides both confidentiality and integrity (authenticated encryption). A random 12-byte IV is generated for each encryption operation.
The encryption flow for a secret value:
plaintext + vaultKey + randomIV → [AES-256-GCM] → ciphertext || tag
The stored blob contains: IV (12 bytes) || ciphertext || GCM tag (16 bytes).
Decryption requires the vault key and will fail with an authentication error if the ciphertext has been tampered with.
File encryption
Files use a per-file AES-256 key (random, not derived from the vault key). The per-file key is encrypted with the vault key and stored as a "key wrapping" blob alongside the file metadata. The encrypted binary is stored in S3. This allows the file encryption key to be included in Shamir shares independently of the user's vault key.
Cryptographic implementation
Web: SubtleCrypto (browser native), AES-GCM algorithm, extractable: false keys where possible.
Mobile: @deadman/crypto-native package using the device's hardware-backed crypto primitives.
Both implementations share the same base64-encoded wire format for compatibility.
Shamir's Secret Sharing (2-of-3 Threshold)
When a secret is assigned to a beneficiary, the service generates three Shamir shares of the secret's decryption key using a (2-of-3) threshold scheme. Any two shares can reconstruct the key.
Share distribution
- Share 1 (Owner): Stored in the owner's account, encrypted under their vault key.
- Share 2 (Service): Encrypted under a per-secret KMS data key inside the Nitro Enclave and stored in DynamoDB.
- Share 3 (Beneficiary): Encrypted under a key derived from the beneficiary's email address and a random per-rule salt. Delivered to the beneficiary's email when triggered.
Why 2-of-3?
| Scenario | Can decrypt? |
|---|---|
| Beneficiary alone (receives email) | No — needs service share |
| Service alone (database compromised) | No — needs beneficiary share |
| Owner alone (account compromised) | No — needs service share |
| Owner + Service (legitimate delivery) | Yes — authorised use case |
| Beneficiary + Service (triggered delivery) | Yes — intended after deadline |
| Owner + Beneficiary (out-of-band) | Yes — owner controls this |
This scheme means that neither the service nor a rogue beneficiary can decrypt independently. The service's share is only provided by the Nitro Enclave after verifying the trigger conditions (deadline passed, grace period expired).
SSS implementation
Shamir operations run exclusively in cmd/enclave using internal/crypto/sss.go.
The API binary (cmd/server) never imports this package — a compile-time guarantee that
SSS logic cannot execute outside the enclave.
HTTP Message Signatures (RFC 9421)
Every authenticated API request requires two independent authentication proofs:
- A Bearer JWT — proves the session is valid
- An RFC 9421 HTTP Message Signature — proves the request was created by the registered device
The signature covers: @method, @path, @authority, authorization
(when present), content-type, content-digest (for POST/PUT), and a created
timestamp.
Key generation
On first login from any device, the client generates an Ed25519 key pair in the browser's
SubtleCrypto API (or device Secure Enclave on mobile). The private key is stored as
extractable: false in IndexedDB (web) or the platform keystore (mobile) — it cannot be
exported or exfiltrated by JavaScript.
The public key is registered via POST /api/v1/auth/keys. The server stores it with a
keyId that the client includes in the keyid parameter of subsequent signatures.
Pre-authentication signing
The challenge and login endpoints accept signed requests before a JWT is issued. The signature allows
the server to identify a returning device and skip CAPTCHA verification, even before the session
token exists. The authorization component is only included in the signed headers when
a JWT is present, making the same signing code valid for both pre-auth and authenticated requests.
Replay protection
Signatures include a created timestamp. The server rejects signatures older than 60 seconds
and maintains a short-lived nonce cache to reject exact duplicate replays within the window.
AWS KMS & Attestation Gating
The Deadman Secrets KMS Customer Managed Key (CMK) has a key policy that grants kms:Decrypt
and kms:GenerateDataKey only to requests that include a valid AWS Nitro Enclave attestation
document with a specific PCR0 value.
PCR0 (Platform Configuration Register 0)
PCR0 is the SHA-384 hash of the enclave image file (EIF) compiled from cmd/enclave.
It uniquely identifies the exact code that is running inside the enclave. Rebuilding the EIF with any
code change produces a different PCR0 and would fail the KMS key policy check.
The expected PCR0 value is stored in infra/pcr0.txt and referenced in the CDK KmsStack.
Updating the EIF requires updating this file and redeploying the KMS stack — a deliberate, auditable step.
What this means in practice
Even if an attacker gains full control of the EKS node running the API server, they cannot use the KMS key because they are not running inside an attested enclave. The data keys protecting Shamir shares in DynamoDB remain inaccessible.
AWS Nitro Enclave
The AWS Nitro Enclave (cmd/enclave) is an isolated process running inside a parent EC2 instance
that provides the following guarantees:
- No network: The enclave has no network interface. It communicates only via a vsock (virtual socket) with the parent instance.
- No persistent storage: The enclave has no disk. All state is in-memory and destroyed when the enclave process terminates.
- No interactive access: There is no SSH, console, or debug interface. Even the instance owner cannot inspect enclave memory.
- Cryptographic attestation: AWS generates signed attestation documents on demand, binding the running process image (PCR0–PCR8) to requests made to KMS.
Operations performed in the enclave
- Shamir share generation (splitting a secret key into 3 shares)
- Shamir share combination (reconstructing a key from 2 shares)
- KMS data key generation and decryption (for wrapping/unwrapping Shamir shares)
- Validation of delivery conditions before releasing the service share
vsock communication
The API server sends requests to the enclave over AF_VSOCK. The protocol is a simple JSON
RPC over the vsock stream. The vsock CID of the enclave is known only to the parent instance; no external
party can reach the enclave.
Transport Security
- All public endpoints are served over TLS 1.2+ (TLS 1.3 preferred). HTTP is redirected to HTTPS by CloudFront.
- The wildcard certificate (
*.deadmansecrets.com) is managed by AWS Certificate Manager with automatic renewal. - CloudFront enforces
SecurityPolicyProtocol.TLS_V1_2_2021(TLS 1.2 minimum, no RC4, no 3DES). - HSTS is configured with a one-year max-age and
includeSubDomains. - Internal vsock communication between the API server and enclave is physically isolated — it does not traverse any network interface.
Data Storage
DynamoDB
All application data is stored in a single DynamoDB table with partition key PK and sort key SK.
DynamoDB encryption at rest is enabled using AWS-managed keys (SSE-DynamoDB).
All state transitions (e.g. secret status changes) use ConditionExpression — unconditional writes are never used.
S3 (file attachments)
Encrypted file blobs are stored in a dedicated S3 bucket with:
- Block all public access enabled
- Server-side encryption (SSE-S3)
- SSL-only access enforced via bucket policy
- Pre-signed URLs for upload/download (30-minute expiry)
The files stored in S3 are already encrypted client-side with AES-256-GCM before upload. SSE-S3 is a second layer of defence.
Email addresses
User email addresses are stored as HMAC-SHA256 blind indexes under a KMS-managed symmetric key that never leaves AWS. The plaintext email is encrypted separately with a KMS-envelope encryption scheme and stored as a ciphertext blob. This allows lookup by email (using the blind index) without exposing email addresses to anyone with read access to DynamoDB.
Email Delivery
Transactional and delivery emails are sent via Amazon SES from a verified sending domain. Delivery emails (containing Shamir shares) are constructed by the enclave to ensure the share bytes are never accessible to the API server process.
Beneficiary shares embedded in delivery emails are encoded as base64 and are only meaningful in combination with the service's share (which the Nitro Enclave holds). A stolen email alone cannot decrypt anything.
SES DKIM and SPF records are configured on the sending domain. Delivery emails include a one-time-use token in the beneficiary portal URL that expires after 30 days.
Audit Logging
All security-relevant events are written to a tamper-evident audit log stored in DynamoDB. Events include:
- User login (timestamp, device keyId, IP)
- Check-in events
- Secret created, updated, deleted (no values — metadata only)
- Sharing rule created, updated, deleted
- Beneficiary portal access (timestamp, sharing rule)
- Delivery triggered / cancelled
- Device key registration / revocation
Audit log entries are append-only (no update or delete operations in the application code). Standard and Professional plan users can export their audit log as CSV or JSON from Account Settings.
Threat Model
| Threat | Severity | Mitigation |
|---|---|---|
| Database breach (DynamoDB dump) | Low | Encrypted blobs are useless without vault key. Email HMAC indexes are irreversible without KMS. Shamir shares require a second share to combine. |
| Compromised API server (EKS node) | Low | API server never holds vault keys or plaintext secrets. KMS access requires enclave attestation — a compromised API process cannot access enclave-held keys. |
| Compromised Nitro Enclave code | Medium | PCR0 attestation is pinned in the KMS key policy. Deploying modified enclave code produces a different PCR0 and loses KMS access. Requires explicit PCR0 rotation + CDK redeploy. |
| Rogue beneficiary attempts early access | Low | The Nitro Enclave validates delivery conditions before releasing its share. Beneficiary share alone is insufficient to decrypt. |
| Man-in-the-middle attack | Low | All traffic is TLS 1.2+. RFC 9421 request signatures bind the request content to the authenticated session — a replayed or modified request fails signature verification. |
| Phishing / account takeover | Medium | Attacker who gains JWT still cannot read secrets without the vault key, which requires the master password (never transmitted). Device signing key is hardware-backed and non-exportable. |
| Stolen device with active session | Medium | Mobile app locks on background. Vault key is in-memory only; not persisted to disk. Remote logout (JWT revocation) available from account settings. |
| Weak or reused master password | High (user) | Argon2id makes offline brute-force very expensive. Use a randomly generated password stored in a password manager. |
| Compromised beneficiary email account | Medium | Email compromise alone grants only the beneficiary's Shamir share. Service share is not released until delivery conditions are met. One-time-use delivery tokens expire after 30 days. |
| AWS account compromise | High | PCR0 gating remains in effect even for AWS account owners; the KMS policy is evaluated against the attestation document regardless of IAM permissions. Credential rotation and MFA are enforced on the AWS account. |
| Side-channel / timing attacks | Low | All cryptographic comparisons use constant-time algorithms. Secret metadata queries use opaque DynamoDB keys to avoid timing leaks on presence/absence. |
Security Assumptions
No security model is unconditional. The following assumptions underpin the guarantees above:
- Browser / OS integrity: The client device is assumed not to be running malicious software that could exfiltrate the vault key from memory. A keylogger on the client device is out of scope.
- AWS hardware integrity: Nitro Enclave security ultimately relies on AWS's hardware root of trust. We trust AWS's published enclave security claims and their documented PCR attestation guarantees.
- Correct PCR0 governance: The KMS key policy is only as strong as the process for updating
pcr0.txt. We maintain a strict review process for enclave code changes and PCR0 updates. - Email channel integrity: Delivery notifications and beneficiary share delivery use email, which is not itself end-to-end encrypted. Beneficiary email account security is the user's responsibility.
- Argon2id parameterisation: The memory-hard parameters chosen (64 MiB, 3 iterations) are designed to resist GPU-accelerated brute force. These should be reviewed as hardware advances.
Responsible Disclosure
We take security seriously and appreciate the work of security researchers. If you discover a security vulnerability in Deadman Secrets, please disclose it responsibly.
How to report
Email security@deadmansecrets.com with:
- A description of the vulnerability
- Steps to reproduce
- Potential impact
- Your proposed fix (if any)
Our commitments
- We will acknowledge your report within 48 hours.
- We will provide a substantive response within 7 days.
- We will not pursue legal action against researchers acting in good faith.
- We will credit you in our security changelog (if you wish).
Scope
In scope: app.deadmansecrets.com, api.deadmansecrets.com, the mobile app, the browser extension, and the open-source cryptography packages.
Out of scope: social engineering, physical access attacks, attacks on end-user devices not controlled by us.
Please do not use automated scanners against production. If you need to test, set up your own instance from the open-source repository. We are happy to provide guidance.