-
Notifications
You must be signed in to change notification settings - Fork 598
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
NIP-112: Encrypted Group Events #580
base: master
Are you sure you want to change the base?
Conversation
Is it correct that this doesn't hide who is posting to the group? If so , can we also use nip76 style encapsulation to hide the pubkeys of who is posting? As long as we're combining several nips, might as well add this valuable feature. Also, it should be made explicit in the NIP how to specify (multiple) relays for the chat... This was not adequately documented in nip-28, seems like the correct way is multiple e-tags in the metadata. |
please look at draft nip59 Maybe I should reproduce it here The sender always rolls a new public private key pair every time they post to the group. indeed every message is sent this way even the invitations. there's an outer message (using an ephemeral public private keypair) and an inner message (which is signed by the actual sender) Only members can verify the inner events and clients must validate signatures, since the relays cannot do it I will make this more clear
agreed I will make that modification that is the correct way |
So the gift wrap is with a temporary key? That's fine but where do the other chat participants get the key from? I would understand if the wrapping for each message is done with the same key as is used to get access to the encrypted room, but if a new key is used each time, then where do the others get the key from? |
|
keep in mind that Signal group chats are basically a bunch of 1-to-1 chats. There is no shared secret. I wonder which solution is better. |
@earonesty ok i get it. sorry for the confused question. thanks for explaining. I like this nip. Would implement. +1 on the relay thing. |
agreed, it's all tradeoffs. a shared secret with frequent rotations has some weaknesses and some efficiencies. pros:
cons:
it seems to be working so far. code isn't complete (indeed, i dont handle owner transfers properly, or even creation the way the nip says), but you can build the app right now off main and start chatting and the security properties are the same. |
No need to be a 1-char (indexed) tag. Relays is just a list of recommended relays for the whole channel. Owner can change this in the META changes or when doing 404 key rotations.
use "r" tag to match other nips
This is a very well written nip, but it states:
Which I don't think is true. I recently wrote a note about this topic, which you can read here. It shares the gift-wrap approach, but makes different tradeoffs. The main trade-off of my idea is that it relies on an access-controlled nsec bunker rather than a private key shared directly over the network. I tend to think that's an improvement, because you get a lot more flexibility and control, not to mention consistency, without making clients do super complicated things to get the right channel metadata. My main point here though is that my approach basically creates a generic cryptographic subset of the entire nostr protocol using gift wrap, rather than specifying each individual component independently. Private chat is an obvious start, but with this PR every time a feature is added to chat, two NIPs need to be updated. Likewise, every time we want encryption for some other part of the protocol (e.g. kind 1s), a new NIP needs to be added. I would strongly prefer to see a new NIP that specifies a generic way to 1. define channels based on a shared public key, 2. communicate over gift-wrapped channels, and 3. specifies multiple ways to manage the key rotation (nsec bunker and DM'd private keys are both viable alternatives, as well as FROSTR potentially). |
genuinely confused. how does this not satisfy 1 and 2? "bunker" based approach can be used if you simply don't invite people. you can make an invitation to the bunker instead. probably it's not more secure though, also not decentralized. also requires an always-on, non-nostr server. the purpose of this protocol is to be a pure nostr encrypted chat that satisfies all the needs of people who want encrypted chat without having to change relay server requirements |
Yes, I just think the authentication method should be decoupled from the data structure so you can more easily choose your solution.
Also, true, but more flexible and simpler. It doesn't make sense to me to re-implement centralized administration features on top of a decentralized, eventually consistent network. My main point was the "a generic way" bit, which probably should have been its own bullet point. This NIP solves chat, but it doesn't solve private kind 1 for example, or anything else. I think we should start by creating a way to generically create "groups" within which a nostr sub-network can be run. That automatically solves groups, along with many other problems. Again, I know the nsec bunker vs in-protocol key management is contentious, specifying the auth strategy when creating the group would be a good way to decouple and keep possibilities open for future improvements. I will try to create a NIP later this week so I can explain myself more concretely. |
#468 does what you want. it does solve private kind 1. you can use the groups in this NIP for for non-chat group applications. like diagon alley events. it's just a group of people that know the same private key. you can send anything you want. because #468 allows for any kind to be sent. all you have to do is get rid of the "send chat message" - and replace it with wrapped-kind-1 instead of wrapped-kind-403. see? then you can just say "wrapped-kind-X" is a generic "Send to group anything you want" but @fiatjaf chimed in on this last week in another message. He thought "kind-1-delivered-to-a-group" was more confusing than just using a new number for group-events, and that it was better to simply invent a new kind for every "encrypted group thing" or "encrypted direct thing" and then reference the nonencrypted version if you want to stay in sync. because you still want a different UX for "kind 1 delivered encrypted just to me" and "kind 1 delivered to an encrypted group" |
Awesome, I see, so apart from the nsec bunker vs in-protocol group management tradeoff (which we agree can coexist), we are on the same page. To make things clearer, I'd suggest removing the term "chat" entirely from this PR (except in the motivation/use cases section), and defining the term "channel" or "group" at the top of the NIP as something like the nostr subnet concept. I can then create my own PR for nsec-bunker-controlled channels at some future date. |
that sounds reasonable, thanks. |
Ok, now that we have that figured out, let me propose a slightly different design. Instead of following the NIP 40 channel definition model, what about overloading pubkeys? IOW if a pubkey represents an "entity" rather than a "person", you could create a "channel" by publishing a kind 0 (for public metadata), or a gift-wrapped kind 0 (for private metadata). Then all these 4xx events would be used to extend/revoke the ability for channel "members" to publish data on behalf of/to the group. This seems simpler, since you need a shared key anyway, you might as well refer to self rather than a sort of arbitrary event id. The same NIP could also be used to implement key rotation for regular accounts. I don't think it would increase the attack surface area for rogue group members by widening the scope of published events to public, since they have the private key anyway in your scenario and could publish weird public claims on behalf of the whole group. One other small thing, might 405/406 be expanded to support multiple admins? |
this is a generic "group events" system
what key would you encrypt with? still a shared key? actually, that's how i built it in the first place, there is no "create" event in arclib - just invitations with metadata and a privkey. but there's no difference because any time you add/remove members, you need a new "entity" (kind 0), and you wind up pointing to a bunch of those to get a channel history. im ok with either, but.... storing encrypted metadata in kind0 is weird, imo. i think @fiatjaf wouldn't like it from other convos also by wrapping everything and never using kind0, you get a bigger anonset. you wind up with a lot of symmetry in the protocol, lots of reusable code (everything is wrapped, the server knows nothing about the group)
you can delegate multiple "p" tags in a delegation event. but then you have to worry about forks (if 2 admins revoke around the same time, you can fork the group). can add moderation too, by giving people the wrong privkey in an invite... but the right pubkey for the channel, and then reposting their rewrapped-events after moderation happens. would like to point out this works very well, and people using it see snappy performance and a nice experience. working on push notifications... no way to know what event was pushed, but if you tell our service a pubkey, we'll tell you're phone |
Yeah, you would just do symmetric encryption using the group's key.
I see what you mean. The approach I propose would make the nsec bunker approach simpler, but wouldn't help at all with the in-protocol approach since you have to rotate keys anyhow. Thanks for working through my questions, I believe I can retrofit a custodial key approach onto either this nip or the gift wrap one directly if I end up going that direction. |
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.
Some fairly minor suggestions to try to make things clearer. Other thoughts:
- What about creating two new key pairs? One for group moderators, and one for group members. Posting about the group would be done by keypair A, posting to the group would be done by keypair B. This would make it easier to ignore malicious events.
What's the deal with event deletion in this context? I think the owner of a channel should be able to delete events. Why not extend nip-09 so that channel owner pubkey is additionally valid to delete any event in the channel, not just the author of the event. |
Co-authored-by: Jon Staab <jstaab@protonmail.com>
Co-authored-by: Jon Staab <jstaab@protonmail.com>
one cool thing i realized with this is that the relay url is also hidden |
if the author wants to be able to delete an event at the relay, then the author needs to use a "derived key" instead of an ephemeral key. we can write-up what this means, but consider something very simple:
this then allows the author to delete an event later (at the relay level!). the key is no longer "ephemeral" i would be unwilling to put this in the spec without at least one other cryptographer chiming in |
Co-authored-by: Jon Staab <jstaab@protonmail.com>
## Kind 1059: Encrypted Gift Wrap | ||
|
||
For all encrypted group events, the client first generates the approrpiate kind 4XX message, as below, and signs it with the user's identity key. | ||
|
||
Then they generate a new public/private keypair, and uses this to sign new kind 1059 message. | ||
|
||
The `content` of that message is a [NIP-44](https://github.com/nostr-protocol/nips/pull/574) encrypted JSON string. | ||
|
||
The content is encrypted using the public key of the destination (the chat room public key, or the invitee public key. | ||
|
||
All events called "wrapped-kind XX" refer to these kind 1059 gift wrapped events. | ||
|
||
For more information on gift-wrapping, see: [NIP-59](https://github.com/nostr-protocol/nips/pull/468) |
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.
## Kind 1059: Encrypted Gift Wrap | |
For all encrypted group events, the client first generates the approrpiate kind 4XX message, as below, and signs it with the user's identity key. | |
Then they generate a new public/private keypair, and uses this to sign new kind 1059 message. | |
The `content` of that message is a [NIP-44](https://github.com/nostr-protocol/nips/pull/574) encrypted JSON string. | |
The content is encrypted using the public key of the destination (the chat room public key, or the invitee public key. | |
All events called "wrapped-kind XX" refer to these kind 1059 gift wrapped events. | |
For more information on gift-wrapping, see: [NIP-59](https://github.com/nostr-protocol/nips/pull/468) | |
## Kind 1059: Encrypted Gift Wrap | |
All events called "wrapped-kind XX" refer to kind `1059` gift wrapped events, using an ephemeral key for signing, and a known public key for the recipient (a chat room public key, or invitee public key depending on the event kind). | |
For more information on gift-wrapping, see: [NIP-59](https://github.com/nostr-protocol/nips/pull/468) |
@earonesty I don't really understand your logic regarding deletion. Why should the author have to use a derived key or do anything special? I'm suggesting that the standard practice be that the room creator should have the power to delete events. If this is something that makes sense, then we don't necessarily need extra logic -- just specify in the nip that the creator of the room pubkey can delete events that are part of the channel. |
@fyookball The relay does not know that the inner event (gift-wrapped) is a chat message. So you can't edit NIP-09 (event deletion) the way you want. There are only
|
@arthurfranca @earonesty i'm fine with the deletion being done client-side. As long as its nip-standardized it will work across any clients who implement. It doesn't have to be strict, but if it's there, it will improve the user experience. |
@earonesty taking advantage of your encryption knowledge, is there a way any group member could derive a pubkey from the channel owner's pubkey (+ shared group key? or kind 403 event id? or kind 403 event hash? or channel create event id? or channel create hash) in a way that the channel owner with its privkey could derive the corresponging privkey? Sorry that I have no idea on this. If it was possible, the kind 403 wrapping events could have:
If possible, the ! tag derived value would hide the real channel owner pubkey while allowing the channel owner to delete kind 403 channel messages by signing kind 9 (updated with this relay rule) events using corresponding privkey. Currently the channel owner lacks ability to delete kind 403 messages (on relays, not client-side). |
the only way a user can delete a message is if they prove they own the key that produced the message. this can be done deriving keys |
Today yes, but look at the ! tag proposal I linked on comment above. That's why I asked if such a keypair could be derived so to be used as ! tag. |
maybe there can be a boolean flag whether to allow owner to delete events. So you could have moderated channels like Telegram and open channels like Reddit without centralized moderation. |
My reading says that there can be one and only one group owner? |
that's the current construction. technically there's nothing stopping you from delegating to moderators or multiple owners with a group-update message. they're just tags in the message that have to chain back the the creator. can just update the nip to include all that or release as-is, and wait for PR's from people who actually have experiences that require these changes. |
The proposal does not seem to have a 'member's list'. One result of that would be that the members would not 'know', or the clients will not be able to find out, whether the messages that are being received for the group are from invited members, or whether some interloper is also sending messages ( by getting the shared secret somehow). I think for this kind of sanity check, a membership-list is desirable; NIP 38 has such a list, and I find that this kind of list will help people only interact with members who are invited, and will reduce bad-use cases of interlopers using group etc. A 'members list' will also improve UI, in that the clients can show who the members of a group are. So my suggestion would be that only the admin should send invites, and at that time also update the members list. So when user is added to group ( by admin), then the members list is updated in kind 402, etc. |
@vishalxl I don't like the membership-list idea because it's not going to be accurate. Being in the list is no guarantee those people have even viewed the channel once. It wouldn't be so bad if it was a one-time thing, but the overhead of continually updating a list with poor accuracy doesn't seem great. |
I see the following issues with this NIP Pull Request.
|
agreed, simpler is better
agree. ownersig not needed (handled with giftwrap). not sure camel is consistent with other nips, but i agree.
yes, we can do that, replaced-by can just point to the new owner's channel
moderator is a nice feature. list of pubkeys
agreed. kind 1 is fine
this is not possible. the relay cannot verify deletion without the sender remembering the key. you can have the client "agree to delete it", but you cannot have the relay delete it unless the sender uses a tweaked "sending key" and then can delete later. i am implementing this. sender will not use emphemeral key, an will instead use tweaked (private key * event id)... so we can delete for real.
agreed. that's baked in to the gift-wrap. no need to change it.
again, that's baked into gift wrap, all public keys are unique for each message. |
but the new owner needs to say { "replaces": "original channel id" } that way they are consenting up front doing it without consent is a recipe for problems. ... but then it creates a weird UX. the new owners needs to create a group "as a replacement" first. then the new owner needs to tell the old owner about it. then the old owner can delegate. this is reminding me of why i added those messages in the first place. it's a better flow:
|
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 NIP38, with the exception that gift-wrap is used on every message with an ephemeral public key, and issues like metadata leakage and optional forward secrecy are addressed. Also lists of participants are not published.
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. By using NIP-59, it effectively hides metadata from external users (and from the relay).
All messages are posted using the "gift wrap" protocol, meaning no information about the sender is visible to non-participants.
Because all messages are gift-wrapped, you can send any "kind" of message to the recipients. The use case of "403 group chat" is one such use case.