Come funziona un messenger decentralizzato sulla blockchain?

All'inizio del 2017, abbiamo iniziato a creare un messenger sulla blockchain [nome e link sono nel profilo] discutendo i vantaggi rispetto ai classici messenger P2P.

andato 2.5 anno e abbiamo potuto confermare il nostro concetto: le applicazioni di messaggistica sono ora disponibili per iOS, Web PWA, Windows, GNU/Linux, Mac OS e Android.

Oggi ti diremo come funziona il blockchain messenger e come le applicazioni client possono funzionare con la sua API.
Come funziona un messenger decentralizzato sulla blockchain?

Volevamo che la blockchain risolvesse i problemi di sicurezza e privacy dei classici messenger P2P:

  • Un clic per creare un account: niente telefoni o e-mail, nessun accesso a rubriche o geolocalizzazione.
  • Gli interlocutori non stabiliscono mai collegamenti diretti; tutta la comunicazione avviene attraverso un sistema distribuito di nodi. Gli indirizzi IP degli utenti non sono accessibili tra loro.
  • Tutti i messaggi sono crittografati end-to-end curve25519xsalsa20poly1305. Sembra che questo non sorprenderà nessuno, ma il nostro codice sorgente è aperto.
  • L'attacco MITM è escluso: ogni messaggio è una transazione ed è firmato da Ed25519 EdDSA.
  • Il messaggio finisce nel proprio blocco. Coerenza e timestamp Non è possibile correggere i blocchi e quindi l'ordine dei messaggi.
  • "Non l'ho detto" non funzionerà con i messaggi sulla blockchain.
  • Non esiste una struttura centrale che controlli l’“autenticità” di un messaggio. Questo viene fatto da un sistema distribuito di nodi basato sul consenso ed è di proprietà degli utenti.
  • Impossibilità di censura: gli account non possono essere bloccati e i messaggi non possono essere eliminati.
  • Blockchain 2FA è un'alternativa all'infernale 2FA via SMS, rovinato molta salute.
  • La possibilità di ricevere tutte le tue conversazioni da qualsiasi dispositivo in qualsiasi momento significa che non devi affatto archiviare le conversazioni localmente.
  • Conferma della consegna del messaggio. Non al dispositivo dell'utente, ma alla rete. In sostanza, questa è la conferma della capacità del destinatario di leggere il tuo messaggio. Questa è una funzionalità utile per inviare notifiche critiche.

I vantaggi della blockchain includono anche una stretta integrazione con le criptovalute Ethereum, Dogecoin, Lisk, Dash, Bitcoin (questa è ancora in fase di sviluppo) e la possibilità di inviare token nelle chat. Abbiamo anche creato uno scambiatore di criptovalute integrato.

E poi - come funziona tutto.

Un messaggio è una transazione

Tutti sono già abituati al fatto che le transazioni nella blockchain trasferiscono token (monete) da un utente all'altro. Come Bitcoin. Abbiamo creato un tipo speciale di transazione per la trasmissione di messaggi.

Per inviare un messaggio in un messenger sulla blockchain, è necessario eseguire diversi passaggi:

  1. Crittografa il testo del messaggio
  2. Inserisci il testo cifrato in una transazione
  3. Firma la transazione
  4. Invia una transazione a qualsiasi nodo della rete
  5. Un sistema distribuito di nodi determina l’“autenticità” di un messaggio
  6. Se tutto è ok, la transazione con il messaggio viene inclusa nel blocco successivo
  7. Il destinatario recupera la transazione del messaggio e la decrittografa

I passaggi 1–3 e 7 vengono eseguiti localmente sul client, mentre i passaggi 5–6 vengono eseguiti sugli host.

Crittografia dei messaggi

Il messaggio viene crittografato con la chiave privata del mittente e la chiave pubblica del destinatario. Prenderemo la chiave pubblica dalla rete, ma per questo il conto del destinatario dovrà essere inizializzato, cioè avere almeno una transazione. È possibile utilizzare una richiesta REST GET /api/accounts/getPublicKey?address={ADAMANT address}, e durante il caricamento delle chat saranno già disponibili le chiavi pubbliche degli interlocutori.

Come funziona un messenger decentralizzato sulla blockchain?

Il messenger crittografa i messaggi utilizzando l'algoritmo curve25519xsalsa20poly1305 (Scatola NaCl). Poiché l'account contiene chiavi Ed25519, per formare una scatola, le chiavi devono prima essere convertite in Curve25519 Diffie-Hellman.

Ecco un esempio in 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)
  }
}

Formare una transazione con un messaggio

L’operazione ha la seguente struttura generale:

{
  "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": {}
}

Per una transazione di messaggi, la cosa più importante è asset - devi inserire un messaggio nell'oggetto chat con struttura:

  • message - salvare il messaggio crittografato
  • own_message - nonce
  • type — tipo di messaggio

Anche i messaggi sono divisi in tipologie. In sostanza, il parametro type ti dice come capire message. Puoi inviare solo un testo oppure inviare un oggetto con cose interessanti all'interno: ad esempio, è così che il messenger effettua trasferimenti di criptovaluta nelle chat.

