Wie funktioniert ein dezentraler Messenger auf der Blockchain?

Anfang 2017 haben wir mit der Entwicklung eines Messenger-Dienstes auf der Blockchain [Name und Link sind im Profil] begonnen, um die Vorteile gegenüber klassischen P2P-Messengern zu diskutieren.

Es sind 2.5 Jahre vergangen, und es ist uns gelungen, unser Konzept zu bestätigen: Jetzt sind Messenger-Apps für iOS, Web PWA, Windows, GNU/Linux, Mac OS und Android verfügbar.

Heute erklären wir, wie der Messenger auf der Blockchain funktioniert und wie Client-Anwendungen mit seiner API interagieren.
Wie funktioniert ein dezentraler Messenger auf der Blockchain?

Wir wollten, dass die Blockchain die Sicherheits- und Privatsphäre-Probleme klassischer P2P-Messenger löst:

  • Ein Klick zur Erstellung eines Kontos — keine Telefonnummern und E-Mail-Adressen, kein Zugriff auf Adressbücher und Geolokalisierungen.
  • Gespräche werden niemals direkt gekoppelt; die gesamte Kommunikation erfolgt über ein verteiltes System von Knoten. Die IP-Adressen der Nutzer sind füreinander nicht zugänglich.
  • Alle Nachrichten werden End-to-End mit curve25519xsalsa20poly1305 verschlüsselt. Das mag nicht überraschend sein, aber der Quellcode ist bei uns offen.
  • Man-in-the-Middle-Angriffe sind ausgeschlossen — jede Nachricht ist eine Transaktion und wird mit Ed25519 EdDSA signiert.
  • Die Nachricht gelangt in ihren Block. Die Reihenfolge und timestamp Blöcke kann man nicht ändern, und somit auch nicht die Reihenfolge der Nachrichten.
  • „Das habe ich nicht gesagt“ zieht nicht bei Nachrichten in der Blockchain.
  • Es gibt keine zentrale Struktur, die die „Echtheit“ der Nachricht überprüft. Dies wird von einem verteilten Netz von Knoten auf Basis von Konsens gemacht, das den Benutzern gehört.
  • Die Unmöglichkeit von Zensur – Konten können nicht blockiert und Nachrichten nicht gelöscht werden.
  • Blockchain 2FA – eine Alternative zur schrecklichen 2FA per SMS, die vielen Menschen geschadet hat.
  • Die Möglichkeit, alle Ihre Dialoge von jedem Gerät zu jeder Zeit abzurufen – das bedeutet, dass Dialoge überhaupt nicht lokal gespeichert werden müssen.
  • Bestätigung der Nachrichtenlieferung. Nicht an das Benutzergerät, sondern ins Netzwerk. Im Grunde handelt es sich um die Bestätigung, dass der Empfänger Ihre Nachricht lesen kann. Das ist eine nützliche Funktion für kritische Benachrichtigungen.

Die Vorzüge der Blockchain umfassen auch die enge Integration mit Kryptowährungen wie Ethereum, Dogecoin, Lisk, Dash, Bitcoin (dieser ist noch in der Entwicklung) und die Möglichkeit, Token in Chats zu versenden. Wir haben sogar einen integrierten Krypto-Austausch.

Und weiter – wie funktioniert das alles.

Eine Nachricht ist eine Transaktion.

Alle sind bereits daran gewöhnt, dass Transaktionen in der Blockchain Token (Münzen) von einem Benutzer zu einem anderen übertragen. Wie bei Bitcoin. Wir haben jedoch einen speziellen Transaktionstyp für die Übertragung von Nachrichten entwickelt.

Um eine Nachricht im Messenger auf der Blockchain zu senden, sind mehrere Schritte erforderlich:

  1. Den Nachrichtentext verschlüsseln
  2. Den verschlüsselten Text in die Transaktion einfügen
  3. Die Transaktion signieren
  4. Die Transaktion an einen beliebigen Knoten im Netzwerk senden
  5. Das verteilte System der Knoten bestimmt die 'Zuverlässigkeit' der Nachricht
  6. Wenn alles in Ordnung ist, wird die Transaktion mit der Nachricht in den nächsten Block aufgenommen
  7. Der Empfänger extrahiert die Transaktion mit der Nachricht und entschlüsselt sie

Die Schritte 1–3 und 7 erfolgen lokal auf dem Client, während 5–6 auf den Knoten im Netzwerk ausgeführt werden.

