简单概括就是:建立TCP连接-->密钥协商(auth、auth-ack)-->双发导出相同的密钥-->发送hello(协议协商)-->创建完成。具体实现需要看Ethereum源码。
分帧在auth之后的所有数据包都是分帧传输的。任何一方如果第一帧数据包验证失败都可以断开连接。
分帧传输的主要目的是在单一连接上可靠的支持多路复用协议。其次,因数据包分帧,为消息认证码产生了适当的分界点,使得加密流变得简单了。数据帧通过握手产生的密钥进行验证。
数据帧头部提供了数据包的大小和协议信息。
frame = header || header-mac || frame-data || frame-mac header = frame-size || header-data || padding frame-size = size of frame excluding padding, integer < 2**24, big endian header-data = rlp.list(protocol-type[, context-id]) protocol-type = integer < 2**16, big endian context-id = integer < 2**16, big endian padding = zero-fill to 16-byte boundary frame-content = any binary data header-mac = left16(egress-mac.update(aes(mac-secret,egress-mac)) ^ header-ciphertext).digest frame-mac = left16(egress-mac.update(aes(mac-secret,egress-mac)) ^ left16(egress-mac.update(frame-ciphertext).digest)) egress-mac = keccak256 state, continuously updated with egress bytes ingress-mac = keccak256 state, continuously updated with ingress bytes left16(x) is the first 16 bytes of x || is concatenate ^ is xor对发送与接收的密文数据不断更新egress-mac或ingress-mac实现消息认证;对头部数据,是通过将加密输出数据的头部与相应的mac进行异或运算(参见header-mac)。这样做是为了确保对明文mac和密文执行统一操作。所有的mac都是以明文形式发送的。
填充字节用于防止缓存区饥饿,使得帧组件按指定区块字节大小对齐。
已知的问题RLPx握手被认为是易破解的,因为aes-secret和mac-secret被重复用于读取和写入
。RLPx连接的两端从相同的密钥,nonce和IV生成两个CTR流。如果攻击者知道一个明文,他们就可以用重用的密钥流破解未知明文。
帧编码提供了用于多路复用的协议类型字段protocol-type,但devp2p未使用该字段。
RLPx传输协议的前向安全性RLPx使用了(PerfectForwardSecrecy),简单来说。链接的两方都生成随机的私钥,通过随机的私钥得到公钥。然后双方交换各自的公钥,这样双方都可以通过自己随机的私钥和对方的公钥来生成一个同样的共享密钥(shared-secret)。后续的通讯使用这个共享密钥作为对称加密算法的密钥。这样来说。如果有一天一方的私钥被泄露,也只会影响泄露之后的消息的安全性,对于之前的通讯是安全的(因为通讯的密钥是随机生成的,用完后就消失了)。
前向安全性前向安全或前向保密FS(ForwardSecrecy),有时也被称为完美前向安全PFS(PerfectForwardSecrecy),是密码学中通讯协议的安全属性,指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏。前向安全能够保护过去进行的通讯不受密码或密钥在未来暴露的威胁。如果系统具有前向安全性,就可以保证万一密码或密钥在某个时刻不慎泄露,过去已经进行的通讯依然是安全,不会受到任何影响,即使系统遭到主动攻击也是如此。
最后,需要说明一下,这篇文档对RLPx协议进行了简述,具体实现协议还是有很多细节需处理,深入请看以太坊源码。
如果文中有问题或者不对的地方,可关注微信公众号与我交流学习,欢迎指教!