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,
nonce: string,
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
timestamp e messageId
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
publicKey como 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
messageId para rejeitar replays tardios
- Possível implementação de janela temporal (mensagens com
timestamp muito 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. 🙏