区块链中文网

Annchain深度之以太坊系列:p2p网络RLPx传输协议
4365
发表时间:2019-02-23 11:28

  作者介绍

  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. 读取完毕,将消息内容交给上层协议处理


分享到: