此翻译是使用机器学习生成的,可能不是100%准确。 查看英文版本

NTCP 2

Proposal 111
已关闭
Author EinMByte, orignal, psi, str4d, zzz
Created 2014-02-13
Last Updated 2019-08-13
Target Version 0.9.36
Implemented In 0.9.36
Supercedes: 106

注意

提案阶段已结束。
请参阅 SPEC 获取官方规范。
本提案仍可作为背景信息参考。

概述

本提案描述了一种认证密钥协商协议,以提高 NTCP 抵抗各种形式的自动化识别和攻击的能力。

提案结构如下:首先介绍安全目标,然后讨论基本协议。接着给出所有协议消息的完整规范。最后讨论路由器地址和版本识别。附录中还包含对常见填充方案的通用攻击分析,以及一组经认证密码候选方案。

与其它 I2P 传输方式一样,NTCP2 仅定义用于 I2NP 消息的点对点(路由器到路由器)传输。
它不是通用数据管道。

动机

NTCP 数据在第一条消息之后即被加密(第一条消息看似随机数据),从而防止通过“载荷分析”进行协议识别。
但仍易受“流量分析”识别。这是因为前 4 条消息(即握手阶段)长度固定(分别为 288、304、448 和 48 字节)。

通过在每条消息中添加随机数量的随机数据,可以大大增加识别难度。

作者承认,标准安全实践建议使用现有协议如 TLS,但这是 Prop104 的范畴,且其自身存在问题。
在适当位置已添加“未来工作”段落,以指出缺失功能或讨论主题。

设计目标

  • 在单个端口上同时支持 NTCP 1 和 2,自动检测,并在 NetDB 中作为单一“传输”(即 RouterAddress)发布。

  • 在 NetDB 中通过独立字段发布仅支持版本 1、仅支持版本 2 或同时支持 1+2 的信息,默认仅支持版本 1(不将版本支持绑定到特定路由器版本)。

  • 确保所有实现(Java/i2pd/Kovri/go)可按各自进度独立添加版本 2 支持(或不添加)。

  • 为所有 NTCP 消息(包括握手和数据消息)添加随机填充(即长度混淆,使所有消息长度不为 16 字节的倍数)。提供选项机制,允许双方请求最小和最大填充量和/或填充分布。填充分布的具体细节依赖于实现,可能在协议本身中指定,也可能不指定。

  • 混淆未加密消息(1 和 2)的内容,使其足以防止 DPI 设备和杀毒软件签名轻易分类。同时确保发往单个对等方或一组对等方的消息不具有相似的比特模式。

  • 修复由于 Java 格式导致的 DH 位丢失问题 Ticket1112 ,可能(很可能)通过切换到 X25519 实现。

  • 切换到真正的密钥派生函数(KDF),而不是直接使用 DH 结果?

  • 添加“探测抵抗”(如 Tor 所称);这包括重放抵抗。

  • 维持双向认证密钥交换(2W-AKE)。单向认证密钥交换(1W-AKE)对我们的应用而言不足够。

  • 继续使用可变类型、可变长度的签名(来自发布的 RouterIdentity 签名密钥)作为认证的一部分。依赖发布在 RouterInfo 中的静态公钥作为认证的另一部分。

  • 在握手中添加选项/版本字段以支持未来扩展。

  • 尽可能增加对恶意 MitM TCP 分段的抵抗能力。

  • 不显著增加连接建立所需的 CPU 开销;如果可能,显著降低。

  • 添加消息认证(MAC),可能使用 HMAC-SHA256 和 Poly1305,并移除 Adler 校验和。

  • 缩短并简化 I2NP 头部:
    将过期时间缩短为 4 字节,如 SSU 所做。
    移除一个字节的截断 SHA256 校验和。

  • 如果可能,将 4 消息、两轮往返的握手缩短为 3 消息、一轮往返的握手,如 SSU 所做。这需要将 Bob 在消息 4 中的签名移到消息 2。研究十年前电子邮件/状态/会议档案中为何使用 4 消息的原因。

  • 最小化填充前的协议开销。虽然将添加填充,可能大量添加,但填充前的开销仍然是开销。低带宽节点必须能够使用 NTCP2。

  • 维持时间戳以进行重放和时钟偏移检测。

  • 避免时间戳中的 2038 年问题,必须至少工作到 2106 年。

  • 将最大消息大小从 16K 增加到 32K 或 64K。

  • 任何新的加密原语都应在 Java(1.7)、C++ 和 Go 路由器实现可用的库中广泛可用。

  • 在设计中包含 Java、C++ 和 Go 路由器开发者的代表。

  • 最小化变更(但仍会有大量变更)。

  • 在一组通用代码中支持两个版本(这可能无法实现,且依赖于实现)。

非目标

  • 完全防 DPI……那将是可插拔传输,Prop109

  • 基于 TLS(或类似 HTTPS)的传输……那将是 Prop104

  • 更改对称流加密是允许的。

  • 基于时间的 DPI 抵抗(消息间定时/延迟可依赖于实现;消息内延迟可在任何点引入,例如在发送随机填充之前)。人工延迟(obfs4 称为 IAT 或到达间隔时间)独立于协议本身。

  • 参与会话的可否认性(其中有签名)。

可能部分重新考虑或讨论的非目标:

  • 对深度包检测(DPI)的保护程度

  • 抗量子(PQ)安全

  • 可否认性

相关目标

  • 实现 NTCP 1/2 测试设置

安全目标

我们考虑三方:

  • Alice,希望建立新会话。
  • Bob,Alice 希望建立会话的对象。
  • Mallory,Alice 和 Bob 之间的“中间人”。

最多两个参与者可进行主动攻击。

Alice 和 Bob 均拥有一个静态密钥对,包含在其 RouterIdentity 中。

提议的协议试图允许 Alice 和 Bob 在以下要求下协商共享密钥(K):

  1. 私钥安全:Bob 或 Mallory 无法获知 Alice 的静态私钥。对称地,Alice 无法获知 Bob 的静态私钥。

  2. 会话密钥 K 仅由 Alice 和 Bob 知晓。

  3. 完美前向保密:即使 Alice 和/或 Bob 的静态私钥在密钥协商后被泄露,协商的会话密钥在未来仍保持秘密。

  4. 双向认证:Alice 确信她已与 Bob 建立会话,反之亦然。

  5. 抵抗在线 DPI:确保仅使用直接的深度包检测(DPI)技术无法轻易检测到 Alice 和 Bob 正在进行该协议。见下文。

  6. 有限可否认性:Alice 和 Bob 均无法否认参与了协议,但如果任一方泄露了共享密钥,另一方可以否认所传输数据内容的真实性。

本提案试图基于 Station-To-Station(STS)协议提供所有五项要求。注意,该协议也是 SSU 协议的基础。

额外 DPI 讨论

我们假设两种 DPI 组件:

1) 在线 DPI

