Are BitChat's Private Envelopes Compatible with NIP-17 or NIP-44?
BitChat's private envelopes are not compatible with NIP-17 or NIP-44. Although the protocol borrows event kind numbers (13, 14, and 1059) and the v2: payload prefix from these Nostr Improvement Proposals, it implements a proprietary encryption scheme using secp256k1 ECDH and HKDF-SHA256 that diverges from standardized specifications.
BitChat is an open-source messaging application built on the Nostr protocol that employs a custom private-envelope system for end-to-end encrypted direct messages. While it transports these messages over standard Nostr relays, the encryption format documented in docs/privacy-assessment.md and WHITEPAPER.md intentionally diverges from NIP-17 and NIP-44 standards to implement specific privacy guarantees.
Understanding BitChat's Proprietary Encryption Stack
The BitChat private envelope uses a three-layer nested structure that encrypts messages using a custom key derivation path unlike the standardized NIP-44 key schedule.
Event Kind Structure and Wrapping
According to bitchat/Nostr/NostrProtocol.swift, the envelope construction follows this chain:
- Inner message (kind 14): The plaintext content encrypted with a symmetric key
- Outer seal (kind 13): A one-time-key signed wrapper containing the encrypted payload
- Public envelope (kind 1059): The final relay-visible container
This layering differs from NIP-17's gift-wrap semantics despite using identical kind numbers. The source code explicitly states that this envelope "is not NIP-17, NIP-44, or NIP-59 compatible" in its top-level comments.
Key Derivation and Cipher Suite
The encryption stack combines several cryptographic primitives:
- secp256k1 ECDH: For shared secret generation between sender and recipient
- HKDF-SHA256: For symmetric key derivation using the info string
"nip44-v2" - XChaCha20Poly1305: For authenticated encryption with a 24-byte nonce
This construction creates a v2: prefixed payload that appears similar to NIP-44 but uses a different key schedule. As documented in the privacy assessment, this approach "does not follow the NIP-44 key-schedule or the NIP-17 gift-wrap semantics."
Creating and Decrypting Private Envelopes
The implementation in bitchat/Nostr/NostrProtocol.swift provides Swift APIs for envelope manipulation that cannot be replaced with standard NIP-17 or NIP-44 libraries.
Building a Private Envelope
To create a private message, the client executes an ECDH exchange followed by symmetric encryption:
// Creating a private envelope (BitChat‑specific)
let recipientPubKey = Secp256k1PublicKey(hex: "…")
let senderKeyPair = Secp256k1KeyPair() // one‑time key
let sharedSecret = try senderKeyPair.privateKey.sharedSecret(with: recipientPubKey)
let symmetricKey = HKDF<SHA256>.deriveKey(
inputKeyMaterial: sharedSecret,
info: "nip44-v2".data(using: .utf8)!)
let nonce = try XChaCha20Poly1305.generateNonce()
let ciphertext = try XChaCha20Poly1305.encrypt(messageData,
using: symmetricKey, nonce: nonce)
let sealedPayload = "v2:" + Base64URL.encode(nonce + ciphertext)
// Wrap in outer seal and public envelope (kinds 13 & 1059)
let outerSeal = NostrEvent(kind: 13, content: sealedPayload, tags: [...])
let envelope = NostrEvent(kind: 1059, content: outerSeal.serialized, tags: [...])
This process creates the nested structure visible to relays while keeping the sender's stable identity hidden through the use of one-time keys for the outer seal.
Decrypting Received Envelopes
Decryption reverses the process using the recipient's static private key:
// Decrypting a private envelope (BitChat‑specific)
guard let outerSeal = try? NostrEvent.deserialize(event) else { return }
guard let sealedPayload = outerSeal.content.removingPrefix("v2:") else { return }
let decoded = Base64URL.decode(sealedPayload)
let nonce = decoded.prefix(24)
let cipher = decoded.dropFirst(24)
let symmetricKey = // derive as above using recipient’s private key
let plaintext = try XChaCha20Poly1305.decrypt(cipher,
using: symmetricKey, nonce: nonce)
Because the key derivation relies on BitChat-specific parameters, standard Nostr clients implementing NIP-44 cannot execute this decryption even if they recognize the event kinds.
Security Model and Limitations
The docs/privacy-assessment.md file outlines specific trade-offs in BitChat's approach. While the protocol effectively hides sender identity from relays through one-time keys, it does not provide forward secrecy. If a recipient's static Nostr private key is compromised, all previously stored envelopes can be decrypted retroactively.
This limitation contrasts with protocols that implement ephemeral key exchanges for each message. The whitepaper explicitly acknowledges this design choice, prioritizing offline message recovery over forward secrecy.
Compatibility with Standard Nostr Clients
Because BitChat envelopes are proprietary, interoperability requires specific implementation of BitChat's format rather than standard NIP support. The README.md confirms that "the private-envelope format is proprietary and not NIP-compatible," meaning other clients will treat these messages as opaque payloads unless they specifically implement BitChat's encryption logic.
Summary
- BitChat's private envelopes use event kinds 13, 14, and 1059 but implement a proprietary encryption protocol incompatible with NIP-17 and NIP-44.
- The encryption stack combines secp256k1 ECDH, HKDF-SHA256, and XChaCha20Poly1305 with a specific key derivation path documented in
WHITEPAPER.md. - Only BitChat clients can create or decrypt these envelopes; standard Nostr clients see them as opaque payloads.
- The design provides sender anonymity through one-time keys but lacks forward secrecy, exposing historical messages if the recipient's private key is compromised.
Frequently Asked Questions
Can standard Nostr clients read BitChat private messages?
No. Standard Nostr clients implementing NIP-17 or NIP-44 cannot decrypt BitChat private envelopes. While they recognize the event kinds (13, 14, 1059), the proprietary key derivation using HKDF-SHA256 and the specific ECDH implementation create a format that only BitChat-compatible software can parse, as detailed in bitchat/Nostr/NostrProtocol.swift.
Why does BitChat use the same event kinds as NIP-17?
BitChat repurposes kind numbers 13, 14, and 1059 to ensure message routing through standard Nostr relays while maintaining a proprietary payload format. This approach leverages existing relay infrastructure for transport while keeping the encryption layer exclusive to BitChat's implementation, effectively creating a closed system for message content despite using standard transport envelopes.
Is BitChat's encryption secure despite not following NIPs?
BitChat's encryption uses industry-standard primitives (XChaCha20Poly1305, HKDF-SHA256, secp256k1) that provide strong confidentiality when keys are secure. However, the protocol sacrifices forward secrecy—a feature present in many modern messaging protocols—to enable offline message recovery. If a user's private key is compromised, all past messages become decryptable, a trade-off documented in docs/privacy-assessment.md.
Will BitChat add support for standard NIP-44 encryption?
According to the current source code and documentation, BitChat maintains its proprietary envelope format as a core architectural decision. The whitepaper and protocol implementation suggest this incompatibility is intentional to preserve specific privacy characteristics, making standard NIP-44 support unlikely without significant protocol changes that would break existing message histories.
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 →