Skip to content
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

Add NIP-104: E2EE messaging using MLS #1427

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

erskingardner
Copy link
Contributor

Take 2!

This NIP outlines a way to do secure direct and group messaging on Nostr using the MLS protocol.

Easy to read


### Clients

The device/client pair (e.g. Primal on iOS or Coracle on web) with which a user joins the group is represented as a `LeafNode` in the tree. The terms `Client` and `Member` are interchangeable in this regard. It is not possible to share group state across multiple `Clients`. If a user joins a group from 2 separate devices, their state is separate and they will be tracked as 2 separate members of the group.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be misunderstanding but can't clients share keypackages with the user or other clients? Making it interoperable within the same device?

Copy link
Contributor Author

@erskingardner erskingardner Aug 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I had been operating under the assumption that this private key couldn't/shouldn't be shared between devices but given these key packages should only be used once there is the option of encrypting the private key (to yourself) in the content field of the key package event using NIP-44 encryption. This would mean the following:

  • Any client could respond to a group welcome message (the invite to a new group) provided they had access to your nostr identity private key.
  • The client that responded to the welcome message would be the client that would be added to the group. Any other clients that you wanted to have access to the group would have to be added separately.
  • If your identity key leaks then the private key to these key package keys also leaks. Given these events are meant to be used once and deleted, I don't think this is actually that big a deal tbh.

Would love thoughts from others on this.


If a client receives 2 or more `Commit` messages attempting to change same epoch, they MUST apply only one of the `Commit` messages they receive, determined by the following:

1. Using the `created_at` timestamp on the kind `445` event. The `Commit` with the lowest value for `created_at` is the message to be applied. The other `Commit` message is discarded.
Copy link
Contributor

@abhay-raizada abhay-raizada Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Malicious clients can chose to send lower created_at values on purpose, making sure their commits are the ones that are used?
Maybe we can use NIP-03 (timestamp attestation) to make sure these timestamps are valid?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the time it takes to get the commit OTS attested will cause more troubles than it solves.

Fundamentally, a malicious client that is part of the group was added to the group by someone in the group. Anyone in the group can evict that malicious client as soon as they notice bad behavior. I think this is probably not something the spec can solve and rather is the domain of the applications that are implementing this spec to decide.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there someway to have an "authority" identified for these Commits? like a group admin or similar.

Copy link

@jmanero jmanero Sep 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the time it takes to get the commit OTS attested will cause more troubles than it solves.

Fundamentally, a malicious client that is part of the group was added to the group by someone in the group. Anyone in the group can evict that malicious client as soon as they notice bad behavior. I think this is probably not something the spec can solve and rather is the domain of the applications that are implementing this spec to decide.

Could a malicious client use a similar technique to preempt attempts to remove it?

Also consider that a client with an out of sync clock can effectively become a denial of service attack and would probably be extremely difficult to detect/mitigate unless other client software was specifically looking for such behavior.

One way to mitigate this would be to define a way to establish a quorum and push the COMMIT acknoledgement from a relay OK to a response event from N/2 + 1 of whatever constitutes that quorum to get something similar to the serialization provided by Raft or Paxos

104.md Outdated

### Deleting KeyPackage Events

Clients MUST delete the KeyPackage Event on all the listed relays any time they successfully process a group request event for a given KeyPackage Event. Clients MAY also create a new KeyPackage Event at the same time.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RFC says1 that

In order to avoid replay attacks and provide forward secrecy for messages sent using the initial keying material, KeyPackages are intended to be used only once.

However, reliable message deletion on Nostr is not possible. Especially when using a 3rd party relay, even moreso when using a bunch of relays.

Doesn't this mean MLS on Nostr can't guarantee forward secrecy? Or am I missing something?

Footnotes

  1. https://www.ietf.org/archive/id/draft-ietf-mls-architecture-13.html#name-key-storage-and-retrieval

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why users or the clients implementing this should select the relays that they publish key packages to carefully based on advertised support of event deletion and (optionally) support of the - tag to ensure that the even isn't rebroadcast to other relays.

Another mitigation strategy for this (that resides at the client/application level) is that clients could require some level of freshness for the Key Package events that they use and refuse to use Key Package events that are older than a certain time in the past.

### Goals of this NIP

1. Private _and_ Confidential DMs and Group messages
1. **Private** means that an observer cannot tell that Alice and Bob are talking to one another, or that Alice is part of a specific group. This necessarily requires protecting metadata.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still the case for a malicious or compromised Delivery Service (relay)?

The RFC says that

the DS may be able to determine group membership

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why we publish all the Group events from ephemeral keys. A sophisticated malicious relay could use IP addresses to triangulate a give user but if users are using a VPN then this is mitigated (and this is the case across all of nostr).

@rabble
Copy link
Collaborator

rabble commented Aug 17, 2024