在线 DPI 实时检查所有流量。连接可能被阻断或以其他方式篡改。连接数据或元数据可能被识别并存储以供离线分析。在线 DPI 无法访问 I2P 网络数据库。在线 DPI 仅有有限的实时计算能力,包括长度计算、字段检查和简单计算如 XOR。在线 DPI 具备快速实时加密功能如 AES、AEAD 和哈希的能力,但这些功能应用于大多数或所有流量成本过高。任何这些加密操作的应用仅限于之前通过离线分析识别的 IP/端口组合的流量。在线 DPI 无法使用高开销加密功能如 DH 或 elligator2。在线 DPI 并非专门设计用于检测 I2P,尽管可能有有限的分类规则用于此目的。

目标是防止在线 DPI 识别协议。

此处“在线”或“直接”DPI 的概念包括以下对手能力:

  1. 检查目标发送或接收的所有数据的能力。

  2. 对观察到的数据执行操作的能力,例如应用分组密码或哈希函数。

  3. 存储和与先前发送的消息比较的能力。

  4. 修改、延迟或分段数据包的能力。

然而,假设在线 DPI 有以下限制:

  1. 无法将 IP 地址映射到路由器哈希。虽然实时访问网络数据库时这很简单,但这需要专门设计用于针对 I2P 的 DPI 系统。

  2. 无法使用时间信息检测协议。

  3. 总的来说,在线 DPI 工具箱不包含任何专门设计用于 I2P 检测的内置工具。这包括创建“蜜罐”,例如在其消息中包含非随机填充。注意,只要满足其他要求,这不排除机器学习系统或高度可配置的 DPI 工具。

为对抗载荷分析,确保所有消息与随机数据无法区分。这也要求其长度随机,这比仅添加随机填充更复杂。事实上,在附录 A 中,作者认为简单的(即均匀的)填充方案无法解决问题。因此附录 A 建议包含随机延迟或开发一种替代填充方案,以提供对提议攻击的合理保护。

为防止上述第六点,实现应包含协议中的随机延迟。此类技术不在本提案范围内,但它们也可能解决填充长度问题。总之,当考虑附录 A 中的建议时,该提案对载荷分析提供了良好保护,但对流量分析的保护有限。

2) 离线 DPI

离线 DPI 检查在线 DPI 存储的数据以供后续分析。离线 DPI 可能专门设计用于检测 I2P。离线 DPI 具有对 I2P 网络数据库的实时访问权限。离线 DPI 可访问本提案及其他 I2P 规范。离线 DPI 具有无限计算能力,包括本规范中定义的所有加密功能。

离线 DPI 无法阻断现有连接。离线 DPI 具备在连接建立后几分钟内向主机/端口发送数据的能力,例如 TCP RST。离线 DPI 具备在连接建立后几分钟内重放先前消息(修改或未修改)以进行“探测”或其他目的的能力。

目标不是防止离线 DPI 识别协议。在前两条消息中由 I2P 路由器实现的混淆数据解码,也可能由离线 DPI 实现。

目标是拒绝使用重放先前消息的连接尝试。

未来工作

  • 考虑当数据包被攻击者丢弃或重新排序时协议的行为。该领域最近的有趣工作可在 IACR-1150 找到。

  • 提供对 DPI 系统更准确的分类,考虑与该主题相关的现有文献。

  • 讨论提议协议的形式化安全,理想情况下考虑 DPI 攻击者模型。

噪声协议框架

本提案基于噪声协议框架 NOISE (修订版 33,2017-10-04)提供要求。噪声具有与 Station-To-Station 协议类似的属性,后者是 SSU 协议的基础。在噪声术语中,Alice 是发起者,Bob 是响应者。

NTCP2 基于噪声协议 Noise_XK_25519_ChaChaPoly_SHA256。
(初始密钥派生函数的实际标识符为 “Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256”,以指示 I2P 扩展——见下文 KDF 1 部分)
该噪声协议使用以下原语:

  • 握手模式:XK
    Alice 向 Bob 传输她的密钥(X)
    Alice 已知 Bob 的静态密钥(K)

  • DH 函数:X25519
    X25519 DH,密钥长度为 32 字节,如 RFC-7748 所规定。

  • 密码函数:ChaChaPoly
    AEAD_CHACHA20_POLY1305,如 RFC-7539 第 2.8 节所规定。
    12 字节 nonce,前 4 字节设为零。

  • 哈希函数:SHA256
    标准 32 字节哈希,在 I2P 中已广泛使用。

对框架的增强

本提案定义了对 Noise_XK_25519_ChaChaPoly_SHA256 的以下增强。这些通常遵循 NOISE 第 13 节中的指南。

  1. 明文临时密钥使用已知密钥和 IV 的 AES 加密进行混淆。这比 elligator2 更快。

  2. 向消息 1 和 2 添加随机明文填充。
    明文填充包含在握手哈希(MixHash)计算中。
    有关消息 2 和消息 3 第一部分的 KDF,请参见下文。
    向消息 3 和数据阶段消息添加随机 AEAD 填充。

  3. 添加两字节帧长度字段,这是噪声在 TCP 上运行所必需的,如 obfs4 所做。这仅用于数据阶段消息。
    消息 1 和 2 的 AEAD 帧长度固定。
    消息 3 第一部分的 AEAD 帧长度固定。
    消息 3 第二部分的 AEAD 帧长度在消息 1 中指定。

  4. 两字节帧长度字段使用 SipHash-2-4 进行混淆,如 obfs4 所做。

  5. 定义了消息 1、2、3 和数据阶段的有效载荷格式。
    当然,噪声中未定义此格式。

I2P 的新加密原语

现有 I2P 路由器实现将需要以下标准加密原语的实现,这些原语在当前 I2P 协议中不需要:

  1. X25519 密钥生成和 DH

  2. AEAD_ChaCha20_Poly1305(下文简称为 ChaChaPoly)

  3. SipHash-2-4

处理开销估计

三条消息的消息大小:

  1. 64 字节 + 填充(NTCP 为 288 字节)
  2. 64 字节 + 填充(NTCP 为 304 字节)
  3. 约 64 字节 + Alice 路由器信息 + 填充 平均路由器信息约 750 字节
    填充前总平均 814 字节(NTCP 为 448 字节)
  4. NTCP2 中不需要(NTCP 为 48 字节)

填充前总计:
NTCP2:942 字节
NTCP:1088 字节
注意,如果 Alice 连接 Bob 的目的是发送她的 RouterInfo 的 DatabaseStore 消息,则该消息不需要,节省约 800 字节。

为完成握手并启动数据阶段,各方需要以下加密操作:

  • AES:2
  • SHA256:7(Alice),6(Bob)(不包括 1 Alice,2 Bob 为所有连接预计算)(不包括 HMAC-SHA256)
  • HMAC-SHA256:19
  • ChaChaPoly:4
  • X25519 密钥生成:1
  • X25519 DH:3
  • 签名验证:1(Bob)(Alice 之前在生成其 RI 时已签名)
    可能为 Ed25519(依赖于 RI 签名类型)