Di conseguenza, creiamo una transazione:

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

Firma della transazione

Per garantire che tutti siano sicuri dell'autenticità del mittente e del destinatario, dell'ora di invio e del contenuto del messaggio, la transazione viene firmata. Una firma digitale consente di verificare l'autenticità di una transazione utilizzando una chiave pubblica: per questo non è necessaria una chiave privata.

Ma la firma stessa viene eseguita utilizzando la chiave privata:

Come funziona un messenger decentralizzato sulla blockchain?

Il diagramma mostra che prima eseguiamo l'hashing della transazione con SHA-256 e poi la firmiamo Ed25519 EdDSA e ottenere una firma signaturee l'ID della transazione fa parte dell'hash SHA-256.

Esempio di implementazione:

1 — Forma un blocco di dati, incluso un messaggio

/**
 * 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 - Contare SHA-256 dal blocco dati

/**
 * 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 — Firma la transazione

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'))
}

Invio di una transazione con un messaggio a un nodo di rete

Poiché la rete è decentralizzata, andrà bene qualsiasi nodo con un'API aperta. Effettuare una richiesta POST all'endpoint api/transactions:

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

In risposta riceveremo un ID transazione del tipo

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

Convalida della transazione

Un sistema distribuito di nodi, basato sul consenso, determina l’“autenticità” del messaggio di transazione. Da chi e a chi, quando, se il messaggio è stato sostituito con un altro e se l'orario di invio è stato indicato correttamente. Questo è un vantaggio molto importante della blockchain: non esiste una struttura centrale responsabile della verifica e la sequenza dei messaggi e il loro contenuto non possono essere falsificati.

Innanzitutto, un nodo ne verifica l'accuratezza e poi lo invia ad altri: se la maggioranza dice che tutto è in ordine, la transazione verrà inclusa nel blocco successivo della catena: questo è consenso.

Come funziona un messenger decentralizzato sulla blockchain?

La parte del codice del nodo preposta ai controlli è visualizzabile su GitHub - validatore.js и verificare.js. Sì, il nodo viene eseguito su Node.js.

Includere una transazione con un messaggio in un blocco

Se viene raggiunto il consenso, la transazione con il nostro messaggio verrà inclusa nel blocco successivo insieme ad altre transazioni valide.

I blocchi hanno una sequenza rigorosa e ogni blocco successivo viene formato in base agli hash dei blocchi precedenti.

Come funziona un messenger decentralizzato sulla blockchain?

Il punto è che anche il nostro messaggio è compreso in questa sequenza e non può essere “riordinato”. Se più messaggi rientrano in un blocco, il loro ordine sarà determinato da timestamp messaggi.

Lettura dei messaggi

L'applicazione di messaggistica recupera le transazioni dalla blockchain che vengono inviate al destinatario. Per questo abbiamo creato un punto finale api/chatrooms.

Tutte le transazioni sono disponibili a tutti: puoi ricevere messaggi crittografati. Ma solo il destinatario può decrittografare utilizzando la sua chiave privata e la chiave pubblica del mittente:

**
 * 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) : ''
}

Che altro?

Poiché i messaggi vengono consegnati in questo modo in circa 5 secondi - questo è il momento in cui appare un nuovo blocco di rete - abbiamo creato una connessione socket da client a nodo e da nodo a nodo. Quando un nodo riceve una nuova transazione, ne verifica la validità e la trasmette ad altri nodi. La transazione è disponibile per i client messenger anche prima che avvenga il consenso e l'inclusione nel blocco. In questo modo consegneremo i messaggi istantaneamente, proprio come i normali servizi di messaggistica istantanea.

Per archiviare la rubrica, abbiamo creato KVS - Key-Value Storage - questo è un altro tipo di transazione in cui asset non è la NaCl-box ad essere crittografata, ma Scatola segreta NaCl. Ecco come il messenger memorizza altri dati.

I trasferimenti di file/immagini e le chat di gruppo richiedono ancora molto lavoro. Naturalmente, in un formato di errore e errore questo può essere “rovinato” rapidamente, ma vogliamo mantenere lo stesso livello di privacy.

Sì, c'è ancora del lavoro da fare: idealmente, la vera privacy presuppone che gli utenti non si connettano ai nodi della rete pubblica, ma aumentino i propri. Quale percentuale di utenti pensi che faccia questo? Esatto, 0. Siamo riusciti a risolvere parzialmente questo problema con la versione Tor del messenger.

Abbiamo dimostrato che può esistere un messaggero sulla blockchain. In precedenza, c'era stato un solo tentativo nel 2012: bit messaggio, che non è riuscito a causa dei lunghi tempi di consegna dei messaggi, del carico della CPU e della mancanza di applicazioni mobili.

E lo scetticismo è dovuto al fatto che i messaggeri sulla blockchain sono in anticipo sui tempi: le persone non sono pronte ad assumersi la responsabilità del proprio account, possedere informazioni personali non è ancora una tendenza e la tecnologia non consente alte velocità sulla blockchain. Successivamente appariranno altri analoghi tecnologici del nostro progetto. Vedrai.

Fonte: habr.com

Aggiungi un commento