Jak zdecentralizowany komunikator działa na blockchainie?

Na początku 2017 roku rozpoczęliśmy tworzenie komunikatora na blockchainie [nazwa i link w profilu] omawiając jego zalety w stosunku do klasycznych komunikatorów P2P.

Odszedł 2.5 roku i mogliśmy potwierdzić naszą koncepcję: aplikacje komunikatorowe są teraz dostępne dla systemów iOS, Web PWA, Windows, GNU/Linux, Mac OS i Android.

Dziś opowiemy Wam jak działa komunikator blockchain i jak aplikacje klienckie mogą współpracować z jego API.
Jak zdecentralizowany komunikator działa na blockchainie?

Chcieliśmy, aby blockchain rozwiązał problemy bezpieczeństwa i prywatności klasycznych komunikatorów P2P:

  • Jedno kliknięcie, aby założyć konto - bez telefonów i e-maili, bez dostępu do książek adresowych i geolokalizacji.
  • Rozmówcy nigdy nie nawiązują bezpośrednich połączeń, cała komunikacja odbywa się poprzez rozproszony system węzłów. Adresy IP użytkowników nie są sobie wzajemnie dostępne.
  • Wszystkie wiadomości są szyfrowane od końca do końca curve25519xsalsa20poly1305. Wydaje się, że nikogo to nie zdziwi, ale nasz kod źródłowy jest otwarty.
  • Atak MITM jest wykluczony - każda wiadomość jest transakcją i jest podpisana przez Ed25519 EdDSA.
  • Wiadomość kończy się w oddzielnym bloku. Spójność i timestamp Nie można naprawić bloków, a tym samym kolejności wiadomości.
  • „Nie powiedziałem tego” nie będzie działać z wiadomościami w blockchainie.
  • Nie ma centralnej struktury sprawdzającej „autentyczność” wiadomości. Odbywa się to poprzez rozproszony system węzłów oparty na konsensusie i jest własnością użytkowników.
  • Niemożność cenzury - nie można blokować kont i usuwać wiadomości.
  • Blockchain 2FA to alternatywa dla piekielnego 2FA poprzez SMS, zrujnował dużo zdrowia.
  • Możliwość uzyskania wszystkich rozmów z dowolnego urządzenia w dowolnym momencie oznacza, że ​​nie musisz w ogóle przechowywać rozmów lokalnie.
  • Potwierdzenie dostarczenia wiadomości. Nie do urządzenia użytkownika, ale do sieci. Zasadniczo jest to potwierdzenie zdolności odbiorcy do odczytania Twojej wiadomości. Jest to przydatna funkcja do wysyłania krytycznych powiadomień.

Korzyści z Blockchain obejmują także ścisłą integrację z kryptowalutami Ethereum, Dogecoin, Lisk, Dash, Bitcoin (ten jest wciąż w toku) oraz możliwość wysyłania tokenów na czatach. Stworzyliśmy nawet wbudowany wymiennik kryptowalut.

A potem - jak to wszystko działa.

Wiadomość jest transakcją

Wszyscy są już przyzwyczajeni do tego, że transakcje w blockchainie przenoszą tokeny (monety) od jednego użytkownika do drugiego. Podobnie jak Bitcoin. Stworzyliśmy specjalny rodzaj transakcji do przesyłania wiadomości.

Aby wysłać wiadomość w komunikatorze na blockchainie należy przejść kilka kroków:

  1. Zaszyfruj tekst wiadomości
  2. Umieść szyfrogram w transakcji
  3. Podpisz transakcję
  4. Wyślij transakcję do dowolnego węzła sieci
  5. Rozproszony system węzłów określa „autentyczność” wiadomości
  6. Jeśli wszystko jest w porządku, transakcja z komunikatem zostaje uwzględniona w kolejnym bloku
  7. Odbiorca pobiera transakcję wiadomości i odszyfrowuje ją

Kroki 1–3 i 7 są wykonywane lokalnie na kliencie, a kroki 5–6 na hostach.

Szyfrowanie wiadomości

Wiadomość jest szyfrowana kluczem prywatnym nadawcy i kluczem publicznym odbiorcy. Pobierzemy klucz publiczny z sieci, jednak w tym celu konto odbiorcy musi zostać zainicjowane, czyli posiadać przynajmniej jedną transakcję. Możesz użyć żądania REST GET /api/accounts/getPublicKey?address={ADAMANT address}, a podczas ładowania czatów klucze publiczne rozmówców będą już dostępne.

Jak zdecentralizowany komunikator działa na blockchainie?

Komunikator szyfruje wiadomości przy użyciu algorytmu curve25519xsalsa20poly1305 (Pudełko NaCl). Ponieważ konto zawiera klucze Ed25519, aby utworzyć pudełko, klucze należy najpierw przekonwertować na Curve25519 Diffie-Hellman.

Oto przykład w 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)
  }
}

Tworzenie transakcji z komunikatem

Transakcja ma następującą ogólną 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": {}
}

W przypadku transakcji wiadomości najważniejszą rzeczą jest asset - musisz umieścić wiadomość w obiekcie chat ze strukturą:

  • message - zapisz zaszyfrowaną wiadomość
  • own_message -chwilowo
  • type - typ wiadomości

Wiadomości są również podzielone na typy. Zasadniczo parametr type mówi ci, jak rozumieć message. Możesz wysłać samego SMS-a lub możesz wysłać przedmiot z ciekawymi rzeczami w środku - na przykład w ten sposób komunikator realizuje przelewy kryptowalut na czacie.

W rezultacie tworzymy transakcję:

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

