Skip to main content

Threat Model - Signal Protocol Implementation

Overview

Comprehensive threat modeling of the Signal Protocol Rust/WASM implementation, analyzing adversary capabilities, attack surfaces, and security properties.

Analysis Date: January 2025 Implementation: Rust with WASM bindings Risk Assessment: 🟡 MEDIUM (Functional with specification gaps)


Executive Summary

The Signal Protocol implementation provides strong cryptographic protection against most adversaries but has specification deviations that introduce attack vectors not present in reference implementations.

Key Threat Findings:

  • Protected: Passive network adversaries (strong encryption)
  • 🟡 Partial: Active network adversaries (missing AAD enables attacks)
  • 🟡 Partial: Malicious peers (missing signed prekey verification)
  • Protected: Compromised endpoints (forward secrecy + PCS)
  • 🟡 Partial: Side-channel adversaries (timing leaks possible)

Critical Vulnerabilities:

  1. Message reordering attacks (AAD not used)
  2. Man-in-the-middle on X3DH (no signed prekey verification)
  3. Interoperability failures (non-standard HKDF)

Adversary Model

1. Passive Network Adversary

Capabilities:

  • Monitor all network traffic
  • Record encrypted messages
  • Analyze metadata (size, timing, frequency)
  • Perform traffic analysis
  • Cannot modify or inject messages

Protection Status:

AttackProtectionEvidence
Decrypt messages✅ ProtectedAES-256-GCM encryption
Derive session keys✅ ProtectedX25519 ECDH (128-bit security)
Break forward secrecy✅ ProtectedDH ratchet per message direction
Identify participants⚠️ Metadata visibleApplication-layer responsibility
Traffic pattern analysis⚠️ PossibleMessage sizes observable
Statistical analysis⚠️ PossibleTiming patterns observable

Risk Level: 🟢 LOW - Core confidentiality protected

Attack Complexity: ~2^128 operations (computationally infeasible)


2. Active Network Adversary

Capabilities:

  • All passive capabilities
  • Modify messages in transit
  • Inject malicious messages
  • Drop or delay messages
  • Replay old messages
  • Perform man-in-the-middle attacks

Protection Status:

AttackProtectionEvidence
Modify ciphertext✅ ProtectedGCM authentication tag
Forge messages✅ ProtectedEd25519 signatures (signed prekey)
Reorder messagesVulnerableAAD not used
Replay messages🟡 PartialSkipped key tracking (limited)
Drop messages⚠️ DetectableApplication can detect missing messages
MITM on X3DHVulnerableSigned prekey not verified
Downgrade attacks✅ ProtectedFixed ciphersuite

Risk Level: 🔴 HIGH - Two critical vulnerabilities

Critical Attack Vectors:

  1. Message Reordering (enabled by missing AAD)
  2. X3DH MITM (enabled by no signed prekey verification)

3. Malicious Peer

Capabilities:

  • Legitimate participant in protocol
  • Can initiate X3DH key agreement
  • Can send encrypted messages
  • Knows session secrets
  • May have compromised keys
  • Attempts to exploit implementation bugs

Protection Status:

AttackProtectionEvidence
Decrypt other sessions✅ ProtectedPer-session keys (isolation)
Forge messages from others✅ ProtectedEd25519 signatures
Impersonate identity✅ ProtectedIdentity key binding
Cause denial of service⚠️ LimitedSkipped key limit (1000)
Exploit implementation bugs⚠️ One panicsimple_ecdh panic issue
Interoperability attack⚠️ PossibleNon-standard HKDF parameters

Risk Level: 🟡 MEDIUM - Limited attack surface


4. Compromised Endpoint

Capabilities:

  • Full access to device memory
  • Read current session state
  • Read private keys
  • Read plaintext messages
  • Exfiltrate all current data
  • Cannot access other devices
  • Cannot access past messages (if deleted)

Protection Status:

AttackProtectionEvidence
Past messagesProtectedForward secrecy (DH ratchet)
Current messages❌ No protectionExpected behavior
Future messagesProtectedPost-compromise security (PCS)
Identity key theft❌ No protectionExpected behavior
Session key theft❌ No protectionExpected behavior
Recovery after compromiseAutomaticNext DH ratchet step heals

Risk Level: 🟡 MEDIUM - Inherent to end-to-end encryption

Time Window:

  • Past messages: ✅ Safe (deleted message keys not recoverable)
  • Current epoch: ❌ Compromised
  • After next DH ratchet: ✅ Recovered (PCS property)

Recovery Time: Single message round-trip (immediate PCS)


5. Side-Channel Adversary

Capabilities:

  • Measure operation timing
  • Analyze power consumption (physical access)
  • Monitor cache behavior
  • Analyze memory access patterns
  • Exploit CPU microarchitectural features