对于每个数据阶段消息,各方需要以下加密操作:

  • SipHash:1
  • ChaChaPoly:1

消息

所有 NTCP2 消息长度小于或等于 65537 字节。消息格式基于噪声消息,但进行了帧和不可区分性的修改。使用标准噪声库的实现可能需要预处理接收/发送的消息以/从噪声消息格式转换。所有加密字段均为 AEAD 密文。

建立序列为:

Alice                           Bob

  SessionRequest ------------------->
  <------------------- SessionCreated
  SessionConfirmed ----------------->

使用噪声术语,建立和数据序列为:(载荷安全属性)

XK(s, rs):           认证   保密性
    <- s
    ...
    -> e, es                  0                2
    <- e, ee                  2                1
    -> s, se                  2                5
    <-                        2                5

会话建立后,Alice 和 Bob 可交换数据消息。

所有消息类型(SessionRequest、SessionCreated、SessionConfirmed、Data 和 TimeSync)均在本节中指定。

一些符号:

  • RH_A = Alice 的路由器哈希(32 字节)
  • RH_B = Bob 的路由器哈希(32 字节)

认证加密

有三个独立的认证加密实例(CipherStates)。一个在握手阶段,两个(发送和接收)在数据阶段。每个都有来自 KDF 的独立密钥。

加密/认证数据表示为

+----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |   加密和认证的数据                    |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

ChaCha20/Poly1305

加密和认证数据格式。

加密/解密函数的输入:

k :: 32 字节密码密钥,由 KDF 生成

  nonce :: 基于计数器的 nonce,12 字节。
           从 0 开始,每条消息递增。
           前四个字节始终为零。
           最后八个字节为计数器,小端编码。
           最大值为 2**64 - 2。
           达到该值后必须断开并重启连接。
           永远不要发送值 2**64 - 1。

  ad :: 在握手阶段:
        关联数据,32 字节。
        所有先前数据的 SHA256 哈希。
        在数据阶段:
        零字节

  data :: 明文数据,0 或更多字节

加密函数的输出,解密函数的输入:

+----+----+----+----+----+----+----+----+
  |Obfs Len |                             |
  +----+----+                             +
  |       ChaCha20 加密数据               |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+
  |  Poly1305 消息认证码 (MAC)            |
  +                                       +
  |             16 字节                   |
  +----+----+----+----+----+----+----+----+

  Obfs Len :: 后续(加密数据 + MAC)的长度,16 - 65535
              使用 SipHash 混淆(见下文)
              在消息 1 或 2,或消息 3 第一部分中不使用,因为长度固定
              在消息 3 第一部分中不使用,因为长度在消息 1 中指定

  encrypted data :: 与明文数据大小相同,0 - 65519 字节

  MAC :: Poly1305 消息认证码,16 字节

对于 ChaCha20,此处描述的内容对应于 RFC-7539 ,在 TLS RFC-7905 中也类似使用。

注释

  • 由于 ChaCha20 是流密码,明文无需填充。
    额外的密钥流字节被丢弃。

  • 密码的密钥(256 位)通过 SHA256 KDF 协商。
    每条消息的 KDF 详细信息在下文单独章节中。

  • 消息 1、2 和消息 3 第一部分的 ChaChaPoly 帧大小已知。
    从消息 3 第二部分开始,帧大小可变。
    消息 3 第一部分的大小在消息 1 中指定。
    从数据阶段开始,帧前缀为两字节长度,使用 SipHash 混淆,如 obfs4 所做。

  • 消息 1 和 2 的填充在认证数据帧之外。
    填充用于下一条消息的 KDF,因此篡改将被检测到。
    从消息 3 开始,填充在认证数据帧内。

AEAD 错误处理

  • 在消息 1、2 和消息 3 的第 1 和 2 部分中,AEAD 消息大小已知。
    在 AEAD 认证失败时,接收方必须停止进一步消息处理并关闭连接而不响应。
    这应为异常关闭(TCP RST)。

  • 为实现探测抵抗,在消息 1 中,AEAD 失败后,Bob 应设置随机超时(范围待定)然后读取随机字节数(范围待定)再关闭套接字。
    Bob 应维护一个重复失败的 IP 黑名单。

  • 在数据阶段,AEAD 消息大小使用 SipHash “加密”(混淆)。
    必须小心避免创建解密预言机。
    在数据阶段 AEAD 认证失败时,接收方应设置随机超时(范围待定)然后读取随机字节数(范围待定)。
    读取后或读取超时后,接收方应发送包含“AEAD 失败”原因码的终止块的有效载荷,并关闭连接。

  • 对数据阶段中无效长度字段值采取相同的错误操作。

密钥派生函数(KDF)(用于握手消息 1)

KDF 使用 HMAC-SHA256(key, data)(如 RFC-2104 所定义)从 DH 结果生成握手阶段密码密钥 k。
这些是 InitializeSymmetric()、MixHash() 和 MixKey() 函数,与噪声规范中定义的完全相同。

这是“e”消息模式:

  // 定义 protocol_name。
  Set protocol_name = "Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256"
   (48 字节,US-ASCII 编码,无 NULL 终止)。

  // 定义 Hash h = 32 字节
  h = SHA256(protocol_name);

  定义 ck = 32 字节链式密钥。将 h 数据复制到 ck。
  Set ck = h

  定义 rs = Bob 在 RouterInfo 中发布的 32 字节静态密钥

  // MixHash(null prologue)
  h = SHA256(h);

  // 到此为止,Alice 可为所有出站连接预计算

  // Alice 必须在此处验证 Bob 的静态密钥是曲线上的有效点。

  // Bob 静态密钥
  // MixHash(rs)
  // || 以下表示追加
  h = SHA256(h || rs);

  // 到此为止,Bob 可为所有入站连接预计算

  这是“e”消息模式:

  Alice 生成她的临时 DH 密钥对 e。

  // Alice 临时密钥 X
  // MixHash(e.pubkey)
  // || 以下表示追加
  h = SHA256(h || e.pubkey);

  // h 用作消息 1 中 AEAD 的关联数据
  // 为消息 2 KDF 保留 Hash h


  “e”消息模式结束。

  这是“es”消息模式:

  // DH(e, rs) == DH(s, re)
  定义 input_key_material = 32 字节 DH 结果,Alice 的临时密钥和 Bob 的静态密钥
  Set input_key_material = X25519 DH 结果

  // MixKey(DH())

  定义 temp_key = 32 字节
  定义 HMAC-SHA256(key, data) 如 [RFC-2104](https://tools.ietf.org/html/rfc2104)
  // 从链式密钥和 DH 结果生成临时密钥
  // ck 是链式密钥,如上定义
  temp_key = HMAC-SHA256(ck, input_key_material)
  // 覆盖内存中的 DH 结果,不再需要
  input_key_material = (全零)

  // 输出 1
  // 从临时密钥设置新链式密钥
  // byte() 以下表示单字节
  ck =       HMAC-SHA256(temp_key, byte(0x01)).

  // 输出 2
  // 生成密码密钥 k
  定义 k = 32 字节
  // || 以下表示追加
  // byte() 以下表示单字节
  k =        HMAC-SHA256(temp_key, ck || byte(0x02)).
  // 覆盖内存中的 temp_key,不再需要
  temp_key = (全零)

  // 为消息 2 KDF 保留链式密钥 ck


  “es”消息模式结束。

1) SessionRequest

