Executando verificação de segurança...
2

Cara, que projeto interessante. Gostei especialmente da separação entre daemon, core e server, porque isso deixa claro que o relay não precisa confiar em absolutamente nada além da entrega dos pacotes.

Fiquei com uma dúvida técnica sobre o modelo de segurança. Como o servidor é totalmente cego ao conteúdo e as chaves nunca saem do dispositivo, como vocês estão lidando com o problema clássico de descoberta de peers e replay prevention em uma rede P2P desse tipo? Existe algum mecanismo de verificação temporal ou assinatura complementar que mitigate tentativas de replay caso alguém capture tráfego do relay?

Pergunto porque já brinquei com libs baseadas em NaCl e sempre acabei caindo nessa mesma discussão. Curioso para entender como vocês abordaram esse ponto. Excelente trabalho.

Carregando publicação patrocinada...
1

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:

  1. Verifica a assinatura digital
  2. Decripta usando sua chave X25519 derivada
  3. 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:

  1. Cliente se conecta via WebSocket
  2. Assina um challenge com sua chave Ed25519
  3. 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. 🙏