From b1769d5ea19d6821ac39f79ed95ca495846716bd Mon Sep 17 00:00:00 2001 From: Jeff Gardner <202880+erskingardner@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:45:25 +0100 Subject: [PATCH] Clean up NIP for real-world use --- 104.md | 65 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/104.md b/104.md index 1a2d7a0..eb47ae9 100644 --- a/104.md +++ b/104.md @@ -78,10 +78,19 @@ This NIP specifies the following: ## Creating groups -When creating a new group clients MUST create a random 32-byte hex-encoded group ID value. This identifier can be changed over the life of the group with an MLS re-init `Proposal`. See [Group Messages](#group-events) for more information. +MLS Groups are created with a random 32-byte ID value that is effectively permanent. This ID should be treated as private to the group and MUST not be published to relays in any form. Clients must also ensure that the ciphersuite, capabilities, and extensions they use when creating the group are compatible with those advertised by the users they'd like to invite to the group. They can check this info via the user's published KeyPackage Events. +When creating a new group, the following MLS extensions MUST be used. + +- [`required_capabilities`](https://docs.rs/openmls/latest/openmls/extensions/struct.RequiredCapabilitiesExtension.html) +- [`ratchet_tree`](https://docs.rs/openmls/latest/openmls/extensions/struct.RatchetTreeExtension.html) +- [`nostr_group_data`](https://github.com/erskingardner/nostr-openmls/blob/master/src/nostr_group_data_extension.rs) + +And the following MLS extension is highly recommended (more [here](#keypackage-event-and-signing-keys)): +- [`last_resort`](https://docs.rs/openmls/latest/openmls/extensions/struct.LastResortExtension.html) + Changes to an MLS group are affected by first creating one or more `Proposal` events and then committing to a set of proposals in a `Commit` event. These are MLS events, not Nostr events. However, for the group state to properly evolve the Commit events (which represent a specific set of proposals - like adding a new user to the group) must be published to relays for the other group members to see. See [Group Messages](#group-events) for more information. ## MLS Credentials @@ -90,15 +99,29 @@ A `Credential` in MLS is an assertion of who the user is coupled with a signing A `Credential` also has an associated signing key. The initial signing key for a user is included in the KeyPackage event. The signing key MUST be different from the user's Nostr identity key. This signing key SHOULD be rotated over time to provide improved post-compromise security. +## Nostr Group Data Extension + +As mentioned above, the `nostr_group_data` extension is a required MLS extension used to associate Nostr-specific data with an MLS group in a cryptographically secure and proveable way. This extension MUST be included as a required capability when creating a new group. + +The extension stores the following data about the group: + +- `nostr_group_id`: A 32-byte ID for the group. This is a different value from the group ID used by MLS and CAN be changed over time. This value is the group ID value used in the `h` tags when sending group message events. +- `name`: The name of the group. +- `description`: A short description of the group. +- `admin_pubkeys`: An array of the hex-encoded public keys of the group admins. The MLS protocol itself does not have a concept of group admins. Clients MUST check the list of `admin_pubkeys` before making any change to the group data (anything in this extension), or before changing group membership (add/remove members), or updating any other aspect of the group itself (e.g. ciphersuite, etc.). Note, all members of the group can send `Proposal` and `Commits` messages for changes to their own credentials (e.g. updating their signing key). +- `relays`: An array of the Nostr relay URLs that the group uses to publish and receive messages. + +All of these values can be updated over time using MLS `Proposal` and `Commit` events (by group admins). + ## KeyPackage Event and Signing Keys -Each user that wishes to be reachable via MLS-based messaging MUST first publish at least one KeyPackage event. The KeyPackage Event is used to authenticate users and create the necessary `Credential` to add members to groups in an asynchronous way. Users can publish multiple KeyPackage Events with different parameters (supporting different ciphersuites or MLS extensions, for example). +Each user that wishes to be reachable via MLS-based messaging MUST first publish at least one KeyPackage event. The KeyPackage Event is used to authenticate users and create the necessary `Credential` to add members to groups in an asynchronous way. Users can publish multiple KeyPackage Events with different parameters (supporting different ciphersuites or MLS extensions, for example). KeyPackages include a signing key that is used for signing MLS messages within a group. This signing key MUST not be the same as the user's Nostr identity key. -KeyPackages SHOULD be used only once. Reuse of KeyPackage Events can lead to replay attacks. In most cases, clients that implement this NIP will manage the creation and rotation of KeyPackage Events. +KeyPackage reuse SHOULD be minimized. However, in normal MLS use, KeyPackages are consumed when joining a group. In order to reduce race conditions between invites for multiple groups using the same Key Package, Nostr clients SHOULD use "Last resort" KeyPackages. This requires the inclusion of the `last_resort` extension on the KeyPackage's capabilities (same as with the Group). -The signing key (the public key included in the KeyPackage Event) is used for signing within the group that adds a new user via the KeyPackage Event. Therefore, clients implementing this NIP MUST ensure that they retain access to the private key material of the signing key for each group they are a member of. +It's important that clients immediately rotate a user's signing key after joining a group via a last resort key package to improve post-compromise security. The signing key (the public key included in the KeyPackage Event) is used for signing within the group. Therefore, clients implementing this NIP MUST ensure that they retain access to the private key material of the signing key for each group they are a member of. -In addition, the signing key MUST not be the same as the user's Nostr identity key. +In most cases, it's assumed that clients implementing this NIP will manage the creation and rotation of KeyPackage Events. ### Example KeyPackage Event @@ -113,7 +136,6 @@ In addition, the signing key MUST not be the same as the user's Nostr identity k ["mls_protocol_version", "1.0"], ["ciphersuite", ], ["extensions", ], - ["signing_key", ], ["client", , , ], ["relays", ], ["-"] @@ -126,14 +148,13 @@ In addition, the signing key MUST not be the same as the user's Nostr identity k - The `mls_protocol_version` tag is required and MUST be the version number of the MLS protocol version being used. For now, this is `1.0`. - The `ciphersuite` tag is the value of the MLS ciphersuite that this KeyPackage Event supports. [Read more about ciphersuites in MLS](https://www.rfc-editor.org/rfc/rfc9420.html#name-mls-cipher-suites). - The `extensions` tag is an array of MLS extension IDs that this KeyPackage Event supports. [Read more about MLS extensions](https://www.rfc-editor.org/rfc/rfc9420.html#name-extensions). -- The `signing_key` tag is the signing key public key. -- The `client` tag helps other clients manage the user experience when they receive group invites but don't have access to the signing key. +- (optional) The `client` tag helps other clients manage the user experience when they receive group invites but don't have access to the signing key. - The `relays` tag identifies each of the relays that the client will attempt to publish this KeyPackage event. This allows for deletion of KeyPackage Events at a later date. -- The `-` tag is optional, but can be used to ensure that KeyPackage Events are only published by their authenticated author. Read more in [NIP-70](70.md) +- (optional) The `-` tag can be used to ensure that KeyPackage Events are only published by their authenticated author. Read more in [NIP-70](70.md) ### 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. +Clients SHOULD 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. If clients cannot process a Welcome message (e.g. because the signing key was generated on another client), clients MUST not delete the KeyPackage Event and SHOULD show a human-understandable error to the user. @@ -141,9 +162,9 @@ If clients cannot process a Welcome message (e.g. because the signing key was ge Clients MUST regularly rotate the user's signing key in each group that they are a part of. The more often the signing key is rotated the stronger the post-compromise security. This rotation is done via `Proposal` and `Commit` events and broadcast to the group via a Group Event. [Read more about forward secrecy and post-compromise security inherent in MLS](https://www.rfc-editor.org/rfc/rfc9420.html#name-forward-secrecy-and-post-co). -### KeyPackage Relays Event +### KeyPackage Relays List Event -A `kind: 10051` event indicates the relays that a user will publish their KeyPackage Events to. The event MUST include a list of relay tags with relay URIs. +A `kind: 10051` event indicates the relays that a user will publish their KeyPackage Events to. The event MUST include a list of relay tags with relay URIs. These relays SHOULD be readable by anyone the user wants to be able to contact them. ```json { @@ -169,8 +190,9 @@ Clients creating the Welcome Event SHOULD wait until they have received acknowle "kind": 444, "created_at": , "pubkey": , - "content": , + "content": , "tags": [ + ["e", ], ["relays", ], ], "sig": @@ -178,9 +200,15 @@ Clients creating the Welcome Event SHOULD wait until they have received acknowle ``` - The `content` field is required and is a serialized MLSMessage object containing the MLS `Welcome` object. +- The `e` tag is required and is the ID of the KeyPackage Event used to add the user to the group. - The `relays` tag is required and is a list of relays clients should query for Group Events. Welcome Events are then sealed and gift-wrapped as detailed in [NIP-59](59.md) before being published. Like all events that are sealed and gift-wrapped, `kind: 444` events MUST never be signed. This ensures that if they were ever leaked they would not be publishable to relays. + +#### Large Groups + +For groups above ~150 participants, welcome messages will become larger than the maximum event size allowed by Nostr. There is currently work underway on the MLS protocol to support "light" client welcomes that don't require the full Ratchet Tree state to be sent to the new member. This section will be updated with recommendations for how to handle large groups. + ## Group Events Group Events are all the messages that are sent within a group. This includes all "control" events that update the shared group state over time (`Proposal`, `Commit`) and messages sent between members of the group (`Application` messages). @@ -200,17 +228,18 @@ Group Events are published using an ephemeral Nostr keypair to obfuscate the num "sig": } ``` -- 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. +- 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). However, instead of using the sender and receivers keys the NIP-44 encryption is done using a Nostr keypair generated from the MLS [`exporter_secret`](https://www.rfc-editor.org/rfc/rfc9420.html#section-8.5) to calulate the `conversation key` value. Essentially, you use the hex-encoded `exporter_secret` value as the private key, calculate the public key, and then use those two keys to encrypt and decrypt messages. +- 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. - The `pubkey` is the hex-encoded public key of the ephemeral sender. -- The `h` tag is the group ID value +- The `h` tag is the nostr group ID value (from the Nostr Group Data Extension). ### Application Messages -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. +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 normal DM or group messages, clents SHOULD use `kind: 9` chat message events. 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. +This means that once the application message has been decrypted and 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. -The Nostr event MUST use the member's Nostr identity key for the `pubkey` field. +The Nostr event MUST use the member's Nostr identity key for the `pubkey` field and clients MUST check that the identity of them member who sent the message matches the pubkey of the inner Nostr event.**** These Nostr events MUST remain **unsigned** to ensure that if they were to leak to relays they would not be published publicly.