Como funciona um mensageiro descentralizado no blockchain?

No início de 2017, começamos a criar um mensageiro no blockchain [nome e link estão no perfil] discutindo as vantagens em relação aos mensageiros P2P clássicos.

Passado 2.5 ano, e pudemos confirmar nosso conceito: aplicativos de mensagens agora estão disponíveis para iOS, Web PWA, Windows, GNU/Linux, Mac OS e Android.

Hoje vamos contar como funciona o blockchain messenger e como os aplicativos clientes podem funcionar com sua API.
Como funciona um mensageiro descentralizado no blockchain?

Queríamos que o blockchain resolvesse os problemas de segurança e privacidade dos mensageiros P2P clássicos:

  • Um clique para criar uma conta - sem telefones ou e-mails, sem acesso a catálogos de endereços ou geolocalizações.
  • Os interlocutores nunca estabelecem conexões diretas; toda a comunicação ocorre através de um sistema distribuído de nós. Os endereços IP dos usuários não são acessíveis entre si.
  • Todas as mensagens são criptografadas curva ponta a ponta25519xsalsa20poly1305. Parece que isso não surpreenderá ninguém, mas nosso código-fonte está aberto.
  • O ataque MITM está excluído - cada mensagem é uma transação e é assinada por Ed25519 EdDSA.
  • A mensagem termina em seu próprio bloco. Consistência e timestamp Você não pode consertar os blocos e, portanto, a ordem das mensagens.
  • “Eu não disse isso” não funcionará com mensagens no blockchain.
  • Não existe uma estrutura central que verifique a “autenticidade” de uma mensagem. Isso é feito por um sistema distribuído de nós baseado em consenso e é de propriedade dos usuários.
  • Impossibilidade de censura – as contas não podem ser bloqueadas e as mensagens não podem ser apagadas.
  • Blockchain 2FA é uma alternativa ao infernal 2FA via SMS, arruinou muita saúde.
  • A capacidade de obter todas as suas conversas de qualquer dispositivo a qualquer momento significa que você não precisa armazenar as conversas localmente.
  • Confirmação da entrega da mensagem. Não para o dispositivo do usuário, mas para a rede. Essencialmente, esta é a confirmação da capacidade do destinatário de ler sua mensagem. Este é um recurso útil para enviar notificações críticas.

Os benefícios do Blockchain também incluem estreita integração com as criptomoedas Ethereum, Dogecoin, Lisk, Dash, Bitcoin (esta ainda em andamento) e a capacidade de enviar tokens em chats. Até criamos um trocador de criptografia integrado.

E então - como tudo funciona.

Uma mensagem é uma transação

Todo mundo já está acostumado com o fato de que as transações no blockchain transferem tokens (moedas) de um usuário para outro. Como Bitcoin. Criamos um tipo especial de transação para transmissão de mensagens.

Para enviar uma mensagem em um mensageiro no blockchain, você precisa seguir várias etapas:

  1. Criptografar o texto da mensagem
  2. Coloque texto cifrado em uma transação
  3. Assine a transação
  4. Envie uma transação para qualquer nó da rede
  5. Um sistema distribuído de nós determina a “autenticidade” de uma mensagem
  6. Se tudo estiver OK, a transação com a mensagem será incluída no próximo bloco
  7. O destinatário recupera a transação da mensagem e descriptografa

As etapas 1 a 3 e 7 são executadas localmente no cliente e as etapas 5 a 6 são executadas nos hosts.

Criptografia de mensagens

A mensagem é criptografada com a chave privada do remetente e a chave pública do destinatário. Pegaremos a chave pública da rede, mas para isso a conta do destinatário deve estar inicializada, ou seja, ter pelo menos uma transação. Você pode usar uma solicitação REST GET /api/accounts/getPublicKey?address={ADAMANT address}, e ao carregar os chats, as chaves públicas dos interlocutores já estarão disponíveis.

Como funciona um mensageiro descentralizado no blockchain?

O mensageiro criptografa mensagens usando o algoritmo curve25519xsalsa20poly1305 (Caixa de NaCl). Como a conta contém chaves Ed25519, para formar uma caixa, as chaves devem primeiro ser convertidas para Curve25519 Diffie-Hellman.

Aqui está um exemplo em JavaScript:

/**
 * Encodes a text message for sending to ADM
 * @param {string} msg message to encode
 * @param {*} recipientPublicKey recipient's public key
 * @param {*} privateKey our private key
 * @returns {{message: string, nonce: string}}
 */
adamant.encodeMessage = function (msg, recipientPublicKey, privateKey) {
  const nonce = Buffer.allocUnsafe(24)
  sodium.randombytes(nonce)

  if (typeof recipientPublicKey === 'string') {
    recipientPublicKey = hexToBytes(recipientPublicKey)
  }

  const plainText = Buffer.from(msg)
  const DHPublicKey = ed2curve.convertPublicKey(recipientPublicKey)
  const DHSecretKey = ed2curve.convertSecretKey(privateKey)

  const encrypted = nacl.box(plainText, nonce, DHPublicKey, DHSecretKey)

  return {
    message: bytesToHex(encrypted),
    nonce: bytesToHex(nonce)
  }
}

