概述
什么是节点发现协议
节点发现协议是一个用于在点对点网络中寻找其他节点的系统。该系统可以被任意节点用于任何目的。除了需要运行协议和存储有限的几个节点信息外,不需要任何成本。任一节点都可以作为网络的入口。
系统的设计在某种程度上受到Kademlia分布式哈希表(DHT)的启发,但与大多数DHT不同,它不存储任意的键值对。相反,DHT存储和传递“节点记录”,这些记录是经过签名的文档,提供了有关网络中节点的信息。节点发现作为网络中所有活跃节点的数据库执行三个基本功能:
- 对所有活跃参与者进行采样:通过遍历DHT,可以枚举整个网络。
- 搜索提供特定服务的参与者:Node Discovery v5包括一种可扩展的注册“主题广告”的机制。这些广告可以被查询,从中可以找到广告该主题的节点。
- 对节点记录进行权威解析:如果已知节点的ID,则可以检索其记录的最新版本。
通过这些功能,节点发现模块可以帮助网络中的节点发现其他节点,并获取有关节点的信息。它提供了一种有效的方式来管理和维护网络中的节点状态,并支持节点之间的通信和交互。通过使用节点记录进行身份验证和信息共享,节点发现模块有助于构建一个可信和健壮的网络。
和其他节点发现机制进行对比
像MDNS/Bonjour这样的系统主要用于在局域网中查找主机。节点发现协议旨在在互联网上工作,对于在互联网上的有大量参与者的应用程序非常有用。
使用约会服务的系统:这些系统通常由桌面应用程序或云服务使用,以将参与者相互连接。虽然无疑高效,但这需要值得信任的提供服务的运营商,并且这些系统容易受到审查。与约会服务器相比,节点发现协议不依赖于单一运营商,并且对每个参与者仅有少量的信任。随着网络规模的增大,它对审查的抵抗力也会增强,并且多个不同的点对点网络的参与者可以共享发现网络,进一步增加其韧性。
节点发现协议的致命弱点在于其加入网络的过程:虽然可以使用任何其他节点作为入口点,但必须通过其他机制首先找到该节点。可以使用几种方法,包括在DNS中保存初始入口点的可扩展列表或在本地网络中发现参与者,以合理安全地加入网络。
通过这些机制,节点发现协议实现了一种分布式的、不依赖于单一运营商的节点发现和连接机制。它提供了一种更加去中心化和抗审查的方式来构建大规模的点对点网络。
节点发现协议的握手(我理解为确认会话密钥的过程)
节点发现协议的交流使用一个对话密钥来加密和验证。网络中的每个节点既是客户端也是服务端,所以“握手”可以被交流的任一方在任意时刻发起。
握手步骤
假设节点A想和节点B通信(比如,发一个findnode包),为了和其通信,节点A必须有节点B的节点记录
- 如果先前A和B成功通信,有通信密钥,就用通信密钥对报文加密。如果没有,就发一个数据包来触发握手过程(这个数据包可以是具有任意内容的普通包)
- (1)节点B收到这个包,并从包中提取节点节点A的节点ID。如果节点B先前和A成功通信,有通信密钥,就会尝试用通信密钥对报文解密,如果解密成功,就没有必要进行握手过程,节点B可以正常的与节点A进行通信。 (2)如果没有通信密钥或者解密失败,节点B就会回应一个WHOAREYOU包初始握手过程:首先生成一个id-nonce并放入数据包中。节点B会检查本地是否保存有节点A的节点记录,如果有,将对应记录的序列号(标记记录的版本)放入包中;如果没有则将对应的序列号enr-seq的值设为0。 (3)因为后续需要,节点B会将节点A的节点记录和发送给A的WHOAREYOU数据包保存一段时间
- (1)节点A收到节点B发来的数据包,确认节点B是活跃的并准备进行握手过程。在握手过程中,节点A发送的请求数据包中包含一个nonce,而节点B发送的挑战数据包中也包含相同的nonce。通过比较这两个nonce,节点A可以将挑战追溯回引发挑战的请求数据包,从而确保挑战与正确的请求数据包相关联。这种方式可以防止重放攻击和确保握手过程的正确性。(2)节点A通过将FINDNODE请求重新发送为握手消息数据包来继续握手过程。除了消息外,此数据包还包含三个部分:id-signature、ephemeral-pubkey和record。 (3)节点A现在可以生成新的会话密钥,为此,它首先在节点B的身份鉴定方案中所使用的椭圆曲线上生成一个临时密钥对即ephemeral-pubkey。例如,假设节点B的节点记录使用 "v4" 静态公钥进行Diffie-Hellman密钥协商,并使用HKDF密钥派生函数从中派生出会话密钥。
(4)节点A会根据自己的静态私钥节点A创建id-signature,它证明了它控制了签署其节点记录的私钥。这个签名还防止了握手过程的重放攻击。dest-pubkey = 与节点B的静态私钥相对应的节点公钥 secret = ecdh(dest-pubkey, ephemeral-key) kdf-info = "discovery v5 key agreement" || node-id-A || node-id-B prk = HKDF-Extract(secret, challenge-data) key-data = HKDF-Expand(prk, kdf-info) initiator-key = key-data[:16] recipient-key = key-data[16:]
(5)最后,节点A将WHOAREYOU挑战中的enr-seq元素与自己的节点记录序列号进行比较。如果挑战中的序列号较低,它将自己的记录包含在握手消息数据包中。请求现在被重新发送,使用新的会话密钥加密消息。id-signature-text = "discovery v5 identity proof" id-signature-input = id-signature-text || challenge-data || ephemeral-pubkey ||node-id-B id-signature = id_sign(sha256(id-signature-input))
- (1)节点B收到握手数据包,首先加载其在第二步时保存的A的节点记录和发送的给A的包(这里用challenge包称呼)。如果节点B没有A的节点记录,那么接收到的握手数据包中必须有A的节点记录。如果节点A确定其记录比B的当前副本更新,则也可能存在记录。如果数据包包含节点记录,B必须首先通过检查记录的签名来验证它。 (2)然后,节点B根据A的节点记录中的公钥验证id-signature。上述步骤中任意一个验证不通过即拒绝通话。 (3)然后,节点B使用其自己的静态私钥和握手数据包中的临时公钥生成和A生成的一样的会话密钥。并根据生成的会话密钥,对数据包中的信息进行解密。如果消息可以解密和验证,节点B将认为新的会话密钥有效,并对消息作出响应。
- 节点A接收到响应消息数据包,并使用新的会话密钥进行身份验证/解密。如果解密/身份验证成功,节点B的身份得到验证,节点A也认为新的会话密钥有效。
关于session keys即会话密钥的生成
1、节点A处生成session keys:
根据节点B的身份认证算法生成随机公钥ephemeral-pubkey:Node A can now derive the new session keys. To do so, it first generates an ephemeral key pair on the elliptic curve used by node B's identity scheme. As an example, let's assume the node record of B uses the "v4" scheme. In this case the ephemeral-pubkey will be a public key on the secp256k1 curve.
用生成的ephemeral-pubkey和节点B的静态公钥生成session keys:The ephemeral key is used to perform Diffie-Hellman key agreement with node B's static public key and the session keys are derived from it using the HKDF key derivation function.
2、节点B处生成session keys:
节点B使用A节点传来的随机公钥ephemeral-pubkey和自己的静态私钥生成session keys:B can perform the key derivation using its own static private key and the ephemeral-pubkey from the handshake packet. Using the resulting session keys, it attempts to decrypt the message contained in the packet.
注意
会话密钥的建立取决于接收方(发送WHOAREYOU的节点)使用的身份方案。也就是说,会话密钥的建立是根据接收方使用的身份方案来进行的。这意味着发起方和接收方在各自的节点记录中使用的身份方案可以不同。实现必须能够支持所有支持的身份方案来进行握手。