Comment fonctionne une messagerie décentralisée sur la blockchain ?

Début 2017, nous avons commencé à créer une messagerie sur la blockchain [le nom et le lien sont dans le profil] en discutant des avantages par rapport aux messageries P2P classiques.

Réussi 2.5 année, et nous avons pu confirmer notre concept : les applications de messagerie sont désormais disponibles pour iOS, Web PWA, Windows, GNU/Linux, Mac OS et Android.

Aujourd'hui, nous allons vous expliquer comment fonctionne le messager blockchain et comment les applications clientes peuvent fonctionner avec son API.
Comment fonctionne une messagerie décentralisée sur la blockchain ?

Nous voulions que la blockchain résolve les problèmes de sécurité et de confidentialité des messageries P2P classiques :

  • Un clic pour créer un compte - pas de téléphone ni d'e-mail, pas d'accès aux carnets d'adresses ni aux géolocalisations.
  • Les interlocuteurs n’établissent jamais de connexions directes ; toute communication s’effectue via un système distribué de nœuds. Les adresses IP des utilisateurs ne sont pas accessibles entre elles.
  • Tous les messages sont cryptés de bout en bout Curve25519xsalsa20poly1305. Il semble que cela ne surprendra personne, mais notre code source est ouvert.
  • L'attaque MITM est exclue - chaque message est une transaction et est signé par Ed25519 EdDSA.
  • Le message se retrouve dans son propre bloc. Cohérence et timestamp Vous ne pouvez pas corriger les blocs, et donc l'ordre des messages.
  • « Je n’ai pas dit ça » ne fonctionnera pas avec les messages sur la blockchain.
  • Il n’existe pas de structure centrale qui vérifie « l’authenticité » d’un message. Cela se fait par un système distribué de nœuds basé sur le consensus et appartenant aux utilisateurs.
  • Impossibilité de censure - les comptes ne peuvent pas être bloqués et les messages ne peuvent pas être supprimés.
  • La Blockchain 2FA est une alternative à l'infernal 2FA via SMS, a ruiné beaucoup de santé.
  • La possibilité d'obtenir toutes vos conversations depuis n'importe quel appareil à tout moment signifie que vous n'avez pas du tout besoin de stocker les conversations localement.
  • Confirmation de la livraison du message. Pas sur l'appareil de l'utilisateur, mais sur le réseau. Il s'agit essentiellement d'une confirmation de la capacité du destinataire à lire votre message. Il s'agit d'une fonctionnalité utile pour envoyer des notifications critiques.

Les avantages de la blockchain incluent également une intégration étroite avec les crypto-monnaies Ethereum, Dogecoin, Lisk, Dash, Bitcoin (celle-ci est toujours en cours) et la possibilité d'envoyer des jetons dans les chats. Nous avons même créé un échangeur cryptographique intégré.

Et puis, comment tout cela fonctionne.

Un message est une transaction

Tout le monde est déjà habitué au fait que les transactions dans la blockchain transfèrent des jetons (pièces) d'un utilisateur à un autre. Comme Bitcoin. Nous avons créé un type spécial de transaction pour transmettre des messages.

Pour envoyer un message dans une messagerie sur la blockchain, vous devez passer par plusieurs étapes :

  1. Chiffrer le texte du message
  2. Mettre du texte chiffré dans une transaction
  3. Signez la transaction
  4. Envoyer une transaction à n'importe quel nœud du réseau
  5. Un système distribué de nœuds détermine « l’authenticité » d’un message
  6. Si tout va bien, la transaction avec le message est incluse dans le bloc suivant
  7. Le destinataire récupère la transaction du message et décrypte

Les étapes 1 à 3 et 7 sont effectuées localement sur le client et les étapes 5 à 6 sont effectuées sur les hôtes.

Cryptage des messages

Le message est chiffré avec la clé privée de l'expéditeur et la clé publique du destinataire. Nous prendrons la clé publique du réseau, mais pour cela, le compte du destinataire doit être initialisé, c'est-à-dire avoir au moins une transaction. Vous pouvez utiliser une requête REST GET /api/accounts/getPublicKey?address={ADAMANT address}, et lors du chargement des chats, les clés publiques des interlocuteurs seront déjà disponibles.

Comment fonctionne une messagerie décentralisée sur la blockchain ?

Le messager crypte les messages à l'aide de l'algorithme Curve25519xsalsa20poly1305 (Boîte NaCl). Puisque le compte contient des clés Ed25519, pour former une boîte, les clés doivent d'abord être converties en Curve25519 Diffie-Hellman.

Voici un exemple en 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)
  }
}

Former une transaction avec un message

La transaction a la structure générale suivante :

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

Pour une transaction de message, le plus important est asset - vous devez placer un message dans l'objet chat avec structure :

  • message - enregistrer le message crypté
  • own_message - rarement
  • type - type de message

Les messages sont également divisés en types. En gros, le paramètre type vous dit comment comprendre message. Vous pouvez envoyer simplement un texte ou envoyer un objet contenant des éléments intéressants - par exemple, c'est ainsi que le messager effectue des transferts de crypto-monnaie dans les chats.