Formando uma transação com uma mensagem

A transação tem a seguinte estrutura geral:

{
  "id": "15161295239237781653",
  "height": 7585271,
  "blockId": "16391508373936326027",
  "type": 8,
  "block_timestamp": 45182260,
  "timestamp": 45182254,
  "senderPublicKey": "bd39cc708499ae91b937083463fce5e0668c2b37e78df28f69d132fce51d49ed",
  "senderId": "U16023712506749300952",
  "recipientId": "U17653312780572073341",
  "recipientPublicKey": "23d27f616e304ef2046a60b762683b8dabebe0d8fc26e5ecdb1d5f3d291dbe21",
  "amount": 204921300000000,
  "fee": 50000000,
  "signature": "3c8e551f60fedb81e52835c69e8b158eb1b8b3c89a04d3df5adc0d99017ffbcb06a7b16ad76d519f80df019c930960317a67e8d18ab1e85e575c9470000cf607",
  "signatures": [],
  "confirmations": 3660548,
  "asset": {}
}

Para uma transação de mensagem, o mais importante é asset - você precisa colocar uma mensagem no objeto chat com estrutura:

  • message - salve a mensagem criptografada
  • own_message - nenhuma vez
  • type - tipo de mensagem

As mensagens também são divididas em tipos. Essencialmente, o parâmetro type diz como entender message. Você pode enviar apenas um texto, ou pode enviar um objeto com coisas interessantes dentro – por exemplo, é assim que o mensageiro faz transferências de criptomoedas em chats.

Como resultado, criamos uma transação:

{
  "transaction": {
    "type": 8,
    "amount": 0,
    "senderId": "U12499126640447739963",
    "senderPublicKey": "e9cafb1e7b403c4cf247c94f73ee4cada367fcc130cb3888219a0ba0633230b6",
    "asset": {
      "chat": {
        "message": "cb682accceef92d7cddaaddb787d1184ab5428",
        "own_message": "e7d8f90ddf7d70efe359c3e4ecfb5ed3802297b248eacbd6",
        "type": 1
      }
    },
    "recipientId": "U15677078342684640219",
    "timestamp": 63228087,
    "signature": "тут будет подпись"
  }
}

Assinatura da transação

Para garantir que todos tenham confiança na autenticidade do remetente e do destinatário, no horário de envio e no conteúdo da mensagem, a transação é assinada. Uma assinatura digital permite verificar a autenticidade de uma transação usando uma chave pública - não é necessária uma chave privada para isso.

Mas a assinatura em si é feita usando uma chave privada:

Como funciona um mensageiro descentralizado no blockchain?

O diagrama mostra que primeiro fazemos o hash da transação com SHA-256 e depois a assinamos Ed25519 EdDSA e obter uma assinatura signaturee o ID da transação faz parte do hash SHA-256.

Exemplo de implementação:

1 — Forme um bloco de dados, incluindo uma mensagem

/**
 * Calls `getBytes` based on transaction type
 * @see privateTypes
 * @implements {ByteBuffer}
 * @param {transaction} trs
 * @param {boolean} skipSignature
 * @param {boolean} skipSecondSignature
 * @return {!Array} Contents as an ArrayBuffer.
 * @throws {error} If buffer fails.
 */

adamant.getBytes = function (transaction) {

  ...

  switch (transaction.type) {
    case constants.Transactions.SEND:
      break
    case constants.Transactions.CHAT_MESSAGE:
      assetBytes = this.chatGetBytes(transaction)
      assetSize = assetBytes.length
      break

…

    default:
      alert('Not supported yet')
  }

  var bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 64 + 64 + assetSize, true)

  bb.writeByte(transaction.type)
  bb.writeInt(transaction.timestamp)

  ...

  bb.flip()
  var arrayBuffer = new Uint8Array(bb.toArrayBuffer())
  var buffer = []

  for (var i = 0; i < arrayBuffer.length; i++) {
    buffer[i] = arrayBuffer[i]
  }

  return Buffer.from(buffer)
}

2 – Conte SHA-256 do bloco de dados

/**
 * Creates hash based on transaction bytes.
 * @implements {getBytes}
 * @implements {crypto.createHash}
 * @param {transaction} trs
 * @return {hash} sha256 crypto hash
 */
adamant.getHash = function (trs) {
  return crypto.createHash('sha256').update(this.getBytes(trs)).digest()
}

3 — Assine a transação

adamant.transactionSign = function (trs, keypair) {
  var hash = this.getHash(trs)
  return this.sign(hash, keypair).toString('hex')
}

/**
 * Creates a signature based on a hash and a keypair.
 * @implements {sodium}
 * @param {hash} hash
 * @param {keypair} keypair
 * @return {signature} signature
 */
adamant.sign = function (hash, keypair) {
  return sodium.crypto_sign_detached(hash, Buffer.from(keypair.privateKey, 'hex'))
}

