Security Architecture
Key Rotation
Overview
Semafore uses two key rotation mechanisms to enforce periodicity and replenish ephemeral key material. Both are asynchronous and driven by the device, not forced by the server.
Signed Pre-Key (SPK) Rotation
Trigger
The SPK is rotated when it exceeds SPK_MAX_AGE_HOURS (default: 168 hours, or 7 days) in age. The device is responsible for tracking the SPK’s age and initiating rotation. There is no server-enforced automatic revocation.
Procedure
When rotation is triggered:
- Device generates new key pair: The device creates a new Curve25519 key pair
- Device signs the public key: Using its Identity Key private key, the device signs the new SPK public key with Ed25519
- Device uploads via HTTP: The device calls
PUT /api/mobile/key-bundles/{deviceID}/signed-pre-keywith the new public key and signature - Server stores and retains old key: The server stores the new SPK as the current active key and retains the previous SPK in the database for a grace period
Grace Period
For SPK_GRACE_PERIOD_HOURS (default: 168 hours, or 7 days) after rotation, the previous SPK remains available in the server’s key bundle responses. This grace period serves a critical purpose:
In-flight sessions: If a sender initiated an X3DH session with the recipient’s old SPK just before rotation occurred, the sender’s first message will reach the recipient using that old SPK in the header. The recipient’s Double Ratchet will process the message correctly because the SPK used in the X3DH computation is still available. Without the grace period, recipients would reject valid messages that used the previous SPK.
After the grace period expires, the previous SPK is deleted and no longer returned in key bundle lookups.
Expiry Enforcement
If a device fails to rotate its SPK and it exceeds SPK_MAX_AGE_HOURS, the server will reject new session initiations from other users to that device:
- Response: HTTP 428 Precondition Required
- Error code:
spk_expired - Device notification: The server sends a
key_bundle.spk_expiredWebSocket event to the device owner, indicating the device ID and the deadline for rotation
This is not a connection failure or an outage. Sessions using the expired SPK will continue to work (via the Double Ratchet), but new sessions cannot be initiated until the SPK is rotated. The device owner receives a clear, actionable signal to rotate.
One-Time Pre-Key (OPK) Replenishment
Trigger
The server monitors the count of available OPK public keys for each device. When the count falls below KEY_BUNDLE_REPLENISHMENT_THRESHOLD (default: 5), replenishment is triggered.
The threshold is checked after every key-bundle lookup that consumes an OPK. If a lookup reduces the remaining count below the threshold, the server immediately notifies the device.
Notification and Response
The server sends a key_bundle.replenishment_needed WebSocket event to the device with the device ID and the current remaining OPK count. The device responds by:
- Generating a new batch: The device creates a new batch of Curve25519 key pairs (typically 25-50 keys)
- Uploading public keys: The device calls the key-bundle publish endpoint to upload the new batch
- Deleting private keys: The device deletes the private keys for all uploaded OPKs
Replenishment is best-effort and asynchronous. If the device is offline, the notification is not queued—the device will receive it upon reconnection.
Graceful Degradation
If a device is offline and other users attempt to initiate sessions with it, the server will consume the device’s remaining OPKs. If the OPK supply is exhausted while the device remains offline:
- X3DH continues with 3 DH operations: Senders will establish sessions using only
DH1,DH2, andDH3, omitting theDH4operation with the missing OPK - No message loss: Messages are delivered normally; X3DH simply uses the three-key variant
- Upon reconnection: The device receives a
key_bundle.replenishment_needednotification and uploads a new batch
This graceful degradation means offline devices do not accumulate a backlog of undelivered messages—messages are encrypted to the three-key variant and delivered immediately.
SPK_B could allow an attacker to perform ECDH with the recipient’s ephemeral key. While the three-key variant remains cryptographically secure per the Signal Protocol specification, it is stronger to keep OPKs in stock. Devices should check connectivity regularly and replenish their OPK supply as soon as they come online.Summary
| Key Type | Rotation Trigger | Enforcement | Consequence of Delay |
|---|---|---|---|
| SPK | Age exceeds 7 days | Server refuses new sessions; device notified to rotate | Existing sessions continue; new sessions fail with 428 |
| OPK | Count falls below 5 | Server notifies device to replenish | Senders use three-key X3DH variant; no message loss |
Both rotation mechanisms are transparent to the sender. The server enforces SPK rotation to ensure periodic re-keying; OPK replenishment is automatic and preserves delivery even if the device is offline.