Verschlüsselung der Nachricht

Die Nachricht wird mit dem privaten Schlüssel des Absenders und dem öffentlichen Schlüssel des Empfängers verschlüsselt. Den öffentlichen Schlüssel nehmen wir aus dem Netzwerk, jedoch muss das Konto des Empfängers initialisiert sein, also mindestens eine Transaktion aufweisen. Es kann eine REST-Anfrage verwendet werden: GET /api/accounts/getPublicKey?address={ADAMANT address}, und beim Laden von Chats werden die öffentlichen Schlüssel der Gesprächspartner bereits verfügbar sein.

Wie funktioniert ein dezentraler Messenger auf der Blockchain?

Der Messenger verschlüsselt Nachrichten mit dem Algorithmus curve25519xsalsa20poly1305 (NaCl Box). Da das Konto Ed25519-Schlüssel enthält, müssen diese zur Bildung des Box zuerst in Curve25519 Diffie-Hellman umgewandelt werden.

Hier ist ein Beispiel 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)
  }
}

Erstellung einer Transaktion mit Nachricht

Die Transaktion hat folgende allgemeine Struktur:

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

Für Transaktionsnachrichten ist der wichtigste Wert asset — das Nachrichtenobjekt sollte sich unter chat folgender Struktur befinden:

  • message — das verschlüsselte Nachricht wird gespeichert
  • own_message — nonce
  • type — Nachrichtentyp

Nachrichten werden ebenfalls in Typen eingeteilt. Der Parameter type informiert darüber, wie man messageverstehen soll. Man kann einfach Text senden, oder ein Objekt mit interessanten Inhalten — beispielsweise so macht der Messenger Kryptowährungsüberweisungen in Chats.

Am Ende erstellen wir die Transaktion:

{
  "transaction": {
    "type": 8,
    "amount": 0,
    "senderId": "U12499126640447739963",
    "senderPublicKey": "e9cafb1e7b403c4cf247c94f73ee4cada367fcc130cb3888219a0ba0633230b6",
    "asset": {
      "chat": {
        "message": "cb682accceef92d7cddaaddb787d1184ab5428",
        "own_message": "e7d8f90ddf7d70efe359c3e4ecfb5ed3802297b248eacbd6",
        "type": 1
      }
    },
    "recipientId": "U15677078342684640219",
    "timestamp": 63228087,
    "signature": "hier wird die Unterschrift sein"
  }
}

Unterschrift der Transaktion

Um sicherzustellen, dass der Absender und der Empfänger, der Zeitpunkt der Sendung und der Inhalt der Nachricht richtig sind, wird die Transaktion signiert. Die digitale Signatur ermöglicht es, die Authentizität der Transaktion anhand des öffentlichen Schlüssels zu überprüfen — der private Schlüssel ist dafür nicht notwendig.

Und die Unterschrift wird mit dem privaten Schlüssel erstellt:

Wie funktioniert ein dezentraler Messenger auf der Blockchain?

Aus dem Diagramm geht hervor, dass wir die Transaktion zunächst mit SHA-256 hashen und dann signieren. Ed25519 EdDSA und erhalten die Unterschrift signature, und die Transaktions-ID ist Teil des SHA-256-Hashes.

Beispiel einer Implementierung:

1 — Wir erstellen einen Datenblock, der die Nachricht enthält.