Protection Status:

AttackProtectionEvidence
Timing attacks on cryptoProtecteddalek constant-time libs
Timing attacks on comparisons⚠️ VulnerableNon-constant-time ==
Power analysis🟡 PartialPlatform dependent
Cache timing⚠️ VulnerableHashMap lookups
Spectre/Meltdown✅ ProtectedBrowser mitigations
Branch prediction🟡 Partialdalek protects crypto

Risk Level: 🟡 MEDIUM - Practical attacks difficult but possible

Attack Complexity: High (requires physical/local access + sophisticated tools)


Attack Surface Analysis

External Input Points

Entry PointRiskValidationIssues
x3dh_initiate()🔴 High⚠️ PartialNo signed prekey verify
x3dh_receive()🔴 High⚠️ PartialNo signed prekey verify
ratchet_encrypt()🟡 Medium✅ GoodAAD not used
ratchet_decrypt()🟡 Medium✅ GoodAAD not used
deserialize_*()🟡 Medium✅ GoodSerde validation

Total Attack Surface: 5 major input points

Validation Quality:

  • ✅ Length validation present
  • ✅ Type validation via serde
  • ❌ Cryptographic validation incomplete (signatures not checked)
  • ⚠️ AAD missing (metadata not authenticated)

Attack Scenarios & Mitigations

Scenario 1: Message Reordering Attack

Severity: 🔴 CRITICAL

Enabled By: AAD not used in AES-GCM encryption

Attack Flow:

1. Alice sends messages: M1(seq=1), M2(seq=2), M3(seq=3)
2. Attacker intercepts all three messages
3. Attacker delivers in wrong order: M2, M1, M3
4. Bob decrypts successfully (no binding to sequence)
5. Application processes messages out-of-order
6. Potential security bypass depending on application logic

Current Protection: ❌ None

Impact:

  • HIGH: Commands executed out-of-order
  • State machine confusion possible
  • Access control bypass in applications
  • Data corruption if order matters

Example Attack:

Message 1: "Transfer $100 to account A"
Message 2: "Transfer $100 to account B"
Message 3: "Cancel previous transfer"

Reordered:
Message 2, Message 3, Message 1
→ Result: Transfer to B cancelled, transfer to A executes

Mitigation:

// Add AAD with message metadata
let mut aad = Vec::new();
aad.extend_from_slice(&message_number.to_le_bytes());
aad.extend_from_slice(&prev_chain_length.to_le_bytes());

let payload = Payload {
msg: plaintext,
aad: &aad,
};

cipher.encrypt(nonce, payload)?;

Effort: 4-6 hours Priority: P0 (critical)


Scenario 2: Man-in-the-Middle on X3DH

Severity: 🔴 CRITICAL

Enabled By: Signed prekey signature never verified

Attack Flow:

1. Alice requests Bob's prekey bundle
2. Server (or MITM) substitutes Bob's signed prekey with attacker's
3. Alice completes X3DH with attacker's key (doesn't verify signature)
4. Attacker completes X3DH with Bob using Bob's real key
5. Attacker acts as transparent proxy, decrypting all messages

Current Protection: ❌ None

Impact:

  • CRITICAL: Complete loss of confidentiality
  • No authentication of Bob's identity
  • Attacker can read/modify all messages
  • Defeats purpose of end-to-end encryption

Signal Specification Requirement:

"The client MUST verify the signature on the signed prekey before using it"

Mitigation:

pub fn x3dh_initiate(
alice_identity: &IdentityKeyPair,
alice_ephemeral: &EphemeralKeyPair,
bob_identity_public: &[u8],
bob_signed_prekey_public: &[u8],
bob_signed_prekey_signature: &[u8], // Add parameter
bob_onetime_prekey_public: Option<&[u8]>,
) -> Result<X3DHResult, SignalError> {

// ✅ CRITICAL: Verify signature before using key
if !ed25519_verify(
bob_identity_public,
bob_signed_prekey_public,
bob_signed_prekey_signature
)? {
return Err(SignalError::InvalidSignature);
}

// Rest of X3DH...
}

Effort: 1-2 hours Priority: P0 (critical)


Scenario 3: Replay Attack

Severity: 🟡 MEDIUM

Enabled By: Limited replay protection

Attack Flow:

1. Attacker captures encrypted message M
2. Days/weeks later, attacker replays M
3. Receiver attempts decryption
4. Success depends on key state:
- If in-order: Message already processed, key deleted → fails ✅
- If out-of-order: Key might still be in skipped_keys → succeeds ❌

Current Protection: 🟡 Partial

  • In-order messages: Protected (key deleted after use)
  • Out-of-order messages: Window of ~1000 messages

