Annchain深度之以太坊系列:p2p網路RLPx傳輸協議

買賣虛擬貨幣
作者介紹Shor,Annchain核心開發成員,畢業於中科大。負責Annchain高效能p2p網路、通訊與編碼、基於DAG的高效交易同步、交易執行邏輯、wasm虛擬機器智慧合約平臺、rpc等模組的研發以及系統最佳化。RLPx為基於TCP 的傳輸協議,用於以太坊節點之間的安全加密通訊。以太坊p2p網路可以分為三層,從下到上依次分別為:基於udp的鄰居發現層、基於tcp的加密通訊層和核心協議層。基於udp的鄰居發現層使用Kad (Kademlia p2p網路協議)節點發現機制,與網路中的其他節點進行ping pong握手、交換鄰居的方式發現鄰居,計算節點之間的距離,動態維護鄰居表。基於tcp的加密通訊層與節點發現層發現的節點進行握手建立安全加密連線,負責對核心層協議提供的資料進行編解碼與加解密、安全傳輸,rlpx為該層實際使用的協議。核心協議層負責將需要傳送的業務資料傳入加密通訊層,並處理從加密通訊層收到的業務資料。RLPx使用了完全前向保密技術(perfect forward secrecy),通訊雙方生成隨機公私鑰對,交換各自的公鑰,使用自己的隨機私鑰和對方的公鑰生成共享秘密(shared-secret)。後續使用這個共享秘密對稱加密傳輸的資料,即使一方的私鑰被洩露,過去的通訊還是安全的。ECIES加密

ECIES(Elliptic Curve Integrated Encryption Scheme,橢圓曲線綜合加密方案) 作為非對稱秘鑰 用於RLPx協議握手。在RLPx協議中用到EXIES的以下幾個點:

RLPx 協議建立連線

通訊雙方進行加密握手,生成共享秘密建立加密通道,再進行協議握手。後續業務資料在這個加密通道中傳輸。

發起者(initator ) 為主動開啟TCP連線的一個節點,接受者(Recipient)為接受連線的節點。

節點透過底層的節點發現機制獲取對方節點的 ip,tcp地址資訊。發起者開啟tcp連線。

E 為EXIES 非對稱加密函式,Rlpx 加密握手協議如下:

authPacket -> E(remote-pubk, S(ephemeral-privk, static-shared-secret ^ nonce) || H(ephemeral-pubk) || pubk || nonce || 0x0)
authResponsePacket- -> E(remote-pubk, remote-ephemeral-pubk || nonce || 0x0)
static-shared-secret = ecdh.agree(privkey, remote-pubk)

加密握手

發起者的加密握手流程如下:

1.生成一個隨機數init-nonce
2.透過ecies生成隨機秘鑰對 ,隨機私鑰ephemeral-privk 與隨機公鑰ephemeral-pubk
3.用自己的私鑰privk和對方的公鑰remote_pubk 生成靜態共享秘密static-shared-secrets
4.將生成的共享秘密static-shared-secrets與隨機數init-nonce進行異或運算,得到一個雜湊值
5.使用自己的隨機私鑰ephemeral-privk 對該雜湊值進行ECDSA簽名計算,得到簽名sig
6.將簽名sig、自己的公鑰pubk 、初始nonce 作為認證資訊authMsg。
7.對authMsg進行編碼,然後再用對方的公鑰remote_pubk進行ecies加密,得到認證資料包authPacket,將資料包透過傳送給對方節點
8.等待讀取對方節點的響應
9.收到對方響應之後,讀取資料,使用自己的私鑰privk 進行解密,再進行解碼,得到認證響應authRespMsg
10.讀取響應nonce, 和對方的隨機公鑰remote-ephemeral-pubk。

接受者加密握手流程:

1.接收方讀取對方傳送的加密握手資料包。
2.先透過自己的私鑰privk解密資料包,然後進行解碼,得到認證資料authMsg。
3.獲取對方的remote_pubk, 和nonce。
4.生成隨機ECDH 秘鑰對, 私鑰ephemeral-privk 與公鑰ephemeral-pubk。
5.透過自己的私鑰privk 和對方的公鑰remote-pubk生成協商的靜態共享秘密static-shared-secrets
6.用nonce對靜態共享秘密進行異或運算得到 簽名的訊息signedMsg
7.用signedMsg 和authMsg中的簽名資訊,恢復出對方的隨機公鑰remote-ephemeral-pubk
8.生成隨機數responseNonce, 與隨機公鑰ephemeral-pubk 作為認證響應authRespMsg
9.對authRespMsg進行編碼, 然後用對方的公鑰remote-pubk進行加密生成認證響應資料authresponsePacket
10.將authResponsePacket 傳送給對方。

計算共享秘密(shared secret)

發起者和接受者在握手完成之後,透過認證訊息authPacket  和認證響應訊息authRespPacket 計算協商的連線秘密 。 該連線秘密只有在當前連線中有效,所以當一方的私鑰被洩露之後,之前的通訊訊息還是安全的。

計算共享秘密步驟如下:

1. 用當前連線的隨機私鑰ephemeral-privk和對端隨機公鑰emote-ephemeral-pubk計算一個個ECDH共享秘密:

2. 計算共享秘密:

3.計算AES秘密:

4. 計算訊息認證碼秘密

5.  對發起方(initiator),計算出口連線訊息認證碼

6.對發起方(initiator), 計算入口連線訊息認證碼

7. 對接收方(Recipient), 計算出口連線訊息認證碼

8. 對接收方(Recipient),計算入口連線訊息認證碼

9. 將對方公鑰remote-pubk,  aes-secret, mac-secre,egress-mac, ingress-mac 作為當前連線的協商秘密(connection secrets) 用於資料分幀。

協議握手

協議握手傳送簡單的握手資訊,檢查對方的響應,判斷加密握手是否起作用,同時判斷對方是否支援snappy壓縮。握手成功則建立連線完成。

資料分幀

加密握手成功之後,在此連線上傳送的所有業務資訊,都透過連線協商秘密(connection secrets) 按一定格式進行資料分幀。

分幀格式:
frame = header || header-mac || frame-data || frame-mac
header = frame-size || header-data || padding

對於要傳送的資料,按如下步驟寫入tcp連線:

1. 如果啟用了snappy,則用snappy壓縮訊息內容
2. 將訊息大小和型別等寫入訊息頭header
3. 計算訊息頭認證碼header-mac

其中left16()為取前16個位元組。
4. 對訊息內容加密,寫入加密後的的幀資料 frame-data
5. 計算幀訊息認證碼,並寫入


對於從tcp層接受到到資料,按如下步驟讀取:


1. 讀取32個位元組的訊息頭header
2. 校驗header-mac 是否正確
3. 讀取訊息型別和訊息大小
4. 校驗frame-mac是否正確
5. 讀取訊息內容並使用當前連線的秘密(connection secrets)AES解密
6. 如果使用了snappy壓縮,則用snappy解壓獲取訊息內容
7. 讀取完畢,將訊息內容交給上層協議處理
 透過密文持續更新egress-mac 與ingress-mac 以及header-mac得到訊息認證。

Reference :  https://github.com/ethereum/go-ethereum/tree/master/p2p
注: || 為串接 ,^為異或運算。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;