以太坊RLPx传输协议

本文主要内容翻译自:The RLPx Transport Protocol,其中添加了一些个人的理解,由于密码学水平有限,不正确之处望指正。另外原文可能已经更新,最新内容请直接阅读原文。

本文档定义了RLPx传输协议,一种基于TCP的用于Ethereum节点间通信的传输协议。该协议适用于任意内容的加密帧,但它通常用于承载devp2p应用程序协议。

节点标识

所有加密操作都基于secp256k1椭圆曲线。每个节点都需要维护一个在会话间保存和复原的静态私钥。建议私钥只能手动重置,例如,通过删除文件或数据库条目。

ECIES加密

ECIES(Elliptic Curve Integrated Encryption Scheme)是在RLPx握手中使用的非对称加密方法。RLPx使用的密码系统是:

secp256k1椭圆曲线,生成元\(G\)

\(KDF(k,len)\):NIST SP 800-56级联密钥导出函数

\(MAC(k,m)\):HMAC使用SHA-256哈希函数

\(AES(k,iv,m)\):AES-256加密算法CTR模式

(这里原文是AES-128,但是Ethereum源代码中是AES-256,所以本文更改为AES-256)

Alice想要发送可以被Bob的静态私钥\(k_B\)解密的加密消息。Alice知道Bob的静态公钥\(K_B\)

为了加密消息\(m\),Alice生成了一个随机数\(r\),相应的生成了椭圆曲线公钥\(R=r*G\)并计算出共享密钥\(S=P_x\)\((P_x,P_y)=r*K_B\)。接着,\(k_E\mid\mid k_M=KDF(S,32)\)导出加密和认证主密钥,随机生成一个初始向量\(iv\)。Alice向Bob发送加密消息\(R \mid\mid iv \mid\mid c \mid\mid d\),其中\(c=AES(k_E,iv,m)\)\(d=MAC(k_M,iv\mid\mid c)\)

Bob需要解密消息\(R \mid\mid iv \mid\mid c \mid\mid d\),为此,需要导出共享密钥\(S=P_x\),其中\((P_x,P_y)=k_B*R\),以及导出加密和认证密钥\(k_E \mid\mid k_M=KDF(S,32)\)。Bob通过等式\(d==MAC(k_M,iv\mid\mid c)\)是否成立验证消息后计算\(m=AES(k_E,iv\mid\mid c)\)获取明文消息。

握手

“握手”过程构建了会话阶段中使用的主密钥。握手是在发起端(发起TCP连接请求的节点)和接收端(接受连接的节点)之间完成。

握手协议:

E是上面定义的ECIES非对称加密函数。

补充说明: E代表加密;S代表签名;H代表Hash运算

auth -> E(remote-pubk, S(ephemeral-privk, static-shared-secret ^ nonce) || H(ephemeral-pubk) || pubk || nonce || 0x0) # 由握手发起方发送,向对方发送密钥协商需要的本节点(公钥+临时公钥+随机数) auth-ack -> E(remote-pubk, remote-ephemeral-pubk || nonce || 0x0) # 接收方回应auth消息,向对方发送密钥协商需要的本节点(临时公钥+随机数),本节点公钥对方已经知道,所以这里不需要发送了。 static-shared-secret = ecdh.agree(privkey, remote-pubk)

握手后值的计算(步骤如下):

ephemeral-shared-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk) shared-secret = keccak256(ephemeral-shared-secret || keccak256(nonce || initiator-nonce)) aes-secret = keccak256(ephemeral-shared-secret || shared-secret) # destroy shared-secret mac-secret = keccak256(ephemeral-shared-secret || aes-secret) # destroy ephemeral-shared-secret Initiator: egress-mac = keccak256.update(mac-secret ^ recipient-nonce || auth-sent-init) # destroy nonce ingress-mac = keccak256.update(mac-secret ^ initiator-nonce || auth-recvd-ack) # destroy remote-nonce Recipient: egress-mac = keccak256.update(mac-secret ^ initiator-nonce || auth-sent-ack) # destroy nonce ingress-mac = keccak256.update(mac-secret ^ recipient-nonce || auth-recvd-init) # destroy remote-nonce

用临时密钥,一定程度上可以保证前向安全性。后文中有前向安全性的描述。

握手过程最重要的是协商密钥(对应上面的aes-secret、mac-secret)。

密钥协商过程中需要知道本节点和对方节点的(公钥+临时公钥+随机数)。这不是绝对的,不同的密钥协商算法有不同的实现方式,但基本上都需要双方交换一些数据,从而分别推到出相同的密钥。

补充一点,整个协商密钥的过程核心是ECDH密钥协商,但ECDH协商的过程前提是要对方是认证过的,可信的,前面的ECIES加密,实际上相当于对消息接收方进行认证,因为只有拥有对应的私钥才能解密消息。

创建加密连接主要流程如下:

发起端向接收端发起TCP连接,发送auth消息

接收端接受连接,解密、验证auth消息(检查recovery of signature == keccak256(ephemeral-pubk))

接收端生成auth-ack消息

接收端导出密钥,发送第一个数据帧

发起端接收到auth-ack消息,导出密钥

发起端发送第一个数据帧(代码中对应Hello packet)

接收端接收并验证数据帧

发起端接收并验证数据帧

如果MAC两边都验证通过,加密握手完成。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsxjx.html