死磕以太坊源码分析之rlpx协议

死磕以太坊源码分析之rlpx协议

本文主要参考自eth官方文档:rlpx协议

符号

  • X || Y:表示X和Y的串联
  • X ^ Y: X和Y按位异或
  • X[:N]:X的前N个字节
  • [X,Y,Z,...]:[X,...]的RLP递归编码
  • keccak256(MESSAGE):以太坊使用的keccak256哈希算法
  • ecies.encrypt(PUBKEY,MESSAGE,AUTHDATA):RLPx使用的非对称身份验证加密函数 AUTHDATA是身份认证的数据,并非密文的一部分 但是AUTHDATA会在生成消息tag前,写入HMAC-256哈希函数
  • ecdh.agree(PRIVKEY,PUBKEY):是PRIVKEY和PUBKEY之间的椭圆曲线Diffie-Hellman协商函数

ECIES加密

ECIES (Elliptic Curve Integrated Encryption Scheme) 非对称加密用于RLPx握手。RLPx使用的加密系统:

  • 椭圆曲线secp256k1基点G
  • KDF(k,len):密钥推导函数 NIST SP 800-56 Concatenation
  • MAC(k,m):HMAC函数,使用了SHA-256哈希
  • AES(k,iv,m):AES-128对称加密函数,CTR模式

假设Alice想发送加密消息给Bob,并且希望Bob可以用他的静态私钥kB解密。Alice知道Bob的静态公钥KB

Alice为了对消息m进行加密:

  1. 生成一个随机数r并生成对应的椭圆曲线公钥R = r * G
  2. 计算共享密码S = Px,其中 (Px,Py) = r * KB
  3. 推导加密及认证所需的密钥kE || kM = KDF(S,32)以及随机向量iv
  4. 使用AES加密 c = AES(kE,m)
  5. 计算MAC校验 d = MAC(keccak256(kM),iv || c)
  6. 发送完整密文R || iv || c || d给Bob

Bob对密文R || iv || c || d进行解密:

  1. 推导共享密码S = Px,其中(Px,Py) = r * KB = kB * R
  2. 推导加密认证用的密钥kE || kM = KDF(S,32)
  3. 验证MACd = MAC(keccak256(kM),iv || c)
  4. 获得明文m = AES(kE,iv || c)

节点身份

所有的加密操作都基于secp256k1椭圆曲线。每个节点维护一个静态的secp256k1私钥。建议该私钥只能进行手动重置(例如删除文件或数据库条目)。

握手流程

RLPx连接基于TCP通信,并且每次通信都会生成随机的临时密钥用于加密和验证。生成临时密钥的过程被称作“握手” (handshake),握手在发起端(initiator,发起TCP连接请求的节点)和接收端(recipient,接受连接的节点)之间进行。

  1. 发起端向接收端发起TCP连接,发送auth消息
  2. 接收端接受连接,解密、验证auth消息(检查recovery of signature == keccak256(ephemeral-pubk)
  3. 接收端通过remote-ephemeral-pubknonce生成auth-ack消息
  4. 接收端推导密钥,发送首个包含Hello消息的数据帧 (frame)
  5. 发起端接收到auth-ack消息,导出密钥
  6. 发起端发送首个加密后的数据帧,包含发起端Hello消息
  7. 接收端接收并验证首个加密后的数据帧
  8. 发起端接收并验证首个加密后的数据帧
  9. 如果两边的首个加密数据帧的MAC都验证通过,则加密握手完成

如果首个数据帧的验证失败,则任意一方都可以断开连接。

握手消息

发送端:

auth = auth-size || enc-auth-body
auth-size = size of enc-auth-body,encoded as a big-endian 16-bit integer
auth-vsn = 4
auth-body = [sig,initiator-pubk,initiator-nonce,auth-vsn,...]
enc-auth-body = ecies.encrypt(recipient-pubk,auth-body || auth-padding,auth-size)
auth-padding = arbitrary data

接收端:

ack = ack-size || enc-ack-body
ack-size = size of enc-ack-body,encoded as a big-endian 16-bit integer
ack-vsn = 4
ack-body = [recipient-ephemeral-pubk,recipient-nonce,ack-vsn,...]
enc-ack-body = ecies.encrypt(initiator-pubk,ack-body || ack-padding,ack-size)
ack-padding = arbitrary data

实现必须忽略auth-vsnack-vsn中的所有不匹配。

实现必须忽略auth-bodyack-body中的所有额外列表元素。

握手消息互换后,密钥生成:

static-shared-secret = ecdh.agree(privkey,remote-pubk)
ephemeral-key = ecdh.agree(ephemeral-privkey,remote-ephemeral-pubk)
shared-secret = keccak256(ephemeral-key || keccak256(nonce || initiator-nonce))
aes-secret = keccak256(ephemeral-key || shared-secret)
mac-secret = keccak256(ephemeral-key || aes-secret)

帧结构

握手后所有的消息都按帧 (frame) 传输。一帧数据携带属于某一功能的一条加密消息。

分帧传输的主要目的是在单一连接上实现可靠的支持多路复用协议。其次,因数据包分帧,为消息认证码产生了适当的分界点,使得加密流变得简单了。通过握手生成的密钥对数据帧进行加密和验证。

帧头提供关于消息大小和消息源功能的信息。填充字节用于防止缓存区不足,使得帧组件按指定区块字节大小对齐。

frame = header-ciphertext || header-mac || frame-ciphertext || frame-mac
header-ciphertext = aes(aes-secret,header)
header = frame-size || header-data || header-padding
header-data = [capability-id,context-id]
capability-id = integer,always zero
context-id = integer,always zero
header-padding = zero-fill header to 16-byte boundary
frame-ciphertext = aes(aes-secret,frame-data || frame-padding)
frame-padding = zero-fill frame-data to 16-byte boundary

MAC

RLPx中的消息认证 (Message authentication) 使用了两个keccak256状态,分别用于两个传输方向。egress-macingress-mac分别代表发送和接收状态,每次发送或者接收密文,其状态都会更新。初始握手后,MAC状态初始化如下:

发送端:

egress-mac = keccak256.init((mac-secret ^ recipient-nonce) || auth)
ingress-mac = keccak256.init((mac-secret ^ initiator-nonce) || ack)

接收端:

egress-mac = keccak256.init((mac-secret ^ initiator-nonce) || ack)
ingress-mac = keccak256.init((mac-secret ^ recipient-nonce) || auth)

当发送一帧数据时,通过即将发送的数据更新egress-mac状态,然后计算相应的MAC值。通过将帧头与其对应MAC值的加密输出异或来进行更新。这样做是为了确保对明文MAC和密文执行统一操作。所有的MAC值都以明文发送。

header-mac-seed = aes(mac-secret,keccak256.digest(egress-mac)[:16]) ^ header-ciphertext
egress-mac = keccak256.update(egress-mac,header-mac-seed)
header-mac = keccak256.digest(egress-mac)[:16]

计算 frame-mac

egress-mac = keccak256.update(egress-mac,frame-ciphertext)
frame-mac-seed = aes(mac-secret,keccak256.digest(egress-mac)[:16]) ^ keccak256.digest(egress-mac)[:16]
egress-mac = keccak256.update(egress-mac,frame-mac-seed)
frame-mac = keccak256.digest(egress-mac)[:16]

只要发送者和接受者按相同方式更新egress-macingress-mac,并且在ingress帧中比对header-macframe-mac的值,就能对ingress帧中的MAC值进行校验。这一步应当在解密header-ciphertextframe-ciphertext之前完成。

功能消息

初始握手后的所有消息均与“功能”相关。单个RLPx连接上就可以同时使用任何数量的功能。

功能由简短的ASCII名称和版本号标识。连接两端都支持的功能在隶属于“ p2p”功能的Hello消息中进行交换,p2p功能需要在所有连接中都可用。

消息编码

初始Hello消息编码如下:

frame-data = msg-id || msg-data
frame-size = length of frame-data,encoded as a 24bit big-endian integer

其中,msg-id是标识消息的由RLP编码的整数,msg-data是包含消息数据的RLP列表。

Hello之后的所有消息均使用Snappy算法压缩。请注意,压缩消息的frame-sizemsg-data压缩前的大小。消息的压缩编码为:

frame-data = msg-id || snappyCompress(msg-data)
frame-size = length of (msg-id || msg-data) encoded as a 24bit big-endian integer

基于msg-id的复用

frame中虽然支持capability-id,但是在本RLPx版本中并没有将该字段用于不同功能之间的复用(当前版本仅使用msg-id来实现复用)。

每种功能都会根据需要分配尽可能多的msg-id空间。所有这些功能所需的msg-id空间都必须通过静态指定。在连接和接收Hello消息时,两端都具有共享功能(包括版本)的对等信息,并且能够就msg-id空间达成共识。

msg-id应当大于0x11(0x00-0x10保留用于“ p2p”功能)。

p2p功能

所有连接都具有“p2p”功能。初始握手后,连接的两端都必须发送HelloDisconnect消息。在接收到Hello消息后,会话就进入激活状态,并且可以开始发送其他消息。由于前向兼容性,实现必须忽略协议版本中的所有差异。与处于较低版本的节点通信时,实现应尝试靠近该版本。

任何时候都可能会收到Disconnect消息。

Hello (0x00)

[protocolVersion: P,clientId: B,capabilities,listenPort: P,nodeKey: B_64,...]

握手完成后,双方发送的第一包数据。在收到Hello消息前,不能发送任何其他消息。实现必须忽略Hello消息中所有其他列表元素,因为可能会在未来版本中用到。

  • protocolVersion当前p2p功能版本为第5版
  • clientId表示客户端软件身份,人类可读字符串,比如"Ethereum(++)/1.0.0“
  • capabilities支持的子协议列表,名称及其版本:[[cap1,capVersion1],[cap2,capVersion2],...]
  • listenPort节点的收听端口 (位于当前连接路径的接口),0表示没有收听
  • nodeIdsecp256k1的公钥,对应节点私钥

Disconnect (0x01)

[reason: P]

通知节点断开连接。收到该消息后,节点应当立即断开连接。如果是发送,正常的主机会给节点2秒钟读取时间,使其主动断开连接。

reason 一个可选整数,表示断开连接的原因:

Reason Meaning
0x00 Disconnect requested
0x01 TCP sub-system error
0x02 Breach of protocol,e.g. a malformed message,bad RLP,...
0x03 Useless peer
0x04 Too many peers
0x05 Already connected
0x06 Incompatible P2P protocol version
0x07 Null node identity received - this is automatically invalid
0x08 Client quitting
0x09 Unexpected identity in handshake
0x0a Identity is the same as this node (i.e. connected to itself)
0x0b Ping timeout
0x10 Some other reason specific to a subprotocol

Ping (0x02)

[]

要求节点立即进行Pong回复。

Pong (0x03)

[]

回复节点的Ping包。

源码分析

主要功能

返回传输对象

返回一个transport对象,连接持续5秒

// handshakeTimeout 5
func newRLPX(fd net.Conn) transport {
....
}

读取消息

返回Msg对象,调用读写器的ReadMsg,连接持续30秒

func (t *rlpx) ReadMsg() (Msg,error) {
  ..
	t.fd.SetReadDeadline(time.Now().Add(frameReadTimeout))
}

写入消息

调用读写器的WriteMsg写信息,连接持续20秒

func (t *rlpx) WriteMsg(msg Msg) error {
  ...
	t.fd.SetWriteDeadline(time.Now().Add(frameWriteTimeout))
}

协议版本握手

协议握手,输入输出均是protoHandshake对象,包含了版本号、名称、容量、端口号、ID和一个扩展属性,握手时会对这些信息进行验证

加密握手

握手时主动发起者叫initiator

接收方叫receiver

分别对应两种处理方式initiatorEncHandshake和receiverEncHandshake

两种处理方式成功以后都会得到一个secrets对象,保存了共享密钥信息,它会跟原有的net.Conn对象一起生成一个帧处理器:rlpxFrameRW

握手双方使用到的信息有:各自的公私钥地址对(iPrv,iPub,rPrv,rPub)、各自生成的随机公私钥对(iRandPrv,iRandPub,rRandPrv,rRandPub)、各自生成的临时随机数(initNonce,respNonce).
其中i开头的表示发起方(initiator)信息,r开头的表示接收方(receiver)信息.

func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey,dial *ecdsa.PublicKey) (*ecdsa.PublicKey,error) {
	var (
		sec secrets
		err error
	)
	if dial == nil {
		sec,err = receiverEncHandshake(t.fd,prv) // 接收者
	} else {
		sec,err = initiatorEncHandshake(t.fd,prv,dial) //主动发起者
	}
...
	t.rw = newRLPXFrameRW(t.fd,sec)
	t.wmu.Unlock()
	return sec.Remote.ExportECDSA(),nil
}

