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

NIP-59 Gift Wrap #468

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions 59.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
NIP-59
======

Gift Wrap
-------------------------------

`draft` `optional` `author:kieran`

This NIP defines a simple method to protect event metadata leakage by using randomly generated keys.

A gift wrap event is a `kind 1059` event which wraps another event with a single use key.

This event kind can be used to wrap DM's or other events which are considered private.

This is similar in concept to Onion Routing.
Copy link
Contributor

@earonesty earonesty Jun 1, 2023

Choose a reason for hiding this comment

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

if the sender of the event would like to decrypt it at a later date, they can use their "tweaked" private key, rather than a random key when producing the event. the "tweak value" can use any outer-envelope data, depending on the needs of the user. tweaking secp256k1 private keys is a well-known mechanism, and should be considered if a client wants to be able to read these events later from a message store, for example.

Copy link
Member Author

Choose a reason for hiding this comment

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

Any change in how key material is handled needs to be added to NIP-07 or else it's not possible for me to use, this is why ephemeral keys work for me because i create the secret material.

Copy link
Contributor

@earonesty earonesty Jun 5, 2023

Choose a reason for hiding this comment

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

this would not be a change to how key material is handled. these keys are still ephemeral. its just that if you really wanted to decrypt it later, however annoying that may be. you, optionally, could.

doesn't have to be part of this NIP, but it might want to be mentioned that since the creator of the event controls the ephemeral key, there's no hard requirement that it not be a derived key or a weak key, and no guarantee that the recipient can be assured that the creator cannot decrypt it later, or that the key was made poorly

if you wanted to be able to guarantee the key is hard, you can derive the ephemeral key in such a way that the recipient can verify it

for example, you can use the inner event id as the private key

then you'd have to crack the dlp or reverse a hash to create a weak key - the recipient can be assured the ephemeral key is free of artifacts or structure

Copy link
Contributor

@arthurfranca arthurfranca Jun 6, 2023

Choose a reason for hiding this comment

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

What do you think of adding a nonce key with a random string to the wrapped event JSON (not as a tag, but as an extra field)?

So for the DM example, you could send the gift wrapped DM with 2 1059 events from different random keys and 2 nonces, one to the recipient and another one to yourself (so you can keep chat history). With 2 nonces, the 2 gift wrap contents won't match, so no one can tell it is the same message.

This way you don't need to keep track of the wrapping key for decrypting chat history.

The down side is the created_ats will be close to each other and your copy won't have a p tag with the recipient so you cant fetch just the messages sent to a specific user. =(

Copy link
Member

@staab staab Jun 6, 2023

Choose a reason for hiding this comment

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

Here is another way to solve this problem (not the decryption part, but the querying part).

What about optionally adding tags to the outer event, but encrypted? So ["p", pubkey] would be added to the wrapper as ["p", "encrypted pubkey"]. This would allow users in the know to still execute filters, supporting larger groups (though still not scaling as well as native nostr). A new k tag could be used to denote the kind of the inner event as well.

A 1k member group, each publishing 1k events over the course of a year would result in 1MM events, which would be far too many to process without filters.

Copy link
Contributor

Choose a reason for hiding this comment

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

@staab nice so the sender should encrypt the receiver pubkey using the shared secret

But from NIP-04, is secp256k1.getSharedSecret(SENDER_privkey, '02' + RECEIVER_pubkey).slice(1, 33) the same value as secp256k1.getSharedSecret(RECEIVER_privkey, '02' + SENDER_pubkey).slice(1, 33)? So the receiver can use it to also encrypt his own pubkey and query with { #p: [encrypted_receiver_pubkey] }?

Copy link
Member

Choose a reason for hiding this comment

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

No idea, encrypted tags would rely on signing with a shared secret. This PR uses ephemeral keys, but it would be easy to convert it to using a shared secret (since one is necessary for decryption anyway). Encrypted tags could then be encrypted symmetrically.


To construct a wrapped `kind 4` you first start by producing the `kind 4` event as normal, then create a random key and produce the `kind 1059` by encrypting the `kind 4` as a JSON string and storing it in `content` of the `kind 1059` using the same `p` tag to notify the recipient.
v0l marked this conversation as resolved.
Show resolved Hide resolved

Example wrapped event:
```json
{
"id": "3731dc639ada388aa5ee8c3404351868d4dd0fdcac00183e540669ddc25e8ad4",
"pubkey": "4d02c0b4e16db38b80fa3b46548417a4ccc975337e29bc22da72132cf69f3b4a",
"content": "7sW2yHvC5o/x6b0xCKPpH8KZJQeQYxt3Y53mxMV53yGQ5kn/jgMM74k+vfwegYBY9zjlQeTvyF5gYuJIHM4KN2SZq7zaCIKqTqMjYhLPI4VjIvowhmMuuZgRlB7cestrYh6OSSteICye6sxkS1NdpON31u+JNK/GHFVRJqfYF4Fnzaka2u9WHQKmm9/nN040OHZ41eT3YUWpaZHupVx1fuN6UoeyPjb2PW4yiwxD72LhlAoNGjR+otpWZ3kD6LU3Btrz8btjSD272wJXT/FBX5Gs59BMCDeJ4eReNfbbVBB5mlXvVqVC/tnHntVgNvAP5GsZE56/4t+kLqql0dqZU/haC8wpOxqtzqzhzxTHxS/IQ3HYpDnU8XuXUqNw1zOQhp0F65rXZUf9nN0gIa/zGa35fK2ek2p8K/3exKssO7fVxaMeTVxP7jEoCmn47gsy5mv4fH7MtxoWvWMapvGFLZf58NPy54ktvldOdGp9BJLO+6/2P7KJqb7bvu0EIPdYCcUt+muYwiJCUoHNFfFQwKrrpr23XuPQlPYNPUs6orbNBs/Oj8vMfZC5hb/AUaNanGtsqsBgqfEufyonBtihMzLZqn9OZNWHE7lYfnhSOZnUjStl03Bd94Qx1rp7IHZN?iv=cIiZuLb2FMsLzc+yYf+3IQ==",
"kind": 1059,
"created_at": 1682286690,
"tags": [
[
"p",
"63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"
]
],
"sig": "48e50d7c2d301a494e0cf250685fe4a1d7d03bdb0efae38a0e9575d2dea04b8c227f8bc35fd0451f7e1d7828a8c5f7b2f81fdab135d7a3ef92c4364aaaa8514f"
}
```

The `content` is NIP-04 encrypted JSON string with the temporary key, this is the inner event:
v0l marked this conversation as resolved.
Show resolved Hide resolved
```json
{
"id": "08eb6f89e916b0bd747c039d33c11be0575c4c6d2c08363ada1243ea771a92d2",
"pubkey": "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed",
"content": "73ZaB2WzPlIwwC55YnhsFA==?iv=1R2WlyrAk9VQ1NdhNuWmsw==",
"kind": 4,
"created_at": 1682286690,
Copy link
Contributor

Choose a reason for hiding this comment

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

created_at can be made optional to save space, verifier assumes created_at is the same, and verifies the sig as if it is present (not as if it is excluded!).

Copy link
Member

Choose a reason for hiding this comment

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

I don't think the additional complexity is worth the space saving.

"tags": [
[
"p",
"63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"
]
],
"sig": "703bbc5e15208b106ae0885c6c3f20ee97c1f9e6e45e617d49ea6200f3437d74cd5cb186df3656a18fa9b23c362c39676229ab98a323b29112b33cbbd8a910ca"
}
```

Clients can decrypt the gift wrapped event and can continue to work as normal.