Alice 发送给 Bob。

噪声内容:Alice 的临时密钥 X
噪声载荷:16 字节选项块
非噪声载荷:随机填充

(载荷安全属性)

XK(s, rs):           认证   保密性
    -> e, es                  0                2

    认证:无(0)。
    此载荷可能由任何方发送,包括主动攻击者。

    保密性:2。
    加密到已知接收方,仅发送方妥协时具有前向保密,易受重放攻击。  
    此载荷仅基于涉及接收方静态密钥对的 DHs 加密。  
    如果接收方的静态私钥被泄露,即使在将来,此载荷也可被解密。  
    此消息也可被重放,因为接收方没有临时贡献。

    “e”:Alice 生成新的临时密钥对并存储在 e 变量中,将临时公钥以明文写入消息缓冲区,并将公钥与旧 h 哈希以派生新 h。

    “es”:在 Alice 的临时密钥对和 Bob 的静态密钥对之间执行 DH。  
          结果与旧 ck 哈希以派生新 ck 和 k,n 设为零。

X 值被加密以确保载荷不可区分性和唯一性,这是必要的 DPI 对策。
我们使用 AES 加密实现此目的,而不是更复杂和更慢的替代方案如 elligator2。
使用 Bob 的路由器公钥进行非对称加密会太慢。
AES 加密使用 Bob 的路由器哈希作为密钥,使用网络数据库中发布的 Bob 的 IV。

AES 加密仅用于 DPI 抵抗。
任何知道 Bob 的路由器哈希和 IV(在网络数据库中发布)的方都可能解密此消息中的 X 值。

填充未被 Alice 加密。
Bob 可能需要解密填充以抑制定时攻击。

原始内容:

+----+----+----+----+----+----+----+----+
|                                       |
+        使用 RH_B 混淆                 +
|       AES-CBC-256 加密的 X            |
+             (32 字节)                 +
|                                       |
+                                       +
|                                       |
+----+----+----+----+----+----+----+----+
|                                       |
+                                       +
|   ChaChaPoly 帧                       |
+             (32 字节)                 +
|   k 在消息 1 的 KDF 中定义            |
+   n = 0                               +
|   见 KDF 中的关联数据                 |
+----+----+----+----+----+----+----+----+
|     未加密的认证                      |
~         填充 (可选)                   ~
|     长度在选项块中定义                |
+----+----+----+----+----+----+----+----+

X :: 32 字节,AES-256-CBC 加密的 X25519 临时密钥,小端
        密钥:RH_B
        iv:如在 Bob 的网络数据库条目中发布

padding :: 随机数据,0 或更多字节。
           总消息长度必须为 65535 字节或更少。
           如果 Bob 将其地址发布为 NTCP,则总消息长度必须为 287 字节或更少
           (见下文版本检测部分)。
           Alice 和 Bob 将在消息 2 的 KDF 中使用填充数据。
           它经过认证,因此任何篡改将导致下一条消息失败。

未加密数据(Poly1305 认证标签未显示):

+----+----+----+----+----+----+----+----+
|                                       |
+                                       +
|                   X                   |
+              (32 字节)                +
|                                       |
+                                       +
|                                       |
+----+----+----+----+----+----+----+----+
|               options                 |
+              (16 字节)                +
|                                       |
+----+----+----+----+----+----+----+----+
|     未加密的认证                      |
+         填充 (可选)                   +
|     长度在选项块中定义                |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+

X :: 32 字节,X25519 临时密钥,小端

options :: 选项块,16 字节,见下文

padding :: 随机数据,0 或更多字节。
           总消息长度必须为 65535 字节或更少。
           如果 Bob 将其地址发布为 "NTCP",则总消息长度必须为 287 字节或更少
           (见下文版本检测部分)
           Alice 和 Bob 将在消息 2 的 KDF 中使用填充数据。
           它经过认证,因此任何篡改将导致下一条消息失败。

选项块:
注意:所有字段均为大端。

+----+----+----+----+----+----+----+----+
| id | ver|  padLen | m3p2len | Rsvd(0) |
+----+----+----+----+----+----+----+----+
|        tsA        |   Reserved (0)    |
+----+----+----+----+----+----+----+----+

id :: 1 字节,网络 ID(目前为 2,测试网络除外)
      从 0.9.42 开始。见提案 147。

ver :: 1 字节,协议版本(目前为 2)

padLen :: 2 字节,填充长度,0 或更多
          最小/最大指南待定。最小随机大小从 0 到 31 字节?
          (分布待定,见附录 A。)

m3p2Len :: 2 字节,SessionConfirmed 中第二 AEAD 帧的长度
           (消息 3 第二部分)见下文注释

Rsvd :: 2 字节,设为 0 以兼容未来选项

tsA :: 4 字节,Unix 时间戳,无符号秒。
       2106 年回绕

Reserved :: 4 字节,设为 0 以兼容未来选项

