nostr-protocol / nips

Nostr Implementation Possibilities
2.41k stars 586 forks source link

Migrating from NIP-04 to NIP-44 encryption #1598

Open arthurfranca opened 1 week ago

arthurfranca commented 1 week ago

We should take the opportunity to do nip44.encrypt('{ "k-i-n-d": event.kind, data: "<plaintext>" }') (the arg is a stringifed JSON) instead of nip44.encrypt("<plaintext>") when encrypting things inside events.

This way nip07/46 can decrypt and inform it is decrypting something related to a specific event kind so the user can make an informed decision to allow or deny it.

Ack or nack?

note: used "k-i-n-d" key instead of "kind" to avoid collision with a plaintext that happens to be a stringified json with "kind" and "data" keys.

vitorpamplona commented 1 week ago

I don't know if it makes sense to keep kind as the reference point. Many encryptions are not based on a specific kind. And some kinds have more than one encryption in such a way that it would be important to differentiate. For instance, health data has 2 encryptions, one that stores a key to view the health data and another one to edit that data. It would be important for a signer to highlight when the permission is not only to see but to edit.

arthurfranca commented 1 week ago

And some kinds have more than one encryption in such a way that it would be important to differentiate

The idea is to just inform it is e.g. health data related. So that user can be sure it is something that a health related app should be safely allowed to decrypt.

edit: just don't want Health App to decrypt my secret pr0n bookmarks? edit: in your example, maybe you can use 2 different event kinds to hold each of the 2 ciphertexts if you need that level of permission granurality?

vitorpamplona commented 1 week ago

But why stay there? Why not allow the writer an alt that describes what's in the payload? The signer just displays the alt.

arthurfranca commented 1 week ago

I think alt would be ok but it can't be automated. I mean, client can easily just add the event.kind for you when encrypting, without asking for alt. Edit: Unless alt text (or "code") is part of each NIP that encrypts things?

vitorpamplona commented 1 week ago

The client just adds the alt in the same way we add the alt tag to each event without asking the user about it.

vitorpamplona commented 1 week ago

Another way of doing this would be to specify a few codes for different payloads in each nip and then use those codes instead of kinds. In that way the signer doesn't need dynamic translations of alt content.

arthurfranca commented 1 week ago

Yeah so you proposing nip44.encrypt('{ "c-o-d-e": "HEALTH_EDIT_PRIVKEY", data: "<plaintext>" }')?

vitorpamplona commented 1 week ago

Something on those lines. I am not exactly sure if it is the best way. What I know is that kinds alone might not be enough info for signers, depending on the application.

Maybe a <kind>:<int> and then we have a table somewhere to describe what each code means? In that way it is scoped to the kind to avoid reusing codes.

arthurfranca commented 1 week ago

Better if <kind>:<code>? cause if the nip07 extension wasn't updated for a while, it can show just the code, which is sort of human readable.

Like 32323:HEALTH_EDIT_PRIVKEY would be translated if nip07 is up-to-date or else it would show just "HEALTH_EDIT_PRIVKEY" which is better than a number

fiatjaf commented 6 days ago

Not a bad idea but I don't think it's worth the trouble.

Specially because I think our best possible future involve not dealing with nip44 encryption with user keys directly anymore, but only as a way to talk to bunkers, and bunkers would just sign events, while all encryption needs will be provided by per-device keys using MLS or something else.

arthurfranca commented 6 days ago

I'm skeptical about MLS saving the world. Per-device keys? Like I have to kinda "join" a group with myself (my other device) so that I can decrypt something the other device encrypted? Seems to be the wrong tool when not doing DMs/chat.

fiatjaf commented 6 days ago

Like I have to kinda "join" a group with myself (my other device) so that I can decrypt something the other device encrypted?

Yes, exactly.

I'm skeptical about MLS saving the world.

Me too, and I hate all this crappy IETF I-am-cryptographer-and-I-love-Signal technology vibes but I don't see any other way because I want FROST bunkers and they can't encrypt. Also per-device encryption keys sounds like a sensible idea even if we don't use full-blown MLS.

arthurfranca commented 4 days ago

@vitorpamplona at #1228, the same alt would apply to many event kinds (all replaceables/addressables in that case).

So maybe better be <code>(:<kind>) then sometimes its just the code and other times there's the :<kind> suffix.

Or maybe we always do <prefix>:<code> and the prefix is a random string created at a specifc NIP/NUD that may be reused in new NIPs/NUDs if applicable.

vitorpamplona commented 4 days ago

Even on #1228, the code should describe what kind of event this is first and then what type of information it is trying to assess. Knowing that the app is asking for an edit key is irrelevant if I don't know what that key is for: health data, spreadsheets, docs, etc. In fact, It could be said that even kind + code is not enough. As a user, I want to know which file I am giving access to. The signer should say something like: "Amethyst is asking to decrypt edit keys of the spreadsheet 'My Clients'".

So, <App Requester> is asking to <signer method> <code inside the encrypted file> of the <event kind> <user-facing identification of that event>.

arthurfranca commented 4 days ago

edit: skip this, see next comment

Maybe a \<kind>:\<int> and then we have a table somewhere to describe what each code means? In that way it is scoped to the kind to avoid reusing codes.

Wait, could 76:1 meaning be completely different from 77:1 or the 1 int is always mapped to the same thing no matter the kind?

The signer should say something like: "Amethyst is asking to decrypt edit keys of the spreadsheet 'My Clients'"

As the ciphertext is used when computing the event.id, we cannot add a regular event reference like <event-id>:<int> then do nip44.decrypt(pubkey, <ciphertext>, <optional event>) so that the signer could inspect the event, get its kind and be smart if it knows its a spreadsheet and how to get its name.

Thought its possible to do like <int>:<kind><author>:<optional tag name 1>:<optional tag-value 1, a unique identifier><...more optional tag name/value pairs>.

Now we are able to do nip44.decrypt(pubkey, <ciphertext>, <optional event>) and the signer would show to the user a detailed info.

arthurfranca commented 3 days ago

Borrowing ideas from #1540 (use one-letter tags or @), using inlined shebang instead of { "c-o-d-e": ...., "data": "<plaintext>" } stringified json, picking ´ as separator between plaintext and ~code~label and if there's ´ inside plaintext, adding a preceding - to escape it:

Plaintext "label" could be <int code>:<kind><author>:<optional, event tags and/or @ ref that makes event unique as in #1549>, preceded by #!nostr <plaintext>

Examples:

Now when client uses nip07 like nip44.decrypt(pubkey, <ciphertext>, { event: <optional event> }), signer can tell user that "example.com is asking to decrypt edit keys of the spreadsheet 'My Clients'"