这里我们就讲解一下主动握手部分源码initiatorEncHandshake

①:初始化握手对象

h := &encHandshake{initiator: true,remote: ecies.ImportECDSAPublic(remote)}

②:生成验证信息

authMsg,err := h.makeAuthMsg(prv) 
func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4,error) {
	// 生成己方随机数initNonce
	h.initNonce = make([]byte,shaLen)
	_,err := rand.Read(h.initNonce)
...
	}
// 生成随机的一组公私钥对
	h.randomPrivKey,err = ecies.GenerateKey(rand.Reader,crypto.S256(),nil)
...
	}
	// 生成静态共享秘密token(用己方私钥和对方公钥进行有限域乘法)
	token,err := h.staticSharedSecret(prv)
	...
	}
//  和己方随机数异或后用随机生成的私钥签名
	signed := xor(token,h.initNonce)
	signature,err := crypto.Sign(signed,h.randomPrivKey.ExportECDSA())
...
	}
...
	return msg,nil
}

③:封包,将验证信息和握手进行rlp编码并拼接前缀信息

authPacket,err := sealEIP8(authMsg,h)

④:通过conn发送消息

conn.Write(authPacket)

⑤:处理接收的信息,得到响应包

readHandshakeMsg比较简单。 首先用一种格式尝试解码。如果不行就换另外一种。应该是一种兼容性的设置。 基本上就是使用自己的私钥进行解码然后调用rlp解码成结构体。