注释

  • 当发布的地址为 “NTCP” 时,Bob 在同一端口上支持 NTCP 和 NTCP2。
    为兼容性,当向发布为 “NTCP” 的地址发起连接时,Alice 必须将此消息的最大大小(包括填充)限制为 287 字节或更少。
    这有助于 Bob 自动识别协议。
    当发布为 “NTCP2” 时,无大小限制。
    见下文发布的地址和版本检测部分。

  • 初始 AES 块中的唯一 X 值确保每个会话的密文不同。

  • Bob 必须拒绝时间戳值与当前时间相差太远的连接。
    称最大时间差为 “D”。
    Bob 必须维护一个先前使用的握手值的本地缓存并拒绝重复值,以防止重放攻击。
    缓存值的生命周期必须至少为 2*D。
    缓存值依赖于实现,但可使用 32 字节 X 值(或其加密等效值)。

  • 为防止密码攻击,Diffie-Hellman 临时密钥绝不能重用,重用将被拒绝为重放攻击。

  • “KE”和“auth”选项必须兼容,即共享密钥 K 必须具有适当大小。
    如果添加更多“auth”选项,这可能隐式改变“KE”标志的含义,以使用不同的 KDF 或不同的截断大小。

  • Bob 必须在此处验证 Alice 的临时密钥是曲线上的有效点。

  • 填充应限制在合理范围内。
    Bob 可拒绝具有过多填充的连接。
    Bob 将在消息 2 中指定其填充选项。
    最小/最大指南待定。最小随机大小从 0 到 31 字节? (分布待定,见附录 A。)

  • 在任何错误时,包括 AEAD、DH、时间戳、明显重放或密钥验证失败,Bob 必须停止进一步消息处理并关闭连接而不响应。
    这应为异常关闭(TCP RST)。
    为实现探测抵抗,AEAD 失败后,Bob 应设置随机超时(范围待定)然后读取随机字节数(范围待定),再关闭套接字。

  • DoS 缓解:DH 是相对昂贵的操作。与之前的 NTCP 协议一样,路由器应采取所有必要措施防止 CPU 或连接耗尽。
    限制最大活动连接数和最大连接建立进行数。
    强制执行读取超时(每次读取和“慢龙”总计)。
    限制来自同一源的重复或同时连接。
    维护反复失败源的黑名单。
    不响应 AEAD 失败。

  • 为促进快速版本检测和握手,实现必须确保 Alice 缓冲然后一次性刷新第一条消息的全部内容,包括填充。
    这增加了数据包含在单个 TCP 数据包中的可能性(除非被操作系统或中间盒分段),并被 Bob 一次性接收。
    此外,实现必须确保 Bob 缓冲然后一次性刷新第二条消息的全部内容,包括填充,并且 Bob 缓冲然后一次性刷新第三条消息的全部内容。
    这也是为了效率并确保随机填充的有效性。

  • “ver”字段:整体噪声协议、扩展和 NTCP 协议(包括载荷规范),指示 NTCP2。
    此字段可用于指示对未来的更改的支持。

  • 消息 3 第二部分长度:这是将在 SessionConfirmed 消息中发送的第二 AEAD 帧的大小(包括 16 字节 MAC),包含 Alice 的 Router Info 和可选填充。
    由于路由器定期重新生成和重新发布其 Router Info,消息 3 发送前当前 Router Info 的大小可能改变。
    实现必须选择两种策略之一:
    a) 保存将发送到消息 3 的当前 Router Info,因此大小已知,并可选择为填充留出空间;
    b) 增加指定大小以允许 Router Info 大小的可能增加,并在实际发送消息 3 时始终添加填充。
    无论哪种情况,消息 1 中包含的“m3p2len”长度必须与消息 3 中发送的帧大小完全相同。

  • Bob 必须在验证消息 1 并读取填充后,如果仍有任何传入数据,则失败连接。
    不应有来自 Alice 的额外数据,因为 Bob 尚未用消息 2 响应。

  • 网络 ID 字段用于快速识别跨网络连接。
    如果此字段非零且不匹配 Bob 的网络 ID,Bob 应断开连接并阻止未来连接。
    从 0.9.42 开始。更多信息见提案 147。

密钥派生函数(KDF)(用于握手消息 2 和消息 3 第一部分)


// 从消息 1 KDF 保存 h
// MixHash(ciphertext)
h = SHA256(h || 消息 1 的 32 字节加密载荷)

// MixHash(padding)
// 仅当填充长度非零时
h = SHA256(h || 消息 1 的随机填充)

这是“e”消息模式:

Bob 生成他的临时 DH 密钥对 e。

// h 来自握手消息 1 的 KDF
// Bob 临时密钥 Y
// MixHash(e.pubkey)
// || 以下表示追加
h = SHA256(h || e.pubkey);

// h 用作消息 2 中 AEAD 的关联数据
// 为消息 3 KDF 保留 Hash h

“e”消息模式结束。

这是“ee”消息模式:

// DH(e, re)
定义 input_key_material = 32 字节 DH 结果,Alice 的临时密钥和 Bob 的临时密钥
设置 input_key_material = X25519 DH 结果
// 覆盖内存中的 Alice 临时密钥,不再需要
// Alice:
e(公钥和私钥) = (全零)
// Bob:
re = (全零)

// MixKey(DH())

定义 temp_key = 32 字节
定义 HMAC-SHA256(key, data) 如 [RFC-2104](https://tools.ietf.org/html/rfc2104)
// 从链式密钥和 DH 结果生成临时密钥
// ck 是来自握手消息 1 的 KDF 的链式密钥
temp_key = HMAC-SHA256(ck, input_key_material)
// 覆盖内存中的 DH 结果,不再需要
input_key_material = (全零)

// 输出 1
// 从临时密钥设置新链式密钥
// byte() 以下表示单字节
ck =       HMAC-SHA256(temp_key, byte(0x01)).

// 输出 2
// 生成密码密钥 k
定义 k = 32 字节
// || 以下表示追加
// byte() 以下表示单字节
k =        HMAC-SHA256(temp_key, ck || byte(0x02)).
// 覆盖内存中的 temp_key,不再需要
temp_key = (全零)

// 为消息 3 KDF 保留链式密钥 ck

“ee”消息模式结束。

2) SessionCreated

Bob 发送给 Alice。

噪声内容:Bob 的临时密钥 Y
噪声载荷:16 字节选项块
非噪声载荷:随机填充

(载荷安全属性)

XK(s, rs):           认证   保密性
  <- e, ee                  2                1

  认证:2。
  发送方认证抵抗密钥泄露冒充(KCI)。
  发送方认证基于发送方静态密钥对和接收方临时密钥对之间的临时-静态 DH(“es”或“se”)。
  假设相应私钥安全,此认证无法伪造。

  保密性:1。
  加密到临时接收方。
  此载荷具有前向保密,因为加密涉及临时-临时 DH(“ee”)。
  然而,发送方尚未认证接收方,
  因此此载荷可能发送到任何方,包括主动攻击者。

  “e”:Bob 生成新的临时密钥对并存储在 e 变量中,
  将临时公钥以明文写入消息缓冲区,
  并将公钥与旧 h 哈希以派生新 h。

  “ee”:在 Bob 的临时密钥对和 Alice 的临时密钥对之间执行 DH。
  结果与旧 ck 哈希以派生新 ck 和 k,n 设为零。

Y 值被加密以确保载荷不可区分性和唯一性,这是必要的 DPI 对策。
我们使用 AES 加密实现此目的,而不是更复杂和更慢的替代方案如 elligator2。
使用 Alice 的路由器公钥进行非对称加密会太慢。
AES 加密使用 Bob 的路由器哈希作为密钥,使用消息 1 的 AES 状态(使用网络数据库中发布的 Bob 的 IV 初始化)。

AES 加密仅用于 DPI 抵抗。
任何知道 Bob 的路由器哈希和 IV(在 I2P 网络数据库中发布)并捕获了消息 1 前 32 字节的方,都可能解密此消息中的 Y 值。

原始内容:

+----+----+----+----+----+----+----+----+
|                                       |
+        使用 RH_B 混淆                 +
|       AES-CBC-256 加密的 Y            |
+              (32 字节)                +
|                                       |
+                                       +
|                                       |
+----+----+----+----+----+----+----+----+
|   ChaChaPoly 帧                       |
+   加密和认证的数据                    +
|   32 字节                             |
+   k 在消息 2 的 KDF 中定义            +
|   n = 0;见 KDF 中的关联数据          |
+                                       +
|                                       |
+----+----+----+----+----+----+----+----+
|     未加密的认证                      |
+         填充 (可选)                   +
|     长度在选项块中定义                |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+

