2017 年初,我們開始在區塊鏈上建立一個信使(名稱和連結在個人資料中),討論相對於經典 P2P 信使的優勢。
透過 2.5
今年,我們確認了我們的概念:即時通訊應用程式現在可用於 iOS、Web PWA、Windows、GNU/Linux、Mac OS 和 Android。
今天我們將告訴您區塊鏈信使如何運作以及客戶端應用程式如何與其 API 配合使用。
我們希望區塊鏈能夠解決經典 P2P 通訊工具的安全與隱私問題:
- 一鍵建立帳戶 - 無需電話或電子郵件,無法存取通訊錄或地理位置。
- 對話者從不建立直接連線;所有通訊都是透過分散式節點系統進行的。 用戶的IP位址不能互相存取。
- 所有訊息均經過端對端 curve25519xsalsa20poly1305 加密。 看來這不會讓任何人感到驚訝,但我們的原始碼是開放的。
- 排除 MITM 攻擊 - 每個訊息都是一個交易,並由 Ed25519 EdDSA 簽署。
- 訊息最終出現在它自己的區塊中。 一致性和
timestamp
您無法修復區塊,因此也無法修復訊息的順序。 - 「我沒有這麼說」不適用於區塊鏈上的消息。
- 沒有中央結構來檢查訊息的「真實性」。 這是由基於共識的分散式節點系統完成的,並由使用者擁有。
- 無法進行審查 - 帳戶無法被阻止,訊息也無法被刪除。
- 區塊鏈 2FA 是地獄般的簡訊 2FA 的替代方案,
毀掉了很多健康。 - 能夠隨時從任何裝置獲取所有對話意味著您根本不必在本地儲存對話。
- 確認訊息傳送。 不是到使用者的設備,而是到網路。 本質上,這是對收件人能夠閱讀您的郵件的確認。 這是發送重要通知的有用功能。
區塊鏈的好處還包括與加密貨幣以太坊、狗狗幣、Lisk、達世幣、比特幣(這個項目仍在進行中)的緊密整合以及在聊天中發送代幣的能力。 我們甚至製作了一個內建的加密貨幣交換器。
然後——這一切是如何運作的。
一則訊息就是一筆交易
每個人都已經習慣了區塊鏈中的交易將代幣(硬幣)從一個用戶轉移到另一個用戶的事實。 就像比特幣一樣。 我們創建了一種特殊類型的交易來傳輸訊息。
要在區塊鏈上的Messenger中發送訊息,需要經過幾個步驟:
- 加密訊息文字
- 將密文放入交易中
- 簽署交易
- 向任何網路節點發送交易
- 分散式節點系統確定訊息的“真實性”
- 如果一切正常,帶有訊息的交易將包含在下一個區塊中
- 接收者檢索訊息交易並解密
步驟1-3和7在客戶端本機執行,步驟5-6在主機上執行。
訊息加密
該訊息使用發送者的私鑰和接收者的公鑰進行加密。 我們將從網路中取得公鑰,但為此,接收者的帳戶必須被初始化,即至少有一筆交易。 您可以使用 REST 請求 GET /api/accounts/getPublicKey?address={ADAMANT address}
,並且在載入聊天時,對話者的公鑰將已經可用。
信差使用 curve25519xsalsa20poly1305 演算法加密訊息(
這是一個 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 對交易進行哈希處理,然後對其進行簽名 signature
,交易 ID 是 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'
作為回應,我們將收到以下類型的交易 ID
{
"success": true,
"nodeTimestamp": 63228852,
"transactionId": "6146865104403680934"
}
交易驗證
基於共識的分散式節點系統確定交易訊息的「真實性」。 訊息來自誰、發送給誰、何時、訊息是否被其他訊息替換以及發送時間是否指示正確。 這是區塊鏈非常重要的優勢——沒有負責驗證的中心結構,訊息的順序及其內容無法偽造。
首先,一個節點檢查準確性,然後將其發送給其他節點 - 如果大多數人認為一切正常,則該交易將包含在鏈的下一個區塊中 - 這就是共識。
負責檢查的節點程式碼部分可以在GitHub上查看——
在區塊中包含帶有訊息的交易
如果達成共識,帶有我們訊息的交易將與其他有效交易一起納入下一個區塊中。
區塊具有嚴格的順序,每個後續區塊都是基於先前區塊的雜湊值形成的。
關鍵是我們的訊息也包含在這個序列中並且不能被「重新排列」。 如果多個訊息落入一個區塊,它們的順序將由 timestamp
消息。
閱讀信息
訊息應用程式從區塊鏈中檢索發送給收件人的交易。 為此我們做了一個端點 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 - 鍵值儲存 - 這是另一種類型的事務,其中 asset
加密的不是 NaCl-box,而是
文件/影像傳輸和群組聊天仍然需要大量工作。 當然,在錯誤和錯誤的格式中,這可能會很快“搞砸”,但我們希望保持相同程度的隱私。
是的,仍有工作要做——理想情況下,真正的隱私假設用戶不會連接到公共網路節點,而是會提升自己的節點。 您認為有多少比例的使用者會這樣做? 沒錯,0。我們能夠使用 Tor 版本的信差部分來解決這個問題。
我們已經證明區塊鏈上的信使是可以存在的。 此前,2012年只有一次嘗試——
懷疑是由於區塊鏈上的信使超前了——人們還沒有準備好對自己的帳戶負責,擁有個人資訊還不是一種趨勢,而且技術不允許區塊鏈上高速。 接下來將出現更多與我們專案類似的技術。 你會看到的。
來源: www.habr.com