Valeu pelo feedback! Como o Nodus trata essas duas questões:
Prevenção de Replay
O XSalsa20-Poly1305 (via TweetNaCl) usa um nonce único de 24 bytes gerado aleatoriamente para cada mensagem. Cada payload também carrega um timestamp e um messageId único (gerado por CryptoManager.generateMessageId()).
A estrutura de uma mensagem encriptada é a seguinte:
{
ciphertext: string, // Conteúdo cifrado
nonce: string, // 24 bytes único por mensagem
senderPublicKey: string
}
Além disso, cada mensagem é assinada com Ed25519 antes de ser enviada. O receptor:
- Verifica a assinatura digital
- Decripta usando sua chave X25519 derivada
- Recebe o payload com
timestampemessageId
Se alguém capturar tráfego e tentar replay, a mensagem até pode ser reenviada, mas o messageId pode ser checado pelo cliente para detectar duplicatas. Reconheço que a implementação atual não persiste um cache de IDs usados (o que seria o próximo passo para mitigar replay de forma mais robusta), mas o nonce único já impede que a mesma mensagem cifrada seja reaproveitada para gerar um novo "texto válido".
Descoberta de Peers
A descoberta é feita pelo servidor relay via registro autenticado:
- Cliente se conecta via WebSocket
- Assina um challenge com sua chave Ed25519
- Servidor verifica assinatura e registra a
publicKeycomo online
Quando você quer enviar para alguém, você já precisa conhecer a publicKey do destinatário (troca fora-de-banda, verificação pessoal, etc.)
O servidor só sabe "quem está online", não consegue relacionar identidades a pessoas reais. É um modelo similar ao que o Signal fazia antes de usar Sealed Sender — o relay sabe quem fala com quem (metadados), mas não o conteúdo.
Para melhorar ainda mais:
- Cache persistente de
messageIdpara rejeitar replays tardios - Possível implementação de janela temporal (mensagens com
timestampmuito antigo são rejeitadas) - Sealed Sender para esconder até o remetente do relay
Fico muito feliz que curtiu o projeto! Se quiser contribuir com ideias ou PRs para melhorar esses pontos, tá mais que convidado. 🙏