Cum funcționează un mesager descentralizat pe blockchain?

La începutul lui 2017, am început să creăm un mesager pe blockchain [numele și linkul sunt în profil], discutând avantajele față de mesagerii P2P clasici.

Plecat 2.5 anul, și am putut să ne confirmăm conceptul: aplicațiile de mesagerie sunt acum disponibile pentru iOS, Web PWA, Windows, GNU/Linux, Mac OS și Android.

Astăzi vă vom spune cum funcționează messengerul blockchain și cum pot funcționa aplicațiile client cu API-ul său.
Cum funcționează un mesager descentralizat pe blockchain?

Am vrut ca blockchain-ul să rezolve problemele de securitate și confidențialitate ale mesageriei P2P clasice:

  • Un singur clic pentru a crea un cont - fără telefoane sau e-mailuri, fără acces la agende sau geolocalizări.
  • Interlocutorii nu stabilesc niciodată conexiuni directe; toată comunicarea are loc printr-un sistem distribuit de noduri. Adresele IP ale utilizatorilor nu sunt accesibile unul altuia.
  • Toate mesajele sunt criptate End-to-End curve25519xsalsa20poly1305. Se pare că acest lucru nu va surprinde pe nimeni, dar codul nostru sursă este deschis.
  • Atacul MITM este exclus - fiecare mesaj este o tranzacție și este semnat de Ed25519 EdDSA.
  • Mesajul ajunge în propriul bloc. Consecvența și timestamp Nu puteți remedia blocurile și, prin urmare, ordinea mesajelor.
  • „Nu am spus asta” nu va funcționa cu mesajele din blockchain.
  • Nu există nicio structură centrală care să verifice „autenticitatea” unui mesaj. Acest lucru se realizează printr-un sistem distribuit de noduri bazat pe consens și este deținut de utilizatori.
  • Imposibilitatea cenzurii - conturile nu pot fi blocate și mesajele nu pot fi șterse.
  • Blockchain 2FA este o alternativă la infernul 2FA prin SMS, a stricat multă sănătate.
  • Capacitatea de a obține toate conversațiile de pe orice dispozitiv în orice moment este capacitatea de a nu stoca deloc conversațiile local.
  • Подтверждение доставки сообщений. Не на устройство пользователя, а в сеть. По сути, это подтверждение возможности получателя прочитать ваше сообщение. Это полезная фича для отправки критических уведомлений.

Из плюшек блокчейна также тесная интеграция с криптовалютами Ethereum, Dogecoin, Lisk, Dash, Bitcoin (этот пока в процессе) и возможность отправки токенов в чатах. Мы даже сделали встроенный крипто-обменник.

Și apoi - cum funcționează totul.

Un mesaj este o tranzacție

Toată lumea este deja obișnuită cu faptul că tranzacțiile din blockchain transferă jetoane (monede) de la un utilizator la altul. Ca Bitcoin. Am creat un tip special de tranzacție pentru transmiterea mesajelor.

Pentru a trimite un mesaj într-un messenger pe blockchain, trebuie să parcurgeți câțiva pași:

  1. Criptați textul mesajului
  2. Pune text cifrat într-o tranzacție
  3. Semnează tranzacția
  4. Trimiteți o tranzacție către orice nod de rețea
  5. Un sistem distribuit de noduri determină „autenticitatea” unui mesaj
  6. Dacă totul este OK, tranzacția cu mesajul este inclusă în blocul următor
  7. Destinatarul preia tranzacția mesajului și o decriptează

Pașii 1–3 și 7 sunt efectuati local pe client, iar pașii 5–6 sunt efectuati pe gazde.

Criptarea mesajelor

Mesajul este criptat cu cheia privată a expeditorului și cheia publică a destinatarului. Vom lua cheia publică din rețea, dar pentru aceasta, contul destinatarului trebuie să fie inițializat, adică să aibă cel puțin o tranzacție. Puteți utiliza o solicitare REST GET /api/accounts/getPublicKey?address={ADAMANT address}, iar la încărcarea chat-urilor, cheile publice ale interlocutorilor vor fi deja disponibile.

Cum funcționează un mesager descentralizat pe blockchain?

Мессенджер шифрует сообщения алгоритмом curve25519xsalsa20poly1305 (Cutie NaCl). Deoarece contul conține chei Ed25519, pentru a forma o cutie, cheile trebuie mai întâi convertite în Curve25519 Diffie-Hellman.

Iată un exemplu în 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)
  }
}

Formarea unei tranzacții cu un mesaj

Tranzacția are următoarea structură generală:

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

Pentru o tranzacție cu mesaje, cel mai important lucru este asset - trebuie să plasați un mesaj în obiect chat cu structura:

  • message - salvați mesajul criptat
  • own_message - nonce
  • type — tipul mesajului

Mesajele sunt, de asemenea, împărțite în tipuri. În esență, parametrul type iti spune cum sa intelegi message. Puteți trimite doar un text sau puteți trimite un obiect cu lucruri interesante în interior - de exemplu, așa face mesagerul transferuri de criptomonede în chat-uri.

Ca rezultat, creăm o tranzacție:

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

