Skip to content

Security Properties

Yura Filatov edited this page Aug 8, 2026 · 3 revisions

Security Properties

Property Status Notes
Private key extraction ✅ Not possible SE-wrapped; operations performed inside SE chip
Private key at rest ✅ SE-wrapped in Keychain Protected by SE hardware key, not stored inside the SE
Key agreement (classical) ✅ ECDH P-256 Industry standard
Key agreement (PQ) ✅ Hybrid ECDH + ML-KEM-1024 NIST Level 5, SE-backed, iOS 26+
Session key derivation ✅ HKDF-SHA256 Domain-separated info strings per path
Content encryption ✅ AES-256-GCM Authenticated encryption, quantum-resistant
Nonce reuse ✅ Random per message Each seal call generates a fresh nonce
Bundle integrity ✅ AAD covers version + key-exchange fields Tampering causes GCM failure
Forward secrecy ✅ Per-message, v3fs Ephemeral + single-use prekeys; SE deletion on decrypt
FS quantum resistance ✅ Direct (PQ contacts) ML-KEM secrets mixed into every FS session key
FS quantum resistance ⚠️ Transitive only (classical contacts) Prekeys protected by classical encryption of delivery payload
Prekey confidentiality ✅ Encrypted in transit Public keys travel only inside AES-GCM ciphertext
Metadata leakage ✅ No contact identifiers on wire WirePrekey carries id + publicKey only
ML-KEM private key isolation ✅ Secure Enclave SE reference released after decapsulation, never persisted
ML-KEM secret storage ⚠️ Application-layer encrypted Stored in SwiftData under hybrid local DB key, not SE-wrapped
MITM during exchange ✅ Diceware verification + peer ID guard Human-verifiable, nonce-freshened
Proximity spoofing ✅ UWB hardware enforcement ≤ 0.25m threshold
Server-side exposure ✅ None No backend exists
Remote account takeover ✅ No attack surface No phone number, server account, or password
Identity re-verification ✅ Challenge-response Signed ECDSA challenge; proves SE key continuity without re-exchange
Backward compatibility ✅ Classical fallback v1 peers and iOS < 26 exchange classically, no breakage
Android interoperability ❌ Not supported iOS + Secure Enclave only
PIN app lock ✅ Implemented SE-bound AES-GCM verifier; constant-time 500ms gate; incremental lockout; grace period
Duress PIN / decoy view ✅ Static + live-protocol indistinguishability Visually identical at rest and behaviorally identical live — inbound bundles are never rejected because of restriction state, closing the accept/reject test a coercer could otherwise use to detect duress mode. Accepted residuals: a hidden contact's genuinely new message can still render on screen during a coercion window (decryption itself must not vary by depth); a contact created during duress before this protection existed can't be identified retroactively
Coercion-resistant toggle ✅ Implemented Gate lowers without removing verifiers; unknown PIN at depth > 0 silently creates new layer
Key rotation on activation ✅ Implemented 11-step atomic sequence; staged key; WAL-checkpoint ordering; rollback on failure
Layer store (sensitive contact recovery) ✅ Implemented 32-slot fixed-size AES-GCM file; exists from first launch; excluded from backup
Multi-layer duress ✅ Implemented Up to 32 nested layers; cold-start routing aliases; cascade deactivation
SQLite freed-page zeroing ✅ secure_delete = ON Set in DB header at launch; persists for all connections including SwiftData
Store file protection ✅ completeFileProtection Stamped on store, WAL, and SHM on every ModelContext save; excluded from backup

Clone this wiki locally