Documentation
KSMP (Kemet Secure Messaging Protocol)
Transparency-first protocol spec in a developer template format. This document is concrete about cryptographic/session/message behavior and explicit about what is intentionally withheld for adversarial safety.
0. Scope & Claims
Guarantees claimed
- End-to-end confidentiality for message and attachment plaintext.
- Cryptographic sender authentication at identity/device level.
- Forward secrecy for previously delivered messages.
- Post-compromise recovery after new clean ratchet traffic.
- Replay resistance via dedupe + ratchet state checks.
- Idempotent convergence for delivery/read transitions.
Explicitly not guaranteed
- Perfect metadata privacy (timing/routing metadata still exists).
- Protection against compromised endpoints with active malware.
- Undetectable communication patterns under global traffic analysis.
Assumptions
- Client honesty: compliant clients follow session persistence and no-downgrade rules.
- Server behavior: server may be curious or malicious but cannot forge valid ciphertext auth tags.
- Network: packets may be delayed, dropped, replayed, and reordered.
1. System Model
1.1 Entities
type IdentityId = string; // stable account id
type DeviceId = string; // per install (UUIDv4)
type ConversationId = string; // deterministic from participant set + scope
Identity:
- Long-lived identity keypair + account binding.
- Lifecycle: created at enrollment, revoked/rotated by policy event.
Device:
- One installation + one device key domain.
- Unique by DeviceId and device public key fingerprint.
Conversation:
- Logical thread binding participants and message sequence namespace.
- conversationId = H(canonicalParticipantSet || domain || version).Server role: stateful message store + relay. It is not trusted for plaintext integrity or confidentiality, but is authoritative for queue persistence and fan-out scheduling.
1.2 Trust boundaries
- Server can see: envelope routing IDs, timestamps, message type markers, encrypted object refs, size classes.
- Server can modify: delivery timing/order/drop/replay behavior, but not valid ciphertext content.
- If server is malicious: availability and ordering degrade; confidentiality/integrity still hold if clients enforce protocol checks.
2. Key & Identity Model
2.1 Identity keys
- Identity signing keypair: Ed25519 (long-lived).
- Identity agreement keypair: X25519 (long-lived).
- Stored in OS secure keystore (Secure Enclave / Android Keystore).
- Rotation allowed only via explicit rebind flow; old fingerprints remain auditable.
2.2 Device keys
- Per-device signing keypair and per-device agreement keypair.
- Generated locally from CSPRNG at device enrollment.
- Linked to identity via identity-signed device attestation record.
2.3 Prekeys
- Generated in batches of 100 signed one-time prekeys.
- Replenish when inventory < 25.
- Hard failure on depletion (no insecure fallback); sender retries after replenish.
2.4 Recovery / restore
- Restore on new device creates new DeviceId + new device key domain.
- Old sessions do not transfer unless encrypted session backup is explicitly restored.
- Peers observe a new device event and must establish fresh sessions.
3. Session Lifecycle
3.1 Session state object
type SessionState = {
sessionId: string;
conversationId: string;
localDeviceId: string;
remoteDeviceId: string;
rootKey: string; // persisted
sendChainKey: string; // persisted
recvChainKey: string; // persisted
sendIndex: number; // persisted
recvIndex: number; // persisted
previousChainLength: number; // persisted
skippedKeys: Record<string,string>; // persisted bounded map
remoteIdentityFingerprint: string; // persisted
status: "NEW"|"ACTIVE"|"DESYNCED"|"EXPIRED"|"REKEY_REQUIRED";
updatedAtMs: number;
};3.2 Session creation
Inputs:
- remote identity bundle
- remote device bundle
- one-time prekey + signed prekey
- local identity/device keys
API:
GET /ksmp/prekeys/:identityId/:deviceId
POST /ksmp/envelopes
Failure behavior:
- missing prekeys -> queue retryable error PREKEY_UNAVAILABLE
- identity mismatch -> hard fail IDENTITY_VERIFICATION_FAILED
- network failure mid-init -> keep local draft, retry init idempotently3.3 Session states + transitions
NEW -> ACTIVE (first successful decrypt/ack)
ACTIVE -> DESYNCED (ratchet/header mismatch threshold exceeded)
DESYNCED -> ACTIVE (successful rekey and decrypt)
ACTIVE -> EXPIRED (ttl or explicit peer rekey event)
EXPIRED -> REKEY_REQUIRED (send attempt or receive re-init trigger)
REKEY_REQUIRED -> ACTIVE (new bootstrap success)3.4 Session breakage handling
- Repeated decrypt failure (>= 5 consecutive): move to `DESYNCED`, request rekey.
- Invalid header schema/signature: reject envelope, security-log event, no state advance.
- Ratchet mismatch beyond skipped-key window: hold message pending re-establishment.
4. Message Model
4.1 Message object (wire shape)
type KsmpMessage = {
version: "2.x";
messageId: string; // ULID
dedupeId: string; // BLAKE3(conversationId|senderDeviceId|sendIndex|ciphertextHash)
conversationId: string;
senderIdentityId: string;
senderDeviceId: string;
recipientIdentityId: string;
recipientDeviceId?: string;
type: "text"|"media"|"system"|"receipt";
ratchetHeader: {
dhPub: string;
pn: number;
n: number;
};
ciphertext: string; // base64url
attachments?: AttachmentRef[];
sentAtMs: number;
};
Limits:
text plaintext <= 16 KiB
total encrypted envelope <= 64 KiB4.2 Encryption flow
- Encrypted: message body, attachment metadata, app-level semantic fields.
- Plaintext: routing identifiers, protocol version, ratchet header, dedupe/materialized IDs.
- Authenticated (AEAD AD): immutable envelope header + version + conversationId.
4.3 Dedupe / replay
- `dedupeId` is deterministic from ratchet position and ciphertext digest context.
- Stored in local replay cache and durable message index.
- Retention: 30 days active cache + persisted historical index keyed by messageId.
5. Send Pipeline
onSend(userInput):
1) create local draft message object
2) assign messageId (ULID)
3) resolve session (or bootstrap)
4) encrypt payload + build envelope + dedupeId
5) persist encrypted outbound record (PENDING_SEND)
6) enqueue transport job
7) POST envelope
8) on 2xx mark SENT and emit delivery wait stateCrash behavior by stage
- Crash before persist: message is lost unless UI draft persisted separately.
- Crash after persist before send: job resumes from outbound queue on restart.
- Crash after send before ack apply: idempotent resend using same `messageId` + `dedupeId`.
6. Receive Pipeline
onEnvelope(env):
1) schema validate envelope
2) dedupe check (fast cache then durable index)
3) resolve session by (conversationId, senderDeviceId)
4) derive receive key from ratchetHeader
5) decrypt + authenticate
6) begin transaction
7) apply message + advance ratchet + store dedupe marker
8) commit
9) emit receipt if policy allowsFailure behavior
- Cannot decrypt: quarantine envelope + trigger rekey path.
- Session missing: attempt bootstrap-from-inbound, else hold pending.
- Duplicate message: no-op, may re-emit ack idempotently.
- Corrupted payload: drop, log tamper event, never render.
7. Ordering & Consistency
Ordering guarantee: no global strict order guarantee. KSMP guarantees causal-safe processing with out-of-order tolerance.
For arrival `5 -> 2 -> 4 -> 3 -> 1`: receiver applies decryptable messages, buffers missing ratchet positions within skipped-key bound, and converges once missing links arrive or rekey path completes.
8. Multi-Device Model
8.1 Fan-out
Encryption is per recipient device, not once per user.
8.2 Sync
- Messages sync by envelope fan-out queue and per-device acks.
- Read/delivery state sync by monotonic receipt events keyed by messageId.
- Receipts are idempotent and causally bounded.
8.3 Conflicts
- Concurrent read markers: highest monotonic `readAtMs` wins.
- Simultaneous sends: both accepted; display order uses server receive timestamp + tiebreak messageId.
9. Receipts
Delivered:
emitter: recipient device
trigger: message decrypted + committed locally
reference: messageId + senderDeviceId
Read:
emitter: recipient device
trigger: user-visible read transition
reference: messageId (or up-to watermark by conversation)
Idempotency:
receiptKey = H(type|messageId|deviceId)
duplicate receiptKey => no-op10. Attachments
Upload
- File encrypted client-side before upload.
- Attachment content key encrypted inside message plaintext metadata.
- Envelope references objectRef + digest + byteLength + mime.
Download
- Client fetches encrypted object, decrypts locally, verifies digest/size.
- Digest mismatch => reject render, mark attachment integrity failure.
Limits
- Max attachment size: 25 MiB per item.
- Max attachments per message: 8.
- Default media URL expiry: 15 minutes per signed access token.
11. Storage Model
Client must store
- Identity keys, device keys, and trust/fingerprint records.
- Session states and skipped-key cache.
- Outbound pending queue and dedupe cache.
- Message index + receipt state + attachment integrity metadata.
If lost
- Without keys/session state: historical undecryptable messages remain opaque.
- Recovery path is new-device enrollment + new session establishment.
12. Retries & Delivery
- Retry on network/5xx/timeout with exponential backoff + jitter.
- Schedule: 1s, 2s, 4s, 8s, 16s ... capped at 15m between attempts.
- Max retry attempts per message: 50 before terminal `DELIVERY_EXPIRED`.
- Undelivered TTL: 7 days default for pending outbound records.
13. Versioning
- Version stored in every envelope (`version` field).
- Newer unsupported major: reject + emit `UNSUPPORTED_PROTOCOL_VERSION`.
- Older but allowed minor/patch: accept in compatibility mode.
14. Protocol Limits
maxMessagePlaintextBytes = 16384
maxEnvelopeBytes = 65536
maxAttachmentsPerMessage = 8
maxAttachmentBytes = 25 * 1024 * 1024
maxDevicesPerIdentity = 10
maxSkippedKeys = 2000
maxRetryAttempts = 5015. Security Edge Cases
- Device compromised: mark device untrusted, revoke device keys, force re-establish with new device material.
- Keys leaked: rotate affected key domain, invalidate sessions, require peer trust revalidation.
- Server replay: dedupe + ratchet window prevents state rollback or duplicate render.
- Flooding: envelope queue guards and bounded processing; anti-abuse internals intentionally not disclosed here.
16. Real Flows
Case 1: First message ever
A gets B prekeys -> A bootstraps session -> A sends encrypted envelope
B decrypts, initializes session, commits message, sends delivered receiptCase 2: Receiver offline 3 days
Server queues envelopes -> B reconnects -> pulls backlog -> dedupe/decrypt/apply in order-safe loop
receipts emitted idempotently for committed messagesCase 3: Out-of-order arrival
Receive n+4 first -> skipped-key handling attempts bounded derivation
buffer until missing links arrive or rekey path resolvesCase 4: Device restored
New device enrolled -> new DeviceId + keys
peers fan-out to new device after trust update; old non-backed-up sessions do not transferCase 5: Session broken mid-conversation
Consecutive decrypt failures -> DESYNCED
trigger rekey bootstrap -> mark failed envelopes pending -> resume ACTIVE after successful decrypt17. Implementation Checklist (Strict)
- If client does not persist ratchet state atomically, it is broken.
- If client allows insecure fallback when prekeys are absent, it is broken.
- If client does not dedupe before apply, it is broken.
- If client accepts protocol downgrade weakening guarantees, it is broken.
- If client cannot recover from crash without duplicate sends/applies, it is broken.
- If client renders attachments without digest verification, it is broken.
Disclosure Line (Deliberate)
This spec publishes the cryptographic and state-machine contract. It does not publish anti-abuse scoring internals, exploit-sensitive thresholds, or operational topology details that directly improve attacker optimization.
Status: KSMP transparency spec draft v2. Built for technical humans and implementers-in-waiting, without publishing attacker-optimization internals.