Impact:

  • MEDIUM: Old messages could be replayed within window
  • Application may process duplicate messages
  • Depends on application-level duplicate detection

Mitigation:

// Application-level sequence tracking
struct SessionState {
highest_received_seq: u32,
received_messages: HashSet<u32>,
}

fn validate_message_number(seq: u32, state: &SessionState) -> bool {
if received_messages.contains(&seq) {
return false; // Replay detected
}
true
}

Effort: Application responsibility Priority: P2 (application layer)


Scenario 4: Interoperability Attack

Severity: 🟡 MEDIUM

Enabled By: Non-standard HKDF parameters

Attack Flow:

1. User Alice uses this implementation
2. User Bob uses official Signal app (libsignal)
3. Alice and Bob attempt X3DH key agreement
4. Different HKDF derivations produce different shared secrets
5. Session establishment fails
6. Or worse: Appears to succeed but messages undecryptable

Current Protection: ❌ None

Impact:

  • MEDIUM: Cannot communicate with standard Signal apps
  • Isolated ecosystem (vendor lock-in)
  • Potential confusion/misconfiguration attacks
  • Users may think they have E2EE when they don't

Mitigation:

// Use EXACT Signal specification parameters
fn derive_x3dh_secret(dh1: &[u8], dh2: &[u8], dh3: &[u8], dh4: Option<&[u8]>) -> Vec<u8> {
let mut km = Vec::new();
km.extend_from_slice(&[0xFF; 32]); // F (padding)
km.extend_from_slice(dh1);
km.extend_from_slice(dh2);
km.extend_from_slice(dh3);
if let Some(dh4) = dh4 {
km.extend_from_slice(dh4);
}

// Standard Signal HKDF usage
let hk = Hkdf::<Sha256>::new(None, &km); // No salt
let mut output = vec![0u8; 32];
hk.expand(b"WhisperText", &mut output).expect("HKDF failed");
output
}

Effort: 2-3 hours Priority: P1 (high)


Scenario 5: Denial of Service via Skipped Keys

Severity: 🟡 MEDIUM

Enabled By: Bounded but potentially abusable skipped key storage

Attack Flow:

1. Attacker sends message with seq=1
2. Attacker sends message with seq=1001
3. Victim stores 1000 skipped keys (hits MAX_SKIP)
4. Attacker repeats, filling memory
5. Legitimate out-of-order messages may be rejected

Current Protection:Present

  • MAX_SKIP = 1000 (bounded)
  • Old keys eventually evicted

Impact:

  • LOW: Limited memory exhaustion (bounded)
  • Potential rejection of legitimate messages
  • Annoyance but not catastrophic

Mitigation:

  • ✅ Already implemented: MAX_SKIP limit
  • Could add: Per-peer rate limiting
  • Could add: Adaptive skip window

Priority: P3 (low - already mitigated)


Scenario 6: Timing Side-Channel Attack

Severity: 🟡 MEDIUM

Enabled By: Non-constant-time comparison operations

Attack Flow:

1. Attacker with local access measures decryption timing
2. Timing varies based on:
- Message found in skipped_keys vs. not
- Error types (different codepaths)
- Success vs. failure branches
3. Over many samples, timing leaks information
4. Attacker infers message patterns or state

Current Protection: 🟡 Partial

  • Core crypto operations constant-time (dalek)
  • Application logic not constant-time

Impact:

  • LOW-MEDIUM: Requires local access
  • Practical exploitation difficult
  • Information leakage limited

Mitigation:

use subtle::ConstantTimeEq;

// Replace regular comparisons
if key1 == key2 { // ⚠️ Not constant-time
// ...
}

// With constant-time comparisons
if bool::from(key1.ct_eq(&key2)) { // ✅ Constant-time
// ...
}

Effort: 2-3 hours Priority: P2 (medium)


Security Properties Matrix

PropertyPassive NetActive NetMalicious PeerCompromised DeviceSide-Channel
Confidentiality✅ Protected✅ Protected✅ Protected❌ No protection✅ Protected
Integrity✅ Protected⚠️ Partial✅ Protected❌ No protection✅ Protected
Authentication✅ Protected❌ Vulnerable✅ Protected❌ No protection✅ Protected
Forward Secrecy✅ Protected✅ Protected✅ Protected✅ Protected✅ Protected
PCS✅ Protected✅ Protected✅ Protected✅ Protected✅ Protected
Message OrderingN/A❌ Vulnerable❌ VulnerableN/AN/A
Replay ProtectionN/A🟡 Partial🟡 PartialN/AN/A
Deniability✅ Protected✅ Protected✅ Protected✅ Protected✅ Protected

Risk Assessment Summary

By Adversary Type

