{"id":"5593736f307fd0e3c9ea1357f332a58faf123d9eefa4067b0bd7a546058390d1","pubkey":"bbf923aa9246065f88c40c7d9bf61cccc0ff3fcff065a8cb2ff4cfbb62088f1e","created_at":1765689662,"kind":30817,"tags":[["d","signal-protocol-over-nostr"],["title","Signal Protocol over Nostr"],["client","nostrhub.io"]],"content":"# Signal Protocol over Nostr | NIP DRAFT\n\n```\n  NIP: undefined\n  Title: Signal Protocol over Nostr\n  Author: Keychat Developer\n  Status: Draft\n  Type: TBD\n  Created: 2025-11-06\n  License: CC0-1.0\n```\n\n\n\n## Introduction\n\n### Abstract\n\nThis NIP proposes an implementation scheme standard for the double ratchet algorithm (Signal Protocol) over Nostr clients.\n\n### Motivation\n\nThis proposal aims to introduce Signal Protocol into the Nostr network, which allows Nostr clients to have end-to-end encryption for private communications with forward secrecy and post-compromise security. Specifically, the address-refreshing technique is used to minimize tracking and avoid social engineering.\n\n### Dependencies\n\nThis spec needs NIP-17 implementation in advance.\n\n## Description\n\n### Signal Protocol Introduction\n\nThe Signal Protocol is a set of cryptographic specifications that provides end-to-end encryption for private communications. \n\n#### Algorithms\n\n- X3DH\n- Double Ratchet\n\n### Design \n\n#### Architecture\n\nThe private conversation is between Sender (Alice) and Recipient (Bob), while all communications are routed through the Nostr relays, so there is no direct connection between them. \n\n#### Work Flow\n\n1. Create identity\n\n   Both Alice and Bob need to create a nostr account before starting a conversation, namely nsec & npub based on Secp256k1 elliptic curve using Schnnor algorithm.\n\n2. Add contact(create a session)\n\n   Alice SHALL add Bob as a contact before messaging, i.e., they SHALL join the same session.  It's recommended that Alice and Bob ONLY share one live session at the same time. \n\n   1. Pubkey retrieval\n\n      To begin with, Alice SHOULD know Bob's npub by public information or any alternative way. Instead of publishing the npub, Bob can share the one-time key with Alice which reduce tracking.\n\n   2. Generate corresponding identity\n\n      Alice randomly generates the key pairs for Bob. The key is independent of the nostr keys. \n\n   3. Add contact request\n\n      Alice's identity info, along with the **ratchet algorithm initialization vector**, is encrypted by Bob's npub under NIP-17 spec. After that, Bob gets the complete info for the key exchange.\n\n   4. Prekey message\n\n      Before Bob confirms the request, Alice can send messages to Bob continuously based on NIP-17 protocol, e.g., the prekey message.\n\n   5. Add contact confirmation\n\n      When receiving the request, Bob replied with a certain fixed message to accept the request.  A session between Alice and Bob is established.\n\n3. Private messaging\n\n   Alice can send private messages to Bob if they share a live session, which indicates that they use the same ratchet algorithm initialization vector and each other's public keys.\n\n   1. Key Derivation Function (KDF)\n\n      According to Signal Protocol, the Key Derivation Function (KDF) provides the ratcheting mechanism. The first \"ratchet\" is applied to the symmetric root key, the second ratchet to the asymmetric Diffie Hellman (DH) key. \n\n      - When a message is sent or received, a symmetric-key ratchet step is applied to the sending or receiving chain to derive the message key.\n      - When a new ratchet public key is received, a DH ratchet step is performed prior to the symmetric-key ratchet to replace the chain keys.\n\n   2. Address refresh\n\n      The original Signal protocol operates over a private network; when applied to Nostr, a public network, the privacy issue becomes more pronounced. Although the eavesdropper cannot decrypt the message's content, if the sender's (or recipient's) address remains unchanged, the eavesdropper can still analyze the identity and other information from the communication frequency, timing, etc.  Address refresh means Alice updates the receiving address when sending a new message. The new address is the DH ratchet's new pubkey. When the new address receives a message from Bob, Alice can ensure Bob receives the new address. Alice should listen to the old addresses for some time to avoid missing messages or delays. The address refresh strategy can be flexible, depending on the software operator.\n\n   3. Encrypt message\n\n      Alice encrypts the message using the chain key along with the signature. AES-256 is recommended as the encryption algorithm. CBC mode is the default setting but must be used with the HMAC to avoid bit flipping. \n\n   4. Decrypt message\n\n      Bob checks the signature, if valid, decrypts the message using the corresponding chain key. When Bob needs to respond , the process is shown as \"Encrypt message\".\n\n#### Concerns in public networks\n\nPrivate communication in public networks operates in a more complex environment, including issues such as relay reliability, network analysis, eavesdropping, and sustainable incentive compatibility.\n\n1. Relay reliability\n\n   Although the relay nodes of the Nostr network have risks such as censorship and message discarding, as long as there is an honest node, confidential communication between Alice and Bob can be achieved.\n\n2. Network analysis\n\n   The random root address and address refresh mechanisms make network traffic analysis between the sending and receiving addresses as difficult as possible. However, network analysis based on behavioral patterns or IP addresses still exists.\n\n3. Eavesdropping\n\n   The double ratchet algorithm ensures the confidentiality of a single message and also ensures forward secrecy and post-compromise security.\n\n4. Sustainable incentive compatibility\n\n   Relays can collect fees for forwarding messages, ensuring the network's sustainability and preventing spam. In addition to paying fees outside the network, participants can also pay fees using Ecash, as detailed in the specification section.\n\n\n\n## Specification\n\nThe following implementations are based on libsignal.rs.\n\n### Database Design\n\nThe basic database includes IDENTITY, RATCHET_STATUS, SESSION_STATUS, OPPONENT_SIGNED_KEY, PRE_KEY.\n\n`identity (`\n        `id integer primary key AUTOINCREMENT,`\n        `nextPrekeyId integer,`\n        `registrationId integer,`\n        `address text,`\n        `privateKey text,`\n        `publicKey text,`\n`);`\n\n`ratchet_status (`\n    `id integer primary key AUTOINCREMENT,`\n    `aliceRatchetKeyPublic text,`\n    `address text,`\n    `roomId integer,`\n    `bobRatchetKeyPrivate text,`\n    `ratcheKeyHash text,`\n`);`\n\n`session_status (`\n    `id integer primary key AUTOINCREMENT,`\n    `aliceSenderRatchetKey text,`\n    `address text,`\n    `record text,`\n    `bobSenderRatchetKey text,`\n    `bobAddress text,`\n    `aliceAddresses text,`\n`);`\n\n`opponent_signed_key (`\n    `id integer primary key AUTOINCREMENT,`\n    `keyId integer,`\n    `record text,`\n    `used bool Not NULL DEFAULT false,`\n`);`\n\n`pre_key (`\n    `id integer primary key AUTOINCREMENT,`\n    `keyId integer,`\n    `record text,`\n    `used bool Not NULL DEFAULT false,`\n`);`\n\n### Create Identity\n\nSignal identity is a key pair generated by a *pseudorandom number generator*. It's recommended to use a reliable PRNG; do not use any self-implementations. Alice's public key is the signal identity that will be shared with Bob, and the private key is used to sign the initial message.\n\n### Add Contact\n\nAlice's request, wrapped in NIP-17:\n\n```\n`ID: Hash`  \n`Kind: 1059`\n`From: Random Address`\n`To: Bob's npub OR One-time address`\n`Content: Request to start an encrypted chat...`\n`Signature: Alice's signature`\n```\n\nBob's response(if accepted), wrapped in Signal protocol:\n\n```\n`ID: Hash`  \n`Kind: 4`\n`From: Address of current chainkey`\n`To: Alice's one-time address`\n`Content: Hi, I'm Bob. Let's start an encrypted chat...`\n`Signature: Bob's signature`\n```\n\nAfter that, Alice and Bob joined the same session and can start a private conversation.\n\n```\n`pub struct SignalSession {`\n    `pub alice_sender_ratchet_key: Option<String>,`\n    `pub address: String,`\n    `pub bob_sender_ratchet_key: Option<String>,`\n    `pub record: String,`\n    `pub bob_address: Option<String>,`\n    `pub alice_addresses: Option<String>,`\n`}`\n```\n\nBefore Bob responds, Alice can still send pre-key messages to Bob. The message encryption process is the same as the add contact request process, and will not be described in detail here. \n\n### Send Encrypted Messages\n\nAlice sends messages to Bob's Signal address. The plain text is encrypted as shown:\n\n```\ncipher_text = message_encrypt(\n            ptext.as_bytes(),\n            &remote_address,\n            &mut store.session_store,\n            &mut store.identity_store,\n            SystemTime::now(),\n            is_prekey,\n        )\n```\n\nThe session key and the identity key are updated with every message. Bob's address is updated when Bob responds.\n\n### Decrypt Received Messages\n\nBob listens to the addresses corresponding to the current session's pubkey shared with Alice and pulls the message from a relay (relays). \n\n    plain_text = message_decrypt_signal(\n                &ciphertext,\n                &remote_address,\n                &mut store.session_store,\n                &mut store.identity_store,\n                &mut store.ratchet_key_store,\n                room_id,\n                &mut csprng,\n            )\n\n## Security Consideration\n\nThe secure communication system is designed to meet specific security goals under certain assumptions. Moreover, the complexity of Nostr systems introduces different threats and affects the security design philosophy.\n\n### Security Assumption\n\nThe security of the entire system is based on the following assumptions: \n\n1. The primary security assumption is based on the **computational difficulty** of AES-256, which means that an adversary cannot break the encryption in any time frame relevant to real-world applications. \n2. The second security assumption is that at least one relay is honest and will deliver all messages between the commuters. However, the relays can analyze or even try to break down the encrypted information in the meantime.\n3. The RNG provides enough randomness, thus no one can infer the session key.\n\n### Security Goals\n\n1. Confidentiality\n\n   Confidentiality means the enemy can't obtain any information from the ciphertext. In practice, the key space needs to be large enough and should resist cryptoanalysis. At the same time, confidentiality is not only for the present moment, but also needs to provide maximum forward secrecy and backward security even if the key is leaked. In this spec, AES-256 is a reliable algorithm, and the double ratchet algorithm as a key update mechanism helps minimize the consequences of key leakage.\n\n2. Integrity\n\n   Integrity means the information should not be modified without authorization. This is usually implemented with HMAC, which can detect any data changes.\n\n3. Authentication\n\n   It is essential to verify the identity of the users you are talking to. Signature is a tool for authentication when Alice and Bob start a session. After that, all authentication is based on the KDF algorithm, which uses the double ratchet.\n\n4. Non-repudiation\n\n   It's hard to provide non-repudiation unless one participant retains all the messages since the session began. However, this is not the primary goal.\n\n### Threat Model\n\nIt's important to identify the potential threats and vulnerabilities in the public network. Some are active attacks by the hacker; the rest are passive attacks carried out by relays.\n\n1. Phishing Client\n    Attackers can forge apps or inject malicious code into the source code. After a user downloads the app, the attacker can monitor the user's communications. To address this, app providers should strengthen verification of app installation package fingerprints and enhance user security education.\n2. Message Forgery\n    Relay can truncate messages. Furthermore, when using certain AES operating modes (such as CBC), based on the bit-flipping property of ciphertext, Relay can modify the ciphertext, thereby tampering with the plaintext. Therefore, all information must be verified for integrity using HMAC.\n3. Denial of Service\n    Relay can refuse service to certain users, failing to forward corresponding information and causing message congestion. The key refresh function ensures that communicators frequently change their addresses, avoiding targeted denial-of-service attacks.\n4. Traffic Identification\n    Despite address refresh, relays can still track one's communication habits based on IP addresses and message sending patterns, and combine this with social engineering techniques to increase the risk to them. In addition to using tools like TOR or VPNs for self-protection, communicators can also employ strategies such as switching relays to prevent a single relay from possessing complete information, or fetching non-personal encrypted messages locally to increase traffic obfuscation and reduce tracking.\n\n## Reference\n\n[1] The X3DH Key Agreement Protocol, https://signal.org/docs/specifications/x3dh/\n\n[2] The Double Ratchet Algorithm, https://signal.org/docs/specifications/doubleratchet/\n\n[3] Not in The Prophecies: Practical Attacks on Nostr, https://crypto-sec-n.github.io/\n\n[4] libsignal in Rust, https://github.com/signalapp/libsignal","sig":"7f386fbcd9ef12b567a847bc9798c5943634b58c9e1555180ae04dc084f6ff3d388d1ae714302caba0ddc1529544742f57558a72efcd91d3c0e20103db3bbc08"}