Forward Secrecy
Compromising today's keys does not reveal yesterday's messages.
What Forward Secrecy Means
Forward secrecy (also called perfect forward secrecy, PFS) is a property of a cryptographic protocol where compromise of long-term secret keys does not compromise past session keys. In practical terms: if an attacker obtains your private keys right now, they cannot decrypt messages you sent or received before this moment.
This stands in contrast to protocols without forward secrecy. Consider PGP email encryption: if your PGP private key is compromised, every message ever encrypted to that key becomes readable. Past, present, and future. A single key compromise is a total compromise.
RVNT provides forward secrecy at multiple levels through the Double Ratchet algorithm. Key material is continuously generated, used once, and deleted. The window of exposure for any single key compromise is exactly one message.
How the Ratchet Provides Forward Secrecy
Level 1: Message Key Deletion
Every message is encrypted with a unique message key. After encryption (sender) or decryption (recipient), the message key is securely zeroed from memory. It is never written to disk. It cannot be reconstructed.
Timeline of a message key's existence:
t=0 chain_key → HMAC-SHA256 → message_key
t=1 message_key used to encrypt message
t=2 secure_zero(message_key) ← key is gone forever
t=3 message_key does not exist in RAM, disk, or anywhere
Even if an attacker captures a full memory dump at t=3,
they cannot find message_key. It has been overwritten. Level 2: Chain Key Advancement
The chain key advances after each message via a one-way function. Given chain_key[n+1], it is computationally infeasible to derive chain_key[n] or any previous chain key.
chain_key[0] → chain_key[1] → chain_key[2] → chain_key[3] → ...
│ │ │ │
msg_key[0] msg_key[1] msg_key[2] msg_key[3]
(deleted) (deleted) (deleted) (current)
Forward direction (trivial):
chain_key[3] → chain_key[4] → ...
Backward direction (infeasible):
chain_key[3] → chain_key[2] ✗ IMPOSSIBLE
chain_key[3] → msg_key[0] ✗ IMPOSSIBLE
The chain key function is HMAC-SHA256, which is a PRF.
PRFs are not invertible. Knowing the output does not reveal the input. Level 3: DH Ratchet (Break-In Recovery)
The DH ratchet generates entirely new key material when the conversation direction changes. Even if an attacker compromises the current chain key, the next DH ratchet step produces a new chain key that the attacker cannot derive.
State at time of compromise:
Attacker knows: chain_key[current], root_key[current]
Attacker can: decrypt remaining messages in current chain
DH Ratchet step occurs (other party replies):
new_dh_private = X25519.generate() // attacker doesn't know this
dh_output = DH(new_dh_private, remote_pub)
root_key[new], chain_key[new] = HKDF(root_key, dh_output)
Attacker cannot compute:
- new_dh_private (generated randomly, never transmitted)
- dh_output (requires new_dh_private)
- root_key[new] (requires dh_output)
- chain_key[new] (requires root_key[new])
Result: The session "heals" after the next DH ratchet step.
The attacker's window of access is closed. Concrete Example
Day 1: Alice and Bob exchange 50 messages
50 unique message keys generated and deleted
5 DH ratchet steps performed
Day 2: Attacker compromises Alice's device
Attacker obtains current ratchet state:
- root_key (current)
- send_chain_key (current)
- recv_chain_key (current)
- dh_self (current keypair)
What attacker CAN decrypt:
✓ Messages in the current sending chain (not yet ratcheted)
✓ Messages in the current receiving chain (not yet ratcheted)
What attacker CANNOT decrypt:
✗ Any of the 50 messages from Day 1
(message keys deleted, chain keys advanced, DH keys deleted)
✗ Previous chain keys (one-way function)
✗ Previous root keys (one-way function + deleted DH keys)
What happens next:
Day 3: Bob sends a reply → DH ratchet step
New root_key and chain_key derived from fresh DH
Attacker's compromise is healed
Even future messages become unreadable to the attacker Key Deletion Implementation
Forward secrecy is only as strong as the key deletion mechanism. If "deleted" keys persist in memory, swap, or disk, forward secrecy is undermined.
Memory Zeroing
RVNT uses the zeroize crate (Rust) for secure key deletion:
use zeroize::{Zeroize, ZeroizeOnDrop};
// [derive ZeroizeOnDrop]
struct MessageKey([u8; 32]);
// When MessageKey is dropped (goes out of scope), the zeroize
// implementation overwrites the 32 bytes with zeros using
// volatile writes and a compiler barrier.
// Volatile writes prevent the compiler from optimizing away
// the zeroing operation (dead-store elimination).
// The compiler barrier (core::sync::atomic::compiler_fence)
// prevents instruction reordering across the barrier. Memory Locking
// On platforms that support it, key material pages are mlock'd
// to prevent them from being swapped to disk:
// [cfg unix]
unsafe {
libc::mlock(key_ptr as *const libc::c_void, key_len);
}
// This ensures the key material stays in RAM and is never
// written to the swap file/partition, where it could persist
// after being "deleted" from application memory. Database Key Handling
Ratchet state stored in SQLCipher database:
1. Database encrypted with Argon2id-derived key
2. Old chain keys are overwritten (not just deleted) when advanced
3. SQLCipher uses PRAGMA secure_delete = ON
(zeroes deleted data pages instead of just marking them free)
4. Message keys are NEVER stored in the database
(they exist only in memory, for the duration of one encrypt/decrypt) Comparison: Forward Secrecy Across Systems
| System | Forward Secrecy | Granularity | Break-In Recovery |
|---|---|---|---|
| PGP/GPG | None | N/A | None (key compromise = total compromise) |
| TLS 1.3 | Per-session | Session (may be hours) | Per-session (new handshake) |
| Signal Protocol | Per-message | Single message | Per DH ratchet step |
| RVNT | Per-message | Single message | Per DH ratchet step + PQ hybrid |
What Forward Secrecy Does Not Protect Against
- Real-time surveillance: If an attacker is reading your messages as you send them (e.g., malware on your device), forward secrecy is irrelevant. They see the plaintext before it is encrypted.
- Recipient retention: Forward secrecy protects keys and transport. It does not prevent the recipient from storing, forwarding, or screenshotting decrypted messages.
- Metadata: Forward secrecy protects message content. Metadata (who, when, how often) requires separate protection (sealed sender, Tor, cover traffic).
- Backup compromise: If you back up your database and the backup is later compromised, the attacker has the database key and all stored messages. RVNT does not create cloud backups for this reason.
Further Reading
- Double Ratchet -- Technical deep dive into the ratchet mechanism
- Protocol Specification -- KDF chain functions and state management
- Panic Mode -- Emergency key destruction for immediate forward secrecy