AdversaryRisk LevelPrimary ThreatsStatus
Passive Network🟢 LOWTraffic analysisAcceptable
Active Network🔴 HIGHMITM, reorderingRequires fixes
Malicious Peer🟡 MEDIUMDoS, exploitsAcceptable
Compromised Device🟡 MEDIUMCurrent keysInherent limitation
Side-Channel🟡 MEDIUMTiming leaksMonitoring needed

By Attack Category

Attack TypeLikelihoodImpactRiskPriority
Message ReorderingHighHigh🔴 CRITICALP0
X3DH MITMMediumCritical🔴 CRITICALP0
Replay AttackLowMedium🟡 MEDIUMP2
Interoperability FailureHighMedium🟡 MEDIUMP1
DoS (Skipped Keys)LowLow🟢 LOWP3
Timing AttacksLowMedium🟡 MEDIUMP2

Defense in Depth

Layer 1: Cryptographic (✅ STRONG)

  • X25519 ECDH key exchange
  • Ed25519 digital signatures
  • AES-256-GCM authenticated encryption
  • HKDF-SHA256 key derivation
  • OS CSRNG for random numbers

Layer 2: Protocol (🟡 MODERATE)

  • X3DH for asynchronous key agreement
  • Double Ratchet for forward secrecy + PCS
  • Out-of-order message handling
  • ⚠️ Missing: AAD in encryption
  • ⚠️ Missing: Signed prekey verification

Layer 3: Implementation (✅ STRONG)

  • ✅ Rust memory safety
  • ✅ Zero unsafe blocks
  • ✅ WASM sandboxing
  • ✅ Type safety across boundaries
  • ⚠️ One panic issue

Layer 4: Operational (⚠️ PARTIAL)

  • ⚠️ No rate limiting
  • ⚠️ No monitoring/alerting
  • ⚠️ No audit logging
  • ⚠️ Limited error messages (good for security)

Recommendations

Critical (P0) - Address Immediately

  1. Implement AAD in AES-GCM encryption

    • Prevents message reordering attacks
    • Effort: 4-6 hours
  2. Add signed prekey verification in X3DH

    • Prevents MITM attacks
    • Effort: 1-2 hours

High (P1) - Within 2 Weeks

  1. Fix HKDF parameters for interoperability

    • Use Signal specification exactly
    • WARNING: Breaks existing sessions
    • Effort: 6-8 hours
  2. Add constant-time comparisons

    • Use subtle crate for sensitive data
    • Prevents timing side-channels
    • Effort: 2-3 hours

Medium (P2) - Within 1 Month

  1. Fix simple_ecdh panic issue

    • Validate lengths before copy
    • Effort: 15 minutes
  2. Add application-level replay protection

    • Track sequence numbers
    • Effort: Application responsibility

Low (P3) - Nice to Have

  1. Add monitoring and alerting

    • Track attack attempts
    • Effort: 1-2 days
  2. Add audit logging

    • Security events only (no secrets)
    • Effort: 1-2 days

Comparison with MLS Threat Model

AspectSignal ProtocolMLS Protocol
Group size1:1 optimalMulti-party optimal
Active adversary risk🔴 HIGH (2 critical issues)🔴 HIGH (different issues)
Replay protection🟡 Partial❌ None
Message ordering❌ Not enforced❌ Not enforced
Forward secrecy✅ Per-message✅ Per-epoch
PCS✅ Immediate✅ Next commit
Implementation quality🟡 Specification gaps🟡 Missing safeguards

Common Issues:

  • Both lack proper replay protection
  • Both have specification compliance gaps
  • Both need operational security improvements

Unique to Signal:

  • Missing signed prekey verification
  • Missing AAD in encryption

Unique to MLS:

  • No input validation
  • Extensive debug logging

Conclusion

Overall Threat Risk: 🟡 MEDIUM-HIGH

Key Findings:

  • ✅ Strong cryptographic foundation
  • ✅ Excellent memory safety (Rust)
  • ❌ Two critical protocol-level vulnerabilities
  • ⚠️ Specification deviations affect security

Attack Surface:

  • External: 5 input points with partial validation
  • Internal: 1 panic vulnerability
  • Cryptographic: Solid (no weaknesses)
  • Protocol: Gaps in specification compliance

Biggest Risks:

  1. Message reordering attacks (AAD not used)
  2. X3DH man-in-the-middle (no signature verification)
  3. Interoperability failures (non-standard HKDF)

Recommendation: Address P0 issues before production deployment. Current implementation is functional but vulnerable to active adversaries exploiting specification deviations.

Time to Secure: ~8-12 hours of development to fix critical issues


Document Version: 1.0 Last Updated: January 2025 Next Review: After P0/P1 fixes implemented