From b0dbdde53ba539ff910a526ddc9b5aef61836f92 Mon Sep 17 00:00:00 2001 From: threeseries Date: Thu, 4 May 2023 21:39:12 -0500 Subject: [PATCH 1/8] NIP-103: Encrypted direct message envelopes --- 103.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 103.md diff --git a/103.md b/103.md new file mode 100644 index 0000000000..08f463279d --- /dev/null +++ b/103.md @@ -0,0 +1,18 @@ +NIP-103 +======= + +Direct Message Envelopes +------------------------ + +`draft` `optional` `author:threeseries` + +This NIP defines a kind 16 event which is a kind 4 event (see [NIP-04](04.md) where the decrypted content is itself a kind 4 or kind 16 event. +If the inner event is kind 16 it must be possible to eventually reach a regular kind 4 after enough rounds of decryption. + +# Motivation + +It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted and only revealed to an intermediate party responsible for relaying messages to their intended recipients. + +On receiving a kind 16 event such a service would decrypt the content and then determine the kind and recipient of the inner event. If the event is kind 4 it should be re-encrypted and then forwarded along to its destination. If the event is kind 16 it should be forwarded without additional encryption. This latter behavior enables forwarding rounds to be chained together before reaching the final recipient, with a layer of nesting being removed at each step. + +Ordinary clients might also opt into being "forwarders" themselves, creating a large mesh network to anonymize everyone's direct messages. To add further anonymity, forwarding could also be configured to inject timing delays (for instance, using the difference in `created_at` timestamps between the outer and inner events), or to only forward messages once enough have been collected into a queue, thus creating a type of "message join" on nostr. From 4bc43f41b7b8cb97136d8497430b132ef3374c70 Mon Sep 17 00:00:00 2001 From: threeseries Date: Thu, 4 May 2023 22:49:31 -0500 Subject: [PATCH 2/8] Clarify --- 103.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/103.md b/103.md index 08f463279d..028ac17838 100644 --- a/103.md +++ b/103.md @@ -11,8 +11,12 @@ If the inner event is kind 16 it must be possible to eventually reach a regular # Motivation -It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted and only revealed to an intermediate party responsible for relaying messages to their intended recipients. +It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted and only revealed to an intermediate party responsible for forwarding messages to their intended recipients. -On receiving a kind 16 event such a service would decrypt the content and then determine the kind and recipient of the inner event. If the event is kind 4 it should be re-encrypted and then forwarded along to its destination. If the event is kind 16 it should be forwarded without additional encryption. This latter behavior enables forwarding rounds to be chained together before reaching the final recipient, with a layer of nesting being removed at each step. +# Usage -Ordinary clients might also opt into being "forwarders" themselves, creating a large mesh network to anonymize everyone's direct messages. To add further anonymity, forwarding could also be configured to inject timing delays (for instance, using the difference in `created_at` timestamps between the outer and inner events), or to only forward messages once enough have been collected into a queue, thus creating a type of "message join" on nostr. +On receiving a kind 16 event a forwarding service should decrypt the content and then determine the kind and recipient of the inner event. If this event is kind 4 it should be re-encrypted and then forwarded along to its destination. If the inner event is kind 16, then its content must itself be decrypted (hence the content of the outer and inner events need to be encrypted with the public key of the outer event recipient), and the inner event recreated using the keys of the forwarding party. Recreating the event in this way serves to remove any association between the message and the original sender before being forwarded on to the next recipient. And by allowing for forwarding rounds to be chained together in this way we enable use cases that would add further plausible deniability as to the source of a message. + +One such use case would be if ordinary clients were allowed to opt into being forwarders themselves. Then one could send a kind 16 to any one of these users, and when they forward the message along it's impossible to know if they are forwarding a message or are the source of a message themselves. This could create a large mesh network within which it would be nearly impossible to interpret who is messaging whom. + +To add further anonymity, forwarding could also be configured to inject timing delays (for instance, using the difference in `created_at` timestamps between the outer and inner events), or to only forward messages once enough have been collected into a queue, thus creating a type of "message join" on nostr. From 3fdc2bcf30b677b6aa7cc5147b6f7d2dbdf9152f Mon Sep 17 00:00:00 2001 From: threeseries Date: Fri, 5 May 2023 08:40:50 -0500 Subject: [PATCH 3/8] Simplify the NIP --- 103.md | 59 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/103.md b/103.md index 028ac17838..4d865e2459 100644 --- a/103.md +++ b/103.md @@ -6,17 +6,48 @@ Direct Message Envelopes `draft` `optional` `author:threeseries` -This NIP defines a kind 16 event which is a kind 4 event (see [NIP-04](04.md) where the decrypted content is itself a kind 4 or kind 16 event. -If the inner event is kind 16 it must be possible to eventually reach a regular kind 4 after enough rounds of decryption. - -# Motivation - -It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted and only revealed to an intermediate party responsible for forwarding messages to their intended recipients. - -# Usage - -On receiving a kind 16 event a forwarding service should decrypt the content and then determine the kind and recipient of the inner event. If this event is kind 4 it should be re-encrypted and then forwarded along to its destination. If the inner event is kind 16, then its content must itself be decrypted (hence the content of the outer and inner events need to be encrypted with the public key of the outer event recipient), and the inner event recreated using the keys of the forwarding party. Recreating the event in this way serves to remove any association between the message and the original sender before being forwarded on to the next recipient. And by allowing for forwarding rounds to be chained together in this way we enable use cases that would add further plausible deniability as to the source of a message. - -One such use case would be if ordinary clients were allowed to opt into being forwarders themselves. Then one could send a kind 16 to any one of these users, and when they forward the message along it's impossible to know if they are forwarding a message or are the source of a message themselves. This could create a large mesh network within which it would be nearly impossible to interpret who is messaging whom. - -To add further anonymity, forwarding could also be configured to inject timing delays (for instance, using the difference in `created_at` timestamps between the outer and inner events), or to only forward messages once enough have been collected into a queue, thus creating a type of "message join" on nostr. +This NIP defines a kind 16 event which is a kind 4 event (see [NIP-04](04.md) where the decrypted content is itself a kind 4 event. + +# Motivation and usage + +It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted before being sent. This outer event can be encrypted and signed with a random key pair, thus hiding the true sender of the direct message. On receiving a kind 16 event the client should decrypt both the outer and inner content, displaying the decrypted inner content to the recipient. + +# Example: + +A kind 4 event: + +```json +{ + "id": "67264df8e079a7cf52f81b912debb4e47550743f1f4a5f170407f83bc9dbc12b", + "pubkey": "6475c0ba9c8f5f45dcfdae553189d1b8d089118295ba5b902c0a698e192f535b", + "created_at": 1682886605, + "content": "z2HVzkQXJAJSqebsnrkNWg==?iv=O0pf3XLsEkOo+G+/QosxDg==", + "kind": 4, + "tags": [ + [ + "p", + "208404de380e7c02c366cc667ae9e969d687ec7a3c03aacd364c4716a2e72327" + ] + ], + "sig": "1626034f09101c120f968ef1da14dd23e4e4b14db22227737ad717cc9033188e0426cf0993a0c776b8a5156ef6ece939f50f575ce294b9689864b46f49b5e8c6" +} +``` + +After wrapping in kind 16 with a random key pair: + +```json +{ + "id": "b8b50e4c63102e5c737186e7ed7c23741aea34a7743ea27a41e6a18654261818", + "pubkey": "4c34b0fb27f79d376456f95391b1b43173d890d3e08558fe1e0f56cea59af52a", + "created_at": 1683293572, + "content": "se/NybRw+cM1gMbbjOrg0eP9GWUjJrLP5wBYI2b7CHLHjoP3fv88cycysQQWIQ7MPj5sHvLBJQaGNWWsSP9WLocRt0sZdZtXHwYhshYnpO0lvZxmkthcYqJ3b9I6pHUGcV1G4v9qSUjhvfCnHLf5vIh36XhN0aADUZppR2Yix0C/aCZIyY8xs6HJh8BQyGaDfdO2KsSNCgfxFJe2hXwGjBe9M/MnBos2Kqi3U/Z2Vhqy4tqrbthpP+/dJT4m1FhANJNCfPGrgRdvH00GOm2l/BTxA/GtSo8gOjmkC+uLxI3kNK4+Vb+SuOM5JbAzyMyc3XkxmgS9hlNAyZGyX3vbDBKxhRt0iK0kYg0pw9Gpna2rrBu8qO7khDiZKk5L/pqGrSWXnQ8RcHPkaKgsTTk4XTn5jZzfukFOG/5Zawdbl8nhtl4kYPQSU0pEXrkd89AuW7O4JRqwSXzni/NidES+vD/35RGelVtebFYGvK7TDO8A0V9hOIPKa0a7tc7TdhaUO288wlw0lEE68llzN6c8cxv411RGaSC60pk+Fnd+04rOTSJrc+qUQ3FtO79tDsLaKQhIFgJxIfZcxEaXVQksgR+X0DQTq6RDkr6h2QoIZnlRdIoXjPCSH6JTqcKuVb9l?iv=FT7fwoP8pjjE97MUO9J8nA==", + "kind": 16, + "tags": [ + [ + "p", + "208404de380e7c02c366cc667ae9e969d687ec7a3c03aacd364c4716a2e72327" + ] + ], + "sig": "ea7a1c500e2ebd3a1985777874c66d8fb21d1ae3d2ed5ed643ff7a60d42152923c5545794dac5b30d8ac51327812afb3c7c76c7616165dc92fe0040a2aa5bf12" +} +``` From 282f3c1ab8ded2cc9d4521b40f7762715eff8824 Mon Sep 17 00:00:00 2001 From: threeseries <132516137+threeseries@users.noreply.github.com> Date: Tue, 9 May 2023 11:22:06 -0500 Subject: [PATCH 4/8] Update 103.md --- 103.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/103.md b/103.md index 4d865e2459..14ed0471c3 100644 --- a/103.md +++ b/103.md @@ -10,7 +10,11 @@ This NIP defines a kind 16 event which is a kind 4 event (see [NIP-04](04.md) wh # Motivation and usage -It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted before being sent. This outer event can be encrypted and signed with a random key pair, thus hiding the true sender of the direct message. On receiving a kind 16 event the client should decrypt both the outer and inner content, displaying the decrypted inner content to the recipient. +It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted before being sent. This outer event can be encrypted and signed with a random key pair, thus hiding the true sender of the direct message. + +Alternatively, one can encrypt the kind 16 with one's own key pair and send to an intermediate pubkey which is responsible for forwarding the message on to the final recipient. This too would provide anonymity as long as the intermediate pubkey is receiving messages from different users, and effort is made to remove correlation based on timing. + +On receiving a kind 16 event the client should decrypt both the outer and inner content, displaying the decrypted inner content to the recipient. # Example: @@ -33,7 +37,7 @@ A kind 4 event: } ``` -After wrapping in kind 16 with a random key pair: +After wrapping in kind 16 with another key pair: ```json { From 2ce12d670989ce4c0029fc13f944ae39e15ba171 Mon Sep 17 00:00:00 2001 From: threeseries <132516137+threeseries@users.noreply.github.com> Date: Wed, 10 May 2023 19:51:23 -0500 Subject: [PATCH 5/8] Update 103.md --- 103.md | 66 +++++++++++++++------------------------------------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/103.md b/103.md index 14ed0471c3..32c78483fe 100644 --- a/103.md +++ b/103.md @@ -1,57 +1,25 @@ NIP-103 ======= -Direct Message Envelopes ------------------------- +Onion Routed Direct Messages +---------------------------- -`draft` `optional` `author:threeseries` +`draft` `optional` `author:threeseries` `author:giszmo` -This NIP defines a kind 16 event which is a kind 4 event (see [NIP-04](04.md) where the decrypted content is itself a kind 4 event. +This NIP defines event kinds 174 and 20174 which are events whose RSA-encrypted content is either a kind 4, (see [NIP-04](04.md), kind 174, or kind 20174 event. A kind 20174 event is nothing more than an ephemeral kind 174 event (kind 20174 can be substituted anywhere kind 174 appears in what follows). These events are intended as direct messages that can be routed through a network of bots or ordinary users to obscure sender and receiver. # Motivation and usage -It's well-known that direct message metadata is public on nostr since everyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted before being sent. This outer event can be encrypted and signed with a random key pair, thus hiding the true sender of the direct message. - -Alternatively, one can encrypt the kind 16 with one's own key pair and send to an intermediate pubkey which is responsible for forwarding the message on to the final recipient. This too would provide anonymity as long as the intermediate pubkey is receiving messages from different users, and effort is made to remove correlation based on timing. - -On receiving a kind 16 event the client should decrypt both the outer and inner content, displaying the decrypted inner content to the recipient. - -# Example: - -A kind 4 event: - -```json -{ - "id": "67264df8e079a7cf52f81b912debb4e47550743f1f4a5f170407f83bc9dbc12b", - "pubkey": "6475c0ba9c8f5f45dcfdae553189d1b8d089118295ba5b902c0a698e192f535b", - "created_at": 1682886605, - "content": "z2HVzkQXJAJSqebsnrkNWg==?iv=O0pf3XLsEkOo+G+/QosxDg==", - "kind": 4, - "tags": [ - [ - "p", - "208404de380e7c02c366cc667ae9e969d687ec7a3c03aacd364c4716a2e72327" - ] - ], - "sig": "1626034f09101c120f968ef1da14dd23e4e4b14db22227737ad717cc9033188e0426cf0993a0c776b8a5156ef6ece939f50f575ce294b9689864b46f49b5e8c6" -} -``` - -After wrapping in kind 16 with another key pair: - -```json -{ - "id": "b8b50e4c63102e5c737186e7ed7c23741aea34a7743ea27a41e6a18654261818", - "pubkey": "4c34b0fb27f79d376456f95391b1b43173d890d3e08558fe1e0f56cea59af52a", - "created_at": 1683293572, - "content": "se/NybRw+cM1gMbbjOrg0eP9GWUjJrLP5wBYI2b7CHLHjoP3fv88cycysQQWIQ7MPj5sHvLBJQaGNWWsSP9WLocRt0sZdZtXHwYhshYnpO0lvZxmkthcYqJ3b9I6pHUGcV1G4v9qSUjhvfCnHLf5vIh36XhN0aADUZppR2Yix0C/aCZIyY8xs6HJh8BQyGaDfdO2KsSNCgfxFJe2hXwGjBe9M/MnBos2Kqi3U/Z2Vhqy4tqrbthpP+/dJT4m1FhANJNCfPGrgRdvH00GOm2l/BTxA/GtSo8gOjmkC+uLxI3kNK4+Vb+SuOM5JbAzyMyc3XkxmgS9hlNAyZGyX3vbDBKxhRt0iK0kYg0pw9Gpna2rrBu8qO7khDiZKk5L/pqGrSWXnQ8RcHPkaKgsTTk4XTn5jZzfukFOG/5Zawdbl8nhtl4kYPQSU0pEXrkd89AuW7O4JRqwSXzni/NidES+vD/35RGelVtebFYGvK7TDO8A0V9hOIPKa0a7tc7TdhaUO288wlw0lEE68llzN6c8cxv411RGaSC60pk+Fnd+04rOTSJrc+qUQ3FtO79tDsLaKQhIFgJxIfZcxEaXVQksgR+X0DQTq6RDkr6h2QoIZnlRdIoXjPCSH6JTqcKuVb9l?iv=FT7fwoP8pjjE97MUO9J8nA==", - "kind": 16, - "tags": [ - [ - "p", - "208404de380e7c02c366cc667ae9e969d687ec7a3c03aacd364c4716a2e72327" - ] - ], - "sig": "ea7a1c500e2ebd3a1985777874c66d8fb21d1ae3d2ed5ed643ff7a60d42152923c5545794dac5b30d8ac51327812afb3c7c76c7616165dc92fe0040a2aa5bf12" -} -``` +Despite being encrypted direct messages on nostr have very poor privacy properties since anyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted before being sent, and for the final recipient to be further obfuscated by adding additional hops between sender and receiver. In order to provide additional privacy for users RSA keys are used for encryption since these messages can be decrypted without knowledge of the encrypting user's nostr pubkey. + +The flow works as follows: when Bob wishes to send Alice an onion-routed DM he must first identify a set of intermediate pubkeys that can be used for routing and obtain their corresponding RSA public keys. Once done Bob creates a kind 4 event addressed to Alice using his nsec and then encrypts the whole event JSON using Alice's public RSA key. This becomes the content for the outer kind 174 event. The sender of this outer event is not Bob in general, but is rather the pubkey immediately before Alice in the chain. Events are then iterately wrapped in kind 174, working back up the chain until finally reaching Bob. + +When Bob sends this kind 174 event to the first hop in the chain, the user or bot decrypts the content using their private RSA key. The decrypted content will be either kind 174 or kind 4, and the message is forwarded to the recipient pubkey. In order to provide additional privacy time delays can be added, or messages not forwarded until enough are in a queue. + +# Intermediate hops + +Intermediate nodes can be one of two types: always-online bots that exist solely to perform onion-routing, or ordinary users who have opted into forwarding messages for others (this also provides plausible deniability to the users themselves who are participating in forwarding). In the former case it may be desirable to use kind 20174 to make tracing more difficult, however there needs to be a way for bots to signal that they're online to ensure that such a message will be received. Hence it may be useful to have an ephemeral "heartbeat" event for sending these types of signals. + +# RSA keys + +RSA keys should be derived deterministically from the user's nsec. They should also be advertised in the metadata of a pubkey for any account that can perform onion-routing. From 2a0f346e0590a18b67fcc947907d8b46ee3cc6d9 Mon Sep 17 00:00:00 2001 From: threeseries <132516137+threeseries@users.noreply.github.com> Date: Wed, 10 May 2023 19:55:59 -0500 Subject: [PATCH 6/8] Update 103.md --- 103.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/103.md b/103.md index 32c78483fe..5724781b89 100644 --- a/103.md +++ b/103.md @@ -22,4 +22,4 @@ Intermediate nodes can be one of two types: always-online bots that exist solely # RSA keys -RSA keys should be derived deterministically from the user's nsec. They should also be advertised in the metadata of a pubkey for any account that can perform onion-routing. +RSA keys should be derived deterministically from the user's nsec. They should also be advertised in the metadata of a pubkey for any account that can perform onion routing. From 43a396e34e3efc7c938586da5fa1599ef74399ba Mon Sep 17 00:00:00 2001 From: threeseries <132516137+threeseries@users.noreply.github.com> Date: Thu, 11 May 2023 15:10:00 -0500 Subject: [PATCH 7/8] Update 103.md Co-authored-by: Leo Wandersleb --- 103.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/103.md b/103.md index 5724781b89..8a86ea184b 100644 --- a/103.md +++ b/103.md @@ -12,7 +12,7 @@ This NIP defines event kinds 174 and 20174 which are events whose RSA-encrypted Despite being encrypted direct messages on nostr have very poor privacy properties since anyone can see who is messaging whom and when. One solution to this problem is for the entire event including its metadata to be encrypted before being sent, and for the final recipient to be further obfuscated by adding additional hops between sender and receiver. In order to provide additional privacy for users RSA keys are used for encryption since these messages can be decrypted without knowledge of the encrypting user's nostr pubkey. -The flow works as follows: when Bob wishes to send Alice an onion-routed DM he must first identify a set of intermediate pubkeys that can be used for routing and obtain their corresponding RSA public keys. Once done Bob creates a kind 4 event addressed to Alice using his nsec and then encrypts the whole event JSON using Alice's public RSA key. This becomes the content for the outer kind 174 event. The sender of this outer event is not Bob in general, but is rather the pubkey immediately before Alice in the chain. Events are then iterately wrapped in kind 174, working back up the chain until finally reaching Bob. +The flow works as follows: when Bob wishes to send Alice an onion-routed DM he must first identify a set of intermediary pubkeys that can be used for routing and obtain their corresponding RSA public keys. Once done, Bob creates a kind 4 event addressed to Alice using his nsec and then encrypts the whole event JSON using Alice's public RSA key. This becomes the content for the outer kind 174 event. The sender of this outer event is not Bob in general, but is rather the pubkey immediately before Alice in the chain. Events are then iterately wrapped in kind 174, working back up the chain until finally reaching Bob. When Bob sends this kind 174 event to the first hop in the chain, the user or bot decrypts the content using their private RSA key. The decrypted content will be either kind 174 or kind 4, and the message is forwarded to the recipient pubkey. In order to provide additional privacy time delays can be added, or messages not forwarded until enough are in a queue. From ca86eabbd5f74694e18d08256e7a7c62964186da Mon Sep 17 00:00:00 2001 From: threeseries <132516137+threeseries@users.noreply.github.com> Date: Fri, 12 May 2023 08:54:54 -0500 Subject: [PATCH 8/8] Update 103.md --- 103.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/103.md b/103.md index 8a86ea184b..9806d2feb0 100644 --- a/103.md +++ b/103.md @@ -16,9 +16,9 @@ The flow works as follows: when Bob wishes to send Alice an onion-routed DM he m When Bob sends this kind 174 event to the first hop in the chain, the user or bot decrypts the content using their private RSA key. The decrypted content will be either kind 174 or kind 4, and the message is forwarded to the recipient pubkey. In order to provide additional privacy time delays can be added, or messages not forwarded until enough are in a queue. -# Intermediate hops +# Intermediary hops -Intermediate nodes can be one of two types: always-online bots that exist solely to perform onion-routing, or ordinary users who have opted into forwarding messages for others (this also provides plausible deniability to the users themselves who are participating in forwarding). In the former case it may be desirable to use kind 20174 to make tracing more difficult, however there needs to be a way for bots to signal that they're online to ensure that such a message will be received. Hence it may be useful to have an ephemeral "heartbeat" event for sending these types of signals. +Intermediary nodes can be one of two types: always-online bots that exist solely to perform onion-routing, or ordinary users who have opted into forwarding messages for others (this also provides plausible deniability to the users themselves who are participating in forwarding). In the former case it may be desirable to use kind 20174 to make tracing more difficult, however there needs to be a way for bots to signal that they're online to ensure that such a message will be received. Hence it may be useful to have an ephemeral "heartbeat" event for sending these types of signals. # RSA keys