{"id":"cd616a38cd4639f37c88b69b6574a7ad81da86fde19d3eaead4b3b3165035998","pubkey":"4abc8f17e30e49e8b960ea8211cfcc6448cc908b741e7f9819f066f272470848","created_at":1753745616,"kind":30817,"tags":[["d","nip-encrypted-group-messages"],["title","Encrypted Group Messages"],["client","nostrhub.io"]],"content":"NIP-EGM\n======\n\nEncrypted Group Messages via Symmetric Key Distribution\n-------------\n\n`draft` `optional`\n\n## Abstract\n\nThis NIP defines a method for sending encrypted messages to multiple recipients using symmetric AES encryption. The shared AES key is encrypted individually for each recipient using NIP-44, allowing the message to be decrypted by any of the intended recipients using a single Nostr event.\n\n## Motivation\n\nNIP-44 (v2) provides a secure encryption method for private 1-to-1 messages but does not address group messaging without duplicating events.\n\nThis NIP introduces a way to distribute a single event that contains a message encrypted with a symmetric AES key. The AES key is then individually encrypted for each recipient using the standard NIP-44 flow. This enables efficient and private group communication within the Nostr protocol.\n\n## Tags\n\n- `[\"rec\", <recipient_pubkey>]`: Declares each intended recipient's public key.\n- `[\"key\", <recipient_pubkey>, <encrypted_aes_key>]`: Contains the AES key encrypted specifically for each recipient using `nip44.encrypt`.\n\n## Specification\n\n### Event Structure\n\n- `kind`: Any valid event kind. Default is `1` (text note).\n- `content`: A JSON object containing:\n  - `ciphertext`: AES-GCM encrypted message, encoded in base64.\n  - `iv`: AES-GCM initialization vector, encoded in base64.\n- `tags`: One or more of the following per recipient:\n  - `[\"rec\", <pubkey>]`: Declares a recipient.\n  - `[\"key\", <pubkey>, <encrypted_key>]`: Contains the encrypted AES key using NIP-44.\n\n### Message Creation\n\n1. Generate a 256-bit AES key: `Uint8Array(32)`.\n2. Encrypt the plaintext message using AES-GCM with a randomly generated IV.\n3. For each recipient:\n   - Derive a `conversationKey` using `nip44.getConversationKey(senderPrivkey, recipientPubkey)`.\n   - Encrypt the AES key with `nip44.encrypt(aesKeyHex, conversationKey)`.\n4. Construct a single Nostr event with `rec` and `key` tags.\n5. Sign the event with `finalizeEvent(event, senderPrivkey)`.\n\n### Message Decryption\n\n1. From the recipient's private key, derive the public key.\n2. Locate the `[\"key\", pubkey, encrypted_key]` tag that matches.\n3. Derive the `conversationKey` using `nip44.getConversationKey(recipientPrivkey, senderPubkey)`.\n4. Decrypt the AES key using `nip44.decrypt`.\n5. Decrypt the message content using AES-GCM.\n\n## Example\n\n```json\n{\n  \"kind\": 1,\n  \"created_at\": 1680000000,\n  \"pubkey\": \"<sender_pubkey>\",\n  \"tags\": [\n    [\"rec\", \"<recipient1>\"],\n    [\"rec\", \"<recipient2>\"],\n    [\"key\", \"<recipient1>\", \"<encrypted_key1>\"],\n    [\"key\", \"<recipient2>\", \"<encrypted_key2>\"]\n  ],\n  \"content\": \"{\\\"ciphertext\\\":\\\"...\\\",\\\"iv\\\":\\\"...\\\"}\"\n}\n```\n\n## Compatibility\n\n- Compatible with clients and libraries that already implement NIP-44 (v2).\n- Events using this format can be safely ignored by clients that do not recognize this standard.\n\n---\n\n## Rationale\n\nThis approach enables the publication of a **single event** for multiple recipients, reducing redundancy, bandwidth usage, and synchronization issues.\nEach recipient is only able to decrypt the message if they are explicitly included in the `key` tag list.\n\n---\n\n## Future Considerations\n\n- Integration with `kind:1059` (from NIP-59) to support \"sealable\" or temporary messages.\n- Support for partial anonymity: use `[\"anon-key\", <encrypted_key>]` for messages where only those who can decrypt the key will know they are recipients.\n- Consider implementing deduplication by hashing the decrypted message content.","sig":"893be5f8c1743b74d311dcec17bb2b1982cddea8796ca5d528fae414900cc28a9e9feccdcf650f684d123239424810dec3ee79d632b3481ceddbbc2d64d86658"}