En conséquence, nous créons une transaction :

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

Signature de transaction

Pour s'assurer que chacun a confiance dans l'authenticité de l'expéditeur et du destinataire, de l'heure d'envoi et du contenu du message, la transaction est signée. Une signature numérique vous permet de vérifier l'authenticité d'une transaction à l'aide d'une clé publique - une clé privée n'est pas nécessaire pour cela.

Mais la signature elle-même est réalisée à l'aide de la clé privée :

Comment fonctionne une messagerie décentralisée sur la blockchain ?

Le diagramme montre que nous hachons d'abord la transaction avec SHA-256, puis la signons Ed25519 EdDSA et obtenez une signature signature, et l'ID de transaction fait partie du hachage SHA-256.

Exemple de mise en œuvre :

1 — Former un bloc de données, comprenant un message

/**
 * 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 - Comptez SHA-256 à partir du bloc de données

/**
 * 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 — Signez la transaction

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

Envoi d'une transaction avec un message à un nœud du réseau

Le réseau étant décentralisé, n’importe lequel des nœuds dotés d’une API ouverte fera l’affaire. Faire une requête POST au point de terminaison api/transactions:

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

En réponse, nous recevrons un identifiant de transaction du type

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

Validation des transactions

Un système distribué de nœuds, basé sur le consensus, détermine « l’authenticité » du message de transaction. De qui et à qui, quand, si le message a été remplacé par un autre et si l'heure d'envoi a été correctement indiquée. Il s'agit d'un avantage très important de la blockchain : il n'y a pas de structure centrale chargée de la vérification, et la séquence des messages et leur contenu ne peuvent pas être falsifiés.

Tout d'abord, un nœud vérifie l'exactitude, puis l'envoie aux autres - si la majorité dit que tout est en ordre, la transaction sera incluse dans le bloc suivant de la chaîne - c'est un consensus.

Comment fonctionne une messagerie décentralisée sur la blockchain ?

La partie du code du nœud responsable des vérifications peut être consultée sur GitHub - validateur.js и vérifier.js. Oui, le nœud fonctionne sur Node.js.

Inclure une transaction avec un message dans un bloc

Si un consensus est atteint, la transaction avec notre message sera incluse dans le bloc suivant avec d'autres transactions valides.

Les blocs ont une séquence stricte et chaque bloc suivant est formé sur la base des hachages des blocs précédents.

Comment fonctionne une messagerie décentralisée sur la blockchain ?

Le fait est que notre message est également inclus dans cette séquence et ne peut pas être « réorganisé ». Si plusieurs messages tombent dans un bloc, leur ordre sera déterminé par timestamp des messages.

Lecture des messages

L'application de messagerie récupère les transactions de la blockchain qui sont envoyées au destinataire. Pour cela, nous avons créé un point final api/chatrooms.

Toutes les transactions sont accessibles à tous - vous pouvez recevoir des messages cryptés. Mais seul le destinataire peut décrypter à l’aide de sa clé privée et de la clé publique de l’expéditeur :

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

Quoi d'autre?

Étant donné que les messages sont transmis de cette manière en 5 secondes environ - c'est le moment où un nouveau bloc réseau apparaît - nous avons proposé une connexion socket client-nœud et nœud-nœud. Lorsqu'un nœud reçoit une nouvelle transaction, il vérifie sa validité et la transmet aux autres nœuds. La transaction est disponible pour les clients de messagerie avant même qu'un consensus ne soit obtenu et son inclusion dans le bloc. De cette façon, nous enverrons des messages instantanément, tout comme les messageries instantanées classiques.

Pour stocker le carnet d'adresses, nous avons créé KVS - Key-Value Storage - c'est un autre type de transaction dans lequel asset ce n'est pas NaCl-box qui est crypté, mais Boîte secrète NaCl. C'est ainsi que le messager stocke d'autres données.

Les transferts de fichiers/images et les discussions de groupe nécessitent encore beaucoup de travail. Bien sûr, dans le format des erreurs et des erreurs, cela peut être rapidement « gâché », mais nous voulons conserver le même niveau de confidentialité.

Oui, il reste encore du travail à faire - idéalement, une véritable confidentialité suppose que les utilisateurs ne se connecteront pas aux nœuds du réseau public, mais qu'ils élèveront les leurs. Selon vous, quel pourcentage d'utilisateurs fait cela ? C'est vrai, 0. Nous avons pu résoudre partiellement ce problème avec la version Tor du messager.

Nous avons prouvé qu’un messager sur la blockchain peut exister. Auparavant, il n'y avait eu qu'une seule tentative en 2012 - message binaire, qui a échoué en raison de longs délais de livraison des messages, de la charge du processeur et du manque d'applications mobiles.

Et le scepticisme est dû au fait que les messagers sur la blockchain sont en avance sur leur temps - les gens ne sont pas prêts à assumer la responsabilité de leur compte, la possession d'informations personnelles n'est pas encore une tendance et la technologie ne permet pas des vitesses élevées sur la blockchain. D'autres analogues technologiques de notre projet apparaîtront ensuite. Tu verras.

Source: habr.com

Ajouter un commentaire