首页/新闻资讯/正文详情

Diem 网络层 Noise IK 加密握手协议全解析:从规格说明到源码实现

发布时间:2026/9/23 1:14:16 来源:云帆数科 栏目:资讯中心
Diem 网络层 Noise IK 加密握手协议全解析:从规格说明到源码实现
Diem 网络层 Noise IK 加密握手协议全解析从规格说明到源码实现【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diemDiemNet 节点之间的所有通信都通过 Noise 协议框架 为骨架系统讲解 Diem 如何在三种网络公共全节点网络 PFN、验证者全节点网络 VFN、验证者网络 VN中落地 Noise IK 握手、后握手会话加解密以及防重放攻击设计并结合 network/src/noise/handshake.rs、network/src/noise/stream.rs 与 crates/diem-crypto/src/noise.rs 的源码实现做纵深印证。读完本文你将掌握 DiemNet 安全传输层的完整协议细节、常量计算方式、三种网络的认证模式差异以及如何在源码与测试中验证这套实现。背景DiemNet 安全传输层概览DiemNet见 specifications/network/README.md是 Diem 生态中任意两个节点之间的主要网络协议它只描述线路上的消息结构与顺序底层消息投递依赖 TCP 传输。所有节点间的通信都必须使用 Noise 协议进行加密和认证——这正是 specifications/network/noise.md 这份规格文档的核心内容。Noise 层在 DiemNet 的连接生命周期中扮演安全传输升级Secure Transport Upgrade的角色。一条完整连接的建立顺序为server: TCP::bind [address] client: discover server_address /ip4/[address]/ln-noise-ik/[public_key]/ln-handshake/[version] // TCP 握手 client: TCP::connect [address] server: TCP::accept // DiemNet Noise IK 握手 client: server_peer_id Noise::upgrade_outbound [public_key] server: client_peer_id Noise::upgrade_inbound // DiemNet 版本握手 client: (server_version, server_protocols) Handshake::upgrade [version] server: (client_version, client_protocols) Handshake::upgrade [version]其中Noise::upgrade_outbound与Noise::upgrade_inbound即本文的主角定义于 specifications/network/noise.md#handshake其 Rust 实现位于 network/src/noise/handshake.rs 的NoiseUpgrader结构体。三种网络与两类认证模式规格文档将 Diem 的网络划分为三类分别适用不同的认证强度公共全节点网络Public Full Node Network, PFN公共全节点可以连接由验证者运营的全节点。验证者全节点网络Validator Full Node Network, VFN验证者运营的全节点可以连接它们自己的验证者。验证者网络Validator Network, VN验证者之间相互连接。在认证语义上参见 specifications/network/README.md 的 Authentication Modes 一节Mutual双向认证连接双方都在安全传输握手中认证对端用于 VN。因为 Noise IK 握手的首条消息已加密只有持有目标密钥对的服务器才能解密并响应所以客户端总是能认证服务器类似 TLS 证书固定到具体公钥而服务器是否认证客户端取决于网络配置。Server-only仅服务器认证只有拨号方客户端认证监听方服务器用于 VFN。规格文档特别注明注意VFN 中不进行客户端认证因为它目前是一个私有网络。在源码中这两种模式被建模为HandshakeAuthMode枚举network/src/noise/handshake.rspub enum HandshakeAuthMode { /// 双向认证模式双方使用 trusted_peers 集合互相认证并启用防重放机制。 /// 例如 Diem 验证者网络中验证者只允许当前验证者集合内的对端连接。 Mutual { anti_replay_timestamps: RwLockAntiReplayTimestamps, trusted_peers: ArcRwLockPeerSet, }, /// 半双向认证模式拨号方认证服务器服务器允许所有入站连接 /// 但若入站连接属于其可信集合则将其标记为 Trusted。 MaybeMutual(ArcRwLockPeerSet), }其中MaybeMutual的服务器允许所有入站连接语义正好对应 VFN 私有网络中不做客户端认证的设计——服务器不再强制要求客户端在可信集合中而是退化为校验客户端 peer_id 与其公钥的派生关系。构造入口HandshakeAuthMode::mutual(trusted_peers)、maybe_mutual(...)与server_only()提供了三种便捷创建方式。NetworkAddress 中的 Noise 协议预协商Noise 协议通过节点通告或配置的NetworkAddress实现预协商。规范的 DiemNet 地址在基础传输协议之后包含如下Protocolhuman-readable format: /ln-noise-ik/x25519-public-key其中x25519-public-key是通告方在 Noise 术语中的远端静态公钥remote static public key以小写十六进制编码。例如 specifications/network/README.md 给出的完整示例地址/ip4/10.0.0.61/tcp/6080/ln-noise-ik/080e287879c918794170e258bfaddd75acac5b3e350419044655e4983a487120/ln-handshake/0其协议栈依次为基础传输/ip4/ipaddr/tcp/port等→ 安全传输升级/ln-noise-ik/x25519-public-key→ DiemNet 握手升级/ln-handshake/version。在 specifications/network/network-address.md 的数据结构定义中Protocol枚举包含NoiseIK(x25519::PublicKey)变体其人类可读格式示例为NoiseIK(b080e287879c918794170e258bfaddd75acac5b3e350419044655e4983a487120) /ln-noise-ik/080e287879c918794170e258bfaddd75acac5b3e350419044655e4983a487120,这意味着拨号方在发起连接前就已从地址中得知服务器公钥——这正是 IK 模式预先知晓对方静态公钥的前提。依赖的 Noise 原语与密码套件规格文档明确实现依赖 Noise 规范中定义的以下函数Initialize(handshake_pattern, initiator, prologue, s, e, rs, re)WriteMessage(payload, message_buffer)ReadMessage(message, payload_buffer)EncryptWithAd(ad, plaintext)DecryptWithAd(ad, ciphertext)密码套件组合为X25519密钥交换RFC 7748AES-GCM认证加密算法NIST SP 800-38DSHA-256哈希函数这些组合直接编码进协议名常量中。在 crates/diem-crypto/src/noise.rs 中可以看到注意为满足 Noise 规范对协议名 32 字节定长的要求名字后补了\0\0\0\0/// The only Noise handshake protocol that we implement in this file. const PROTOCOL_NAME: [u8] bNoise_IK_25519_AESGCM_SHA256\0\0\0\0; /// A noise message cannot be larger than 65535 bytes as per the specification. pub const MAX_SIZE_NOISE_MSG: usize 65535; /// The authentication tag length of AES-GCM. pub const AES_GCM_TAGLEN: usize 16;同文件还提供了两个长度计算常量函数它们是下文消息长度推导的基础pub const fn encrypted_len(plaintext_len: usize) - usize { plaintext_len AES_GCM_TAGLEN } pub const fn handshake_init_msg_len(payload_len: usize) - usize { // e 加密的 s 加密的 payload let e_len x25519::PUBLIC_KEY_SIZE; let enc_s_len encrypted_len(x25519::PUBLIC_KEY_SIZE); let enc_payload_len encrypted_len(payload_len); e_len enc_s_len enc_payload_len } pub const fn handshake_resp_msg_len(payload_len: usize) - usize { // e 加密的 payload let e_len x25519::PUBLIC_KEY_SIZE; let enc_payload_len encrypted_len(payload_len); e_len enc_payload_len }选择的握手模式IKDiem 使用 Noise 的IK 握手模式IK: - s ... - e, es, s, ss - e, ee, se这是一个**单轮往返one-round trip**协议其语义是客户端预先知道服务器的静态公钥客户端在握手中发送自己的静态公钥从而实现服务器侧对客户端的认证基础。该模式已在 noiseexplorer 上被形式化验证。关键常量规格文档给出了三个对实现至关重要的常量常量值组成说明PROTOCOL_NAMENoise_IK_25519_AESGCM_SHA256与 Noise 一起使用的协议名实现时补齐 32 字节定长HANDSHAKE_MSG_132 (32 16) (8 16)第一条握手消息大小含公钥、加密公钥和加密的 8 字节负载HANDSHAKE_MSG_232 16第二条握手消息大小含公钥和加密的 0 字节负载对照源码可以精确还原这些数值X25519 公钥长度为 32 字节x25519::PUBLIC_KEY_SIZEAES-GCM 认证标签长度为 16 字节AES_GCM_TAGLEN因此HANDSHAKE_MSG_1 e(32) encrypted_s(3216) encrypted_payload(816) 104字节HANDSHAKE_MSG_2 e(32) encrypted_payload(016) 48字节。在 network/src/noise/handshake.rs 的NoiseUpgrader中这两个尺寸被定义为常量并与 prologue 一起参与整体消息尺寸计算/// The prologue is the clients peer_id and the remotes expected public key. const PROLOGUE_SIZE: usize PeerId::LENGTH x25519::PUBLIC_KEY_SIZE; /// The client message consist of the prologue a noise message with a timestamp as payload. const CLIENT_MESSAGE_SIZE: usize Self::PROLOGUE_SIZE noise::handshake_init_msg_len(AntiReplayTimestamps::TIMESTAMP_SIZE); /// The servers message contains no payload. const SERVER_MESSAGE_SIZE: usize noise::handshake_resp_msg_len(0);注意规格文档中的HANDSHAKE_MSG_1/HANDSHAKE_MSG_2仅指纯 Noise 消息部分而实际线路上客户端还要先发送PROLOGUE_SIZE16 字节 peer_id 32 字节公钥 48 字节的 prologue因此线路上第一条客户端消息总长为48 104 152字节。这也是 specifications/network/README.md 中连接建立示意中Noise::upgrade_outbound [public_key]会携带公钥参数的原因。对端状态Peer State规格文档定义了对端需要维护的变量peer_id对端的 id16 字节值。取值规则为在 VN 中是对端的链上账户地址account address在其他网络中当对端没有账户地址时取对端公钥的最后 16 字节。private_key对端的 X25519 私钥32 字节。public_key对端的 X25519 公钥32 字节。验证者validator在 VN 中还额外维护trusted_peerspeer_id 到公钥的映射代表当前验证者集合validator set。timestampspeer_id 到最近一次见到的 8 字节时间戳的映射。该值可视为无状态且严格递增的计数器用于防止重放攻击详见下文安全考虑一节。源码中trusted_peers的类型为ArcRwLockPeerSetPeerSet即HashMapPeerId, Peer见 network/src/noise/handshake.rs而timestamps的实现是AntiReplayTimestamps结构体内部为HashMapx25519::PublicKey, u64/// 在双向认证网络中客户端消息附带时间戳 /// 用于防止重放攻击——攻击者即使不知道客户端静态密钥 /// 也能重放握手消息迫使对端执行若干次 Diffie-Hellman 运算。 /// 因此响应方总是检查时间戳是否严格递增 /// 将其视为有状态计数器。若时间戳曾出现过或未严格递增 /// 可提前中止握手并避免昂贵的 Diffie-Hellman 计算。 #[derive(Default)] pub struct AntiReplayTimestamps(HashMapx25519::PublicKey, u64);握手流程详解规格文档将握手分为客户端upgrade_outbound与服务端upgrade_inbound两条路径并给出纯逻辑描述源码则给出了完整的异步实现。下面逐一对照。客户端upgrade_outbound(remote_public_key)规格定义的步骤将prologue以明文形式发送给服务器内容为peer_id后跟remote_public_key。调用 Noise 的Initialize(PROTOCOL_NAME, true, prologue, private_key, null, remote_public_key, null)。构造一个 8 字节payload内容为当前纪元 Unix 时间毫秒精度。调用 Noise 的WriteMessage(payload, message_buffer)。将message_buffer发送给服务器。接收大小为HANDSHAKE_MSG_2字节的server_response。调用 Noise 的ReadMessage(server_response, null)返回两个CipherState。源码实现network/src/noise/handshake.rs 的upgrade_outboundpub async fn upgrade_outboundTSocket, F( self, mut socket: TSocket, remote_public_key: x25519::PublicKey, time_provider: F, ) - ResultNoiseStreamTSocket, NoiseHandshakeError where TSocket: AsyncRead AsyncWrite Debug Unpin, F: Fn() - [u8; AntiReplayTimestamps::TIMESTAMP_SIZE], { // buffer to hold prologue first noise handshake message let mut client_message [0; Self::CLIENT_MESSAGE_SIZE]; // craft prologue self_peer_id | expected_public_key client_message[..PeerId::LENGTH].copy_from_slice(self.network_context.peer_id().as_ref()); client_message[PeerId::LENGTH..Self::PROLOGUE_SIZE] .copy_from_slice(remote_public_key.as_slice()); let (prologue_msg, client_noise_msg) client_message.split_at_mut(Self::PROLOGUE_SIZE); // craft 8-byte payload as current timestamp (in milliseconds) let payload time_provider(); // craft first handshake message (- e, es, s, ss) let mut rng rand::rngs::OsRng; let initiator_state self .noise_config .initiate_connection(mut rng, prologue_msg, remote_public_key, Some(payload), client_noise_msg) .map_err(NoiseHandshakeError::BuildClientHandshakeMessageFailed)?; socket.write_all(client_message).await?; socket.flush().await?; // receive the servers response (- e, ee, se) let mut server_response [0u8; Self::SERVER_MESSAGE_SIZE]; socket.read_exact(mut server_response).await?; let (_, session) self .noise_config .finalize_connection(initiator_state, server_response) .map_err(NoiseHandshakeError::ClientFinalizeFailed)?; Ok(NoiseStream::new(socket, session)) }实现细节与规格一一对应time_provider默认注入AntiReplayTimestamps::now其实现为取duration_since_epoch().as_millis() as u64后转为 8 字节小端序——这正是规格中当前纪元 Unix 毫秒时间戳的来源initiate_connection对应 Noise 的InitializeWriteMessageSome(payload)将时间戳作为握手消息的加密负载服务器响应读取固定SERVER_MESSAGE_SIZE字节后finalize_connection完成ReadMessage得到NoiseSession最终包装为NoiseStream。服务端upgrade_inbound()规格定义的步骤接收客户端prologue应包含 32 字节initiator_peer_id后跟 32 字节responder_expected_public_key。校验initiator_peer_idVN 或 VFN在可信对端集合中PFN由公钥正确派生。校验responder_expected_public_key是本机公钥。接收大小为HANDSHAKE_MSG_1字节的client_message。调用Initialize(PROTOCOL_NAME, true, prologue, private_key, null, remote_public_key, null)。调用ReadMessage(client_message, payload_buffer)。VN 中强制initiator_public_key在trusted_peers中且对应该 peer_id强制 payload 大于该 peer_id 已见的时间戳存储新时间戳。PFN 与 VFN 中强制initiator_peer_id由initiator_public_key正确派生。调用WriteMessage(null, message_buffer)存储两个CipherState发送响应。源码实现要点upgrade_inbound// receive the prologue first noise handshake message socket.read_exact(mut client_message).await?; // extract prologue (remote_peer_id | self_public_key) let (remote_peer_id, self_expected_public_key) client_message[..Self::PROLOGUE_SIZE].split_at(PeerId::LENGTH); let remote_peer_id PeerId::try_from(remote_peer_id)?; // reject accidental self-dials if remote_peer_id self.network_context.peer_id() { return Err(NoiseHandshakeError::SelfDialDetected); } // verify that this is indeed our public key if self_expected_public_key ! self.noise_config.public_key().as_slice() { return Err(NoiseHandshakeError::ClientExpectingDifferentPubkey(...)); } let (prologue, client_init_message) client_message.split_at(Self::PROLOGUE_SIZE); let (remote_public_key, handshake_state, payload) self .noise_config .parse_client_init_message(prologue, client_init_message)?;这里有一个规格文档之外、源码补充的细节自拨号检测SelfDialDetected——若收到的客户端 peer_id 恰为服务器自身 peer_id直接拒绝用于防范本机发现配置错误或恶意发现节点通告环回地址与本机公钥的情况。随后按认证模式分流let peer_role match self.auth_mode { HandshakeAuthMode::Mutual { trusted_peers, .. } { match trusted_peers.read().get(remote_peer_id) { Some(peer) Self::authenticate_inbound(remote_peer_short, peer, remote_public_key), None Err(NoiseHandshakeError::UnauthenticatedClient(remote_peer_short, remote_peer_id)), } } HandshakeAuthMode::MaybeMutual(trusted_peers) { match trusted_peers.read().get(remote_peer_id) { Some(peer) Self::authenticate_inbound(remote_peer_short, peer, remote_public_key), None { // if not, verify that their peerid is constructed correctly from their public key let derived_remote_peer_id diem_types::account_address::from_identity_public_key(remote_public_key); if derived_remote_peer_id ! remote_peer_id { Err(NoiseHandshakeError::ClientPeerIdMismatch(...)) } else { Ok(PeerRole::Unknown) } } } } }?;authenticate_inbound进一步校验该 peer 的公钥集合是否包含握手消息中携带的远端公钥fn authenticate_inbound( remote_peer_short: ShortHexStr, peer: Peer, remote_public_key: x25519::PublicKey, ) - ResultPeerRole, NoiseHandshakeError { if !peer.keys.contains(remote_public_key) { return Err(NoiseHandshakeError::UnauthenticatedClientPubkey(...)); } Ok(peer.role) }在双向认证模式VN下还会校验握手负载中的时间戳防重放详见下节然后构造并发送服务器响应if let Some(anti_replay_timestamps) self.auth_mode.anti_replay_timestamps() { // 校验 payload 长度必须为 8 字节 if payload.len() ! AntiReplayTimestamps::TIMESTAMP_SIZE { ... } let client_timestamp u64::from_le_bytes(client_timestamp); let mut anti_replay_timestamps anti_replay_timestamps.write(); if anti_replay_timestamps.is_replay(remote_public_key, client_timestamp) { return Err(NoiseHandshakeError::ServerReplayDetected(...)); } anti_replay_timestamps.store_timestamp(remote_public_key, client_timestamp); } // construct the response (- e, ee, se)服务器负载为空 let mut server_response [0u8; Self::SERVER_MESSAGE_SIZE]; let session self .noise_config .respond_to_client(mut rng, handshake_state, None, mut server_response)?; socket.write_all(server_response).await?; Ok((NoiseStream::new(socket, session), remote_peer_id, peer_role))规格中PFN 与 VFN 中校验 peer_id 由公钥派生的实现即diem_types::account_address::from_identity_public_key(remote_public_key)将 X25519 身份公钥映射为账户地址再与客户端声明的 peer_id 比对MaybeMutual模式下若客户端不在可信集合中则以PeerRole::Unknown标记其连接而非拒绝——这正是 VFN 私有网络不做客户端认证的落地点。后握手会话加密与解密Post-handshake握手成功后双方各持有两个 NoiseCipherState分别用于发送与接收方向。规格文档定义了封装函数encrypt(message)用第一个CipherState调用EncryptWithAd(null, message)构造ciphertext将ciphertext长度作为 2 字节值发送给对端发送ciphertext。decrypt(message)从对端接收 2 字节并解释为length接收length字节ciphertext调用DecryptWithAd(null, ciphertext)并返回结果。核心设计动机是Noise 本身是长度不敏感length-unaware的因此必须在每个 Noise 消息前附加长度前缀。源码 network/src/noise/stream.rs 中NoiseStream的文档注释说明了这一点并在ReadState/WriteState状态机中实现了该协议写路径BufferData缓冲明文→write_message_in_place原地加密并追加 16 字节认证标签→WriteFrameLen写 u16 大端长度→WriteEncryptedFrame写密文帧→Flush读路径ReadFrameLen读 2 字节帧长→ReadFrame读密文帧→session.read_message_in_place原地解密→CopyDecryptedFrame拷贝明文到用户缓冲。帧长的计算与 Noise 的 65535 字节上限约束相关// encrypted messages include a tag along with the payload. const MAX_WRITE_BUFFER_LENGTH: usize noise::decrypted_len(noise::MAX_SIZE_NOISE_MSG);即单帧明文最大为65535 - 16 65519字节。帧长为 0 被视为非预期情况ReadState::Eof(Err(()))读取方向遇到 EOF 则优雅结束Ok(None)表示远端正常关闭读到 1 字节后 EOF 则视为异常断开。NoiseStream实现了futures::io::AsyncRead与AsyncWritetrait因此在 specifications/network/README.md 的消息协议部分DiemNet 消息会先被序列化、切分为不超过 65519 字节的块再逐块交给 Noise 层加密成独立帧发送DiemNet 侧的最大帧长为 8 MiBMAX_DIEMNET_FRAME_LEN。测试用例u16_max_writes与interleaved_writesnetwork/src/noise/stream.rs分别验证了满帧写入与交错读写场景。安全考虑重放攻击Replay Attacks在双向认证的 VN 中中间人观察者理论上可以重放第一条握手消息达到两个目的驱逐evict一条合法的进行中连接迫使服务器执行无用的密码学运算CPU 消耗型 DoS。规格文档的缓解方案由于 Noise IK 握手模式不提供密钥确认key confirmation为阻止第一种攻击必须在确认连接前等待另一条客户端消息即继续观察客户端行为后再确认连接。为阻止第二种攻击在客户端第一条 Noise 消息的负载中附加计数器重放会被计数器未严格递增检测出来。为避免客户端维护计数器状态使用8 字节 Unix 时间戳为避免连接问题阻止客户端连接将精度设为毫秒。借助该对策服务器在检测到重放时可以提前中止握手把必须执行的 Diffie-Hellman 密钥交换次数从 4 次减半到 2 次。源码中的AntiReplayTimestamps精确实现了这一设计network/src/noise/handshake.rspub const TIMESTAMP_SIZE: usize 8; pub fn now() - [u8; Self::TIMESTAMP_SIZE] { let now: u64 duration_since_epoch().as_millis() as u64; now.to_le_bytes() } pub fn is_replay(self, pubkey: x25519::PublicKey, timestamp: u64) - bool { if let Some(last_timestamp) self.0.get(pubkey) { timestamp last_timestamp } else { false } } pub fn store_timestamp(mut self, pubkey: x25519::PublicKey, timestamp: u64) { self.0 .entry(pubkey) .and_modify(|last_timestamp| *last_timestamp timestamp) .or_insert(timestamp); }is_replay判定时间戳 上次记录值即为重放时间戳以小端序to_le_bytes编码为 8 字节负载。防重放仅在Mutual模式下启用HandshakeAuthMode::Mutual持有anti_replay_timestamps字段源码注释解释了原因该机制理论上可处处适用但在非双向认证场景下需要花时间做旧时间戳的垃圾回收以避免无界内存而在双向认证场景下可信对端集合有界且极少变化因此不存在这些问题。规格文档同时指出该方案的边界若验证者崩溃将丢失已见客户端时间戳的记录攻击者可以按顺序重放某个客户端的所有握手但这不能阻止攻击者阻止合法连接尝试因为合法的更新时间戳会被接受。在 FN全节点网络中不对此做防护因为攻击者本来就可以随意构造任意数量的合法握手。测试test_timestamp_replaynetwork/src/noise/handshake.rs完整验证了四段行为有效时间戳成功 → 过去时间戳失败 → 相同时间戳失败 → 未来时间戳成功与规格描述完全一致。Rekey规格文档明确当前实现不进行会话 Rekey因此会话是长生命周期的不具备前向保密与后向保密forward/backward secrecy。文档认为这目前不构成问题理由有二验证者之间不交换关键机密数据重要消息在应用层还有额外的签名保护。负载安全属性Payload Security Property根据 Noise 规范的负载安全属性一节IK 模式下发送方认证易受 KCI密钥泄露伪装攻击如果服务器密钥被泄露攻击者可以向该服务器冒充任何人。规格文档明确表示接受该风险We accept the risk。身份隐藏Identity HidingNoise 规范的身份隐藏一节指出 IK 模式在身份隐藏方面的风险文档同样接受因为 Diem 网络中对端的身份并非私密信息——验证者公钥、账户地址等身份信息本身就是公开可发现的通过链上发现协议。从源码测试验证协议行为仓库中的单元测试为规格提供了直接的可验证证据读者可以按如下方式在本地复现运行握手相关测试network/src/noise/handshake.rs 的#[cfg(test)] mod testcd /data/web/disk1/git_repo/gh_mirrors/di/diem cargo test -p network --lib noise::handshake其中覆盖了双向/仅服务器认证下的成功握手test_handshake_success_*、自拨号拒绝test_handshake_self_fails_*、双向认证下未认证密钥对/未认证 peer_id 拒绝test_handshake_unauthed_*、仅服务器认证下 peer_id 与公钥不匹配拒绝test_handshake_client_peerid_mismatch_fails_server_only_auth、分片读取下的握手成功test_handshake_fragmented_reads、时间戳重放检测test_timestamp_replay。运行流加解密测试network/src/noise/stream.rscd /data/web/disk1/git_repo/gh_mirrors/di/diem cargo test -p network --lib noise::stream其中simple_test、interleaved_writes、u16_max_writes、fragmented_stream分别验证了基本读写、交错双向读写、满帧65535 字节密文帧写入与 TCP 分片下的读写正确性dont_read_forever验证了对全零字节流不会无限读取应报错返回。密码学原语测试crates/diem-crypto/src/noise.rs 对应的单元测试位于 crates/diem-crypto/src/unit_tests/noise_test.rscd /data/web/disk1/git_repo/gh_mirrors/di/diem cargo test -p diem-crypto --lib noise此外network/src/noise/mod.rs 顶部还提供了一段完整的文档示例演示了如何用NoiseUpgrader、HandshakeAuthMode::mutual与内存套接字MemorySocket端到端完成握手并双向收发消息是理解本协议最直观的最小可运行范例。总结Diem 网络层的安全传输完全建立在 Noise IK 单轮往返握手之上规格文档 specifications/network/noise.md 精确规定了协议名、消息尺寸、对端状态、双向握手步骤、后握手加解密框架及安全边界源码 network/src/noise/handshake.rs 与 network/src/noise/stream.rs 则给出了完整的异步实现并在 crates/diem-crypto/src/noise.rs 中提供了精简版Noise_IK_25519_AESGCM_SHA256原语。三者相互印证构成了从规格到实现再到测试的完整闭环。理解这套协议也就理解了 DiemNet 全链路安全的基础基于 IK 的服务器认证、基于 trusted peers 集合的可选双向认证、基于毫秒时间戳的状态化防重放以及明确声明并接受的前向保密缺失、KCI 与身份暴露风险。【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

