Как работи децентрализиран месинджър в блокчейна

В началото на 2017 г. започнахме да създаваме месинджър в блокчейна [името и връзката са в профила], като обсъждахме предимствата пред класическите P2P месинджъри.

Си отиде 2.5 година и успяхме да потвърдим нашата концепция: приложенията за съобщения вече са налични за iOS, Web PWA, Windows, GNU/Linux, Mac OS и Android.

Днес ще ви разкажем как работи блокчейн месинджърът и как клиентските приложения могат да работят с неговия API.
Как работи децентрализиран месинджър в блокчейна

Искахме блокчейнът да реши проблемите със сигурността и поверителността на класическите P2P месинджъри:

  • Едно кликване за създаване на акаунт - без телефони или имейли, без достъп до адресни книги или геолокации.
  • Събеседниците никога не установяват директни връзки; цялата комуникация се осъществява чрез разпределена система от възли. IP адресите на потребителите не са достъпни един за друг.
  • Всички съобщения са криптирани от край до край curve25519xsalsa20poly1305. Изглежда, че това няма да изненада никого, но нашият изходен код е отворен.
  • MITM атаката е изключена - всяко съобщение е транзакция и е подписано от Ed25519 EdDSA.
  • Съобщението завършва в собствен блок. Консистенция и timestamp Не можете да коригирате блоковете и следователно реда на съобщенията.
  • „Не казах това“ няма да работи със съобщения в блокчейна.
  • Няма централна структура, която да проверява „автентичността“ на съобщението. Това се прави от разпределена система от възли, базирана на консенсус, и е собственост на потребителите.
  • Невъзможност за цензура - не могат да се блокират акаунти и да се изтриват съобщения.
  • Blockchain 2FA е алтернатива на адския 2FA чрез SMS, съсипа много здраве.
  • Възможността да получавате всичките си разговори от всяко устройство по всяко време означава, че изобщо не е нужно да съхранявате разговори локално.
  • Потвърждение за доставка на съобщение. Не към устройството на потребителя, а към мрежата. По същество това е потвърждение за способността на получателя да прочете вашето съобщение. Това е полезна функция за изпращане на критични известия.

Предимствата на Blockchain също включват тясна интеграция с криптовалутите Ethereum, Dogecoin, Lisk, Dash, Bitcoin (това все още е в ход) и възможността за изпращане на токени в чатове. Дори направихме вграден крипто обменник.

И след това - как работи всичко.

Съобщението е транзакция

Всички вече са свикнали с факта, че транзакциите в блокчейна прехвърлят токени (монети) от един потребител на друг. Като биткойн. Създадохме специален тип транзакция за предаване на съобщения.

За да изпратите съобщение в месинджър в блокчейна, трябва да преминете през няколко стъпки:

  1. Шифроване на текста на съобщението
  2. Поставете шифрован текст в транзакция
  3. Подпишете сделката
  4. Изпратете транзакция до произволен мрежов възел
  5. Разпределена система от възли определя „автентичността“ на съобщението
  6. Ако всичко е наред, транзакцията със съобщението се включва в следващия блок
  7. Получателят извлича съобщението и го декриптира

Стъпки 1–3 и 7 се изпълняват локално на клиента, а стъпки 5–6 се изпълняват на хостовете.

Шифроване на съобщения

Съобщението е криптирано с частния ключ на подателя и публичния ключ на получателя. Ще вземем публичния ключ от мрежата, но за това акаунтът на получателя трябва да бъде инициализиран, тоест да има поне една транзакция. Можете да използвате REST заявка GET /api/accounts/getPublicKey?address={ADAMANT address}, а при зареждане на чатове вече ще са налични публичните ключове на събеседниците.

Как работи децентрализиран месинджър в блокчейна

Месинджърът криптира съобщения с помощта на алгоритъма curve25519xsalsa20poly1305 (NaCl кутия). Тъй като акаунтът съдържа ключове Ed25519, за да се образува кутия, ключовете трябва първо да бъдат преобразувани в Curve25519 Diffie-Hellman.

Ето един пример в 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)
  }
}

Оформяне на транзакция със съобщение

Сделката има следната обща структура:

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

За транзакция със съобщения най-важното е asset - трябва да поставите съобщение в обекта chat със структура:

  • message - запазете криптираното съобщение
  • own_message - един път
  • type — вид съобщение

Съобщенията също са разделени на типове. По същество параметърът type ви казва как да разберете message. Можете да изпратите само текст или да изпратите обект с интересни неща вътре - например, така месинджърът прави преводи на криптовалута в чатове.