Enviando uma transação com uma mensagem para um nó da rede

Como a rede é descentralizada, qualquer um dos nós com uma API aberta servirá. Fazendo uma solicitação POST para o endpoint api/transactions:

curl 'api/transactions' -X POST 
  -d 'TX_DATA'

Em resposta, receberemos um ID de transação do tipo

{
    "success": true,
    "nodeTimestamp": 63228852,
    "transactionId": "6146865104403680934"
}

Validação de transação

Um sistema distribuído de nós, baseado no consenso, determina a “autenticidade” da mensagem de transação. De quem e para quem, quando, se a mensagem foi substituída por outra e se o horário de envio foi indicado corretamente. Esta é uma vantagem muito importante do blockchain - não existe uma estrutura central responsável pela verificação e a sequência de mensagens e seu conteúdo não podem ser falsificados.

Primeiro, um nó verifica a precisão e depois envia para outros - se a maioria disser que está tudo em ordem, a transação será incluída no próximo bloco da cadeia - isso é consenso.

Como funciona um mensageiro descentralizado no blockchain?

A parte do código do nó responsável pelas verificações pode ser visualizada no GitHub - validador.js и verificar.js. Sim, o nó é executado em Node.js.

Incluindo uma transação com uma mensagem em um bloco

Se o consenso for alcançado, a transação com a nossa mensagem será incluída no próximo bloco junto com outras transações válidas.

Os blocos têm uma sequência estrita e cada bloco subsequente é formado com base nos hashes dos blocos anteriores.

Como funciona um mensageiro descentralizado no blockchain?

A questão é que nossa mensagem também está incluída nesta sequência e não pode ser “reorganizada”. Se várias mensagens caírem em um bloco, sua ordem será determinada por timestamp mensagens.

Lendo mensagens

O aplicativo mensageiro recupera transações do blockchain que são enviadas ao destinatário. Para isso fizemos um endpoint api/chatrooms.

Todas as transações estão disponíveis para todos - você pode receber mensagens criptografadas. Mas apenas o destinatário pode descriptografar usando sua chave privada e a chave pública do remetente:

**
 * Decodes the incoming message
 * @param {any} msg encoded message
 * @param {string} senderPublicKey sender public key
 * @param {string} privateKey our private key
 * @param {any} nonce nonce
 * @returns {string}
 */
adamant.decodeMessage = function (msg, senderPublicKey, privateKey, nonce) {
  if (typeof msg === 'string') {
    msg = hexToBytes(msg)
  }

  if (typeof nonce === 'string') {
    nonce = hexToBytes(nonce)
  }

  if (typeof senderPublicKey === 'string') {
    senderPublicKey = hexToBytes(senderPublicKey)
  }

  if (typeof privateKey === 'string') {
    privateKey = hexToBytes(privateKey)
  }

  const DHPublicKey = ed2curve.convertPublicKey(senderPublicKey)
  const DHSecretKey = ed2curve.convertSecretKey(privateKey)
  const decrypted = nacl.box.open(msg, nonce, DHPublicKey, DHSecretKey)

  return decrypted ? decode(decrypted) : ''
}

O que mais?

Como as mensagens são entregues dessa forma em cerca de 5 segundos - este é o momento em que um novo bloco de rede aparece - criamos uma conexão de soquete cliente-nó e nó-nó. Quando um nó recebe uma nova transação, ele verifica sua validade e a encaminha para outros nós. A transação está disponível para clientes do mensageiro antes mesmo de ocorrer o consenso e inclusão no bloco. Desta forma, entregaremos mensagens instantaneamente, como os mensageiros instantâneos normais.

Para armazenar a agenda de endereços, fizemos KVS - Key-Value Storage - esse é outro tipo de transação em que asset não é a caixa NaCl que está criptografada, mas Caixa secreta de NaCl. É assim que o mensageiro armazena outros dados.

A transferência de arquivos/imagens e bate-papos em grupo ainda exige muito trabalho. É claro que, em um formato de erros e erros, isso pode ser “estragado” rapidamente, mas queremos manter o mesmo nível de privacidade.

Sim, ainda há trabalho a ser feito - idealmente, a privacidade real pressupõe que os usuários não se conectarão aos nós da rede pública, mas criarão os seus próprios. Que porcentagem de usuários você acha que faz isso? Isso mesmo, 0. Conseguimos resolver parcialmente esse problema com a versão Tor do messenger.

Provamos que pode existir um mensageiro no blockchain. Anteriormente, houve apenas uma tentativa em 2012 - mensagem de bits, que falhou devido a longos tempos de entrega de mensagens, carga de CPU e falta de aplicativos móveis.

E o ceticismo se deve ao fato de que os mensageiros no blockchain estão à frente de seu tempo - as pessoas não estão prontas para assumir a responsabilidade por suas contas, possuir informações pessoais ainda não é uma tendência e a tecnologia não permite altas velocidades no blockchain. Mais análogos tecnológicos do nosso projeto aparecerão a seguir. Você vai ver.

Fonte: habr.com

Adicionar um comentário