RISC-V单周期CPU硬件验证环境:Verilog仿真与GTKWave波形调试
RISC-V单周期CPU硬件验证环境:Verilog仿真与GTKWave波形调试

简介:本资源是西南交通大学《计算机组成原理》课程配套的上机实验报告合集,面向高校计算机类专业本科生及数字电路实践学习者,系统支撑从预备实验到期末课程设计的全流程实践训练。压缩包共1757个文件,总大小30.22MB,核… · 2026/9/23 1:14:16

煤矸石目标检测实战:从COCO JSON到YOLOv8训练全流程
煤矸石目标检测实战:从COCO JSON到YOLOv8训练全流程

简介:这份煤矸石识别数据集面向从事矿物识别、目标检测的算法工程师与科研人员,基于现场采集的102张原始图片构建,可支持煤炭、煤矸石与高岭石的多类别识别,适用于煤矿矸石分选、矿物自动分类等工业视觉场景。压缩包共105个文件&a… · 2026/9/23 1:14:16

皮安级电流测量实战:从噪声极限到高阻低阻方案
皮安级电流测量实战:从噪声极限到高阻低阻方案

简介:《低电平测量手册-第七版》中文版是吉时利(Keithley)公司推出的微弱信号测量经典教材,面向电子测量研发人员、仪器应用工程师及从事精密测试的科研工作者,系统讲解纳伏、皮安级电流电压电阻测量的理论基础与工程实… · 2026/9/23 1:14:16

