BitChat Offline Sealed Message Encryption: Noise X Mechanism and Limitations
BitChat encrypts offline sealed messages using a one-way Noise X construction where Version 1 uses static Noise public keys and Version 2 uses one-time pre-keys for forward secrecy, with strict constraints including 16KB payload limits, 24-hour expiry windows, and daily rotating recipient tags.
BitChat implements a specialized store-and-forward protocol for delivering messages to offline peers through a "courier envelope" system. According to the permissionlesstech/bitchat source code, this offline messaging layer found in localPackages/BitFoundation/Sources/BitFoundation/CourierEnvelope.swift uses a one-way Noise X encryption scheme with specific versioned implementations and operational constraints designed for lightweight text transmission.
How the Courier Envelope Encryption Works
The CourierEnvelope struct encapsulates the cryptographic protections that allow untrusted couriers to forward messages without accessing their contents.
One-Way Noise X Construction
At the core of the implementation is an opaque one-way Noise X ciphertext. The Noise X handshake pattern provides sender authentication and encryption, but the construction is deliberately one-way—meaning the recipient cannot encrypt a reply using the same envelope. The envelope contains only a rotating "recipient tag" and the ciphertext, ensuring the forwarding courier remains unable to read the message payload.
Version 1 vs Version 2
BitChat supports two cryptographic versions with different key exchange properties:
- Version 1 (v1) – The payload is encrypted with the recipient's static Noise public key. This provides encryption but lacks forward secrecy.
- Version 2 (v2) – The payload is encrypted with a one-time pre-key identified by
prekeyID. This provides forward secrecy because the pre-key is consumed after a single use and cannot be used to decrypt past messages if compromised.
As implemented in CourierEnvelope.swift (lines 31-37 and 109-115), v2 envelopes contain a prekeyID TLV field that v1 decoders skip silently, creating version compatibility considerations.
Recipient Tag Derivation
The envelope identifies its intended recipient through a rotating tag rather than a static address. The tag is derived via HMAC-SHA-256 of the recipient's static key combined with the UTC day. This produces a recipientTag that rotates daily to prevent long-term correlation. The system generates candidateTags for the creation day plus adjacent days to tolerate clock skew, as detailed in lines 71-93 of CourierEnvelope.swift.
Limitations of BitChat's Offline Messaging
The courier envelope system imposes strict operational constraints that define its security and usability envelope.
Payload Size and Media Restrictions
Offline messages are strictly limited to text-sized payloads. The implementation defines maxCiphertextBytes as 16 KB (line 41), and the source code explicitly notes that "media transfers are out of scope." Large files must use a different transport mechanism, as the envelope cannot accommodate binary media.
Expiry and Copy Budget Constraints
Each envelope carries hardcoded lifetime limits to prevent indefinite storage:
- Expiry window: Enforces a maximum lifetime of 24 hours via
maxLifetimeSeconds(line 43). After expiry, the envelope is discarded. - Copy budget: The
copiesfield limits redistribution to a maximum of 8 copies (maxCopies, lines 46-47). A copy budget of 1 designates "carry-only" mode, where the holder may transport but not re-spray the envelope to other peers.
Daily Rotating Recipient Tags
The recipient tag's daily rotation creates a delivery window limitation. Tags are valid only for the UTC day of creation plus adjacent days (candidateTags). If a recipient remains offline beyond this tolerance window, the envelope will not be recognized upon return, effectively limiting offline duration to approximately 48-72 hours depending on when the message was sent relative to day boundaries.
Version Compatibility and One-Way Design
- Version compatibility: v1-only clients cannot decrypt v2 envelopes. When a v1 decoder encounters the
prekeyIDfield, it skips the TLV and fails to process the envelope silently. - One-way limitation: The Noise X construction provides no reply path. Recipients cannot encrypt responses using the same envelope; replies require a separate courier envelope created by the original recipient.
Summary
- BitChat uses a one-way Noise X construction in
CourierEnvelope.swiftfor offline message encryption. - Version 2 provides forward secrecy via one-time pre-keys, while Version 1 relies on static keys.
- Hard limits restrict payloads to 16KB text-only content with a 24-hour expiry and maximum 8-copy redistribution budget.
- Daily rotating recipient tags limit offline availability to approximately 48-72 hours.
- The scheme is one-way only and lacks backward compatibility for v2 envelopes on v1-only clients.
Frequently Asked Questions
What encryption protocol does BitChat use for offline sealed messages?
BitChat uses a one-way Noise X construction implemented in the CourierEnvelope struct. The protocol encrypts payloads using either static Noise public keys (v1) or one-time pre-keys (v2), wrapped in an envelope that includes a daily rotating recipient tag derived from HMAC-SHA-256.
Why is there a 16KB limit on offline messages?
The maxCiphertextBytes constant in CourierEnvelope.swift (line 41) caps ciphertext at 16KB because the courier envelope system is designed exclusively for text-sized payloads. The source code explicitly excludes media transfers from this transport mechanism, requiring large files to use alternative protocols.
What happens if a recipient is offline for more than 24 hours?
Envelopes expire after 24 hours (maxLifetimeSeconds) and are automatically discarded by the network. Additionally, recipient tags rotate daily based on UTC time, so if a peer remains offline beyond the adjacent-day tolerance window, they will not recognize the envelope even if it hasn't expired, effectively limiting practical offline duration to 48-72 hours.
Can offline messages be decrypted by the forwarding courier?
No. The courier receives only the encrypted ciphertext and a rotating recipient tag. Because the payload is encrypted with the recipient's public key (v1) or one-time pre-key (v2) using the Noise X protocol, the forwarding node cannot access the plaintext content. However, the courier does learn the approximate timing and size of the transmission.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →