2017 elején elkezdtük létrehozni a blokklánc alapú üzenetküldőt [a név és a link a profilban található], amely a hagyományos P2P üzenetküldőkkel szembeni előnyöket vitatja.
Eltelt 2.5 éve, és sikerült megerősíteni a koncepciót: most már elérhetők az üzenetküldő alkalmazásai iOS, Web PWA, Windows, GNU/Linux, Mac OS és Android rendszeren.
Ma elmondjuk, hogyan működik a blokklánc alapú üzenetküldő és hogyan működnek az ügyfélalkalmazások az API-jával.

Azt akartuk, hogy a blokklánc megoldja a klasszikus P2P üzenetküldők biztonsági és magánéleti kérdéseit:
- Egy kattintás az fiók létrehozásához — nincs szükség telefonszámra és e-mail címre, nincsenek hozzáférések a névjegyzékekhez és a geolokációkhoz.
- A beszélgetőpartnerek soha nem létesítenek közvetlen kapcsolatokat, minden kommunikáció egy elosztott csomópont rendszeren keresztül folyik. A felhasználók IP-címei nem elérhetők egymás számára.
- Minden üzenet End-to-End curve25519xsalsa20poly1305 titkosítással van ellátva. Ez talán nem meglepő mások számára, de a mi forráskódunk nyilvános.
- A MITM-támadások kizártak — minden üzenet tranzakció, amelyet Ed25519 EdDSA ír alá.
- Az üzenet a saját blokkjába kerül. A sorrend és
timestampa blokkok nem módosíthatók, így az üzenetek sorrendje sem változtatható meg. - A „Ezt nem mondtam” nem működik a blokkláncon lévő üzenetek esetében.
- Nincs központi struktúra, amely ellenőrzi az üzenetek „hitelességét”. Ezt a konszenzus alapú elosztott csomópont rendszer végzi, és ez a felhasználók tulajdonában van.
- A cenzúra lehetetlensége — az fiókok nem zárhatók le, az üzeneteket nem lehet törölni.
- A blokklánc 2FA — alternatíva a rémes SMS-ként működő 2FA-hoz,
- A lehetőség, hogy minden párbeszédét bármilyen eszközről bármikor megkaphatja — ez azt jelenti, hogy egyáltalán nem lehet az üzeneteket helyben tárolni.
- Az üzenetek kézbesítésének megerősítése. Nem a felhasználó eszközére, hanem a hálózatra. Valójában ez a címzett üzenetének elolvasására vonatkozó lehetőséget erősíti meg. Ez egy hasznos funkció kritikus értesítések küldésére.
A blokklánc további előnyei közé tartozik a közvetlen integráció az Ethereum, Dogecoin, Lisk, Dash, Bitcoin (ez még fejlesztés alatt) kriptovalutákkal és a tokenek küldésének lehetősége a csevegésekben. Még beépített kriptovaluta-átváltót is készítettünk.
Most pedig nézzük meg, hogyan működik mindez.
Az üzenet — ez egy tranzakció
Mindenki hozzászokott, hogy a blokklánc tranzakciói tokent (érmét) továbbítanak egy felhasználótól a másikhoz. Ahogy a bitcoinnál is. Mi azonban egy különleges tranzakciótípust hoztunk létre az üzenetek továbbítására.
Az üzenet elküldéséhez a blokklánc alapú üzenetküldőben több lépést kell végrehajtani:
- Titkosítsa az üzenet szövegét
- Helyezze a titkosított szöveget a tranzakcióba
- Írja alá a tranzakciót
- Küldje el a tranzakciót a hálózat bármely csomópontjára
- A csomópontok elosztott rendszere meghatározza az üzenet „hitelességét”
- Ha minden rendben van, az üzenettel kapcsolatos tranzakciót a következő blokkba beillesztik
- A címzett kiolvassa az üzenettel rendelkező tranzakciót és dekódolja
Az 1-3. és 7. lépéseket lokálisan a kliensen végzik, míg az 5-6. lépéseket a hálózati csomópontok végzik el.
Az üzenet titkosítása
Az üzenet a feladó privát kulcsával és a címzett nyilvános kulcsával kerül titkosításra. A nyilvános kulcsot a hálózatból szerezzük, de ehhez a címzett fiókjának inicializálva kell lennie, vagyis legalább egy tranzakcióval rendelkeznie kell. REST kérést használhatunk. GET /api/accounts/getPublicKey?address={ADAMANT address}, és a csevegések betöltésekor a beszélgetőtársak nyilvános kulcsai már rendelkezésre állnak.