基于Python的SAR图像变化检测系统:神经网络与Web部署实战
基于Python的SAR图像变化检测系统:神经网络与Web部署实战

简介:这套基于Python神经网络学习的SAR图像变化检测系统,以Web应用形式封装,面向遥感图像处理、深度学习入门及SAR变化检测应用开发者。项目融合神经网络模型、前后端交互与图像处理流程,可用于灾害监测、城市规划、环境研究等场景… · 2026/9/23 2:06:04

搞懂JSqlParser版本差异,搞定SQL解析高频面试题
搞懂JSqlParser版本差异,搞定SQL解析高频面试题

搞懂JSqlParser版本差异,搞定SQL解析高频面试题 版本升级后 API 全变了,这是无数 Java 后端在维护老项目或面试时踩过的最大坑。很多人盯着 JSqlParser 的源码看半天,还是写不出一个能稳定运行的 SQL 解析器。… · 2026/9/23 2:06:04

茶叶叶片病害图像分类数据集实战:从数据清洗到模型训练全流程指南
茶叶叶片病害图像分类数据集实战:从数据清洗到模型训练全流程指南

简介:面向茶叶叶片病害识别与图像分类任务,这份数据集提供约4000张已标注的常规茶叶叶片病害图像,涵盖褐枯病、灰枯萎病、红点病等5个类别,适合农业病害检测、深度学习图像分类教学及科研实验。压缩包内已按训练集、验证集和测试集… · 2026/9/23 2:06:04

