-
Notifications
You must be signed in to change notification settings - Fork 721
Secure DM #978
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Secure DM #978
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
2acbd64
Add Private DM
arthurfranca 13177b2
Add info about where to publish events
arthurfranca 43fe354
Add chat session confirmation event
arthurfranca 49d1f73
Change wording from confirm to accept
arthurfranca d8e8ec1
Minor update
arthurfranca dc7ac14
Change dm status kind numbers
arthurfranca 7e9083d
Rename session-privkey-A to session-privkey
arthurfranca d1f2199
Explain conversation keys generation
arthurfranca d5baa6e
Use descending order when resolving session conflict
arthurfranca f9921e6
Fix sealing arg when encrypting
arthurfranca ae1e3a6
Move the session acceptance rumor to the session channel
arthurfranca 84dff71
Change from salt suffix to full salt
arthurfranca e732e56
Add AUTH suggestion
arthurfranca 1ed44ba
Add summary flow
arthurfranca 1d421b4
Minor fix
arthurfranca f50c650
Remove min PoW requirement
arthurfranca 46ad13c
Add rate limit
arthurfranca 1415d76
Expirement using private kind range
arthurfranca 6ddce9f
Move rumor kinds to numbers no one is using
arthurfranca dbaa40d
Ditch private event kinds
arthurfranca f86bb32
Add closing rumor
arthurfranca 40342cc
Detect when new device is used
arthurfranca 2255a5c
Use lid_proof tag on LID Tracker event
arthurfranca 995ee77
Remove PoW
arthurfranca File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| NIP-43 | ||
| ====== | ||
|
|
||
| Private DM | ||
| ---------- | ||
|
|
||
| `draft` `optional` | ||
|
|
||
| This direct message (DM) scheme between two participants is end-to-end encrypted, hides | ||
| most metadata, has forward secrecy and aims to minimize spam. | ||
|
|
||
| This NIP currently uses [NIP-44](44.md) v2 encryption. | ||
|
|
||
| ## Flow | ||
|
|
||
| - Before sending a DM to someone (`B`), an user (`A`) picks a random privkey to be used as chat session (`session-privkey-A`); | ||
| - `A` signs DMs with `session-privkey-A` and publishes them to `A` and `B` [NIP-65](65.md) `read` relays; | ||
| - Meanwhile, `A` sends `session-privkey-A` to `B` through B `read` relays, representing the chat session; | ||
| - `A` also stores `session-privkey-A` to themselves linking it to `B` pubkey (representing `A-B` chat session); | ||
| - Upon receiving `session-privkey-A`, `B` stores the key linking it to `A` pubkey (also representing `A-B` chat session) and sends a chat session acceptance to `A`. Otherwise `B` ignores the key. | ||
| - `B` can fetch DMs from that session's pubkey, filtering by `author`; | ||
| - `B` can send back DMs by signing them with the same `session-privkey-A`. | ||
|
|
||
| Note that: | ||
| - `A` can resend the same session key anytime they want while there is no acceptance from `B`, to make sure `B` didn't miss it; | ||
| - `B` could had sent a session to `A` at the same time. | ||
| In that case, the most recent of the two sessions should be used by both participants. They may also check a single | ||
| time for DMs already sent from the older session before discarding it. If the `.created_at` of both sessions are exactly the same, | ||
| the receiving user should start a new chat session; | ||
|
|
||
| Important: | ||
| - By convention, after three weeks the session expires. | ||
| Either `A` or `B` will need to restart the flow with a new random privkey as session | ||
| when sending new DMs. | ||
|
|
||
| ## Chat Session | ||
|
|
||
| The "Chat Session Envelope" is a [NIP-59](59.md) gift wrap event but with a specific kind: `1043`. | ||
|
|
||
| It MUST include [NIP-13](13.md) PoW with atleast 16 difficulty. | ||
| Receiving client MUST ignore it if below 16. | ||
| Relays SHOULD block it if below 16. | ||
| Relays SHOULD require increasing PoW difficulty the more `kind:1043` events are sent to the same pubkey in a short time. | ||
| Relays SHOULD block it if of byte size higher than 3KB. | ||
|
|
||
| It has a `p` tag set to the recipient's pubkey. | ||
|
|
||
| The NIP-59 rumor is a `kind:1044` "Chat Session Request" event. | ||
| It's `.content` is the session's privkey. | ||
|
|
||
| By convention, the session expires 3 weeks after the rumor's `.created_at` value. | ||
|
|
||
| The "Chat Session Envelope" event should be sent to atleast one of the recipient's NIP-65 `read` relays. | ||
|
|
||
| Event example: | ||
|
|
||
| ```js | ||
| { | ||
| "kind": 1043, | ||
| "pubkey": "<random-pubkey>", | ||
| "tags": [ | ||
| ["p", "<pubkey-B>"], | ||
| ["nonce", "65962", "16"] // atleast 16 PoW difficulty | ||
| ], | ||
| "content": "<nip44EncryptV2(JSON.stringify({ | ||
| "kind": 13, // seal | ||
| "pubkey" "<pubkey-A>", | ||
| "tags": [], | ||
| "content": "<nip44EncryptV2(JSON.stringify({ | ||
| "id": "<...>", | ||
| "kind": 1044, | ||
| "pubkey" "<pubkey-A>", | ||
| "tags": [], | ||
| "content": "<session-privkey-A>", | ||
| "created_at": 1702711000 // now | ||
| // no .sig field | ||
| }))>", | ||
| "created_at": 0 | ||
| // ....other fields | ||
| }))>", | ||
| "created_at": 1702711000 // now | ||
| // ...other fields | ||
| } | ||
| ``` | ||
|
|
||
| ### Chat Session Acceptance | ||
|
|
||
| It uses the same `kind:1043` wrapper. | ||
|
|
||
| It has a `kind:1045` "Chat Session Acceptance" rumor. | ||
|
|
||
| It's `.content` is set to the received "Chat Session"'s content which is enough to | ||
| accept the session identified by the `.pubkey` and the `.content` (session privkey). | ||
|
|
||
| ## Chat Session List | ||
|
|
||
| The [NIP-51](51.md) `kind:10043` is the user's "Chat Session List" event. | ||
|
|
||
| It encrypts `s` (session) tags. | ||
|
|
||
| An `s` tag's 1st value is the `pubkey` of the other side of the chat. | ||
| The 2nd value is the session's `privkey`. | ||
| The 3rd value is the expiration timestamp in seconds and is three weeks ahead | ||
| of the `kind:1044`'s `.created_at` value. | ||
| The 4th value if present is set to "1", meaning the other chat participant accepted the chat session. | ||
|
|
||
| `s` tags of expired sessions should be kept to indicate active chats. | ||
|
|
||
| The user should publish the event to all of their own NIP-65 `write` relays. | ||
|
|
||
| Event example: | ||
|
|
||
| ```js | ||
| { | ||
| "kind": 10043, | ||
| "pubkey": "<pubkey-B>" | ||
| "content": "<nip44EncryptV2(JSON.stringify([ | ||
| ["s", "<pubkey-A>", "<session-privkey-A>", "1704525400", "1"], | ||
| // other "s" tags | ||
| ]))>", | ||
| // ...other fields | ||
| } | ||
| ``` | ||
|
|
||
| ## Session Channel | ||
|
|
||
| The session channel is simply made of the set of NIP-59 `kind:1059` events | ||
| with the session pubkey as their author. | ||
|
|
||
| Either the sender or the recipient may delete these events because both of them know | ||
| the session's privkey. Therefore, clients should store them locally to keep their users in control | ||
| of the DMs lifecycle. | ||
|
|
||
| **Important**: The NIP-44 encryption when wrapping and when sealing session channel events | ||
| uses the first 23 characters of the session's `privkey` as salt suffix. | ||
| This ensures forward secrecy when creating a new session because both users | ||
| are supposed to store just the last session's `privkey` | ||
| in the "Chat Session List" event. | ||
|
|
||
| ### DM | ||
|
|
||
| The DM is a `kind:14` NIP-59 rumor. | ||
|
|
||
| This event should be sent to atleast one of the recipient's NIP-65 `read` relays | ||
| and to atleast one of the sender's `read` relays. | ||
| This way, an user will only have to fetch DMs from their own set of `read` relays. | ||
|
|
||
| DM event example: | ||
|
|
||
| ```js | ||
| { | ||
| "kind": 1059, // gift wrap | ||
| "pubkey": "<session-pubkey-A>", | ||
| "tags": [], | ||
| "content": "<nip44EncryptV2(JSON.stringify({ | ||
| "kind": 13, // seal | ||
| "pubkey" "<pubkey-A>", | ||
| "tags": [], | ||
| "content": "<nip44EncryptV2(JSON.stringify({ | ||
| "id": "<...>", | ||
| "kind": 14, // DM | ||
| "pubkey" "<pubkey-A>", | ||
| "tags": [], | ||
| "content": "Hello, User B", | ||
| "created_at": 1702711000 // now | ||
| // no .sig field | ||
| }))>", | ||
| "created_at": 0 | ||
| // ....other fields | ||
| }))>", | ||
| "created_at": 1702711000 // now | ||
| // ...other fields | ||
| } | ||
| ``` | ||
|
|
||
| ### Last Received At | ||
|
|
||
| Optionally, the `created_at` of a `kind:15` NIP-59 rumor | ||
| informs the last time an user's client received DMs from the other participant. | ||
|
|
||
| This event should be sent to atleast one of the recipient's NIP-65 `read` relays. | ||
|
|
||
| ### Last Read At | ||
|
|
||
| Optionally, the `created_at` of a `kind:16` NIP-59 rumor | ||
arthurfranca marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| informs the last time an user read DMs from the other participant. | ||
|
|
||
| This event should be sent to atleast one of the recipient's NIP-65 `read` relays. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.