Y :: 32 字节,AES-256-CBC 加密的 X25519 临时密钥,小端
        密钥:RH_B
        iv:使用消息 1 的 AES 状态

未加密数据(Poly1305 认证标签未显示):

+----+----+----+----+----+----+----+----+
|                                       |
+                                       +
|                  Y                    |
+              (32 字节)                +
|                                       |
+                                       +
|                                       |
+----+----+----+----+----+----+----+----+
|               options                 |
+              (16 字节)                +
|                                       |
+----+----+----+----+----+----+----+----+
|     未加密的认证                      |
+         填充 (可选)                   +
|     长度在选项块中定义                |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+

Y :: 32 字节,X25519 临时密钥,小端

options :: 选项块,16 字节,见下文

padding :: 随机数据,0 或更多字节。
           总消息长度必须为 65535 字节或更少。
           Alice 和 Bob 将在消息 3 第一部分的 KDF 中使用填充数据。
           它经过认证,因此任何篡改将导致下一条消息失败。

注释

  • Alice 必须在此处验证 Bob 的临时密钥是曲线上的有效点。

  • 填充应限制在合理范围内。
    Alice 可拒绝具有过多填充的连接。
    Alice 将在消息 3 中指定其填充选项。
    最小/最大指南待定。最小随机大小从 0 到 31 字节? (分布待定,见附录 A。)

  • 在任何错误时,包括 AEAD、DH、时间戳、明显重放或密钥验证失败,Alice 必须停止进一步消息处理并关闭连接而不响应。
    这应为异常关闭(TCP RST)。

  • 为促进快速握手,实现必须确保 Bob 缓冲然后一次性刷新第一条消息的全部内容,包括填充。
    这增加了数据包含在单个 TCP 数据包中的可能性(除非被操作系统或中间盒分段),并被 Alice 一次性接收。
    这也是为了效率并确保随机填充的有效性。

  • Alice 必须在验证消息 2 并读取填充后,如果仍有任何传入数据,则失败连接。
    不应有来自 Bob 的额外数据,因为 Alice 尚未用消息 3 响应。

选项块:
注意:所有字段均为大端。


+----+----+----+----+----+----+----+----+
| Rsvd(0) | padLen  |   Reserved (0)    |
+----+----+----+----+----+----+----+----+
|        tsB        |   Reserved (0)    |
+----+----+----+----+----+----+----+----+

Reserved :: 总共 10 字节,设为 0 以兼容未来选项

padLen :: 2 字节,大端,填充长度,0 或更多
          最小/最大指南待定。最小随机大小从 0 到 31 字节?
          (分布待定,见附录 A。)

tsB :: 4 字节,大端,Unix 时间戳,无符号秒。
       2106 年回绕

注释

  • Alice 必须拒绝时间戳值与当前时间相差太远的连接。
    称最大时间差为 “D”。
    Alice 必须维护一个先前使用的握手值的本地缓存并拒绝重复值,以防止重放攻击。
    缓存值的生命周期必须至少为 2*D。
    缓存值依赖于实现,但可使用 32 字节 Y 值(或其加密等效值)。

问题

  • 此处是否包含最小/最大填充选项?

握手消息 3 第一部分的加密(使用消息 2 KDF)


// 从消息 2 KDF 保存 h
// MixHash(ciphertext)
h = SHA256(h || 消息 2 的 24 字节加密载荷)

// MixHash(padding)
// 仅当填充长度非零时
h = SHA256(h || 消息 2 的随机填充)
// h 用作下文消息 3 第一部分中 AEAD 的关联数据

这是“s”消息模式:

定义 s = Alice 的静态公钥,32 字节

// EncryptAndHash(s.publickey)
// EncryptWithAd(h, s.publickey)
// AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data)
// k 来自握手消息 1
// n 为 1
ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, s.publickey)
// MixHash(ciphertext)
// || 以下表示追加
h = SHA256(h || ciphertext);

// h 用作消息 3 第二部分中 AEAD 的关联数据

“s”消息模式结束。

密钥派生函数(KDF)(用于握手消息 3 第二部分)


这是“se”消息模式:

// DH(s, re) == DH(e, rs)
定义 input_key_material = 32 字节 DH 结果,Alice 的静态密钥和 Bob 的临时密钥
设置 input_key_material = X25519 DH 结果
// 覆盖内存中的 Bob 临时密钥,不再需要
// Alice:
re = (全零)
// Bob:
e(公钥和私钥) = (全零)

// MixKey(DH())

定义 temp_key = 32 字节
定义 HMAC-SHA256(key, data) 如 [RFC-2104](https://tools.ietf.org/html/rfc2104)
// 从链式密钥和 DH 结果生成临时密钥
// ck 是来自握手消息 1 的 KDF 的链式密钥
temp_key = HMAC-SHA256(ck, input_key_material)
// 覆盖内存中的 DH 结果,不再需要
input_key_material = (全零)

// 输出 1
// 从临时密钥设置新链式密钥
// byte() 以下表示单字节
ck =       HMAC-SHA256(temp_key, byte(0x01)).

// 输出 2
// 生成密码密钥 k
定义 k = 32 字节
// || 以下表示追加
// byte() 以下表示单字节
k =        HMAC-SHA256(temp_key, ck || byte(0x02)).

// 消息 3 第一部分的 h 用作消息 3 第二部分中 AEAD 的关联数据

// EncryptAndHash(payload)
// EncryptWithAd(h, payload)
// AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data)
// n 为 0
ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, payload)
// MixHash(ciphertext)
// || 以下表示追加
h = SHA256(h || ciphertext);

// 为数据阶段 KDF 保留链式密钥 ck
// 为数据阶段附加对称密钥(SipHash)KDF 保留哈希 h

“se”消息模式结束。

// 覆盖内存中的 temp_key,不再需要
temp_key = (全零)

3) SessionConfirmed

Alice 发送给 Bob。

噪声内容:Alice 的静态密钥
噪声载荷:Alice 的 RouterInfo 和随机填充
非噪声载荷:无

(载荷安全属性)

XK(s, rs):           认证   保密性
  -> s, se                  2                5

  认证:2。
  发送方认证抵抗密钥泄露冒充(KCI)。  
  发送方认证基于发送方静态密钥对和接收方临时密钥对之间的临时-静态 DH(“es”或“se”)。  
  假设相应私钥安全,此认证无法伪造。

  保密性:5。
  加密到已知接收方,强前向保密。  
  此载荷基于临时-临时 DH 以及涉及接收方静态密钥对的临时-静态 DH 加密。  
  假设临时私钥安全,且接收方未被窃取其静态私钥的攻击者主动冒充,此载荷无法解密。

  “s”:Alice 将其静态公钥从 s 变量写入消息缓冲区,加密它,并将输出与旧 h 哈希以派生新 h。

  “se”:在 Alice 的静态密钥对和 Bob 的临时密钥对之间执行 DH。  
  结果与旧 ck 哈希以派生新 ck 和 k,n 设为零。