3个高频坑让mmkao环境崩溃,面试必问的排查思路
3个高频坑让mmkao环境崩溃,面试必问的排查思路

3个高频坑让mmkao环境崩溃,面试必问的排查思路 配置环境就卡半天,这是每个开发者刚接触新工具时的噩梦。你明明照着文档一步步操作,依赖装好了,命令也敲对了,结果一运行直接报错,或者功能完全跑不通。这种时候最让人抓狂,明明看着简单,为什么就… · 2026/9/23 2:06:04

Openship自托管部署平台:把Vercel体验搬到自己服务器
Openship自托管部署平台:把Vercel体验搬到自己服务器

1. 为什么我又折腾了一个自托管部署平台先说结论:Openship 这个项目,是我近半年来自托管折腾里最愿意推荐给朋友的一个。它想做的事情很直接——把 Vercel 那种“推代码就上线、自动构建、自动分配域名、自动签证书”的顺滑体验,整套搬到你自… · 2026/9/23 2:05:58

SpringBoot实战:特殊儿童家长教育平台开发与避坑指南
SpringBoot实战:特殊儿童家长教育平台开发与避坑指南

1. 这个平台到底在解决什么问题先说个我实际遇到的场景。去年有个朋友找我帮看一个SpringBoot毕设项目,题目就是"特殊儿童家长教育能力提升平台"这类。一开始我以为又是那种纯CRUD的管理系统,结果看了需求和设计之后,才意识到这个领… · 2026/9/23 2:05:58

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码