mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-12-23 00:45:53 -05:00
e6d179c41e
Co-authored-by: Jon Staab <jstaab@protonmail.com>
253 lines
10 KiB
Markdown
253 lines
10 KiB
Markdown
NIP-112
|
|
=======
|
|
|
|
Encrypted Group Events
|
|
----------------------
|
|
|
|
`draft` `optional` `author:earonesty`
|
|
|
|
This NIP defines new event kinds for encrypted channels with optional forward secrecy guidelines.
|
|
A channel is a way to create a private nostr sub-network, within which the protocol can operate normally
|
|
visible only to users entrusted with a given decryption key.
|
|
|
|
This NIP builds on the encryption from NIP-44, the gift-wrap of NIP-59 and the distributed chat channel management of NIP-28. It is similar to NIP-38, with the exception that gift-wrap is used on every message, and issues like metadata leakage and optional forward secrecy are addressed.
|
|
|
|
It works by creating a shared secret that is used to encrypt the group or channel messages.
|
|
Only the participants know that secret, so only such members can read/write to this group chat.
|
|
This effectively hides metadata from external users. See drawbacks below.
|
|
|
|
It reserves 8 event kinds (1059, 400-406) for immediate use.
|
|
|
|
- [`1059`](https://github.com/nostr-protocol/nips/pull/468) - encrypted gift-wrap, can be used by many protocols
|
|
- `400` - wrapped kind: create encrypted channel
|
|
- `401` - wrapped kind: invite to encrypted channel
|
|
- `402` - wrapped kind: change channel metadata
|
|
- `403` - wrapped kind: send encrypted chat message
|
|
- `404` - wrapped kind: moved to new channel
|
|
- `405` - wrapped kind: delegate new owner
|
|
- `406` - wrapped kind: accept owner delegation
|
|
|
|
|
|
## Kind 1059: Encrypted Gift Wrap
|
|
|
|
For all encrypted group chat messages, the client first generates a kind 4XX message, as below.
|
|
|
|
Then they generate a new public/private keypair, and uses this to sign and encrypt the message.
|
|
|
|
The `content` is [NIP-44](https://github.com/nostr-protocol/nips/pull/574) encrypted JSON string with the temporary key, this is the inner event.
|
|
|
|
All events called "wrapped-kind XX" refer to these kind 1059 gift wrapped events.
|
|
|
|
## Wrapped Kind 400: Create Encrypted channel
|
|
|
|
Create a Encrypted event channel.
|
|
|
|
In the channel creation `content` field, Client SHOULD include basic channel metadata (`name`, `about` and `picture`).
|
|
|
|
This is akin to NIP-28, kind 40 but using gift-wrap encryption.
|
|
|
|
On creating a channel, the creator MUST also generate a unique/new private-public key pair which will serve as the `shared-secret` for a given channel.
|
|
|
|
This is the "destination pubkey" for the channel creation.
|
|
|
|
A list of recommended relays SHOULD be added to the channel-create message
|
|
|
|
```json
|
|
{
|
|
"content": "{\"name\": \"Demo Channel\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\"}",
|
|
"tags": [["r", "wss://relay-01"], ["r", "wss://relay-02"], ...]]
|
|
}
|
|
```
|
|
|
|
The wrapped INNER event id for the channel creation should be saved. This is the event id that will be used in all future INNER references to the channel creation.
|
|
|
|
Channel creation events MUST be sent to the "destination pubkey" of the channel itself, not to any specific user.
|
|
|
|
As a rule, this "destination pubkey" of the channel MUST used as the sole identifier and external filter that clients use to retrieve events for the channel.
|
|
|
|
There are no other public tags or metadata, all other kinds and tags are "inner" (inside the wrapped message).
|
|
|
|
## Wrapped Kind 401: Invitation to Encrypted Channel
|
|
|
|
Invitations are sent directly to the participants of the channel.
|
|
|
|
```json
|
|
{
|
|
"content": "optional personalized invitation message",
|
|
"tags": [["e", "channel-create-event-id", "relay-url"], ["privkey", "channel-private-key"]]
|
|
}
|
|
```
|
|
|
|
When wrapping invitation messages, the destination pubkey is the user being invited, the relay used should be one of that user's well-known relays.
|
|
|
|
More than one relay can be recommended in the "e" tag.
|
|
|
|
## Wrapped Kind 402: Change channel metadata
|
|
|
|
Update an encrypted channel's public metadata.
|
|
|
|
Clients and SHOULD handle wrapped-kind 402 events similar to kind 33 `replaceable` events. Relays cannot distinguish this from other kind 1059 events.
|
|
|
|
Clients SHOULD ignore kind wrapped-402 from pubkeys other than the current owner.
|
|
|
|
The current owner of a channel is the pubkey that created the given channel using the corresponding wrapped-kind 400 event, or the current owner based on the 405-406 channel ownership delegation flow.
|
|
|
|
In other words, only the owner of a channel can *effectively* send kind wrapped-402 events; kind 402 for any channel sent by other people, even participants of that channel, are ignored.
|
|
|
|
Clients SHOULD support basic metadata fields:
|
|
|
|
- `name` - string - Channel name
|
|
- `about` - string - Channel description
|
|
- `picture` - string - URL of channel picture
|
|
|
|
Clients MAY add additional metadata fields.
|
|
|
|
Clients MUST specify an "e" tag to identify the channel id.
|
|
Clients SHOULD mark the "e" tag to recommend a relay where the original event can be found.
|
|
Clients MAY replace the "r" tag set with a full list of new relays.
|
|
|
|
There need be no overlap between the "e" relay and the "r" list. For example, if a group's channel is moving its relay set to a new set.
|
|
|
|
```json
|
|
{
|
|
"content": "{\"name\": \"Updated Demo Channel\", \"about\": \"Updating a test channel.\", \"picture\": \"https://placekitten.com/201/201\"}",
|
|
"tags": [["e", "<channel-wrapped-400-event-id>", "wss://relay-url"]],
|
|
}
|
|
```
|
|
|
|
|
|
## Kind 403: Send encrypted message to encrypted group channel
|
|
|
|
Clients SHOULD use a marked "e" tags to specify if it is is a reply message.
|
|
|
|
Clients SHOULD use [NIP-10](10.md) marked "e" tags to recommend a relay and specify whether it is a reply or root message.
|
|
|
|
Clients SHOULD append [NIP-10](10.md) "p" tags to replies.
|
|
|
|
### Format
|
|
|
|
Inner-root message:
|
|
|
|
```json
|
|
{
|
|
"content": <string>,
|
|
"tags": [["e", "<kind_400_event_id>", "<relay-url-where-it-was-seen>", "root"], "<optional expiration tag>"]
|
|
}
|
|
```
|
|
|
|
Inner-reply to another message:
|
|
|
|
```json
|
|
{
|
|
"content": <string>,
|
|
"tags": [
|
|
["e", "<kind_400_event_id>", "<relay-url>", "root"],
|
|
["e", "<kind_403_reply_to_event_id>", "<relay-url>", "reply"],
|
|
["p", "<pubkey>", "<relay-url>"]
|
|
]
|
|
}
|
|
```
|
|
|
|
### Wrapped-kind 404: Removing Participants
|
|
|
|
Send a Wrapped Kind 404 (similar to 401)
|
|
|
|
Once a group is created, all participants know the shared-secret, so to remove a participant, we need to create a new group.
|
|
|
|
To do, this the creator of the group issues a new 400 event.
|
|
|
|
Then the creator sends out a kind 404 individually to every member of the new group, *excluding the removed participants*.
|
|
|
|
```json
|
|
{
|
|
"content": "optional friendly invite message",
|
|
"tags": [
|
|
["e", "<previous-kind-400>", "<relay-url>"],
|
|
["e", "<new-kind-400>", "<relay-url>"],
|
|
["privkey", "<channel-private-key>"]
|
|
]
|
|
}
|
|
```
|
|
Clients SHOULD ignore wrapped-kind 404 messages from anyone but the current owner of a group.
|
|
|
|
Clients SHOULD ignore messages to the previous group that occur after their receive the first kind 404. In addition there is no longer a need to subscribe to the public keys of prior groups.
|
|
|
|
Clients use kind 404 to find out which was the precursor channel(s) to this new channel, and should attempt to display all old and new channels in a chain seamlessly to the user as a unified channel history.
|
|
|
|
Clients SHOULD consider the new channel metadata as authoritative.
|
|
|
|
Clients MAY discard previous channel information, since it will no longer be used.
|
|
|
|
### Wrapped-kind 405: Delegate new owner
|
|
|
|
To do, this the creator of the group sends out a wrapped-kind 405 to the main group public key.
|
|
|
|
```json
|
|
{
|
|
"content": "optional friendly invitation to own the group",
|
|
"tags": [["e", "<400-event-id-of-group>", "<relay-url>"], ["p", "<public-key-of-delegate>"]]
|
|
}
|
|
```
|
|
Clients SHOULD ignore wrapped-kind 405 messages from anyone but the current "owner" of a group.
|
|
Clients SHOULD ignore wrapped-kind 405 messages until they receive a 406 CLAIM message from the new "owner" of the group.
|
|
|
|
### Wrapped-kind 406: Accept ownership transfer
|
|
|
|
To do, this the creator of the group sends out a wrapped-kind 405 to the main group public key.
|
|
|
|
```json
|
|
{
|
|
"content": "optional friendly hey, i'm in charge",
|
|
"tags": [["e", "<event-id-of-405>", "<relay-url>]"]
|
|
}
|
|
```
|
|
Clients SHOULD ignore wrapped-kind 406 messages unless they are signed by the delegate in the associated 405 message
|
|
Clients SHOULD treat accepted delegates as the new owner of the group for all 404 and 405 messages.
|
|
|
|
## NIP-10 relay recommendations
|
|
|
|
For [NIP-10](10.md) relay recommendations on replies and on channel creation info, clients generally SHOULD use the relay URL where the event was first seen, if known.
|
|
|
|
|
|
Future extensibility
|
|
--------------------
|
|
|
|
We reserve wrapped-event kinds 407-419 for other wrapped group events, such as delegation, moderation, mute, hide, etc.
|
|
|
|
Motivation
|
|
----------
|
|
This is the easiest way to allow the use of group events with select group of people, while hiding metadata, and preserving nostr's censorship resistence
|
|
|
|
Optional Forward Secrecy
|
|
------------------------
|
|
|
|
Claims of "forward" secrecy in mobile applications can be disingenuous. Many mobile applications allow users to purchase a new phone and restore their account with historic messages intact - therefore this is not forward secrecy.
|
|
|
|
True forward secrecy relies on servers reliably discarding the intermediate data - in addition to frequent key rotations, or prev-key ratcheting. A trusted server, for example, can be requested to discard old messages, by adding an optional NIP-40 expiration.
|
|
|
|
### OPTIONAL forward secrecy extension
|
|
|
|
The owner of a channel can add a "expiration" tag to the channel creation message.
|
|
|
|
The owner of a channel who adds a "expiration" tag SHOULD issue a 404 message before the expiration.
|
|
|
|
All members of a channel SHOULD also adding a NIP-40 "expiration" tag to all messages sent to the channel using the duration (`expiration - created_at`) of the wrapped-400 create.
|
|
|
|
Members of a channel MUST NOT send new messages if a 404 message has not been received before expiration.
|
|
|
|
Servers SHOULD obey NIP-40 expiration tags.
|
|
|
|
Provided that servers obey NIP-40 expiration tags, this will prevent an attacker from reading old group messages before the expiration.
|
|
|
|
|
|
Drawbacks
|
|
---------
|
|
Any member of the group can, implicitly, invite new members to the group, since they have the private key. Any member of a group can "dox" other members by publishing their wrapped messages.
|
|
|
|
Any member of the group can spam the group, or otherwise DOS the group.
|
|
|
|
If any single participant of the group chat leaks the shared secret ( whether intentionally or by accident), all the messages can then be decrypted by others until the next 400 and 404 rotation events.
|
|
|
|
The use of optional frequent forward secrecy rotation events can mitigate these attacks, provided the server is compliant with the expiration times,
|
|
|