这包含两个 ChaChaPoly 帧。
第一个是 Alice 加密的静态公钥。
第二个是噪声载荷:Alice 加密的 RouterInfo、可选选项和可选填充。
它们使用不同的密钥,因为 MixKey() 函数在中间被调用。

原始内容:

+----+----+----+----+----+----+----+----+
|                                       |
+   ChaChaPoly 帧 (48 字节)             +
|   加密和认证的                        |
+   Alice 静态密钥 S                    |
|      (32 字节)                        |
+                                       +
|     k 在消息 2 的 KDF 中定义          |
+     n = 1                             +
|     见 KDF 中的关联数据               |
+                                       +
|                                       |
+----+----+----+----+----+----+----+----+
|                                       |
+     在消息 1 中指定的长度             +
|                                       |
+   ChaChaPoly 帧                       +
|   加密和认证的                        |
+                                       +
|       Alice RouterInfo                |
+       使用块格式 2                    +
|       Alice 选项 (可选)               |
+       使用块格式 1                    +
|       任意填充                        |
+       使用块格式 254                  +
|                                       |
+                                       +
| k 在消息 3 第二部分的 KDF 中定义      |
+     n = 0                             +
|     见 KDF 中的关联数据               |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+

S :: 32 字节,ChaChaPoly 加密的 Alice 的 X25519 静态密钥,小端
     在 48 字节 ChaChaPoly 帧内

未加密数据(Poly1305 认证标签未显示):

+----+----+----+----+----+----+----+----+
|                                       |
+                                       +
|              S                        |
+       Alice 静态密钥                  |
|          (32 字节)                    |
+                                       +
|                                       |
+                                       +
+----+----+----+----+----+----+----+----+
|                                       |
+                                       +
|                                       |
+                                       +
|       Alice RouterInfo 块             |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+
|                                       |
+       可选选项块                      |
|                                       |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+
|                                       |
+       可选填充块                      |
|                                       |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+

S :: 32 字节,Alice 的 X25519 静态密钥,小端

注释

  • Bob 必须执行通常的 Router Info 验证。
    确保签名类型受支持,验证签名,验证时间戳在范围内,以及任何其他必要检查。

  • Bob 必须验证在第一帧中收到的 Alice 的静态密钥与 Router Info 中的静态密钥匹配。
    Bob 必须首先在 Router Info 中搜索具有匹配版本(v)选项的 NTCP 或 NTCP2 Router Address。
    见下文发布的 Router Info 和未发布的 Router Info 部分。

  • 如果 Bob 在其 netdb 中有 Alice 的 RouterInfo 的旧版本,验证 Router Info 中的静态密钥在两者中是否相同(如果存在),且如果旧版本小于 XXX 旧(见下文密钥轮换时间)

  • Bob 必须在此处验证 Alice 的静态密钥是曲线上的有效点。

  • 应包含选项以指定填充参数。

  • 在任何错误时,包括 AEAD、RI、DH、时间戳或密钥验证失败,Bob 必须停止进一步消息处理并关闭连接而不响应。
    这应为异常关闭(TCP RST)。

  • 为促进快速握手,实现必须确保 Alice 缓冲然后一次性刷新第三条消息的全部内容,包括两个 AEAD 帧。
    这增加了数据包含在单个 TCP 数据包中的可能性(除非被操作系统或中间盒分段),并被 Bob 一次性接收。
    这也是为了效率并确保随机填充的有效性。

  • 消息 3 第二部分帧长度:此帧的长度(包括 MAC)由 Alice 在消息 1 中发送。
    见该消息中关于为填充留出足够空间的重要说明。

  • 消息 3 第二部分帧内容:此帧的格式与数据阶段帧的格式相同,除了帧的长度由 Alice 在消息 1 中发送。
    数据阶段帧格式见下文。
    帧必须包含 1 到 3 个块,顺序如下:

    1. Alice 的 Router Info 块(必需)
    2. 选项块(可选)
    3. 填充块(可选)
      此帧绝不能包含任何其他块类型。
  • 如果 Alice 在消息 3 末尾附加一个数据阶段帧(可选包含填充)并一次性发送两者,则消息 3 第二部分填充不是必需的,因为对观察者来说它将显示为一个大的字节流。
    由于 Alice 通常(但不总是)有 I2NP 消息要发送给 Bob(那是她连接他的原因),这是推荐的实现方式,以提高效率并确保随机填充的有效性。

  • 两条消息 3 AEAD 帧(第 1 和 2 部分)的总长度为 65535 字节;第 1 部分为 48 字节,因此第 2 部分最大帧长度为 65487;第 2 部分最大明文长度(不包括 MAC)为 65471。

密钥派生函数(KDF)(用于数据阶段)

数据阶段使用零长度关联数据输入。

KDF 使用 HMAC-SHA256(key, data)(如 RFC-2104 所定义)从链式密钥 ck 生成两个密码密钥 k_ab 和 k_ba。
这是 Split() 函数,与噪声规范中定义的完全相同。


ck = 来自握手阶段

// k_ab, k_ba = HKDF(ck, zerolen)
// ask_master = HKDF(ck, zerolen, info="ask")

// zerolen 是零长度字节数组
temp_key = HMAC-SHA256(ck, zerolen)
// 覆盖内存中的链式密钥,不再需要
ck = (全零)

// 输出 1
// 密码密钥,用于 Alice 发送给 Bob(噪声未明确说明哪个是哪个,但 Java 代码明确)
k_ab =   HMAC-SHA256(temp_key, byte(0x01)).

// 输出 2
// 密码密钥,用于 Bob 发送给 Alice(噪声未明确说明哪个是哪个,但 Java 代码明确)
k_ba =   HMAC-SHA256(temp_key, k_ab || byte(0x02)).


SipHash 长度字段的 KDF:
为 SipHash 生成附加对称密钥(ask)
SipHash 使用两个 8 字节密钥(大端)和 8 字节 IV 作为第一数据。

// "ask" 是 3 字节,US-ASCII,无空终止
ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01))
// sip_master = HKDF(ask_master, h || "siphash")
// "siphash" 是 7 字节,US-ASCII,无空终止
// 覆盖内存中的先前 temp_key
// h 来自消息 3 第二部分的 KDF
temp_key = HMAC-SHA256(ask_master, h || "siphash")
// 覆盖内存中的 ask_master,不再需要
ask_master = (全零)
sip_master = HMAC-SHA256(temp_key, byte(0x01))

Alice 到 Bob 的 SipHash k1, k2, IV:
// sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen)
// 覆盖内存中的先前 temp_key
temp_key = HMAC-SHA256(sip_master, zerolen)
// 覆盖内存中的 sip_master,不再需要
sip_master = (全零)

sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)).
sipk1_ab = sipkeys_ab[0:7], 小端
sipk2_ab = sipkeys_ab[8:15], 小端
sipiv_ab = sipkeys_ab[16:23]

Bob 到 Alice 的 SipHash k1, k2, IV:

sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)).
sipk1_ba = sipkeys_ba[0:7], 小端
sipk2_ba = sipkeys_ba[8:15], 小端
sipiv_ba = sipkeys_ba[16:23]

// 覆盖内存中的 temp_key,不再需要
temp_key = (全零)

4) 数据阶段

噪声载荷:如下定义,包括随机填充
非噪声载荷:无

从消息 3 的第二部分开始,所有消息都在经过认证和加密的 ChaChaPoly “帧”内,前面带有两字节混淆长度。
所有填充都在帧内。
帧内是标准格式,包含零个或多个“块”。
每个块有一个字节类型和两个字节长度。
类型包括日期/时间、I2NP 消息、选项、终止和填充。

注意:Bob 可以选择(但不是必须)在数据阶段向 Alice 发送他的 RouterInfo 作为第一条消息给 Alice。

(载荷安全属性)

XK(s, rs):           认证   保密性
  <-                        2                5
  ->                        2                5

  认证:2。
  发送方认证抵抗密钥泄露冒充(KCI)。
  发送方认证基于发送方静态密钥对和接收方临时密钥对之间的临时-静态 DH(“es”或“se”)。
  假设相应私钥安全,此认证无法伪造。

  保密性:5。
  加密到已知接收方,强前向保密。
  此载荷基于临时-临时 DH 以及涉及接收方静态密钥对的临时-静态 DH 加密。
  假设临时私钥安全,且接收方未被窃取其静态私钥的攻击者主动冒充,此载荷无法解密。

注释

  • 为提高效率并最小化长度字段的识别,实现必须确保发送方缓冲然后一次性刷新数据消息的全部内容,包括长度字段和 AEAD 帧。 这增加了数据包含在单个 TCP 数据包中的可能性(除非被操作系统或中间盒分段),并被另一方一次性接收。 这也是为了效率并确保随机填充的有效性。

  • 路由器可以选择在 AEAD 错误时终止会话,或继续尝试通信。 如果继续,路由器应在重复错误后终止。

SipHash 混淆长度

参考:SipHash

双方完成握手后,它们传输的载荷随后在 ChaChaPoly “帧”中被加密和认证。

每个帧前面有两个字节的长度,大端。 此长度指定要跟随的加密帧字节数,包括 MAC。 为避免在流中传输可识别的长度字段,帧长度通过 XOR 一个从 SipHash 派生的掩码进行混淆,该 SipHash 从数据阶段 KDF 初始化。 注意,两个方向从 KDF 获得唯一的 SipHash 密钥和 IV。

    sipk1, sipk2 = 来自 KDF 的 SipHash 密钥。(两个 8 字节长整数)
    IV[0] = sipiv = 来自 KDF 的 SipHash IV。(8 字节)
    length 为大端。
    对于每个帧:
      IV[n] = SipHash-2-4(sipk1, sipk2, IV[n-1])
      Mask[n] = IV[n] 的前 2 字节
      obfuscatedLength = length ^ Mask[n]

    第一个长度输出将与 IV[1] 进行 XOR。

接收方具有相同的 SipHash 密钥和 IV。 通过派生用于混淆长度的掩码并 XOR 截断的摘要来解码长度,以获得帧的长度。 帧长度是包括 MAC 的加密帧的总长度。

注释

  • 如果您使用的 SipHash 库函数返回无符号长整数,请使用最低有效两个字节作为 Mask。 将长整数转换为下一个 IV 时使用小端。

原始内容

+----+----+----+----+----+----+----+----+
|obf size |                             |
+----+----+                             +
|                                       |
+   ChaChaPoly 帧                       +
|   加密和认证的                        +
+   Alice 到 Bob 的密钥为 k_ab          +
|   Bob 到 Alice 的密钥为 k_ba          +
+   如数据阶段 KDF 中定义               +
|   n 从 0 开始并递增                   +
+   每个方向的每个帧                    +
|   无关联数据                          +
+   最小 16 字节                        +
|                                       |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+

obf size :: 2 字节长度,使用 SipHash 混淆
            去混淆后:16 - 65535

包括长度字段的最小大小为 18 字节。
包括长度字段的最大大小为 65537 字节。
混淆长度为 2 字节。
最大 ChaChaPoly 帧为 65535 字节。

未加密数据

加密帧中有零个或多个块。 每个块包含一个字节标识符、两个字节长度和零个或多个字节数据。

为扩展性,接收方必须忽略未知标识符的块,并将其视为填充。

加密数据最大为 65535 字节,包括 16 字节认证头,因此最大未加密数据为 65519 字节。

(Poly1305 认证标签未显示):

+----+----+----+----+----+----+----+----+
|blk |  size   |       data             |
+----+----+----+                        +
|                                       |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+
|blk |  size   |       data             |
+----+----+----+                        +
|                                       |
~               .   .   .               ~
|                                       |
+----+----+----+----+----+----+----+----+
~               .   .   .               ~

blk :: 1 字节
       0 表示 datetime
       1 表示 options
       2 表示 RouterInfo
       3 表示 I2NP 消息
       4 表示 termination
       224-253 保留用于实验功能
       254 表示 padding
       255 保留用于未来扩展
size :: 2 字节,大端,后续数据大小,0 - 65516
data :: 数据

最大 ChaChaPoly 帧为 65535 字节。
Poly1305 标签为 16 字节
最大总块大小为 65519 字节
最大单个块大小为 65519 字节
块类型为 1 字节
块长度为 2 字节
最大单个块数据大小为 65516 字节。

块排序规则

在握手消息 3 第二部分中,顺序必须为: RouterInfo,然后是(如果存在)Options,然后是(如果存在)Padding。 不允许其他块。

在数据阶段,顺序未指定,但有以下要求: 如果存在,Padding 必须是最后一个块。 如果存在,Termination 必须是最后一个块,除了 Padding。

单个帧中可以有多个 I2NP 块。 单个帧中不允许有多个 Padding 块。 其他块类型在单个帧中可能不会有多个块,但未禁止。

DateTime

时间同步的特殊情况:

+----+----+----+----+----+----+----+
| 0  |    4    |     timestamp     |
+----+----+----+----+----+----+----+

blk :: 0
size :: 2 字节,大端,值 = 4
timestamp :: Unix 时间戳,无符号秒。
             2106 年回绕

选项

传递更新的选项。 选项包括:最小和最大填充。

选项块将为可变长度。

+----+----+----+----+----+----+----+----+
| 1  |  size   |tmin|tmax|rmin|rmax|tdmy|
+----+----+----+----+----+----+----+----+
|tdmy|  rdmy   |  tdelay |  rdelay |    |
~----+----+----+----+----+----+----+    ~
|              more_options             |
~               .