NIP-04
- Mensagem Direta Criptografada
- content
- tags
- tags (opcional)
- Nota
- Exemplo de código em JavaScript para gerar esse evento:
- ⚠️ Aviso de Segurança
- ⚠️ Aviso para Implementação em Clientes
Mensagem Direta Criptografada
final unrecommended optional
Um evento especial com o kind 4, que significa “mensagem direta criptografada”. Ele deve possuir os seguintes atributos:
content
DEVE ser igual a uma string criptografada com AES-256-CBC, codificada em base64, contendo qualquer texto que o usuário queira escrever.
Essa criptografia deve ser feita usando um cifra compartilhada, gerada pela combinação da chave pública do destinatário com a chave privada do remetente.
O conteúdo criptografado deve ser concatenado com o vetor de inicialização (IV), também codificado em base64, como se fosse um parâmetro de query string chamado "iv".
O formato deve ser o seguinte:
"content": "<texto_criptografado>?iv=<vetor_de_inicializacao>"
tags
DEVE conter uma entrada identificando o destinatário da mensagem (para que os relays possam encaminhar naturalmente esse evento a ele), no formato:
["p", "<chave_publica_em_hexadecimal>"]
tags (opcional)
PODE conter uma entrada identificando a mensagem anterior de uma conversa ou uma mensagem específica à qual estamos respondendo (permitindo conversas mais organizadas e contextuais), no formato:
["e", "<event_id>"]
Nota
Por padrão, na implementação ECDH da biblioteca libsecp256k1, o segredo é o hash SHA256 do ponto compartilhado (incluindo as coordenadas X e Y).
No Nostr, apenas a coordenada X do ponto compartilhado é usada como segredo, e ela NÃO é hasheada.
Ao usar a libsecp256k1, uma função personalizada que copie apenas a coordenada X deve ser passada como argumento hashfp na função secp256k1_ecdh. Veja aqui.
Exemplo de código em JavaScript para gerar esse evento:
import crypto from 'crypto'
import * as secp from '@noble/secp256k1'
let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey)
let sharedX = sharedPoint.slice(1, 33)
let iv = crypto.randomFillSync(new Uint8Array(16))
var cipher = crypto.createCipheriv(
'aes-256-cbc',
Buffer.from(sharedX),
iv
)
let encryptedMessage = cipher.update(text, 'utf8', 'base64')
encryptedMessage += cipher.final('base64')
let ivBase64 = Buffer.from(iv.buffer).toString('base64')
let event = {
pubkey: ourPubKey,
created_at: Math.floor(Date.now() / 1000),
kind: 4,
tags: [['p', theirPublicKey]],
content: encryptedMessage + '?iv=' + ivBase64
}
⚠️ Aviso de Segurança
Este padrão não chega nem perto do que é considerado o estado da arte em comunicação criptografada entre pares e vaza metadados nos eventos.
Portanto, não deve ser usado para nada que realmente precise ser mantido em segredo, e somente com relays que utilizem AUTH para restringir quem pode buscar seus eventos de kind:4.
⚠️ Aviso para Implementação em Clientes
Clientes não devem procurar e substituir referências a chaves públicas ou notas dentro do .content.
Se o conteúdo for processado como uma nota de texto comum (onde @npub... é substituído por #[0] com uma tag ["p", "..."]), essas tags serão vazadas e o usuário mencionado receberá a mensagem na caixa de entrada.