结构体的描述就是下面的authRespV4,里面最重要的就是对端的随机公钥。 双方通过自己的私钥和对端的随机公钥可以得到一样的共享秘密。 而这个共享秘密是第三方拿不到的

	authRespMsg := new(authRespV4)
	authRespPacket,err := readHandshakeMsg(authRespMsg,encAuthRespLen,conn)

⑥:填充响应的respNonce(对方随机数,生成共享私钥用)和remoteRandomPub(对方的随机公钥)

 h.handleAuthResp(authRespMsg)

⑦:将请求包和响应包封装成共享秘密(secrets)

h.secrets(authPacket,authRespPacket)

到此RLPX 相关的比较重要的内容就解读差不多了。

参考

https://github.com/blockchainGuide/blockchainguide ☆ ☆ ☆ ☆ ☆

https://mindcarver.cn/ ☆ ☆ ☆ ☆ ☆

https://github.com/ethereum/devp2p/blob/master/rlpx.md

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读903次。文章主要介绍了收益聚合器Beefy协议在币安智能链测试网网上的编译测试部署流程,以Pancake上的USDC-BUSD最新Curve版流动池的农场质押为例,详细介绍了完整的操作流程。_怎么在bsc网络上部署应用
文章浏览阅读952次。比特币的主要思路是,构建一个无中心、去信任的分布式记账系统。交易签名只能保证交易不是他人伪造的,却不能阻止交易的发起者自己进行多重交易,即交易的发起者将一个比特币同时转账给两个人,也就是所谓的双花。比特币应用的区块链场景也叫做公链,因为这个区块链对所有人都是公开的。除此之外,还有一种区块链应用场景,被称作联盟链。区块链的出现,使得低成本,去信任的跨组织合作成为可能,将重构组织间的关系,这个关系既包括企业间的关系,也包括政府和企业间的关系,还有政府部门间的关系。
文章浏览阅读2.5k次。虚拟人从最初的不温不火,到现在步入“出生高峰期”,元宇宙可以说是功不可没。此前,量子位发布了《虚拟数字人深度产业报告》,报告显示,到2030年我国虚拟数字人整体市场规模将达到2700亿元。其中,“身份型虚拟人”市场规模预计达到1750亿元,占主导地位,而“服务型虚拟人”总规模也将超过950亿元。得益于AI、VR/AR 等技术的发展,虚拟人的应用场景正在从传统的虚拟偶像等娱乐行业迈向更多元化的领域。_最喜欢的虚拟角色
文章浏览阅读1.3k次,点赞25次,收藏13次。通过调查和分析用户需求、兴趣和行为,你可以更好地定位你的目标受众,并在市场中找到你的定位。在设计你的Web3.0项目时,注重用户界面的友好性、交互流畅性和功能的创新性,以提供独特的用户体验。通过与有影响力的人或组织进行合作,推广你的Web3.0项目。通过与他们分享你的项目并抓住他们的推荐,可以迅速获得更多的关注度。通过优化你的网站和内容,将有助于提高你的排名,并增加有机流量。通过提供奖励激励计划,如空投、奖励机制等,激励用户参与你的Web3.0项目。的人或组织合作,可以增加你的项目的曝光度。
文章浏览阅读1.7k次。这个智能合约安全系列提供了一个广泛的列表,列出了在 Solidity 智能合约中容易反复出现的问题和漏洞。Solidity 中的安全问题可以归结为智能合约的行为方式不符合它们的意图。我们不可能对所有可能出错的事情做一个全面的列表。然而,正如传统的软件工程有常见的漏洞主题,如 SQL 注入、缓冲区超限和跨网站脚本,智能合约中也有反复出现的。_solidity安全漏洞
文章浏览阅读1.3k次。本文描述了比特币核心的编译与交互方法_编译比特币
文章浏览阅读884次。四水归堂,是中国建筑艺术中的一种独特形式。这种形式下,由四面房屋围出一个天井,房屋内侧坡向天井内倾斜,下雨时雨水会从东西南北四方流入天井,从而起到收集水源,防涝护屋的作用,寓意水聚天心,天人合一。在科技产业当中,很多时候我们需要学习古人的智慧与意蕴,尝试打通各个生态,聚四方之力为我所用,这样才能为最终用户带来最大化价值。随着数字化、智能化的发展,算力成为生产力的根基。在这一大背景下,算力需要贯通软..._超聚变csdn
文章浏览阅读1k次,点赞24次,收藏19次。云计算和区块链是当代科技领域两个备受关注的核心技术。本文将深入探讨云计算和区块链的发展历程,详细剖析其起初阶段的奠基、面临的问题、业务内容、当前研究方向、用到的技术、实际应用场景、未来发展趋势,并提供相关链接供读者深入了解。
文章浏览阅读1.5k次。融入对等网络的奥妙,了解集中式、全分布式和混合式对等网络的差异,以及区块链网络的结构与协议,让你跃入区块链的连结网络。揭开密码学的神秘面纱,探寻对称密码学、非对称密码学、哈希函数、数字签名等关键技术,让你了解信息安全的核心。解码共识算法的精髓,从理论到实践,从PoW、PoS到PBFT,让你深入了解区块链如何达成共识。探索智能合约的世界,从定义到生命周期,从执行引擎到开发与部署,带你进入无限可能的合约领域。了解令人惊叹的区块链世界,从概念到价值,从发展历程到政策法规,一篇章串联出区块链的精髓。
文章浏览阅读777次。8 月份,加密货币市场经历了明显的波动,比特币价格波动幅度较大。与此同时,NFT 市场出现大幅下跌,引发了人们对这一新兴行业未来发展趋势的担忧
文章浏览阅读8.8k次,点赞53次,收藏37次。近二十年来,我国信息科技发展日益成熟,出现的网络完全问题也是“百花齐放”。而元宇宙作为5G技术、AR/VR技术、云计算以及区块链等技术的组合体,其安全性指定会被人们所广泛关注。根据前面所讲,元宇宙融合了虚拟世界和现实世界,通过数据将现实世界的各种元素映射到数字化的虚拟世界中。所以没有数据,就等于没有元宇宙的一切;没有信息安全,元宇宙的社会生产、生活就不能正常有序地进行。所以足以可见数据安全、信息安全对元宇宙发展起到的重要作用!!_元宇宙 安全计算
文章浏览阅读1.4k次。最早使用历史 1991年采用 时间戳 追溯 数字文档,之后 2009年后创始人**中本聪** (satoshi nakamoto )日裔美国人,在设计比特币数字货币中将此理念写入应用程序中_web3.0学习
文章浏览阅读1.7k次。DeFi收益来源全面概述_drfi收益
文章浏览阅读941次,点赞17次,收藏21次。号外:教链内参1.28《从BTC现货ETF的近期数据看到的》隔夜BTC经历现货ETF通过后的情绪冷静,一度破位40k后又逐渐修复至42k上方。请珍惜42k的BTC吧。也许到下个周期,我们将不再有机会见到这个高度的BTC了。下面,让我们重温,42k的BTC,在过去四年穿越牛熊的过程中,带给我们的启迪吧。需要提醒的是,历史文字,自有历史局限性,回顾,也须带着批判性的目光阅读和审视。2021年2月8日,...
文章浏览阅读1.2k次,点赞23次,收藏21次。其实一开始我也是这么想的,但根据PoW算法机制,如果你的计算量不够大,是无法控制区块链的走向的,也就是说,即使你投入了大量的成本用于完成任务,也不能保证自己成功。例如,你持有100个币,总共持有了30天,那么,此时你的币龄就为3000,这个时候,如果你发现了一个PoS区块,那么你的币龄就会被减去一定的值,每减少365个币龄,将会从区块中获得0.05个币的利息(可理解为年利率5%),那么在这个案例中,利息=3000×5%/365=0.41个币。前面说过,谁的算力强,谁最先解决问题的概率就越大。
文章浏览阅读1.9k次。这里主要实现的部分继续下去,对 Blockchain 这个对象有一些修改,如果使用 TS 的话可能要修改对应的 interface,但是如果是 JS 的话就无所谓了。需要安装的依赖有:express现在的 express 已经不内置 body-parser,需要作为单独的依赖下载request不下载会报错,是使用 request-promise 所需要的依赖和已经 deprecated 了,具体 reference 可以参考。_js区块链
文章浏览阅读1k次,点赞19次,收藏19次。作者:Zach Pandl Grayscale编译:象牙山首席村民 碳链价值以太坊在2023年取得了丰厚的回报。但表现不如比特币以及其他一些智能合约公链代币。我们认为,这反映了今年比特币特有的积极因素以及以太坊链上活动的缓慢复苏。尽管以太坊的涨幅低于比特币,但从绝对值和风险调整值来看,今年以太坊的表现优于传统资产类别。以太坊不断增长的L2生态系统的发展可能会吸引新用户,并在2024年支撑以太币的...
文章浏览阅读908次,点赞20次,收藏20次。通证是以数字形式存在,代表的是一种权利、一种固有和内在的价值。徐教授告诉我:多年的职业经历,多年的为易货贸易的思考,认识到在处理贸易和经济领域的关系时,应以提高人民生活水平、保证社会成员充分就业、保证就业成员实际收入和有效需求的大幅稳定增长、实现世界资源的充分利用以及扩大货物的生产和交换为目的,期望通过达成互惠互利安排,实行公开、公平、公正的“三公原则”,开展国家与国家、企业与企业之间的易货贸易,规避因信用问题引起的各类风险,消除国际贸易中的歧视待遇,促进全球国家的经济发展,从而为实现上述目标做出贡献。
文章浏览阅读2.5k次。由于webase文档原因,查找起来比较局限,有时候想找一个api却又忘了在哪个模块的目录下,需要一步一步单独点,而利用文档自带的检索功能又因为查找文档全部信息,显得十分缓慢,所以整理了有关WeBASE的api列表但不可否认,现在只有列表,没有对应的页面跳转,文章目的也只是为了多了解webase的接口_webase私钥管理里获取
文章浏览阅读1.4k次,点赞28次,收藏21次。基于​openzeppelin来构建我们的NFT,并用一个例子来手把手的说明如何在opensea快速发布自己的NFT智能合约(ERC721)。