-
Notifications
You must be signed in to change notification settings - Fork 721
Shared Key DM #945
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
Shared Key DM #945
Changes from 6 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
afd7827
Add nym dm
arthurfranca 9520d4d
Add client behavior section
arthurfranca 7c21198
Use conversation key instead of random pseudonyms
arthurfranca 8c86f8c
Fix the proof event
arthurfranca bdb45b0
Add info about what relays to pick
arthurfranca 1c8ccb5
Fix DM example's wrapper pubkey
arthurfranca 7d63c74
Add a p tag to the gift wrap
arthurfranca a06c0ca
Expand on the chat request resending topic
arthurfranca c0aa0e6
Rename Active Chats to Approved Chats
arthurfranca b39d5b0
Require PoW for Chat Requests
arthurfranca d0f70af
Suggest chat request event byte size limit
arthurfranca 847fb74
Change Chat Request event format
arthurfranca aaa44ee
Add flow summary
arthurfranca 4229bea
Add a section to address the spam issue
arthurfranca 1a88692
Add a way for the sender to delete a DM
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,115 @@ | ||
| NIP-43 | ||
| ====== | ||
|
|
||
| Shared Key DM | ||
| ------------- | ||
|
|
||
| `draft` `optional` | ||
|
|
||
| This direct message (DM) scheme between two participants aims to hide metadata while keeping the regular client-relay filtering experience. | ||
|
|
||
| ## Chat Request | ||
|
|
||
| A `kind:1043` "Chat Request" event is sent from user A to B. If user A has sent a considerable amount of messages with no reply from user B, | ||
| it can send to B another "Chat Request" event. | ||
|
|
||
| The chat request uses a random pubkey to hide who's the requester. It wraps an encrypted `kind:30043` "Pubkey Owner Proof" event | ||
| signed by user A's privkey, proving it owns the key as long as the event has a `created_at` not too far in the past. | ||
|
|
||
| The inner event is a parameterized replaceable one with no `d` tag to make it hard to be accepted by relays if the receiver tries to publish it. | ||
| It has a `p` tag with the receiver pubkey to make it focused to this specific interaction, to avoid its reuse. | ||
| It has an empty `.content` so that it has little value for spammers (there is no attached message to show to the receiver). | ||
|
|
||
| This event should be sent to atleast one of the receiver's [NIP-65](65.md) `read` relays. | ||
|
|
||
| ```js | ||
| { | ||
| "kind": 1043, | ||
| "pubkey": "<random>", | ||
| "tags": [ | ||
| ["p", "<pubkey-B>"] | ||
| ] | ||
| "content": "<nip44Encrypt(JSON.stringify({ | ||
| "kind": 30043, | ||
| "pubkey": "<pubkey-A>", | ||
| // No "d" tag | ||
| "tags": [ | ||
| ["p", "<pubkey-B>"] | ||
| ], | ||
| "content": "", | ||
| "created_at": 1702711000 // now | ||
| // ...other fields | ||
| }))>", | ||
| "created_at": 1702711000 // now | ||
| // ...other fields | ||
| } | ||
| ``` | ||
|
|
||
| ## Active Chats | ||
|
|
||
| When receiving a chat request from user A, an user B can accept it or ignore it. | ||
| The user accepts it by adding the sender's pubkey to its `kind:10043` "Active Chat" event's list of encrypted `p` tags. | ||
|
|
||
| B's client should auto accept it if A is one of its follows or contact. Else | ||
| the client should show A's `kind:0` metadata and link to its profile to help with B decision. | ||
|
|
||
| When sending a chat request, the sender auto adds the receiver's pubkey to its own "Active Chat" event. | ||
|
|
||
| The user should publish the event to all of its [NIP-65](65.md) `write` relays. | ||
|
|
||
| ```js | ||
| { | ||
| "kind": 10043, | ||
| "pubkey": "<pubkey-B>" | ||
| "content": "<nip44Encrypt(JSON.stringify([ | ||
| ["p", "<pubkey-A>"], | ||
| // other "p" tags | ||
| ]))>", | ||
| // ...other fields | ||
| } | ||
| ``` | ||
|
|
||
| ## Direct Message | ||
|
|
||
| After sending a chat request and updating its own active chats, user A is able to send `kind:14` Shared Key DM events | ||
| even though it doesn't know if B will ever fetch and read them. | ||
|
|
||
| Both A and B use their [NIP-44](44.md) "Conversation Key" they have in common as a private key to sign a [NIP-59](59.md) `kind:1059` wrapper event | ||
| (with `kind:13` seal). It wraps the encrypted DM event authored by the real user's pubkey. | ||
|
|
||
| The "Conversation Key" is used to encrypt the inner events. | ||
|
|
||
| There is no `p` tag on the wrapper event because the two participants will fetch messages by the pubkey derived from the "Conversation Key". | ||
| This way no spam can be sent to any of the participants and the new messages download is finished faster. | ||
|
|
||
| The correct current date is used on the wrapper event. | ||
|
|
||
| The inner `kind:14` event MUST NOT include a `.sig` field to avoid it being published to relays. | ||
|
|
||
| ```js | ||
| { | ||
| "kind": 1059, // gift wrap | ||
| "pubkey": "<conversation-key-AB-derived-pubkey>", | ||
| "tags": [] | ||
| "content": "<nip44Encrypt(JSON.stringify({ | ||
| "kind": 13, // seal | ||
| "pubkey" "<pubkey-A>", | ||
| "tags": [], | ||
| "content": "<nip44Encrypt(JSON.stringify({ | ||
| "kind": 14, // DM | ||
| "pubkey" "<pubkey-A>", | ||
| "tags": [], | ||
| "content": "Hello, User B", | ||
| "created_at": 1702711000 // now | ||
| // ....other fields without .sig | ||
| }))>", | ||
| "created_at": 0 | ||
| // ....other fields | ||
| }))>", | ||
| "created_at": 1702711000 // now | ||
| // ...other fields | ||
| } | ||
| ``` | ||
|
|
||
| This event should be sent to atleast one of the receiver's [NIP-65](65.md) `read` relays and also to one or more of the sender's `read` relays. | ||
| This way, an user will only have to fetch DMs from its own set of `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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it is better to keep things consistent and use random keys to sign but p-tag the conversation key...
In that way, it's impossible for the public to know if this is somebody's "nym"/shared key or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ptag downside is someone can flood the 2 participants with bogus gift wraps, forcing them to download garbage just for fun.But you have a good point.
While we don't decide the best approach I will just add a random p tag instead to make it look like a regular gift wrap. Of course this is rather weak as repeating the pubkey field value on gift wraps is supposed to be very hard to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They can do the same with the pubkey... With or without the random p tag. They just need to see many wraps shipped to the same pubkey and blast it.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in the case of DMs cause the filter to fetch them will be
{ authors: ["<conversation-key-AB-derived-pubkey>"], kinds: [1059] }and only the participants can sign valid events with that key (relays aren't supposed to store events with wrong sig).While if using
{ #p: ["<conversation-key-AB-derived-pubkey>"], kinds: [1059] }anyone can send a valid event with that p tag.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yes, makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes "Chat Requests" can be spammed though I think it's a lesser problem to the user cause 1) the spam won't pollute the active chats list; 2) it may not be that tasty of a target to spammers cause it won't carry a message with links and such; 3) The client may not need to download all chat requests (read below).
With number 3 I mean the receiver client could just fetch the last
Nchat requests and ignore the rest.Ncould be a number relative to the number of days the user hasn't opened the app or simply a fixed number like 500. I wouldn't store/sync chat requests on the local DB at all, just always fetch a recent list from relays.This works ok if the sender client is wise enough to send duplicate chat requests to the same user that has never replied with a DM just in case the receiver missed the previous request.
This NIP would force the client to have one tab for approving chat requests (may be flooded yeah) and another tab for all (approved) DMs with no spam.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to highlight this a bit when I wrote on the NIP this: "If user A has sent a considerable amount of messages with no reply from user B, it can send to B another "Chat Request" event."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which is the same we do for regular GiftWrapped DMs.
My point is that if you are trying to solve the spam bloat, this is not doing it. The client still needs to download all requests (which can be made huge -- 100KB each) to see which ones are real and which ones are not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes someone can flood your chat request inbox and unfortunately this can't be avoided but just rate limited by relays =(
There's a slight difference though on regular non-malicious usage: An user A is supposed to send one or a few chat requests to user B and send an unlimited number of DMs to B. But the difference is that if the chat request wasn't approved, the B client will never download any of A's DMs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vitorpamplona I've added suggestion for relays to block chat request events higher than 3KB.
Added PoW requirement to Chat Requests (enforced by the receiving clients). From the user point of view, sender client can send DMs freely (while it is mining the Chat Request PoW in the background).
Receiving client won't ever download any of someone's DMs if the receiving user hasn't approved the specific chat request.