From a7e7cb4c12f060446e40aea2df0616f2a239bd65 Mon Sep 17 00:00:00 2001 From: Jeff Thibault Date: Fri, 21 Oct 2022 19:02:58 -0400 Subject: [PATCH 1/7] create nip24 --- 24.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 24.md diff --git a/24.md b/24.md new file mode 100644 index 0000000000..b07d61ed82 --- /dev/null +++ b/24.md @@ -0,0 +1,102 @@ +NIP-24 +====== + +Private, Encrypted Direct Messages +---------------------------------- + +`draft` `optional` `author:jeffthibault` + +This NIP defines a scheme for clients to send and receive [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) direct messages more privately. + +Part 1: Decoy Key Proof Event +----------------------------- + +A special event with kind 12, meaning `Decoy Key Proof` is defined similar to a NIP-04 event except the the unencrypted content MUST be a JSON-stringified object with the following structure: +``` +{ + "msg": "dk:", + "pk": "<32 byte hex-encoded public key of the event creator>", + "sig": "<64 byte signature of the sha256 hash of >" +} +``` + +The event MUST contain only one `"p"` tag for the intended recipient. + +This event allows users to prove to someone else that they will send NIP-04 messages from a decoy public key and also verify when someone else attests to using a decoy public key for NIP-04 messages. + +Part 2: Protocol +---------------- + +**Definitions**
+`S[-]` = Sender's Nostr private key
+`S[+]` = Sender's Nostr public key
+`R[-]` = Recipient's Nostr private key
+`R[+]` = Recipient's Nostr public key
+`SR[ss]` = Sender and Recipient's shared secret = `ECDH(S[-], R[+])` or `ECDH(R[-], S[+])`
+ +`S[d-]` = Sender's decoy private key
+`S[d+]` = Sender's decoy public key
+`R[d-]` = Recipient's decoy private key
+`R[d+]` = Recipient's decoy public key
+ +`RDIH` is the Recipients's `Decoy Inbox Hash`. It is known only by the sender and recipient because it is defined as the 32 byte, hex-encoded digest of `SHA256(SHA256(<32 byte hex-encoded SR[ss]> + <32 byte hex-encoded R[+]>))`. + +### Sending + +**Step 1**:
+For each new recipient, R, they message with, the sender MUST create a decoy private key, `S[d-]`, defined as `SR[ss] + S[-]`. + +**Step 2**:
+Note: If the sender has already sent the recipient a `Decoy Key Proof` event (defined in Part 1), this step can be ignored. + +The sender MUST send a `Decoy Key Proof` event with the unencrypted content set as a JSON-stringified object structured like so: +``` +{ + "msg": "dk:", + "pk": "", + "sig": "<64 byte signature of the sha256 hash of using >" +} +``` + +`content` for the event is encrypted with the shared secret between `S[d-]` and `R[+]`. **Note**: This is different than `SR[ss]`.
+`pubkey` for the event is `S[d+]`.
+`tags` for the event is `[["p", ]]`.
+The event is signed with `S[d-]`.
+ +**Step 3**:
+The sender sends standard NIP-04 events to the recipient via their `Decoy Inbox Hash`, `RDIH`. + +`content` for the NIP-04 events is encrypted with `SR[ss]`.
+`pubkey` for the NIP-04 events is `S[d+]`.
+`tags` for the NIP-04 events is `[["p", ]]`.
+The NIP-04 events are signed with `S[d-]`.
+ +### Receiving + +**Step 1**:
+The recipient MUST subscribe to all `Decoy Key Proof` events intended for them like so:
+`["REQ", "decoy-key-proofs", {"kinds": [12], "#p": []}]` + +**Step 2**:
+Upon receiving a `Decoy Key Proof` event from a *new* public key, the recipient MUST verify the event, decrypt the content, and verify the decrypted content in which the sender has attested to their decoy public key. The recipient MUST verify the event's `pubkey` matches the decoy public key in the message the sender signed. Once all verification is complete, the recipient SHOULD save a mapping between the sender's real public key and decoy public key. + +**Step 3**:
+The recipient MUST create a subscription for NIP-04 events sent to `RDIH` from the sender's decoy public key:
+`["REQ", "dms-with-sender", {"kinds": [4], authors=[, ], "#p": [RDIH]}]` + +Motivation +---------- + +NIP-04 is flawed because only event content is encrypted and not the metadata about it, and by the nature of Nostr as a protocol designed for public communication in general anyone is able to query relays for any event they want -- thus it's possible to anyone to track conversations between any other Nostr users, not exactly what they're saying, but to whom they're chatting and how often. + +Privacy Analysis +---------------- + +When adhering to this NIP for direct messaging on Nostr, the only information that is available to an outside observer is that someone received a `Decoy Key Proof` event but there is nothing conclusive that can be discerned from that because the sending pubkey is obfuscated and the recipient might do nothing with it. To an outside observer, all the subsequent NIP-04 events are being sent from and to seemingly random keys. + +Acknowledgements +---------------- + +@vinliao - for proposing an initial idea in https://github.com/nostr-protocol/nostr/issues/69
+@fiatjaf - for the `Motivation` section, it is taken from https://github.com/nostr-protocol/nips/blob/nip-21-non-public-dms/21.md `Rationale` section. See that for more information. + From df4559619591a73ebd57df9b581612eeb1522ecf Mon Sep 17 00:00:00 2001 From: Jeff Thibault Date: Fri, 21 Oct 2022 19:22:00 -0400 Subject: [PATCH 2/7] wording and formatting tweaks --- 24.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/24.md b/24.md index b07d61ed82..8516e369b0 100644 --- a/24.md +++ b/24.md @@ -39,7 +39,7 @@ Part 2: Protocol `R[d-]` = Recipient's decoy private key
`R[d+]` = Recipient's decoy public key
-`RDIH` is the Recipients's `Decoy Inbox Hash`. It is known only by the sender and recipient because it is defined as the 32 byte, hex-encoded digest of `SHA256(SHA256(<32 byte hex-encoded SR[ss]> + <32 byte hex-encoded R[+]>))`. +`RDIH` is the `Recipients's Decoy Inbox Hash`. It is known only by the sender and recipient because it is defined as the 32 byte, hex-encoded digest of `SHA256(SHA256(<32 byte hex-encoded SR[ss]> + <32 byte hex-encoded R[+]>))`. ### Sending @@ -58,18 +58,18 @@ The sender MUST send a `Decoy Key Proof` event with the unencrypted content set } ``` -`content` for the event is encrypted with the shared secret between `S[d-]` and `R[+]`. **Note**: This is different than `SR[ss]`.
-`pubkey` for the event is `S[d+]`.
-`tags` for the event is `[["p", ]]`.
-The event is signed with `S[d-]`.
+- `content` for the event is encrypted with the shared secret between `S[d-]` and `R[+]`. **Note**: This is different than `SR[ss]`.
+- `pubkey` for the event is `S[d+]`.
+- `tags` for the event is `[["p", ]]`.
+- The event is signed with `S[d-]`.
**Step 3**:
The sender sends standard NIP-04 events to the recipient via their `Decoy Inbox Hash`, `RDIH`. -`content` for the NIP-04 events is encrypted with `SR[ss]`.
-`pubkey` for the NIP-04 events is `S[d+]`.
-`tags` for the NIP-04 events is `[["p", ]]`.
-The NIP-04 events are signed with `S[d-]`.
+- `content` for the NIP-04 events is encrypted with `SR[ss]`.
+- `pubkey` for the NIP-04 events is `S[d+]`.
+- `tags` for the NIP-04 events is `[["p", ]]`.
+- The NIP-04 events are signed with `S[d-]`.
### Receiving @@ -78,7 +78,7 @@ The recipient MUST subscribe to all `Decoy Key Proof` events intended for them l `["REQ", "decoy-key-proofs", {"kinds": [12], "#p": []}]` **Step 2**:
-Upon receiving a `Decoy Key Proof` event from a *new* public key, the recipient MUST verify the event, decrypt the content, and verify the decrypted content in which the sender has attested to their decoy public key. The recipient MUST verify the event's `pubkey` matches the decoy public key in the message the sender signed. Once all verification is complete, the recipient SHOULD save a mapping between the sender's real public key and decoy public key. +Upon receiving a `Decoy Key Proof` event from a *new* decoy public key, the recipient MUST verify the event, decrypt the content, and verify the decrypted content in which the sender has attested to their decoy public key. The recipient MUST verify the event's `pubkey` matches the decoy public key in the decrypted message the sender signed. Once all verification is complete, the recipient SHOULD save a mapping between the sender's real public key and decoy public key locally. **Step 3**:
The recipient MUST create a subscription for NIP-04 events sent to `RDIH` from the sender's decoy public key:
@@ -92,11 +92,10 @@ NIP-04 is flawed because only event content is encrypted and not the metadata ab Privacy Analysis ---------------- -When adhering to this NIP for direct messaging on Nostr, the only information that is available to an outside observer is that someone received a `Decoy Key Proof` event but there is nothing conclusive that can be discerned from that because the sending pubkey is obfuscated and the recipient might do nothing with it. To an outside observer, all the subsequent NIP-04 events are being sent from and to seemingly random keys. +When adhering to this NIP for direct messaging on Nostr, the only information that is available to an outside observer is that someone received a `Decoy Key Proof` event but there is nothing conclusive that can be discerned from that because the sending pubkey is obfuscated and the recipient might ignore it. To an outside observer, all the subsequent NIP-04 events are being sent from and to seemingly random keys. Acknowledgements ---------------- @vinliao - for proposing an initial idea in https://github.com/nostr-protocol/nostr/issues/69
@fiatjaf - for the `Motivation` section, it is taken from https://github.com/nostr-protocol/nips/blob/nip-21-non-public-dms/21.md `Rationale` section. See that for more information. - From d63b175b76e2fcc6040570138c5e1c967aecacb0 Mon Sep 17 00:00:00 2001 From: Jeff Thibault Date: Fri, 21 Oct 2022 19:27:17 -0400 Subject: [PATCH 3/7] change key derivation --- 24.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/24.md b/24.md index 8516e369b0..bcc847d313 100644 --- a/24.md +++ b/24.md @@ -44,7 +44,7 @@ Part 2: Protocol ### Sending **Step 1**:
-For each new recipient, R, they message with, the sender MUST create a decoy private key, `S[d-]`, defined as `SR[ss] + S[-]`. +For each new recipient, R, they message with, the sender MUST create a decoy private key, `S[d-]`, defined as `S[-] + SR[ss]`. **Step 2**:
Note: If the sender has already sent the recipient a `Decoy Key Proof` event (defined in Part 1), this step can be ignored. From e4708591daeefc4374146a8f1a655ee164ce0ae7 Mon Sep 17 00:00:00 2001 From: Jeff Thibault Date: Sat, 22 Oct 2022 12:58:05 -0400 Subject: [PATCH 4/7] formatting tweaks --- 24.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/24.md b/24.md index bcc847d313..4debac524a 100644 --- a/24.md +++ b/24.md @@ -36,7 +36,6 @@ Part 2: Protocol `S[d-]` = Sender's decoy private key
`S[d+]` = Sender's decoy public key
-`R[d-]` = Recipient's decoy private key
`R[d+]` = Recipient's decoy public key
`RDIH` is the `Recipients's Decoy Inbox Hash`. It is known only by the sender and recipient because it is defined as the 32 byte, hex-encoded digest of `SHA256(SHA256(<32 byte hex-encoded SR[ss]> + <32 byte hex-encoded R[+]>))`. @@ -82,7 +81,7 @@ Upon receiving a `Decoy Key Proof` event from a *new* decoy public key, the reci **Step 3**:
The recipient MUST create a subscription for NIP-04 events sent to `RDIH` from the sender's decoy public key:
-`["REQ", "dms-with-sender", {"kinds": [4], authors=[, ], "#p": [RDIH]}]` +`["REQ", "dms-with-sender", {"kinds": [4], authors=[, ], "#p": []}]` Motivation ---------- From c8d771a19d12835667f8cd82dba92903aea2d623 Mon Sep 17 00:00:00 2001 From: Jeff Thibault Date: Sat, 22 Oct 2022 13:10:11 -0400 Subject: [PATCH 5/7] add nip24 and kind 12 to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index aa419bbc6e..21ca828bfe 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh - [NIP-15: End of Stored Events Notice](15.md) - [NIP-16: Event Treatment](16.md) - [NIP-22: Event created_at Limits](22.md) +- [NIP-24: Private Messages](24.md) - [NIP-25: Reactions](25.md) - [NIP-28: Public Chat](28.md) @@ -33,6 +34,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh | 4 | Encrypted Direct Messages | 4 | | 5 | Event Deletion | 9 | | 7 | Reaction | 25 | +| 12 | Decoy Key Proof | 24 | | 40 | Channel Creation | 28 | | 41 | Channel Metadata | 28 | | 42 | Channel Message | 28 | From 8c9dc2dd10be10667616dc126abd9ae7a7114913 Mon Sep 17 00:00:00 2001 From: jeffthibault Date: Thu, 3 Nov 2022 18:43:53 -0400 Subject: [PATCH 6/7] update nip24 --- 24.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/24.md b/24.md index 4debac524a..52d62a1ed8 100644 --- a/24.md +++ b/24.md @@ -14,9 +14,9 @@ Part 1: Decoy Key Proof Event A special event with kind 12, meaning `Decoy Key Proof` is defined similar to a NIP-04 event except the the unencrypted content MUST be a JSON-stringified object with the following structure: ``` { - "msg": "dk:
", - "pk": "<32 byte hex-encoded public key of the event creator>", - "sig": "<64 byte signature of the sha256 hash of >" + "msg": "dk:", + "pk": "<32 byte, hex-encoded public key of the event creator>", + "sig": "<64 byte, hex-encoded signature of the sha256 hash of >" } ``` @@ -28,22 +28,22 @@ Part 2: Protocol ---------------- **Definitions**
-`S[-]` = Sender's Nostr private key
-`S[+]` = Sender's Nostr public key
-`R[-]` = Recipient's Nostr private key
-`R[+]` = Recipient's Nostr public key
-`SR[ss]` = Sender and Recipient's shared secret = `ECDH(S[-], R[+])` or `ECDH(R[-], S[+])`
+`S[-]` = Sender's Nostr private key (256 bit integer)
+`S[+]` = Sender's Nostr public key (256 bit integer)
+`R[-]` = Recipient's Nostr private key (256 bit integer)
+`R[+]` = Recipient's Nostr public key (256 bit integer)
+`SR[ss]` = Sender and Recipient's shared secret = `ECDH(S[-], R[+])` or `ECDH(R[-], S[+])` (256 bit integer)
-`S[d-]` = Sender's decoy private key
-`S[d+]` = Sender's decoy public key
-`R[d+]` = Recipient's decoy public key
+`S[d-]` = Sender's decoy private key (256 bit integer)
+`S[d+]` = Sender's decoy public key (256 bit integer)
+`R[d+]` = Recipient's decoy public key (256 bit integer)
-`RDIH` is the `Recipients's Decoy Inbox Hash`. It is known only by the sender and recipient because it is defined as the 32 byte, hex-encoded digest of `SHA256(SHA256(<32 byte hex-encoded SR[ss]> + <32 byte hex-encoded R[+]>))`. +`RDIH` is the `Recipients's Decoy Inbox Hash`. It is known only by the sender and recipient because it is defined as the 32 byte, hex-encoded digest of `SHA256(SR[ss] + R[+])`. ### Sending **Step 1**:
-For each new recipient, R, they message with, the sender MUST create a decoy private key, `S[d-]`, defined as `S[-] + SR[ss]`. +For each new recipient, R, they message with, the sender MUST create a decoy private key, `S[d-]`, defined as `S[-] + SR[ss]`. The sender must also create a "throwaway key pair", `T[-]` and `T[+]`. **Step 2**:
Note: If the sender has already sent the recipient a `Decoy Key Proof` event (defined in Part 1), this step can be ignored. @@ -53,17 +53,17 @@ The sender MUST send a `Decoy Key Proof` event with the unencrypted content set { "msg": "dk:", "pk": "", - "sig": "<64 byte signature of the sha256 hash of using >" + "sig": "<64 byte, hex-encoded signature of the sha256 hash of using >" } ``` - `content` for the event is encrypted with the shared secret between `S[d-]` and `R[+]`. **Note**: This is different than `SR[ss]`.
-- `pubkey` for the event is `S[d+]`.
+- `pubkey` for the event is the throwaway public key, `T[+]`.
- `tags` for the event is `[["p", ]]`.
-- The event is signed with `S[d-]`.
+- The event is signed with the throwaway private key, `T[-]`.
**Step 3**:
-The sender sends standard NIP-04 events to the recipient via their `Decoy Inbox Hash`, `RDIH`. +The sender sends standard NIP-04 events to the recipient via the `RDIH`. - `content` for the NIP-04 events is encrypted with `SR[ss]`.
- `pubkey` for the NIP-04 events is `S[d+]`.
@@ -77,7 +77,7 @@ The recipient MUST subscribe to all `Decoy Key Proof` events intended for them l `["REQ", "decoy-key-proofs", {"kinds": [12], "#p": []}]` **Step 2**:
-Upon receiving a `Decoy Key Proof` event from a *new* decoy public key, the recipient MUST verify the event, decrypt the content, and verify the decrypted content in which the sender has attested to their decoy public key. The recipient MUST verify the event's `pubkey` matches the decoy public key in the decrypted message the sender signed. Once all verification is complete, the recipient SHOULD save a mapping between the sender's real public key and decoy public key locally. +Upon receiving a `Decoy Key Proof` event, the recipient MUST decrypt the content and verify the message in which the sender has attested to their decoy public key. Once the verification is complete, the recipient SHOULD save a mapping between the sender's real public key and decoy public key locally. **Step 3**:
The recipient MUST create a subscription for NIP-04 events sent to `RDIH` from the sender's decoy public key:
From f64e434b2db251b1feaedaece3c17f9a3d82d86b Mon Sep 17 00:00:00 2001 From: Jeff Thibault Date: Thu, 15 Dec 2022 09:27:09 -0500 Subject: [PATCH 7/7] fix wording --- 24.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/24.md b/24.md index 52d62a1ed8..7b24be2895 100644 --- a/24.md +++ b/24.md @@ -57,7 +57,7 @@ The sender MUST send a `Decoy Key Proof` event with the unencrypted content set } ``` -- `content` for the event is encrypted with the shared secret between `S[d-]` and `R[+]`. **Note**: This is different than `SR[ss]`.
+- `content` for the event is encrypted with the shared secret between `T[-]` and `R[+]`.
- `pubkey` for the event is the throwaway public key, `T[+]`.
- `tags` for the event is `[["p", ]]`.
- The event is signed with the throwaway private key, `T[-]`.