/**
 * 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 — Wir berechnen den SHA-256-Hash des Datenblocks.

/**
 * 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 — Wir signieren die Transaktion.

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

/**
 * Erstellt eine Signatur basierend auf einem Hash und einem Schlüssel-Paar.
 * @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'))
}

Übertragung der Transaktion mit einer Nachricht an einen Knoten des Netzwerks

Da das Netzwerk dezentralisiert ist, kann jeder Knoten mit offenem API verwendet werden. Wir führen eine POST-Anfrage an den Endpunkt durch api/transactions:

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

Wir erhalten als Antwort die ID der Transaktion vom Typ

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

Überprüfung der Gültigkeit der Transaktion

Das verteilte System von Knoten, das auf Konsens basiert, bestimmt die "Gültigkeit" der Transaktion-Nachricht. Von wem und an wen, wann, ob die Nachricht nicht durch eine andere ersetzt wurde, und ob die angegebene Zeit korrekt ist. Dies ist ein sehr wichtiger Vorteil der Blockchain – es gibt keine zentrale Struktur, die für die Überprüfungen verantwortlich ist, und die Abfolge der Nachrichten und deren Inhalt können nicht gefälscht werden.

Zunächst wird die Gültigkeit von einem Knoten überprüft und dann an andere verteilt – wenn die Mehrheit sagt, dass alles in Ordnung ist, wird die Transaktion in den nächsten Block der Kette aufgenommen – das ist der Konsens.

Wie funktioniert ein dezentraler Messenger auf der Blockchain?

Der Teil des Knotencodes, der für die Prüfungen verantwortlich ist, kann auf GitHub eingesehen werden — validator.js und verify.js. Ja, der Knoten läuft auf Node.js.

Wir aktivieren die Transaktion mit der Nachricht im Block

Wenn Konsens erreicht ist, wird die Transaktion mit unserer Nachricht im nächsten Block zusammen mit anderen gültigen Transaktionen aufgenommen.

Die Blöcke haben eine strenge Reihenfolge, und jeder nachfolgende Block wird auf der Grundlage der Hashes der vorhergehenden Blöcke gebildet.

Wie funktioniert ein dezentraler Messenger auf der Blockchain?

Wesentlich ist, dass unsere Nachricht ebenfalls in diese Reihenfolge aufgenommen wird und nicht "verschoben" werden kann. Falls mehrere Nachrichten in einen Block gelangen, wird ihre Reihenfolge durch timestamp Nachrichten.

Nachrichten lesen

Die Messenger-Anwendung extrahiert Transaktionen aus der Blockchain, die an den Empfänger gesendet wurden. Zu diesem Zweck haben wir den Endpunkt api/chatrooms.

Alle Transaktionen sind für jeden zugänglich — man kann verschlüsselte Nachrichten abrufen. Allerdings kann nur der Empfänger diese mit seinem privaten Schlüssel und dem öffentlichen Schlüssel des Absenders entschlüsseln:

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

Was gibt es noch?

Da Nachrichten auf diese Weise etwa 5 Sekunden - die Zeit für das Erscheinen eines neuen Blocks im Netzwerk - benötigen, haben wir eine Socket-Verbindung zwischen Client und Knoten sowie zwischen Knoten entworfen. Wenn ein Knoten eine neue Transaktion erhält, prüft er deren Gültigkeit und leitet sie an andere Knoten weiter. Die Transaktion ist für Messenger-Clients verfügbar, noch bevor ein Konsens erreicht und sie in einen Block integriert wird. So werden wir Nachrichten sofort zustellen, ähnlich wie bei herkömmlichen Messengern.

Um das Adressbuch zu speichern, haben wir KVS - Key-Value Storage - erstellt, dies ist ein weiterer Typ von Transaktionen, in denen asset nicht die NaCl-box, sondern NaCl-secretbox. So speichert der Messenger auch andere Daten.

Der Austausch von Dateien/Bildern und Gruppenchats erfordert noch viel Arbeit. Natürlich könnte man dies im Handumdrehen im Format „Quick and Dirty“ umsetzen, aber wir möchten das gleiche Maß an Privatsphäre bewahren.

Ja, es gibt noch viel zu tun - idealerweise bedeutet echte Privatsphäre, dass die Benutzer sich nicht mit öffentlichen Knoten im Netzwerk verbinden, sondern ihre eigenen aufbauen. Wie viele Prozent der Benutzer glauben Sie, machen das? Richtig, 0. Teilweise haben wir dieses Problem mit der Tor-Version des Messengers gelöst.

Wir haben bewiesen, dass ein Messenger auf der Blockchain existieren kann. Zuvor gab es nur einen Versuch im Jahr 2012 — bitmessage, der aufgrund der langen Lieferzeiten, der hohen Prozessorlast und des Fehlens mobiler Anwendungen gescheitert ist.

Der Skeptizismus rührt daher, dass Blockchain-Messenger ihrer Zeit voraus sind — die Menschen sind nicht bereit, die Verantwortung für ihr Konto zu übernehmen, der Besitz persönlicher Informationen ist noch nicht im Trend, und die Technologien ermöglichen keine hohen Geschwindigkeiten auf der Blockchain. Es werden bald technologisch fortschrittlichere Alternativen zu unserem Projekt erscheinen. Sie werden sehen.

Quelle: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster