With this NIP a user can grant fine-grained decryption permissions to an app because this spec enables the NIP-07/46/55 signer to show a detailed permission dialog to the user such as "example.com is asking to decrypt admin keys of the spreadsheet My Clients"
It uses two carriage return chars "\r\r" around an added label string, before the plaintext itself.
The char pair is an unusual combination on strings (for being invisible, without a corresponding keyboard key and not used alone for line breaks on modern OSs).
The char is allowed on JSON strings and isn't used by most binary-to-text encodings, thus most likely won't start a plaintext.
A label with metadata may be prepended to the plaintext when encrypting with NIP-44 to help the signer show a more detailed permission dialog to its user upon decryption request.
The additional label parameter for the NIP-44 decryption method is a JSON with optional keys as follows:
Encryption example: const ciphertext = await nostr.nip44.encrypt("<peer pubkey>", "red grape", label)
When encrypting, the signer prepends the label values to the plaintext. The values are surronded by \r\r
and each separated by : like this:
\r\r<isRestricted ? 1 : 0>:<code, optional>:<kind, optional>:<pubkey, optional>:<uid, optional>\r\r<plaintext>.
Note: Keep the ":" separator chars even if not adding some optional label values.
Decrypted data example: \r\r0:FRUIT_9d10d210f580d:123:f7..ca:5c..36\r\rred grape. The signer should use
the extra metadata for adding detail to the permission dialog and if the app is granted permission, the signer
must strip the metadata and return just e.g. red grape to the app.
When decrypting, the client may pass events it deems useful as context for the signer to extract even more detail.
The context is an optional array of nostr events, e.g. [{<some nostr event>}]. Signers should check if
the pubkey and uid from the encryption label match on any of the context events (also kind, if
present) or else discard the context.
Decryption example: const plaintext = await nostr.nip44.decrypt("<peer pubkey>", "<ciphertext>", context)
Gift-wraps and seals upon .content decryption reveal their inner event kinds. That is, if
a decrypted data doesn't have a "\r\r" prefix, the signer should
fallback to JSON.parse(plaintext), check if it is a seal or rumor
and possibly use its kind to assist the user with a more detailed permission dialog.
Fine-grained decryption permissions for apps reduce the damage an app can do. However, chat messages have open-ended scopes.
That is, decrypting direct messages on untrusted apps isn't a good idea, because messages may contain sensitive content that an app can copy and send anywhere after decryption.
Prefer using a trusted client that is solely a messenger or a trusted signer that has the single extra feature of also being a messenger.
{ // This flag when true means the signer shouldn't allow the app to access the plaintext directly, // i.e. the `nip44.decrypt("...", "<ciphertext with isRestricted=true label flag>")` signer method call MUST fail. isRestricted: true, // default is false // A string ending with a _[^_]+ pattern, e.g. "ADMIN_KEY_107febac73685", that is defined on each NIP. // The last part is a unique string. // // This briefly denotes what the encrypted data is for. // // Can't contain the ":" char code, kind, // The kind of the event where the ciphertext will be placed pubkey, // The pubkey of the event where the ciphertext will be placed // The event unique identifier, used in place of the event id // because the ciphertext is required to generate the event id, thus // can't contain it. // // It's a random id-like string expected to be different for every // event from the same author. It should be present on a "uidlv" // (unique id label value) tag uid: <32-bytes lowercase hex-encoded sha256 of a random input> }