Is this the code implementing it?

https://github.com/erskingardner/openmls_nostr_crypto

I feel like this is only part of it.

@reyamir
Copy link

reyamir commented Aug 18, 2024

I'm working on implement this NIP for https://github.com/lumehq/coop
let see how it go 😄

@erskingardner
Copy link
Contributor Author

I feel like this is only part of it.

@rabble yes, this is definitely only part of it. I started that library and intend for it to be complete but then got sucked into updating upstream dependencies to support Schnorr signatures over secp256k1. Ultimately though, it's possible for clients to be built using the other ciphersuites that are already supported by OpenMLS though so I wanted to get the spec out here for review.

The future benefit of that library you liked above will be that nostr clients will have fewer dependencies on underlying crypto primitives, that's basically it.

@erskingardner
Copy link
Contributor Author

I'm working on implement this NIP for https://github.com/lumehq/coop let see how it go 😄

🤙 Definitely feel free to DM me if you run into anything or have questions.

104.md Outdated
"sig": <signed with ephemeral sender key>
}
```
- The `content` field is a [tls-style](https://www.rfc-editor.org/rfc/rfc9420.html#name-the-message-mls-media-type) serialized [`MLSMessage`](https://www.rfc-editor.org/rfc/rfc9420.html#section-6-4) object which is then encrypted according to [NIP-44](44.md) but using the MLS [`exporter_secret`](https://www.rfc-editor.org/rfc/rfc9420.html#section-8.5) and the group ID values to calulate the `conversation key` value. The rest of the NIP-44 encryption process is the same. The `exporter_secret` value should be generated with a 32-byte length and labeled `nostr`. This `exporter_secret` value is rotated on each new epoch in the group. Clients should generate a new 32-byte value each time they process a valid `Commit` message.
Copy link
Contributor Author

@erskingardner erskingardner Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling out a small update here. The serialized MLSMessage object isn't always encrypted and even when encrypted would leak header information.

To remedy this, I'm proposing that we use NIP-44 encryption for the content field, with a twist. Since all members of the group need to be able to decrypt the message content we need to use a shared secret value that they all know.

MLS provides a mechanism to export a secret key from the current state of the ratchet tree. This exporter_secret value is labeled and can be generated to be any length. We'll generate a 32-byte value and use it along with the group ID value (also 32 bytes) to generate a value that we'll use for the conversation key in NIP-44's encryption. This will allow all participants to encrypt and decrypt these content fields and get to the messages.

The exporter_secret is rotated on each group epoch so the key will be rotated after each Commit, providing forward and post-compromise security for this content field encryption. In other words, even if an attacker got the exporter_secret value, they would only be able to decrypt group messages from a single epoch.

@erskingardner erskingardner mentioned this pull request Aug 19, 2024
6 tasks
@paulmillr
Copy link
Contributor

Farcaster implemented Signal protocol in DMs. Then they've dropped it because it was too complex to support on different devices (old / new / different libraries).

It would be great to have some kind of demo app first. I suspect it'll be quite complicated.

@kngako
Copy link

kngako commented Aug 26, 2024

Nice, how can one extend the Group creation to use FROST (Maybe even have the FROST signers as group admins)?

@erskingardner
Copy link
Contributor Author

Nice, how can one extend the Group creation to use FROST (Maybe even have the FROST signers as group admins)?

I've had a bit of a look at FROST but haven't really thought about how it might apply. For now, it's completely out of scope.

@erskingardner
Copy link
Contributor Author

It would be great to have some kind of demo app first. I suspect it'll be quite complicated.

@paulmillr working on it :)

@Sjors
Copy link

Sjors commented Aug 27, 2024

Some very initial thoughts on a potential choice of cipher suite: https://njump.me/nevent1qvzqqqqqqypzpp59a0hkv5ecm45nrckvmu7pnk0sukssvly33u3wwzquy4v037hcqyg8wumn8ghj7mn0wd68ytnddakj7qgwwaehxw309ahx7uewd3hkctcqyprq4w327fynj7wa6lha6pktmer8c0979ds0sc89cgsmh5af4pet60p3dcc

@erskingardner looking forward to see your demo app, and what dependencies it pulls in. That might help inform the above.


When a new user is added to a group via an MLS `Commit` message. The member who sends the `Commit` message to the group is responsible for sending the user being added to the group a Welcome Event. This Welcome Event is sent to the user as a [NIP-59](59.md) gift-wrapped event. The Welcome Event gives the new member the context they need to join the group and start sending messages.

Clients creating the Welcome Event SHOULD wait until they have received acknowledgement from relays that their Group Event with the `Commit` has been received before publishing the Welcome Event.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does an "acknowledgement from relays" look like?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just that you get an ack from a relay when sending the event. Same as any other event that you publish.

104.md Outdated

Application messages are the messages that are sent within the group by members. These are contained within the `MLSMessage` object. The format of these messages should be unsigned Nostr events of the appropriate kind. For example, if a user sends a text note to the group, it would be a `kind: 1` event. If the user reacts to a message, it would be a `kind: 7` event.

This means that once the application message has been deserialized, clients can store those events and treat them as any other Nostr event, effectively creating a private Nostr feed of the group's activity and taking advantage of all the features of Nostr.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "private Nostr feed" makes me think of the local relay ideas. And the various *box models that have surfaced.

@EthnTuttle
Copy link

Nice, how can one extend the Group creation to use FROST (Maybe even have the FROST signers as group admins)?

This makes me wonder how Signal does group admin stuff. They have a nice features to tweak within groups. For these different MLS events, is it expected that all participants are publishing all event types for the group?

@paulmillr
Copy link
Contributor

Signal groups is just a bunch of 1-to-1 messages. Group settings are stored in the server in enclaves.

@github-tijlxyz
Copy link

This looks super great, I have been thinking about something like this for a while and will maybe try building a client for it. Any update on the more specific example process/app?

@erskingardner
Copy link
Contributor Author

This looks super great, I have been thinking about something like this for a while and will maybe try building a client for it. Any update on the more specific example process/app?

I'm working on a reference client right now @github-tijlxyz I'll let everyone know here and on nostr when it's ready. :)

@erskingardner
Copy link
Contributor Author

This is ready for review. I've made some updates based on what I'm doing in White Noise and while I don't have fully completed libraries, I would love to start working with anyone that is interested in implementing this to help find more places where we need supporting library code.

Obviously, I'd love to merge this NIP once we get some more eyes on it.

@fiatjaf @staab @vitorpamplona @pablof7z @paulmillr @Sjors @v0l @hzrd149

@paulmillr
Copy link
Contributor

@erskingardner do you have a fully working MLS over nostr implementation in place?

@erskingardner
Copy link
Contributor Author

Yes. https://github.com/erskingardner/whitenoise

I'm still having some platform specific issues (nothing related to MLS) before it's ready for distribution but it works end to end.

@paulmillr
Copy link
Contributor

  1. Post compromise security means that leaking key material doesn't allow an attacker to continue to read messages indefinitely into the future.

A brief description of how this is solved would be useful. I know MLS is supposed to solve it, but an ELI5 version would nonetheless be helpful. Like, what exactly happens after user's messaging key is leaked. Or if their private key is leaked.

@erskingardner
Copy link
Contributor Author

A brief description of how this is solved would be useful. I know MLS is supposed to solve it, but an ELI5 version would nonetheless be helpful. Like, what exactly happens after user's messaging key is leaked. Or if their private key is leaked.

@paulmillr do you think it's worth having a section in the NIP about the threat model and various compromises possible? Or are you just looking for an explanation here? MLS has a specific section I can link to that goes into lots of detail on this for MLS specifically. There are some nostr specific things I could add though.

@paulmillr
Copy link
Contributor

MLS spec is hard to read. I think a simple section describing how things improve over the status-quo would be useful, including compromises. Best to keep it concise.

@erskingardner
Copy link
Contributor Author

Added a section on Security considerations with some basic details and links to MLS docs on the more in-depth stuff.

@alexfj0
Copy link

alexfj0 commented Nov 25, 2024

is it the idea that we can try Whitenoise? i can't get it to work with the several dependencies

@erskingardner
Copy link
Contributor Author

Which dependencies?

@alexfj0
Copy link

alexfj0 commented Nov 25, 2024

I mean if I should try to run whitenoise now, because I think there are some problems like a release tag missing for https://github.com/erskingardner/nostr-openmls which is specified here https://github.com/erskingardner/whitenoise/blob/master/src-tauri/Cargo.toml and some of the dependencies there point to local directories.

@erskingardner
Copy link
Contributor Author

Ah yeah, good catch. I've been making changes to those libraries locally. Let me try and get a buildable version done tomorrow. Fair warning. I've been building locally on a mac and things seem to be working well but once I try and run the app on any other platform I get a lot of strange behavior which I'm working on fixing now.

@erskingardner
Copy link
Contributor Author

@alexfj0 you should be able to build the app now, I've updated the dependencies to point to GH.

To test out groups and messages, it's easiest to do the following:

  1. Sign in with an account that is following a few users (hopefully at least one other account that is yours).
  2. Go to settings, sign in with the second account.
  3. Publish a key package event from the settings page.
  4. Switch to the first user.
  5. Go to the chats screen, click the plus icon at the top right, select your other user and invite them to start a chat.
  6. Once the group is created, you can try sending a few messages.
  7. Then switch to the other user. You should see an invite waiting for you.
  8. Accept that invite and then you can start replying to the other user.

Let me know if you have any troubles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.