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:

  1. Device generates new key pair: The device creates a new Curve25519 key pair
  2. Device signs the public key: Using its Identity Key private key, the device signs the new SPK public key with Ed25519
  3. Device uploads via HTTP: The device calls PUT /api/mobile/key-bundles/{deviceID}/signed-pre-key with the new public key and signature
  4. 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_expired WebSocket 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:

  1. Generating a new batch: The device creates a new batch of Curve25519 key pairs (typically 25-50 keys)
  2. Uploading public keys: The device calls the key-bundle publish endpoint to upload the new batch
  3. 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, and DH3, omitting the DH4 operation 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_needed notification 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.

Extended Offline Risk: A device that is offline for many hours or days may have all its OPKs consumed by incoming sessions. Senders will adapt by using the three-key variant. However, this increases the time window during which a compromised 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 TypeRotation TriggerEnforcementConsequence of Delay
SPKAge exceeds 7 daysServer refuses new sessions; device notified to rotateExisting sessions continue; new sessions fail with 428
OPKCount falls below 5Server notifies device to replenishSenders 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.