clone

every nostr nip as a chapter of an illustrated, early-2000s printed game manual

main
nip.guide / source-nips / 04.md
04.md 3.0K rendered
1> __Warning__ `unrecommended`: deprecated in favor of [NIP-17](17.md)23NIP-044======56Encrypted Direct Message7------------------------89`final` `unrecommended` `optional` `relay`1011A special event with kind `4`, meaning "encrypted direct message". It is supposed to have the following attributes:1213**`content`** MUST be equal to the base64-encoded, aes-256-cbc encrypted string of anything a user wants to write, encrypted using a shared cipher generated by combining the recipient's public-key with the sender's private-key; this appended by the base64-encoded initialization vector as if it was a querystring parameter named "iv". The format is the following: `"content": "<encrypted_text>?iv=<initialization_vector>"`.1415**`tags`** MUST contain an entry identifying the receiver of the message (such that relays may naturally forward this event to them), in the form `["p", "<pubkey, as a hex string>"]`.1617**`tags`** MAY contain an entry identifying the previous message in a conversation or a message we are explicitly replying to (such that contextual, more organized conversations may happen), in the form `["e", "<event_id>"]`.1819**Note**: By default in the [libsecp256k1](https://github.com/bitcoin-core/secp256k1) ECDH implementation, the secret is the SHA256 hash of the shared point (both X and Y coordinates). In Nostr, only the X coordinate of the shared point is used as the secret and it is NOT hashed. If using libsecp256k1, a custom function that copies the X coordinate must be passed as the `hashfp` argument in `secp256k1_ecdh`. See [here](https://github.com/bitcoin-core/secp256k1/blob/master/src/modules/ecdh/main_impl.h#L29).2021Code sample for generating such an event in JavaScript:2223```js24import crypto from 'crypto'25import * as secp from '@noble/secp256k1'2627let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey)28let sharedX = sharedPoint.slice(1, 33)2930let iv = crypto.randomFillSync(new Uint8Array(16))31var cipher = crypto.createCipheriv(32 'aes-256-cbc',33 Buffer.from(sharedX),34 iv35)36let encryptedMessage = cipher.update(text, 'utf8', 'base64')37encryptedMessage += cipher.final('base64')38let ivBase64 = Buffer.from(iv.buffer).toString('base64')3940let event = {41 pubkey: ourPubKey,42 created_at: Math.floor(Date.now() / 1000),43 kind: 4,44 tags: [['p', theirPublicKey]],45 content: encryptedMessage + '?iv=' + ivBase6446}47```4849## Security Warning5051This standard does not go anywhere near what is considered the state-of-the-art in encrypted communication between peers, and it leaks metadata in the events, therefore it must not be used for anything you really need to keep secret, and only with relays that use `AUTH` to restrict who can fetch your `kind:4` events.5253## Client Implementation Warning5455Clients *should not* search and replace public key or note references from the `.content`. If processed like a regular text note (where `@npub...` is replaced with `#[0]` with a `["p", "..."]` tag) the tags are leaked and the mentioned user will receive the message in their inbox.