Semnătura tranzacției

Pentru a vă asigura că toată lumea are încredere în autenticitatea expeditorului și destinatarului, în momentul expedierii și în conținutul mesajului, tranzacția este semnată. O semnătură digitală vă permite să verificați autenticitatea unei tranzacții folosind o cheie publică - o cheie privată nu este necesară pentru aceasta.

Dar semnătura în sine este efectuată folosind cheia privată:

Cum funcționează un mesager descentralizat pe blockchain?

Diagrama arată că mai întâi hashăm tranzacția cu SHA-256 și apoi o semnăm Ed25519 EdDSA și obțineți o semnătură signature, iar ID-ul tranzacției face parte din hash-ul SHA-256.

Exemplu de implementare:

1 — Formați un bloc de date, inclusiv un mesaj

/**
 * 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 - Numărați SHA-256 din blocul de date

/**
 * 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 — Semnați tranzacția

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

Trimiterea unei tranzacții cu un mesaj către un nod de rețea

Deoarece rețeaua este descentralizată, oricare dintre nodurile cu un API deschis va funcționa. Efectuarea unei cereri POST către punctul final api/transactions:

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

Ca răspuns, vom primi un ID de tranzacție de acest tip

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

Validarea tranzacției

Un sistem distribuit de noduri, bazat pe consens, determină „autenticitatea” mesajului tranzacției. De la cine și cui, când, dacă mesajul a fost înlocuit cu altul și dacă ora trimiterii a fost indicată corect. Acesta este un avantaj foarte important al blockchain-ului - nu există o structură centrală care să fie responsabilă de verificare, iar succesiunea mesajelor și conținutul acestora nu pot fi falsificate.

Mai întâi, un nod verifică acuratețea și apoi o trimite altora - dacă majoritatea spune că totul este în ordine, tranzacția va fi inclusă în următorul bloc al lanțului - acesta este un consens.

Cum funcționează un mesager descentralizat pe blockchain?

Partea din codul nodului care este responsabilă pentru verificări poate fi vizualizată pe GitHub - validator.js и verifica.js. Da, nodul rulează pe Node.js.

Includerea unei tranzacții cu un mesaj într-un bloc

Dacă se ajunge la un consens, tranzacția cu mesajul nostru va fi inclusă în blocul următor împreună cu alte tranzacții valide.

Blocurile au o secvență strictă, iar fiecare bloc ulterior este format pe baza hash-urilor blocurilor anterioare.

Cum funcționează un mesager descentralizat pe blockchain?

Ideea este că mesajul nostru este și el inclus în această secvență și nu poate fi „rearanjat”. Dacă mai multe mesaje cad într-un bloc, ordinea acestora va fi determinată de timestamp mesaje.

Citirea mesajelor

Aplicația de mesagerie preia tranzacțiile din blockchain care sunt trimise destinatarului. Pentru aceasta am făcut un punct final api/chatrooms.

Toate tranzacțiile sunt disponibile pentru toată lumea - puteți primi mesaje criptate. Dar numai destinatarul poate decripta folosind cheia sa privată și cheia publică a expeditorului:

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

Și ce altceva?

Deoarece mesajele sunt livrate în acest fel în aproximativ 5 secunde - acesta este momentul în care apare un nou bloc de rețea - am venit cu o conexiune socket client-nod și nod-nod. Când un nod primește o nouă tranzacție, acesta își verifică valabilitatea și o transmite altor noduri. Tranzacția este disponibilă clienților de mesagerie chiar înainte de apariția consensului și includerea în bloc. În acest fel vom livra mesaje instantaneu, la fel ca mesageria instant obișnuită.

Pentru a stoca agenda de adrese, am făcut KVS - Key-Value Storage - acesta este un alt tip de tranzacție în care asset nu NaCl-box este criptat, dar NaCl-secretbox. Acesta este modul în care messengerul stochează alte date.

Transferurile de fișiere/imagini și conversațiile de grup necesită încă multă muncă. Bineînțeles, în formatul gafă și gafă, acest lucru poate fi „înșurubat” rapid, dar dorim să menținem același nivel de confidențialitate.

Da, mai este de făcut - în mod ideal, confidențialitatea reală presupune că utilizatorii nu se vor conecta la nodurile rețelei publice, ci își vor ridica propriile noduri. Ce procent de utilizatori crezi că face asta? Așa e, 0. Am reușit să rezolvăm parțial această problemă cu versiunea Tor a messengerului.

Am demonstrat că un mesager pe blockchain poate exista. Anterior, a existat o singură încercare în 2012 - bitmessage, care a eșuat din cauza timpilor lungi de livrare a mesajelor, a încărcării procesorului și a lipsei de aplicații mobile.

Iar scepticismul se datorează faptului că mesagerii de pe blockchain sunt înaintea timpului lor - oamenii nu sunt pregătiți să-și asume responsabilitatea pentru contul lor, deținerea de informații personale nu este încă o tendință, iar tehnologia nu permite viteze mari pe blockchain. Mai multe analogi tehnologici ai proiectului nostru vor apărea în continuare. Vei vedea.

Sursa: www.habr.com

Adauga un comentariu