Az üzenetküldő az üzenetek titkosításához a curve25519xsalsa20poly1305 algoritmust használja (). Mivel a fiók Ed25519 kulcsokat tartalmaz, a box létrehozásához először a kulcsokat át kell alakítani Curve25519 Diffie-Hellman formára.
Itt egy példa JavaScript-ben:
/**
* 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)
}
}A tranzakció kibővítése üzenettel
A tranzakció általános szerkezete a következő:
{
"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": {}
} Az üzenettel rendelkező tranzakció szempontjából a legfontosabb érték az asset — ebbe kell elhelyezni az üzenetet a következő szerkezetben: chat — tárolja a titkosított üzenetet
üzenetown_message— nonce— üzenet típusatypeAz üzeneteket is típusuk szerint osztályozzuk. Valójában a paraméter
megmondja, hogyan kell értelmezni type . Küldhetünk egyszerűen szöveget, vagy egy olyan objektumot, amiben érdekes dolgok találhatók — például így a program végrehajtja a kriptovaluta átutalását a csevegők között. üzenetVégül tehát létrehozunk egy tranzakciót:
{
{
"transaction": {
"type": 8,
"amount": 0,
"senderId": "U12499126640447739963",
"senderPublicKey": "e9cafb1e7b403c4cf247c94f73ee4cada367fcc130cb3888219a0ba0633230b6",
"asset": {
"chat": {
"message": "cb682accceef92d7cddaaddb787d1184ab5428",
"own_message": "e7d8f90ddf7d70efe359c3e4ecfb5ed3802297b248eacbd6",
"type": 1
}
},
"recipientId": "U15677078342684640219",
"timestamp": 63228087,
"signature": "itt lesz az aláírás"
}
}A tranzakció aláírása
A küldő és a fogadó hitelessége, az üzenet küldésének időpontja és tartalma szempontjából fontos, hogy a tranzakciót aláírják. A digitális aláírás lehetővé teszi a tranzakció hitelességének ellenőrzését a nyilvános kulcs alapján — ehhez a magánkulcsra nincs szükség.
És itt az aláírás, amelyet a magánkulccsal készítünk:

A diagramon látható, hogy először a tranzakciót SHA-256-tal hasheljük, majd azt aláírjuk. és megkapjuk az aláírást aláírás, míg a tranzakció azonosítója a SHA-256 hash egy része.
Példa megvalósításra:
1 — Adatblokk képzése, beleértve az üzenetet
/**
* 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 számítása az adatblokkról
/**
* 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 — A tranzakció aláírása
adamant.transactionSign = function (trs, keypair) {
var hash = this.getHash(trs)
return this.sign(hash, keypair).toString('hex')
}
/**
* Aláírás létrehozása egy hash és egy kulcspár alapján.
* @implements {sodium}
* @param {hash} hash
* @param {keypair} keypair
* @return {signature} aláírás
*\/
adamant.sign = function (hash, keypair) {
return sodium.crypto_sign_detached(hash, Buffer.from(keypair.privateKey, 'hex'))
}A tranzakció üzenettel való küldése a hálózat csomópontjához
Mivel a hálózat decentralizált, bármely olyan csomópont megfelelő, amely nyílt API-val rendelkezik. POST-kérést küldünk az végpontra api/transactions:
curl 'api/transactions' -X POST
-d 'TX_DATA'Válaszként megkapjuk a tranzakció azonosítóját:
{
"success": true,
"nodeTimestamp": 63228852,
"transactionId": "6146865104403680934"
}A tranzakció hitelességének ellenőrzése
A konszenzus alapú elosztott rendszer csomópontjai határozzák meg a tranzakció-üzenet "hitelességét". Ki küldte, kinek, mikor, nem cserélték-e ki az üzenetet egy másikra, és helyes-e a küldési idő. Ez a blockchain egyik legfontosabb előnye — nincs központi struktúra, amely a hitelesítésekért felelős, és az üzenetek sorrendje, valamint tartalma nem hamisítható.
Először egy csomópont ellenőrzi a hitelességet, majd továbbítja a többi csomópontnak — ha a többség azt mondja, hogy minden rendben van, a tranzakció bekerül a következő blokkba — ez a konszenzus.

A csomópont kódjának része, amely a hitelesítésekért felel, megtekinthető a GitHub-on — és . Igen, a csomópont a Node.js-en fut.
A tranzakció üzenetének beillesztése a blokkba
Ha a konszenzus létrejött, az üzenetünk tranzakciója a következő blokkba kerül más hiteles tranzakciók mellett.
A blokkok szigorú sorrendben állnak, és minden következő blokk az előző blokkok hashek alapján jön létre.

A lényeg, hogy az üzenetünk is része ennek a sorrendnek, és nem lehet „áthelyezni”. Ha több üzenet kerül egy blokkba, azok sorrendjét a timestamp üzenetek határozzák meg.
Üzenetek olvasása
A messenger alkalmazás a blockchainból kiolvassa azokat a tranzakciókat, amelyeket a címzettnek küldtek. Ehhez létrehoztuk az api/chatrooms.
Minden tranzakció elérhető mindenki számára — a titkosított üzeneteket meg lehet kapni. De csak a címzett tudja dekódolni a saját privát kulcsával és a küldő közpublic kulcsával:
**
* 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) : ''
}És mi más?
Mivel az üzenetek körülbelül 5 másodperc alatt érkeznek meg — ez a hálózat új blokkjának megjelenési ideje — kitaláltunk egy socket-kapcsolatot kliens-csomópont és csomópont-csomópont között. Amikor a csomópont egy új tranzakciót kap, ellenőrzi annak érvényességét és továbbítja a többi csomópontnak. A tranzakció elérhető a messenger kliensek számára még a konszenzus előtt és a blokkba való beillesztés előtt. Így az üzeneteket azonnal továbbítjuk, akárcsak a szokásos messengerek.
Az címjegyzék tárolására létrehoztunk egy KVS-t — Key-Value Storage — ez egy másik típusú tranzakció, amelyben asset nem NaCl-boxot titkosítunk, hanem . Így a messenger más adatokat is tárolhat.
Fájlok/képek átadása és csoportos csevegések még sok munkát igényelnek. Persze, a tervezetlen formában gyorsan „rá lehet kötni”, de szeretnénk megőrizni a privát szintet.
Igen, még van mit tennünk — az ideális igazi privát szint azt jelenti, hogy a felhasználók nem csatlakoznak a nyilvános csomópontokhoz, hanem sajátokat állítanak fel. Ön szerint hány százalékuk teszi ezt? Helyesen, 0. Részben ezt a kérdést sikerült megoldanunk a messenger Tor-verziójával.
Bizonyítottuk, hogy a blockchain alapú messenger létezhet. Korábban csak egy kísérlet volt 2012-ben — , amely a hosszú üzenetátviteli idő, a processzorga terheltsége és a mobilalkalmazások hiánya miatt nem volt sikeres.
A szkepticizmus azt jelenti, hogy a blokklánc-alapú üzenetküldők megelőzik az időt — az emberek még nem készen állnak arra, hogy a saját fiókjukért felelősséget vállaljanak, a személyes adatok birtoklása még nem trendi, és a technológiák nem képesek biztosítani a magas sebességeket a blokkláncon. Hamarosan megjelennek majd technológiailag fejlettebb megfelelői a projektünknek. Meglátjátok.
Forrás: habr.com
