¿Cómo funciona un mensajero descentralizado en blockchain?

A principios de 2017, comenzamos a crear un mensajero en blockchain [el nombre y el enlace están en el perfil] discutiendo las ventajas sobre los mensajeros P2P clásicos.

Pasado 2.5 año y pudimos confirmar nuestro concepto: las aplicaciones de mensajería ya están disponibles para iOS, Web PWA, Windows, GNU/Linux, Mac OS y Android.

Hoy te contamos cómo funciona el mensajero blockchain y cómo pueden funcionar las aplicaciones cliente con su API.
¿Cómo funciona un mensajero descentralizado en blockchain?

Queríamos que blockchain resolviera los problemas de seguridad y privacidad de los mensajeros P2P clásicos:

  • Un clic para crear una cuenta: sin teléfonos ni correos electrónicos, sin acceso a libretas de direcciones ni ubicaciones geográficas.
  • Los interlocutores nunca establecen conexiones directas; toda la comunicación se realiza a través de un sistema distribuido de nodos. Las direcciones IP de los usuarios no son accesibles entre sí.
  • Todos los mensajes están cifrados de extremo a extremo curve25519xsalsa20poly1305. Parece que esto no sorprenderá a nadie, pero nuestro código fuente es abierto.
  • El ataque MITM está excluido: cada mensaje es una transacción y está firmado por Ed25519 EdDSA.
  • El mensaje termina en su propio bloque. Consistencia y timestamp No puedes arreglar los bloqueos y, por tanto, el orden de los mensajes.
  • "No dije eso" no funcionará con mensajes en la cadena de bloques.
  • No existe una estructura central que verifique la “autenticidad” de un mensaje. Esto se hace mediante un sistema distribuido de nodos basado en consenso y es propiedad de los usuarios.
  • Imposibilidad de censura: las cuentas no se pueden bloquear y los mensajes no se pueden eliminar.
  • Blockchain 2FA es una alternativa al infernal 2FA vía SMS, arruinó mucha salud.
  • La capacidad de obtener todas sus conversaciones desde cualquier dispositivo en cualquier momento significa que no tiene que almacenar las conversaciones localmente.
  • Confirmación de entrega del mensaje. No al dispositivo del usuario, sino a la red. Básicamente, esto es una confirmación de la capacidad del destinatario para leer su mensaje. Esta es una característica útil para enviar notificaciones críticas.

Los beneficios de Blockchain también incluyen una estrecha integración con las criptomonedas Ethereum, Dogecoin, Lisk, Dash, Bitcoin (ésta todavía está en progreso) y la capacidad de enviar tokens en chats. Incluso creamos un intercambiador de cifrado incorporado.

Y luego, cómo funciona todo.

Un mensaje es una transacción.

Todo el mundo ya está acostumbrado al hecho de que las transacciones en blockchain transfieren tokens (monedas) de un usuario a otro. Como Bitcoin. Creamos un tipo especial de transacción para transmitir mensajes.

Para enviar un mensaje en un mensajero en blockchain, debe seguir varios pasos:

  1. Cifrar el texto del mensaje
  2. Poner texto cifrado en una transacción
  3. Firma la transacción
  4. Enviar una transacción a cualquier nodo de la red.
  5. Un sistema distribuido de nodos determina la “autenticidad” de un mensaje
  6. Si todo está bien, la transacción con el mensaje se incluye en el siguiente bloque.
  7. El destinatario recupera la transacción del mensaje y la descifra.

Los pasos 1 a 3 y 7 se realizan localmente en el cliente y los pasos 5 a 6 se realizan en los hosts.

Cifrado de mensajes

El mensaje se cifra con la clave privada del remitente y la clave pública del destinatario. Tomaremos la clave pública de la red, pero para ello la cuenta del destinatario debe estar inicializada, es decir, tener al menos una transacción. Puede utilizar una solicitud REST GET /api/accounts/getPublicKey?address={ADAMANT address}, y al cargar los chats ya estarán disponibles las claves públicas de los interlocutores.

¿Cómo funciona un mensajero descentralizado en blockchain?

El mensajero cifra los mensajes utilizando el algoritmo curve25519xsalsa20poly1305 (Caja de NaCl). Dado que la cuenta contiene claves Ed25519, para formar un cuadro, las claves primero deben convertirse a Curve25519 Diffie-Hellman.

Aquí hay un ejemplo 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)
  }
}

Formando una transacción con un mensaje

La transacción tiene la siguiente estructura 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": {}
}

Para una transacción de mensajes, lo más importante es asset - necesitas colocar un mensaje en el objeto chat con estructura:

  • message - guardar el mensaje cifrado
  • own_message -mientras tanto
  • type - Tipo de mensaje

Los mensajes también se dividen en tipos. Básicamente, el parámetro type te dice como entender message. Puede enviar solo un mensaje de texto o puede enviar un objeto con cosas interesantes en su interior; por ejemplo, así es como el mensajero realiza transferencias de criptomonedas en los chats.