Podpis transakcji

Aby każdy miał pewność co do autentyczności nadawcy i odbiorcy, czasu wysłania i treści wiadomości, transakcja zostaje podpisana. Podpis cyfrowy pozwala na weryfikację autentyczności transakcji przy pomocy klucza publicznego – klucz prywatny nie jest do tego potrzebny.

Ale sam podpis jest wykonywany przy użyciu klucza prywatnego:

Jak zdecentralizowany komunikator działa na blockchainie?

Diagram pokazuje, że najpierw haszujemy transakcję za pomocą SHA-256, a następnie ją podpisujemy Ed25519 EdDSA i zdobądź podpis signature, a identyfikator transakcji jest częścią skrótu SHA-256.

Przykładowa realizacja:

1 — Utwórz blok danych zawierający wiadomość

/**
 * 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 - Zlicz SHA-256 z bloku danych

/**
 * 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 — Podpisz transakcję

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

Wysłanie transakcji z komunikatem do węzła sieci

Ponieważ sieć jest zdecentralizowana, wystarczy dowolny z węzłów z otwartym API. Wysyłanie żądania POST do punktu końcowego api/transactions:

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

W odpowiedzi otrzymamy identyfikator transakcji danego typu

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

Walidacja transakcji

Rozproszony system węzłów, oparty na konsensusie, określa „autentyczność” komunikatu transakcyjnego. Od kogo i do kogo, kiedy, czy wiadomość została zastąpiona inną i czy poprawnie wskazano czas wysłania. To bardzo istotna zaleta blockchainu – nie ma centralnej struktury odpowiedzialnej za weryfikację, a sekwencji wiadomości i ich treści nie da się sfałszować.

Najpierw jeden węzeł sprawdza poprawność, a następnie przesyła ją do pozostałych – jeśli większość stwierdzi, że wszystko jest w porządku, transakcja zostanie uwzględniona w kolejnym bloku łańcucha – jest to konsensus.

Jak zdecentralizowany komunikator działa na blockchainie?

Część kodu węzła odpowiedzialną za kontrole można wyświetlić na GitHubie - walidator.js и zweryfikuj.js. Tak, węzeł działa na Node.js.

Uwzględnienie transakcji z komunikatem w bloku

W przypadku osiągnięcia konsensusu transakcja z naszym komunikatem zostanie uwzględniona w kolejnym bloku wraz z innymi ważnymi transakcjami.

Bloki mają ścisłą sekwencję, a każdy kolejny blok tworzony jest na podstawie skrótów poprzednich bloków.

Jak zdecentralizowany komunikator działa na blockchainie?

Rzecz w tym, że nasz przekaz także jest wpisany w tę sekwencję i nie da się go „przestawić”. Jeżeli w blok wpadnie kilka wiadomości, o ich kolejności zadecyduje tzw timestamp wiadomości.

Czytanie wiadomości

Aplikacja komunikator pobiera transakcje z blockchainu, które są wysyłane do odbiorcy. W tym celu stworzyliśmy punkt końcowy api/chatrooms.

Wszystkie transakcje są dostępne dla każdego - możesz otrzymywać zaszyfrowane wiadomości. Ale tylko odbiorca może odszyfrować, używając swojego klucza prywatnego i klucza publicznego nadawcy:

**
 * 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 co jeszcze?

Ponieważ wiadomości dostarczane są w ten sposób w ciągu około 5 sekund – w tym momencie pojawia się nowy blok sieci – opracowaliśmy połączenie typu klient-węzeł i węzeł-węzeł. Kiedy węzeł otrzymuje nową transakcję, sprawdza jej ważność i przekazuje ją do innych węzłów. Transakcja dostępna jest dla klientów messengera jeszcze przed osiągnięciem konsensusu i włączeniem do bloku. W ten sposób będziemy dostarczać wiadomości błyskawicznie, zupełnie jak zwykłe komunikatory internetowe.

Do przechowywania książki adresowej wykonaliśmy KVS – Key-Value Storage – jest to kolejny rodzaj transakcji, w której asset to nie NaCl-box jest szyfrowany, ale Sekretne pudełko NaCl. W ten sposób komunikator przechowuje inne dane.

Przesyłanie plików/obrazów i czaty grupowe nadal wymagają dużo pracy. Oczywiście w formie błędów i błędów można to szybko „schrzanić”, ale chcemy zachować ten sam poziom prywatności.

Tak, jest jeszcze wiele do zrobienia – w idealnej sytuacji prawdziwa prywatność zakłada, że ​​użytkownicy nie będą łączyć się z węzłami sieci publicznej, ale będą tworzyć własne. Jak myślisz, jaki procent użytkowników tak robi? Zgadza się, 0. Udało nam się częściowo rozwiązać ten problem za pomocą komunikatora w wersji Tor.

Udowodniliśmy, że komunikator na blockchainie może istnieć. Wcześniej w 2012 r. miała miejsce tylko jedna próba – wiadomość bitowa, co nie powiodło się ze względu na długi czas dostarczania wiadomości, obciążenie procesora i brak aplikacji mobilnych.

A sceptycyzm wynika z faktu, że komunikatory na blockchainie wyprzedzają swoje czasy – ludzie nie są gotowi wziąć odpowiedzialności za swoje konto, posiadanie danych osobowych nie jest jeszcze trendem, a technologia nie pozwala na duże prędkości na blockchainie. W następnej kolejności pojawią się kolejne analogie technologiczne naszego projektu. Zobaczysz.

Źródło: www.habr.com

Dodaj komentarz