BitChat 是一种去中心化的点对点消息传递应用,旨在通过临时、专用网络实现安全、私密和抗审查的通信。它采用分层架构,结合了现代加密技术和灵活的应用协议,利用 Noise 协议框架建立相互认证的端到端加密会话,实现了身份管理、会话生命周期、消息帧和安全。
版本 1.1
日期:2025 年 7 月 25 日
BitChat 是一款去中心化、点对点消息应用,旨在通过临时的、自组织网络进行安全、私密且抗审查的通信。 本白皮书详细介绍了 BitChat 协议栈,这是一种分层架构,将现代密码学基础与灵活的应用程序协议相结合。 BitChat 的核心是利用 Noise 协议框架(特别是 XX
模式)在对等方之间建立相互认证的端到端加密会话。 本文档提供了身份管理、会话生命周期、消息成帧和安全注意事项的技术规范,这些规范是 BitChat 网络的基础。
在中心化通信平台时代,BitChat 通过在没有中心服务器的情况下运行,提供了一种具有弹性的替代方案。 它专为互联网连接不可用或不可信的场景而设计,例如抗议活动、自然灾害或偏远地区。 通信直接在设备之间通过蓝牙低功耗 (BLE) 等传输方式进行。
BitChat 协议的设计目标是:
本文档详细说明了为实现这些目标而设计的协议的技术细节。
BitChat 协议是一个四层栈。 这种分层方法分离了关注点,从而实现了模块化和未来的可扩展性。
graph TD
A[应用层] --> B[会话层];
B --> C[加密层];
C --> D[传输层];
subgraph "BitChat 应用"
A
end
subgraph "消息成帧 & 状态"
B
end
subgraph "Noise 协议框架"
C
end
subgraph "BLE, Wi-Fi Direct, etc."
D
end
style A fill:#cde4ff
style B fill:#b5d8ff
style C fill:#9ac2ff
style D fill:#7eadff
BitchatMessage
)、确认 (DeliveryAck
) 和其他应用层数据的结构。BitchatPacket
)。 这包括路由信息 (TTL)、消息类型、分片和序列化为紧凑的二进制格式。对等方在 BitChat 中的身份由两个持久的密码密钥对定义,这些密钥对在首次启动时生成并安全地存储在设备的 Keychain 中。
Curve25519
): 这是用于 Noise 协议握手的长期身份密钥。 该密钥的公共部分与对等方共享,以建立安全会话。Ed25519
): 此密钥用于签署公告和其他需要不可否认性的协议消息,例如将公钥绑定到昵称。用户唯一、可验证的指纹是其 Noise 静态公钥 的 SHA-256 哈希。 这提供了一种用户友好且安全的方式来带外验证身份(例如,通过大声朗读或扫描二维码)。
Fingerprint = SHA256(StaticPublicKey_Curve25519)
SecureIdentityStateManager
类负责管理所有密码学身份材料和社交元数据(昵称、信任级别等)。 它使用内存缓存来提高性能,并将此缓存持久化到 Keychain,在之后使用单独的 AES-GCM 密钥对其进行加密。
除了密码学身份之外,BitChat 还包含一个社交信任层,允许用户管理他们与对等方的关系。 此功能由 SecureIdentityStateManager
处理。
虽然 Noise 握手通过密码学验证了对等方的密钥,但它并未确认持有该设备的真人的真实身份。 为了解决这个问题,用户可以通过比较指纹来执行带外 (OOB) 验证。 一旦用户确认对等方的指纹与他们期望的指纹匹配,他们就可以将该对等方标记为“已验证”。 此状态本地存储并显示在 UI 中,为以后的对话提供了强大的身份保证。
为了改善用户体验并提供对交互的控制,该协议支持:
BitChat 实现了 Noise 协议框架,以提供强大的、经过身份验证的端到端加密。
实现的特定 Noise 协议是:
Noise_XX_25519_ChaChaPoly_SHA256
XX
模式: 此握手模式提供相互身份验证和前向保密。 它不需要任何一方在握手开始之前知道对方的静态公钥。 密钥在三部分握手期间交换和验证。 这对于去中心化的 P2P 环境来说是理想的。25519
: 使用的 Diffie-Hellman 函数是 Curve25519。ChaChaPoly
: AEAD(带有关联数据的经过身份验证的加密)密码是 ChaCha20-Poly1305。SHA256
: 用于所有密码学哈希运算的哈希函数是 SHA-256。XX
握手XX
握手包含启动者和响应者之间交换的三个消息,以建立共享密钥并派生传输加密密钥。
sequenceDiagram
participant I as 启动者
participant R as 响应者
Note over I, R: 预计算: h = SHA256(protocol_name)
I->>R: -> e
Note right of I: I 生成临时密钥 `e_i`.<br/>h = SHA256(h + e_i.pub)
R->>I: <- e, ee, s, es
Note left of R: R 生成临时密钥 `e_r`.<br/>h = SHA256(h + e_r.pub)<br/>MixKey(DH(e_i, e_r))<br/>R 发送静态密钥 `s_r`, 已加密.<br/>h = SHA256(h + ciphertext)<br/>MixKey(DH(e_i, s_r))
I->>R: -> s, se
Note right of I: I 解密并验证 `s_r`.<br/>I 发送静态密钥 `s_i`, 已加密.<br/>h = SHA256(h + ciphertext)<br/>MixKey(DH(s_i, e_r))
Note over I, R: 握手完成. 传输密钥已派生.
握手流程:
e_i
) 并将公共部分发送给响应者。e_r
),与启动者的临时密钥 (ee
) 执行 DH 交换,发送自己的静态公钥 (s_r
) 并使用生成的对称密钥对其进行加密,并在启动者的临时密钥和自己的静态密钥 (es
) 之间执行另一次 DH 交换。s_i
) 并对其进行加密,并在其静态密钥和响应者的临时密钥 (se
) 之间执行最终 DH 交换。完成后,双方共享一组对称密钥,用于双向传输消息加密。 最终的握手哈希用于通道绑定。
NoiseSessionManager
类管理所有活动的 Noise 会话。 它处理:
sendCipher
、receiveCipher
)。一旦 Noise 会话建立,对等方将交换 BitchatPacket
结构,这些结构被加密为 Noise 传输消息的有效负载。
BitchatPacket
)为了最大限度地减少带宽,BitchatPacket
被序列化为紧凑的二进制格式。 该结构设计为尽可能固定大小,以抵抗流量分析。
字段 | 大小(字节) | 描述 |
---|---|---|
标头 | 13 | 固定大小的标头 |
版本 | 1 | 协议版本(当前为 1 )。 |
类型 | 1 | 消息类型(例如,message ,deliveryAck ,noiseHandshakeInit )。 请参阅 MessageType 枚举。 |
TTL | 1 | 用于网状网络路由的生存时间。 在每个跃点处递减。 |
时间戳 | 8 | 数据包创建的 UInt64 毫秒时间戳。 |
标志 | 1 | 可选字段的位掩码(hasRecipient ,hasSignature ,isCompressed )。 |
有效负载长度 | 2 | 有效负载字段的 UInt16 长度。 |
变量 | ... | 可变大小的字段 |
发送方 ID | 8 | 发送方的 8 字节截断对等方 ID。 |
接收方 ID | 8 (可选) | 接收方的 8 字节截断对等方 ID。 如果设置了 hasRecipient 标志,则显示。 如果为 0xFF..FF ,则广播。 |
有效负载 | 变量 | 数据包的实际内容,如 Type 字段所定义。 |
签名 | 64(可选) | 数据包的 Ed25519 签名。 如果设置了 hasSignature 标志,则显示。 |
填充: 所有数据包都使用 PKCS#7 样式方案填充到下一个标准块大小(256、512、1024 或 2048 字节),以模糊网络观察员的真实消息长度。
BitchatMessage
)对于类型为 message
的数据包,有效负载是包含聊天内容的二进制序列化 BitchatMessage
。
字段 | 大小(字节) | 描述 |
---|---|---|
标志 | 1 | 可选字段的位掩码(isRelay ,isPrivate ,hasOriginalSender )。 |
时间戳 | 8 | 消息创建的 UInt64 毫秒时间戳。 |
ID | 1 + len | 消息的 UUID 字符串。 |
发送方 | 1 + len | 发送方的昵称。 |
内容 | 2 + len | UTF-8 编码的消息内容。 |
原始发送方 | 1 + len (opt) | 如果消息是中继,则为原始发送方的昵称。 |
接收方昵称 | 1 + len (opt) | 私有消息的接收方昵称。 |
BitChat 作为去中心化的网状网络运行,这意味着没有中央服务器来路由消息。 数据包通过网络从对等方传播到对等方。 该协议支持多种消息传递模式。
这是最简单的情况。 如果对等方 A 和对等方 B 直接连接,他们可以在建立相互验证的 Noise 会话后交换数据包。 所有数据包都使用从握手派生的传输密码进行加密。
为了将消息发送到未直接连接的对等方,BitChat 采用“泛洪”或“gossip”协议。 当对等方收到未发往它的数据包时,它将充当一个中继。为了防止无限路由循环并最大程度地减少内存使用量,该协议使用 OptimizedBloomFilter
来跟踪最近看到的 数据包 ID。
逻辑如下:
这种机制允许数据包有效地“泛洪”通过网络,从而最大限度地提高了到达其目的地的机会,同时使用最少的资源来防止循环。
每个 BitchatPacket
都包含一个 8 位 TTL 字段。 该值由始发对等方设置,并在每个中继跃点处减一。 如果对等方收到一个数据包并将其 TTL 减少到 0,它将处理该数据包(如果它是接收方),但不会进一步中继它。 这是防止数据包在网状网络中无休止地循环的关键机制。
路由逻辑尊重私有消息的机密性:
recipientID
的数据包是私有消息。 中继节点转发整个加密的 Noise 消息,而无法访问内部 BitchatPacket
或其有效负载。 只有与发送方共享正确 Noise 会话密钥的最终接收方才能解密该数据包。recipientID
(0xFFFFFFFFFFFFFFFF
) 的数据包旨在发送给所有对等方。 任何接收并解密广播消息的对等方都将处理其内容。 它仍将根据泛洪算法进行中继,以确保其到达整个网络。为了在不可靠的有损网络中运行,该协议包括用于跟踪消息生命周期并确保其传递的功能。
DeliveryAck
): 当私有消息到达其最终目的地时,接收方的设备会将 DeliveryAck
数据包发回给原始发送方。 此确认包含原始消息的 ID。ReadReceipt
): 在消息显示在接收方的屏幕上之后,应用程序可以发送 ReadReceipt
,其中还包含原始消息 ID,以通知发送方该消息已被查看。MessageRetryService
,该服务跟踪传出消息。 如果在特定时间窗口内未收到消息的 DeliveryAck
,该服务将自动重新发送该消息,从而创建更具弹性的用户体验。传输层(如 BLE)具有最大传输单元 (MTU),该单元限制单个数据包的大小。 为了处理大于此限制的消息,BitChat 实现了分片协议。
fragmentStart
: 类型为此的数据包标记分片消息的开头。 它包含有关总大小和分片数量的元数据。fragmentContinue
: 这些数据包携带消息数据的中间块。fragmentEnd
: 此数据包携带消息的最终块,并指示接收方开始重新组装。接收对等方收集所有分片,然后按正确的顺序重新组装,然后将完整消息传递到应用程序层。
NoiseCipherState
实现了一个滑动窗口重放保护机制,以检测和丢弃重放的或乱序的消息。NoiseRateLimiter
,以防止来自单个对等方的快速、重复的握手尝试导致资源耗尽。XX
模式验证双方的身份,从而防止攻击者将一方冒充为另一方。BitChat 协议为去中心化的点对点通信提供了强大而安全的基础。 通过在备受推崇的 Noise 协议框架之上分层一个灵活的应用程序协议,它可以实现强大的保密性、身份验证和前向保密。 紧凑的二进制格式和周到的安全注意事项(例如速率限制和流量分析抵抗)使其适合在具有挑战性的网络环境中使用。
- 原文链接: github.com/permissionles...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!