Como resultado, creamos una transacción:

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

Firma de transacción

Para garantizar que todos confíen en la autenticidad del remitente y el destinatario, el momento del envío y el contenido del mensaje, se firma la transacción. Una firma digital le permite verificar la autenticidad de una transacción utilizando una clave pública; para esto no se necesita una clave privada.

Pero la firma en sí se realiza utilizando la clave privada:

¿Cómo funciona un mensajero descentralizado en blockchain?

El diagrama muestra que primero aplicamos hash a la transacción con SHA-256 y luego la firmamos. Ed25519 EdDSA y obtener una firma signaturey el ID de la transacción es parte del hash SHA-256.

Implementación de ejemplo:

1: formar un bloque de datos, incluido un mensaje

/**
 * 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 - Cuente SHA-256 del bloque de datos

/**
 * 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 - Firma la transacción

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

Enviar una transacción con un mensaje a un nodo de la red

Dado que la red está descentralizada, cualquiera de los nodos con una API abierta servirá. Realizar una solicitud POST al punto final api/transactions:

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

En respuesta recibiremos un ID de transacción del tipo

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

Validación de transacciones

Un sistema distribuido de nodos, basado en el consenso, determina la "autenticidad" del mensaje de transacción. De quién y para quién, cuándo, si el mensaje fue sustituido por otro y si se indicó correctamente la hora de envío. Esta es una ventaja muy importante de blockchain: no existe una estructura central responsable de la verificación y la secuencia de mensajes y su contenido no se pueden falsificar.

Primero, un nodo verifica la precisión y luego la envía a otros; si la mayoría dice que todo está en orden, la transacción se incluirá en el siguiente bloque de la cadena; esto es consenso.

¿Cómo funciona un mensajero descentralizado en blockchain?

La parte del código del nodo responsable de las comprobaciones se puede ver en GitHub: validador.js и verificar.js. Sí, el nodo se ejecuta en Node.js.

Incluir una transacción con un mensaje en un bloque

Si se llega a un consenso, la transacción con nuestro mensaje se incluirá en el siguiente bloque junto con otras transacciones válidas.

Los bloques tienen una secuencia estricta y cada bloque posterior se forma en función de los hashes de los bloques anteriores.

¿Cómo funciona un mensajero descentralizado en blockchain?

La cuestión es que nuestro mensaje también está incluido en esta secuencia y no puede "reorganizarse". Si varios mensajes caen en un bloque, su orden será determinado por timestamp mensajes.

Lectura de mensajes

La aplicación de mensajería recupera transacciones de la cadena de bloques que se envían al destinatario. Para esto hicimos un punto final. api/chatrooms.

Todas las transacciones están disponibles para todos: puede recibir mensajes cifrados. Pero sólo el destinatario puede descifrar utilizando su clave privada y la clave pública del remitente:

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

Que mas

Dado que los mensajes se entregan de esta manera en aproximadamente 5 segundos (este es el momento en que aparece un nuevo bloque de red), se nos ocurrió una conexión de socket de cliente a nodo y de nodo a nodo. Cuando un nodo recibe una nueva transacción, verifica su validez y la reenvía a otros nodos. La transacción está disponible para los clientes de mensajería incluso antes de que se produzca el consenso y la inclusión en el bloque. De esta forma entregaremos mensajes instantáneamente, como los mensajeros instantáneos habituales.

Para almacenar la libreta de direcciones, creamos KVS (Almacenamiento de valores clave): este es otro tipo de transacción en la que asset no es la caja de NaCl la que está encriptada, sino Caja secreta de NaCl. Así es como el mensajero almacena otros datos.

Las transferencias de archivos/imágenes y los chats grupales aún requieren mucho trabajo. Por supuesto, en el formato de errores y errores esto se puede “arruinar” rápidamente, pero queremos mantener el mismo nivel de privacidad.

Sí, todavía queda trabajo por hacer; idealmente, la privacidad real supone que los usuarios no se conectarán a nodos de la red pública, sino que crearán los suyos propios. ¿Qué porcentaje de usuarios crees que hace esto? Así es, 0. Pudimos resolver parcialmente este problema con la versión Tor del messenger.

Hemos demostrado que puede existir un mensajero en blockchain. Anteriormente, en 2012, solo hubo un intento: mensaje de bits, que falló debido a los largos tiempos de entrega de mensajes, la carga de la CPU y la falta de aplicaciones móviles.

Y el escepticismo se debe al hecho de que los mensajeros en blockchain están adelantados a su tiempo: las personas no están preparadas para asumir la responsabilidad de su cuenta, poseer información personal aún no es una tendencia y la tecnología no permite altas velocidades en blockchain. A continuación aparecerán más análogos tecnológicos de nuestro proyecto. Verás.

Fuente: habr.com

Añadir un comentario