В резултат на това създаваме транзакция:

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

Подпис на транзакцията

За да се гарантира, че всеки е уверен в автентичността на подателя и получателя, часа на изпращане и съдържанието на съобщението, транзакцията се подписва. Цифровият подпис ви позволява да проверите автентичността на транзакция с помощта на публичен ключ - за това не е необходим частен ключ.

Но самият подпис се извършва с частния ключ:

Как работи децентрализиран месинджър в блокчейна

Диаграмата показва, че първо хешираме транзакцията с SHA-256 и след това я подписваме Ed25519 EdDSA и вземете подпис signature, а идентификаторът на транзакцията е част от хеша на SHA-256.

Примерно изпълнение:

1 — Формиране на блок от данни, включително съобщение

/**
 * 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 - Брой SHA-256 от блока с данни

/**
 * 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 — Подпишете транзакцията

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

Изпращане на транзакция със съобщение до мрежов възел

Тъй като мрежата е децентрализирана, всеки от възлите с отворен API ще свърши работа. Извършване на POST заявка към крайната точка api/transactions:

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

В отговор ще получим идентификатор на транзакция от вида

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

Валидиране на транзакция

Разпределена система от възли, базирана на консенсус, определя „автентичността“ на съобщението за транзакция. От кого и до кого, кога, дали съобщението е сменено с друго и правилно ли е посочен часът на изпращане. Това е много важно предимство на блокчейна - няма централна структура, която да отговаря за проверката, а последователността от съобщения и тяхното съдържание не могат да бъдат фалшифицирани.

Първо един възел проверява точността и след това го изпраща на други - ако мнозинството каже, че всичко е наред, транзакцията ще бъде включена в следващия блок на веригата - това е консенсус.

Как работи децентрализиран месинджър в блокчейна

Частта от кода на възела, която отговаря за проверките, може да се види в GitHub - validator.js и verify.js. Да, възелът работи на Node.js.

Включване на транзакция със съобщение в блок

Ако се постигне консенсус, транзакцията с нашето съобщение ще бъде включена в следващия блок заедно с други валидни транзакции.

Блоковете имат строга последователност и всеки следващ блок се формира въз основа на хешовете на предишни блокове.

Как работи децентрализиран месинджър в блокчейна

Въпросът е, че нашето съобщение също е включено в тази последователност и не може да бъде „пренаредено“. Ако няколко съобщения попаднат в блок, техният ред ще се определя от timestamp съобщения.

Четене на съобщения

Приложението Messenger извлича транзакции от блокчейна, които са изпратени до получателя. За това направихме крайна точка api/chatrooms.

Всички транзакции са достъпни за всички - можете да получавате криптирани съобщения. Но само получателят може да декриптира, използвайки своя личен ключ и публичния ключ на подателя:

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

И какво друго?

Тъй като съобщенията се доставят по този начин за около 5 секунди - това е времето, когато се появява нов мрежов блок - измислихме връзка клиент към възел и възел към възел на сокет. Когато даден възел получи нова транзакция, той проверява нейната валидност и я препраща към други възли. Транзакцията е достъпна за клиенти на месинджър дори преди да е настъпил консенсус и включване в блока. По този начин ще доставяме съобщения незабавно, точно както обикновените месинджъри.

За съхраняване на адресната книга направихме KVS - Key-Value Storage - това е друг вид транзакция, при която asset не NaCl-кутия е криптирана, а NaCl-тайна кутия. Така месинджърът съхранява други данни.

Прехвърлянето на файлове/изображения и груповите чатове все още изискват много работа. Разбира се, във формата на грешки и грешки това може да бъде „прецакано“ бързо, но ние искаме да запазим същото ниво на поверителност.

Да, има още работа за вършене - в идеалния случай истинската поверителност предполага, че потребителите няма да се свързват с обществени мрежови възли, а ще създават свои собствени. Какъв процент от потребителите мислите, че правят това? Точно така, 0. Успяхме частично да разрешим този проблем с Tor версията на месинджъра.

Доказахме, че може да съществува месинджър в блокчейн. Преди това имаше само един опит през 2012 г. - bitmessage, който се провали поради дълги срокове за доставка на съобщения, натоварване на процесора и липса на мобилни приложения.

А скептицизмът се дължи на факта, че месинджърите в блокчейна са изпреварили времето си – хората не са готови да поемат отговорност за акаунта си, притежаването на лична информация все още не е тенденция, а технологиите не позволяват високи скорости в блокчейна. След това ще се появят още технологични аналози на нашия проект. Ще видите.

Източник: www.habr.com

Добавяне на нов коментар