注意
提案阶段已结束。请参阅规范获取官方规格说明。本提案仍可作为背景信息参考。
概述
此提案描述了一个认证密钥协商协议,用于提高 NTCP 对各种自动化识别和攻击的抗性。
该提案组织如下:首先介绍安全目标,然后讨论基本协议。接下来给出所有协议消息的完整规范。最后讨论router地址和版本识别。还包括一个讨论对常见填充方案的通用攻击的附录,以及一个包含认证密码候选方案的附录。
与其他I2P传输协议一样,NTCP2仅用于I2NP消息的点对点(router到router)传输。它不是通用数据管道。
动机
NTCP 数据在第一条消息之后被加密(并且第一条消息看起来是随机数据),从而通过"有效载荷分析"防止协议识别。但它仍然容易受到通过"流量分析"进行的协议识别攻击。这是因为前4条消息(即握手过程)具有固定长度(288、304、448和48字节)。
通过向每条消息添加随机数量的随机数据,我们可以让攻击变得更加困难。
作者们承认,标准安全实践会建议使用现有协议(如TLS),但这是Prop104,它本身也存在问题。在适当的地方,已添加"未来工作"段落来说明缺失的功能或讨论主题。
设计目标
在单个端口上支持 NTCP 1 和 2,自动检测,并在 NetDB 中作为单个"传输"(即 RouterAddress)发布。
在 NetDB 中的独立字段发布仅支持版本 1、仅支持版本 2 或支持版本 1+2,并默认为仅支持版本 1(不要将版本支持绑定到特定的 router 版本)
确保所有实现(Java/i2pd/Kovri/go)都可以按照各自的时间安排添加版本2支持(或不添加)
为所有 NTCP 消息添加随机填充,包括握手和数据消息 (即长度混淆,使所有消息不是 16 字节的倍数) 为双方提供选项机制来请求最小和最大填充 和/或填充分布。填充分布的具体细节取决于 实现,可能会也可能不会在协议 本身中指定。
混淆未加密消息的内容(1 和 2), 充分混淆以使 DPI 设备和 AV 签名无法轻易对其进行分类。 同时确保发送到单个对等节点或一组对等节点的消息不具有 相似的比特模式。
修复由于 Java 格式导致的 DH 位丢失问题 Ticket1112,可能(很可能?) 通过切换到 X25519 来解决。
切换到真正的密钥派生函数 (KDF) 而不是直接使用 DH 结果?
添加"探测阻抗"(Tor 称之为 probing resistance);这包括重放攻击阻抗。
维持双向认证密钥交换 (2W-AKE)。单向认证密钥交换 (1W-AKE) 对于我们的应用来说是不够的。
继续使用可变类型、可变长度签名(来自已发布的 RouterIdentity 签名密钥)作为身份验证的一部分。依赖 RouterInfo 中发布的静态公钥作为身份验证的另一部分。
在握手中添加选项/版本以实现未来的可扩展性。
如果可能的话,添加对恶意中间人 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 router 实现中的库里轻松可用。
在设计中包含 Java、C++ 和 Go 路由器开发者的代表。
最小化更改(但仍然会有很多)。
在通用代码集中同时支持两个版本(这可能无法实现,并且在任何情况下都取决于具体实现)。
Non-Goals
防弹级 DPI 抗性… 那就是可插拔传输协议, Prop109。
基于 TLS 的(或类似 HTTPS 的)传输协议… 这将是 Prop104。
可以更改对称流密码学。
基于时间的DPI抗性(消息间时序/延迟可以依赖于具体实现;消息内延迟可以在任何点引入,例如在发送随机填充之前)。人工延迟(obfs4称之为IAT或到达间隔时间)独立于协议本身。
参与会话的可否认性(其中包含签名)。
可能被部分重新考虑或讨论的非目标:
对深度数据包检测(DPI)的防护程度
后量子(PQ)安全性
可否认性
Related Goals
- 实现 NTCP 1/2 测试设置
Security Goals
我们考虑三方:
- Alice,希望建立新会话的一方。
- Bob,Alice 希望与之建立会话的一方。
- Mallory,Alice 和 Bob 之间的"中间人"。
最多只有两个参与者可以进行主动攻击。
Alice 和 Bob 都拥有一个静态密钥对,该密钥对包含在他们的 RouterIdentity 中。
提议的协议试图让 Alice 和 Bob 在以下要求下就共享密钥 (K) 达成一致:
私钥安全性:Bob和Mallory都无法获知Alice的静态私钥的任何信息。对称地,Alice也无法获知Bob的静态私钥的任何信息。
会话密钥 K 只有 Alice 和 Bob 知道。
完美前向保密:约定的会话密钥在未来仍然保持秘密,即使 Alice 和/或 Bob 的静态私钥在密钥协商完成后被泄露。
双向认证:Alice 确信她已与 Bob 建立了会话,反之亦然。
防止在线深度包检测(DPI):确保仅使用直接的深度包检测(DPI)技术无法轻易检测到Alice和Bob正在进行该协议。详见下文。
有限否认性:Alice 和 Bob 都不能否认参与了该协议,但如果任何一方泄露了共享密钥,另一方可以否认传输数据内容的真实性。
本提案尝试基于 Station-To-Station (STS) 协议来提供所有五个要求。请注意,该协议也是 SSU 协议的基础。
Additional DPI Discussion
我们假设有两个 DPI 组件:
1) Online DPI
在线深度包检测(DPI)实时检查所有数据流。连接可能被阻止或以其他方式被篡改。连接数据或元数据可能被识别并存储用于离线分析。在线DPI无法访问I2P网络数据库。在线DPI的实时计算能力有限,包括长度计算、字段检查和简单计算如异或运算。在线DPI确实具备快速实时加密功能如AES、AEAD和哈希,但将这些功能应用于大部分或所有数据流将过于昂贵。这些加密操作的任何应用都只会应用于通过离线分析预先识别的IP/端口组合上的数据流。在线DPI不具备高开销加密功能如DH或elligator2的能力。在线DPI并非专门设计用于检测I2P,尽管它可能具有用于该目的的有限分类规则。
目标是防止在线 DPI 识别协议。
在线或"直接"DPI的概念在此包括以下对手能力:
检查目标发送或接收的所有数据的能力。
对观察到的数据执行操作的能力,例如应用分组密码或哈希函数。
存储并与先前发送的消息进行比较的能力。
修改、延迟或分片数据包的能力。
然而,在线DPI被假设具有以下限制:
无法将IP地址映射到router哈希值。虽然通过对网络数据库的实时访问这很容易实现,但这需要专门针对I2P设计的DPI系统。
无法使用时序信息来检测协议。
一般来说,在线DPI工具箱不包含任何专门为I2P检测而设计的内置工具。这包括创建"蜜罐",例如在其消息中包含非随机填充。请注意,这并不排除机器学习系统或高度可配置的DPI工具,只要它们满足其他要求。
为了对抗载荷分析,确保所有消息都与随机数据无法区分。这也要求消息长度是随机的,这比仅仅添加随机填充要复杂得多。实际上,在附录A中,作者论证了简单的(即均匀的)填充方案并不能解决这个问题。因此,附录A提议要么加入随机延迟,要么开发一种替代的填充方案,以便为所提出的攻击提供合理的保护。
为了防范上述第六项威胁,实现方案应在协议中包含随机延迟。此类技术不在本提案的涵盖范围内,但它们也可能解决填充长度问题。总之,本提案为载荷分析提供了良好的保护(在考虑附录A中的注意事项时),但对流量分析仅提供有限的保护。
2) Offline DPI
离线 DPI 检查由在线 DPI 存储的数据以供后续分析。离线 DPI 可能专门设计用于检测 I2P。离线 DPI 确实具有对 I2P netDb 的实时访问权限。离线 DPI 确实可以访问此规范和其他 I2P 规范。离线 DPI 具有无限的计算能力,包括本规范中定义的所有加密函数。
离线DPI无法阻断现有连接。离线DPI确实具备近实时(在设置后几分钟内)向参与方的主机/端口发送数据的能力,例如TCP RST。离线DPI确实具备近实时(在设置后几分钟内)重放之前消息(无论是否修改)进行"探测"或其他目的的能力。
防止离线 DPI 进行协议识别并非一个目标。I2P router 实现的前两条消息中混淆数据的所有解码,离线 DPI 同样可以实现。
拒绝使用重放之前消息的连接尝试是一个目标。
Future work
考虑当数据包被攻击者丢弃或重新排序时协议的行为。在这个领域最近的有趣工作可以在 IACR-1150 中找到。
提供更准确的DPI系统分类,考虑到与该主题相关的现有文献。
讨论所提议协议的正式安全性,理想情况下要考虑 DPI 攻击者模型。
Noise Protocol Framework
此提案基于 Noise Protocol Framework NOISE(修订版 33,2017-10-04)提供要求。Noise 具有与 Station-To-Station 协议类似的特性,该协议是 SSU 协议的基础。在 Noise 术语中,Alice 是发起者,Bob 是响应者。
NTCP2 基于 Noise 协议 Noise_XK_25519_ChaChaPoly_SHA256。(初始密钥派生函数的实际标识符是 “Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256” 以表示 I2P 扩展 - 请参见下面的 KDF 1 部分)此 Noise 协议使用以下原语:
Handshake Pattern: XK Alice 向 Bob 传输她的密钥 (X) Alice 已经知道 Bob 的静态密钥 (K)
DH Function: X25519 X25519 DH,密钥长度为32字节,如RFC-7748中所规定。
Cipher Function: ChaChaPoly AEAD_CHACHA20_POLY1305 如 RFC-7539 第 2.8 节所规定。 12 字节 nonce,前 4 字节设置为零。
Hash Function: SHA256 标准32字节哈希,在I2P中已被广泛使用。
安全目标
此提案定义了对 Noise_XK_25519_ChaChaPoly_SHA256 的以下增强功能。这些通常遵循 NOISE 第 13 节中的指导原则。
明文临时密钥使用已知密钥和 IV 通过 AES 加密进行混淆。这比 elligator2 更快。
随机明文填充被添加到消息1和消息2中。明文填充被包含在握手哈希(MixHash)计算中。请参阅下面消息2和消息3第1部分的KDF部分。随机AEAD填充被添加到消息3和数据阶段消息中。
添加了一个两字节的帧长度字段,这是 Noise over TCP 所必需的,也如同 obfs4 中一样。这仅用于数据阶段的消息。消息 1 和 2 的 AEAD 帧是固定长度的。消息 3 第 1 部分的 AEAD 帧是固定长度的。消息 3 第 2 部分的 AEAD 帧长度在消息 1 中指定。
两字节的帧长度字段使用 SipHash-2-4 进行混淆,如同在 obfs4 中一样。
载荷格式为消息1、2、3和数据阶段定义。当然,这在Noise中没有定义。
New Cryptographic Primitives for I2P
现有的 I2P router 实现需要实现以下标准密码学原语,这些在当前 I2P 协议中并非必需:
X25519 密钥生成和 DH
AEAD_ChaCha20_Poly1305(以下简称为 ChaChaPoly)
SipHash-2-4
Processing overhead estimate
3条消息的消息大小:
- 64 字节 + 填充 (NTCP 为 288 字节) 2) 64 字节 + 填充 (NTCP 为 304 字节) 3) 约 64 字节 + Alice router 信息 + 填充 平均 router 信息约 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
Messages
所有 NTCP2 消息的长度都小于或等于 65537 字节。消息格式基于 Noise 消息,但针对帧封装和不可区分性进行了修改。使用标准 Noise 库的实现可能需要对接收到的消息进行预处理,以便与 Noise 消息格式相互转换。所有加密字段都是 AEAD 密文。
建立序列如下:
Alice Bob
SessionRequest ------------------->
<------------------- SessionCreated
SessionConfirmed ----------------->
使用 Noise 术语,建立和数据序列如下:(有效负载安全属性)
XK(s, rs): Authentication Confidentiality
<- 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 的 Router Hash(32 字节)
- RH_B = Bob 的 Router Hash(32 字节)
Authenticated Encryption
存在三个独立的认证加密实例 (CipherStates)。一个用于握手阶段,两个(发送和接收)用于数据阶段。每个实例都有来自 KDF 的自己的密钥。
加密/认证数据将表示为
+----+----+----+----+----+----+----+----+
| |
+ +
| Encrypted and authenticated data |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
非目标
加密和认证的数据格式。
加密/解密函数的输入:
k :: 32 byte cipher key, as generated from KDF
nonce :: Counter-based nonce, 12 bytes.
Starts at 0 and incremented for each message.
First four bytes are always zero.
Last eight bytes are the counter, little-endian encoded.
Maximum value is 2**64 - 2.
Connection must be dropped and restarted after
it reaches that value.
The value 2**64 - 1 must never be sent.
ad :: In handshake phase:
Associated data, 32 bytes.
The SHA256 hash of all preceding data.
In data phase:
Zero bytes
data :: Plaintext data, 0 or more bytes
加密函数的输出,解密函数的输入:
+----+----+----+----+----+----+----+----+
|Obfs Len | |
+----+----+ +
| ChaCha20 encrypted data |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
| Poly1305 Message Authentication Code |
+ (MAC) +
| 16 bytes |
+----+----+----+----+----+----+----+----+
Obfs Len :: Length of (encrypted data + MAC) to follow, 16 - 65535
Obfuscation using SipHash (see below)
Not used in message 1 or 2, or message 3 part 1, where the length is fixed
Not used in message 3 part 1, as the length is specified in message 1
encrypted data :: Same size as plaintext data, 0 - 65519 bytes
MAC :: Poly1305 message authentication code, 16 bytes
对于 ChaCha20,这里描述的内容对应 RFC-7539,这也在 TLS RFC-7905 中得到类似使用。
Notes
由于 ChaCha20 是流密码,明文无需填充。 多余的密钥流字节会被丢弃。
密码的密钥(256位)通过SHA256 KDF协商确定。 每个消息的KDF详细信息在下面的单独章节中。
ChaChaPoly 帧用于消息 1、消息 2 和消息 3 的第一部分, 大小是已知的。从消息 3 的第二部分开始, 帧大小是可变的。消息 3 第一部分的大小在消息 1 中指定。 从数据阶段开始,帧前会加上一个双字节长度, 使用 SipHash 进行混淆,如同 obfs4 中一样。
在消息 1 和 2 中,填充数据位于已认证数据帧之外。 填充数据用于下一条消息的 KDF,因此篡改将被检测到。从消息 3 开始, 填充数据位于已认证数据帧内部。
相关目标
在消息1、消息2以及消息3的第1和第2部分中,AEAD消息大小是预先已知的。 在AEAD认证失败时,接收方必须停止进一步的消息处理并关闭连接而不响应。这应该是异常关闭(TCP RST)。
为了抵抗探测,在消息1中,当AEAD失败后,Bob应该 设置一个随机超时时间(范围待定)然后读取随机数量的字节(范围待定) 再关闭socket。Bob应该维护一个重复失败IP的黑名单。
在数据阶段,AEAD 消息大小通过 SipHash 进行"加密"(混淆)。 必须注意避免创建解密预言机。 当数据阶段 AEAD 认证失败时,接收方应该 设置一个随机超时(范围待定)然后读取随机数量的字节(范围待定)。 在读取后,或读取超时时,接收方应该发送一个载荷, 其中包含一个带有"AEAD 失败"原因码的终止块, 并关闭连接。
在数据阶段对无效长度字段值采取相同的错误操作。
Key Derivation Function (KDF) (for handshake message 1)
KDF 从 DH 结果生成握手阶段密码密钥 k,使用 RFC-2104 中定义的 HMAC-SHA256(key, data)。这些是 InitializeSymmetric()、MixHash() 和 MixKey() 函数,与 Noise 规范中定义的完全相同。
This is the "e" message pattern:
// Define protocol_name.
Set protocol_name = "Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256"
(48 bytes, US-ASCII encoded, no NULL termination).
// Define Hash h = 32 bytes
h = SHA256(protocol_name);
Define ck = 32 byte chaining key. Copy the h data to ck.
Set ck = h
Define rs = Bob's 32-byte static key as published in the RouterInfo
// MixHash(null prologue)
h = SHA256(h);
// up until here, can all be precalculated by Alice for all outgoing connections
// Alice must validate that Bob's static key is a valid point on the curve here.
// Bob static key
// MixHash(rs)
// || below means append
h = SHA256(h || rs);
// up until here, can all be precalculated by Bob for all incoming connections
This is the "e" message pattern:
Alice generates her ephemeral DH key pair e.
// Alice ephemeral key X
// MixHash(e.pubkey)
// || below means append
h = SHA256(h || e.pubkey);
// h is used as the associated data for the AEAD in message 1
// Retain the Hash h for the message 2 KDF
End of "e" message pattern.
This is the "es" message pattern:
// DH(e, rs) == DH(s, re)
Define input_key_material = 32 byte DH result of Alice's ephemeral key and Bob's static key
Set input_key_material = X25519 DH result
// MixKey(DH())
Define temp_key = 32 bytes
Define HMAC-SHA256(key, data) as in [RFC-2104](https://tools.ietf.org/html/rfc2104)
// Generate a temp key from the chaining key and DH result
// ck is the chaining key, defined above
temp_key = HMAC-SHA256(ck, input_key_material)
// overwrite the DH result in memory, no longer needed
input_key_material = (all zeros)
// Output 1
// Set a new chaining key from the temp key
// byte() below means a single byte
ck = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// Generate the cipher key k
Define k = 32 bytes
// || below means append
// byte() below means a single byte
k = HMAC-SHA256(temp_key, ck || byte(0x02)).
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
// retain the chaining key ck for message 2 KDF
End of "es" message pattern.
其他 DPI 讨论
Alice 发送给 Bob。
Noise内容:Alice的临时密钥X Noise载荷:16字节选项块 非Noise载荷:随机填充
(负载安全属性)
XK(s, rs): Authentication Confidentiality
-> e, es 0 2
Authentication: None (0).
This payload may have been sent by any party, including an active attacker.
Confidentiality: 2.
Encryption to a known recipient, forward secrecy for sender compromise
only, vulnerable to replay. This payload is encrypted based only on DHs
involving the recipient's static key pair. If the recipient's static
private key is compromised, even at a later date, this payload can be
decrypted. This message can also be replayed, since there's no ephemeral
contribution from the recipient.
"e": Alice generates a new ephemeral key pair and stores it in the e
variable, writes the ephemeral public key as cleartext into the
message buffer, and hashes the public key along with the old h to
derive a new h.
"es": A DH is performed between the Alice's ephemeral key pair and the
Bob's static key pair. The result is hashed along with the old ck to
derive a new ck and k, and n is set to zero.
X 值被加密以确保载荷的不可区分性和唯一性,这些是必要的 DPI 对抗措施。我们使用 AES 加密来实现这一点,而不是使用更复杂和更慢的替代方案如 elligator2。使用 Bob 的 router 公钥进行非对称加密会过于缓慢。AES 加密使用 Bob 的 router hash 作为密钥,并使用发布在 netDb 中的 Bob 的 IV。
AES 加密仅用于抵抗 DPI(深度包检测)。任何知道 Bob 的 router hash 和 IV 的一方(这些都在网络数据库中公开发布)都可以解密此消息中的 X 值。
填充数据不会被 Alice 加密。Bob 可能需要解密填充数据,以抑制时序攻击。
原始内容:
+----+----+----+----+----+----+----+----+
| |
+ obfuscated with RH_B +
| AES-CBC-256 encrypted X |
+ (32 bytes) +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| |
+ +
| ChaChaPoly frame |
+ (32 bytes) +
| k defined in KDF for message 1 |
+ n = 0 +
| see KDF for associated data |
+----+----+----+----+----+----+----+----+
| unencrypted authenticated |
~ padding (optional) ~
| length defined in options block |
+----+----+----+----+----+----+----+----+
X :: 32 bytes, AES-256-CBC encrypted X25519 ephemeral key, little endian
key: RH_B
iv: As published in Bobs network database entry
padding :: Random data, 0 or more bytes.
Total message length must be 65535 bytes or less.
Total message length must be 287 bytes or less if
Bob is publishing his address as NTCP
(see Version Detection section below).
Alice and Bob will use the padding data in the KDF for message 2.
It is authenticated so that any tampering will cause the
next message to fail.
未加密数据(未显示 Poly1305 认证标签):
+----+----+----+----+----+----+----+----+
| |
+ +
| X |
+ (32 bytes) +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| options |
+ (16 bytes) +
| |
+----+----+----+----+----+----+----+----+
| unencrypted authenticated |
+ padding (optional) +
| length defined in options block |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
X :: 32 bytes, X25519 ephemeral key, little endian
options :: options block, 16 bytes, see below
padding :: Random data, 0 or more bytes.
Total message length must be 65535 bytes or less.
Total message length must be 287 bytes or less if
Bob is publishing his address as "NTCP"
(see Version Detection section below)
Alice and Bob will use the padding data in the KDF for message 2.
It is authenticated so that any tampering will cause the
next message to fail.
Options 块:注意:所有字段均为大端序。
+----+----+----+----+----+----+----+----+
| id | ver| padLen | m3p2len | Rsvd(0) |
+----+----+----+----+----+----+----+----+
| tsA | Reserved (0) |
+----+----+----+----+----+----+----+----+
id :: 1 byte, the network ID (currently 2, except for test networks)
As of 0.9.42. See proposal 147.
ver :: 1 byte, protocol version (currently 2)
padLen :: 2 bytes, length of the padding, 0 or more
Min/max guidelines TBD. Random size from 0 to 31 bytes minimum?
(Distribution to be determined, see Appendix A.)
m3p2Len :: 2 bytes, length of the the second AEAD frame in SessionConfirmed
(message 3 part 2) See notes below
Rsvd :: 2 bytes, set to 0 for compatibility with future options
tsA :: 4 bytes, Unix timestamp, unsigned seconds.
Wraps around in 2106
Reserved :: 4 bytes, set to 0 for compatibility with future options
Notes
当发布的地址是"NTCP"时,Bob在同一端口上同时支持NTCP和NTCP2。为了兼容性,当Alice向发布为"NTCP"的地址发起连接时,必须将此消息的最大大小(包括填充)限制为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 协议一样,router 应该采取所有必要措施来防止 CPU 或连接耗尽。对最大活动连接数和正在进行的最大连接建立数设置限制。强制执行读取超时(包括单次读取和"slowloris"攻击的总超时)。限制来自同一源的重复或同时连接。为反复失败的源维护黑名单。不要响应 AEAD 失败。
为了便于快速版本检测和握手,实现必须确保 Alice 缓冲然后一次性刷新第一条消息的全部内容,包括填充数据。这增加了数据包含在单个 TCP 数据包中的可能性(除非被操作系统或中间设备分段),并被 Bob 一次性接收。此外,实现必须确保 Bob 缓冲然后一次性刷新第二条消息的全部内容,包括填充数据,以及 Bob 缓冲然后一次性刷新第三条消息的全部内容。这也是为了提高效率并确保随机填充的有效性。
“ver” 字段:整体 Noise 协议、扩展和 NTCP 协议 包括载荷规范,表示 NTCP2。 该字段可用于表示对未来变更的支持。
Message 3 part 2 长度:这是第二个 AEAD 帧的大小(包括 16 字节 MAC), 包含将在 SessionConfirmed 消息中发送的 Alice 的 Router Info 和可选填充。 由于路由器会定期重新生成并重新发布其 Router Info,当前 Router Info 的大小 可能在消息 3 发送之前发生变化。实现必须选择以下两种策略之一: a) 保存当前要在消息 3 中发送的 Router Info,以便知道大小, 并可选择性地为填充留出空间; b) 将指定大小增加足够的量以允许 Router Info 大小可能的增长, 并在实际发送消息 3 时总是添加填充。 无论哪种情况,消息 1 中包含的 “m3p2len” 长度必须与消息 3 中发送的 该帧的确切大小一致。
Bob必须在验证消息1并读取填充数据后,如果仍有任何传入数据残留,则使连接失败。不应该有来自Alice的额外数据,因为Bob尚未用消息2进行响应。
network ID 字段用于快速识别跨网络连接。 如果此字段非零且与 Bob 的 network ID 不匹配, Bob 应断开连接并阻止未来的连接。 从 0.9.42 版本开始。详见提案 147。
Key Derivation Function (KDF) (for handshake message 2 and message 3 part 1)
// take h saved from message 1 KDF
// MixHash(ciphertext)
h = SHA256(h || 32 byte encrypted payload from message 1)
// MixHash(padding)
// Only if padding length is nonzero
h = SHA256(h || random padding from message 1)
This is the "e" message pattern:
Bob generates his ephemeral DH key pair e.
// h is from KDF for handshake message 1
// Bob ephemeral key Y
// MixHash(e.pubkey)
// || below means append
h = SHA256(h || e.pubkey);
// h is used as the associated data for the AEAD in message 2
// Retain the Hash h for the message 3 KDF
End of "e" message pattern.
This is the "ee" message pattern:
// DH(e, re)
Define input_key_material = 32 byte DH result of Alice's ephemeral key and Bob's ephemeral key
Set input_key_material = X25519 DH result
// overwrite Alice's ephemeral key in memory, no longer needed
// Alice:
e(public and private) = (all zeros)
// Bob:
re = (all zeros)
// MixKey(DH())
Define temp_key = 32 bytes
Define HMAC-SHA256(key, data) as in [RFC-2104](https://tools.ietf.org/html/rfc2104)
// Generate a temp key from the chaining key and DH result
// ck is the chaining key, from the KDF for handshake message 1
temp_key = HMAC-SHA256(ck, input_key_material)
// overwrite the DH result in memory, no longer needed
input_key_material = (all zeros)
// Output 1
// Set a new chaining key from the temp key
// byte() below means a single byte
ck = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// Generate the cipher key k
Define k = 32 bytes
// || below means append
// byte() below means a single byte
k = HMAC-SHA256(temp_key, ck || byte(0x02)).
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
// retain the chaining key ck for message 3 KDF
End of "ee" message pattern.
2) SessionCreated
Bob 发送给 Alice。
Noise 内容:Bob 的临时密钥 Y Noise 载荷:16 字节选项块 非 Noise 载荷:随机填充
(负载安全属性)
XK(s, rs): Authentication Confidentiality
<- e, ee 2 1
Authentication: 2.
Sender authentication resistant to key-compromise impersonation (KCI).
The sender authentication is based on an ephemeral-static DH ("es" or "se")
between the sender's static key pair and the recipient's ephemeral key pair.
Assuming the corresponding private keys are secure, this authentication cannot be forged.
Confidentiality: 1.
Encryption to an ephemeral recipient.
This payload has forward secrecy, since encryption involves an ephemeral-ephemeral DH ("ee").
However, the sender has not authenticated the recipient,
so this payload might be sent to any party, including an active attacker.
"e": Bob generates a new ephemeral key pair and stores it in the e variable,
writes the ephemeral public key as cleartext into the message buffer,
and hashes the public key along with the old h to derive a new h.
"ee": A DH is performed between the Bob's ephemeral key pair and the Alice's ephemeral key pair.
The result is hashed along with the old ck to derive a new ck and k, and n is set to zero.
Y 值被加密以确保载荷的不可区分性和唯一性,这是必要的 DPI 对抗措施。我们使用 AES 加密来实现这一点,而不是使用更复杂和更慢的替代方案(如 elligator2)。使用 Alice 的 router 公钥进行非对称加密会太慢。AES 加密使用 Bob 的 router hash 作为密钥,并使用来自消息 1 的 AES 状态(该状态是用发布在 netDb 中的 Bob 的 IV 初始化的)。
AES 加密仅用于抵抗 DPI(深度包检测)。任何知道 Bob 的 router 哈希值和 IV(这些信息发布在网络数据库中)并捕获到消息 1 前 32 字节的一方,都可以解密该消息中的 Y 值。
原始内容:
+----+----+----+----+----+----+----+----+
| |
+ obfuscated with RH_B +
| AES-CBC-256 encrypted Y |
+ (32 bytes) +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| ChaChaPoly frame |
+ Encrypted and authenticated data +
| 32 bytes |
+ k defined in KDF for message 2 +
| n = 0; see KDF for associated data |
+ +
| |
+----+----+----+----+----+----+----+----+
| unencrypted authenticated |
+ padding (optional) +
| length defined in options block |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
Y :: 32 bytes, AES-256-CBC encrypted X25519 ephemeral key, little endian
key: RH_B
iv: Using AES state from message 1
未加密数据(未显示 Poly1305 认证标签):
+----+----+----+----+----+----+----+----+
| |
+ +
| Y |
+ (32 bytes) +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| options |
+ (16 bytes) +
| |
+----+----+----+----+----+----+----+----+
| unencrypted authenticated |
+ padding (optional) +
| length defined in options block |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
Y :: 32 bytes, X25519 ephemeral key, little endian
options :: options block, 16 bytes, see below
padding :: Random data, 0 or more bytes.
Total message length must be 65535 bytes or less.
Alice and Bob will use the padding data in the KDF for message 3 part 1.
It is authenticated so that any tampering will cause the
next message to fail.
Notes
Alice必须在此验证Bob的临时密钥是曲线上的有效点。
填充应限制在合理范围内。 Alice 可能会拒绝过度填充的连接。 Alice 将在消息3中指定她的填充选项。 最小/最大指导原则待定。从0到31字节的随机大小最小值? (分布待确定,见附录A。)
在任何错误情况下,包括 AEAD、DH、时间戳、明显重放或密钥验证失败,Alice 必须停止进一步的消息处理并关闭连接而不响应。这应该是异常关闭(TCP RST)。
为了促进快速握手,实现必须确保 Bob 缓冲然后一次性刷新第一条消息的全部内容,包括填充。这增加了数据被包含在单个 TCP 数据包中的可能性(除非被操作系统或中间盒分段),并被 Alice 一次性接收。这也是为了提高效率并确保随机填充的有效性。
Alice必须在验证消息2并读取填充后,如果仍有任何传入数据残留,则使连接失败。此时不应该有来自Bob的额外数据,因为Alice尚未用消息3进行响应。
Options 块:注意:所有字段都是大端序。
+----+----+----+----+----+----+----+----+
| Rsvd(0) | padLen | Reserved (0) |
+----+----+----+----+----+----+----+----+
| tsB | Reserved (0) |
+----+----+----+----+----+----+----+----+
Reserved :: 10 bytes total, set to 0 for compatibility with future options
padLen :: 2 bytes, big endian, length of the padding, 0 or more
Min/max guidelines TBD. Random size from 0 to 31 bytes minimum?
(Distribution to be determined, see Appendix A.)
tsB :: 4 bytes, big endian, Unix timestamp, unsigned seconds.
Wraps around in 2106
Notes
- Alice 必须拒绝时间戳值与当前时间相差过大的连接。将最大时间差称为"D"。Alice 必须维护一个本地缓存来存储之前使用过的握手值并拒绝重复值,以防止重放攻击。缓存中的值必须具有至少 2*D 的生存期。缓存值依赖于具体实现,但是可以使用 32 字节的 Y 值(或其加密等价物)。
Issues
- 在这里包含最小/最大填充选项?
Encryption for for handshake message 3 part 1, using message 2 KDF)
// take h saved from message 2 KDF
// MixHash(ciphertext)
h = SHA256(h || 24 byte encrypted payload from message 2)
// MixHash(padding)
// Only if padding length is nonzero
h = SHA256(h || random padding from message 2)
// h is used as the associated data for the AEAD in message 3 part 1, below
This is the "s" message pattern:
Define s = Alice's static public key, 32 bytes
// EncryptAndHash(s.publickey)
// EncryptWithAd(h, s.publickey)
// AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data)
// k is from handshake message 1
// n is 1
ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, s.publickey)
// MixHash(ciphertext)
// || below means append
h = SHA256(h || ciphertext);
// h is used as the associated data for the AEAD in message 3 part 2
End of "s" message pattern.
Key Derivation Function (KDF) (for handshake message 3 part 2)
This is the "se" message pattern:
// DH(s, re) == DH(e, rs)
Define input_key_material = 32 byte DH result of Alice's static key and Bob's ephemeral key
Set input_key_material = X25519 DH result
// overwrite Bob's ephemeral key in memory, no longer needed
// Alice:
re = (all zeros)
// Bob:
e(public and private) = (all zeros)
// MixKey(DH())
Define temp_key = 32 bytes
Define HMAC-SHA256(key, data) as in [RFC-2104](https://tools.ietf.org/html/rfc2104)
// Generate a temp key from the chaining key and DH result
// ck is the chaining key, from the KDF for handshake message 1
temp_key = HMAC-SHA256(ck, input_key_material)
// overwrite the DH result in memory, no longer needed
input_key_material = (all zeros)
// Output 1
// Set a new chaining key from the temp key
// byte() below means a single byte
ck = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// Generate the cipher key k
Define k = 32 bytes
// || below means append
// byte() below means a single byte
k = HMAC-SHA256(temp_key, ck || byte(0x02)).
// h from message 3 part 1 is used as the associated data for the AEAD in message 3 part 2
// EncryptAndHash(payload)
// EncryptWithAd(h, payload)
// AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data)
// n is 0
ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, payload)
// MixHash(ciphertext)
// || below means append
h = SHA256(h || ciphertext);
// retain the chaining key ck for the data phase KDF
// retain the hash h for the data phase Additional Symmetric Key (SipHash) KDF
End of "se" message pattern.
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
3) SessionConfirmed
Alice 发送给 Bob。
Noise 内容:Alice 的静态密钥 Noise 载荷:Alice 的 RouterInfo 和随机填充 非 noise 载荷:无
(载荷安全属性)
XK(s, rs): Authentication Confidentiality
-> s, se 2 5
Authentication: 2.
Sender authentication resistant to key-compromise impersonation (KCI). The
sender authentication is based on an ephemeral-static DH ("es" or "se")
between the sender's static key pair and the recipient's ephemeral key
pair. Assuming the corresponding private keys are secure, this
authentication cannot be forged.
Confidentiality: 5.
Encryption to a known recipient, strong forward secrecy. This payload is
encrypted based on an ephemeral-ephemeral DH as well as an ephemeral-static
DH with the recipient's static key pair. Assuming the ephemeral private
keys are secure, and the recipient is not being actively impersonated by an
attacker that has stolen its static private key, this payload cannot be
decrypted.
"s": Alice writes her static public key from the s variable into the
message buffer, encrypting it, and hashes the output along with the old h
to derive a new h.
"se": A DH is performed between the Alice's static key pair and the Bob's
ephemeral key pair. The result is hashed along with the old ck to derive a
new ck and k, and n is set to zero.
这包含两个 ChaChaPoly 帧。第一个是 Alice 的加密静态公钥。第二个是 Noise 载荷:Alice 的加密 RouterInfo、可选选项和可选填充。它们使用不同的密钥,因为在两者之间调用了 MixKey() 函数。
原始内容:
+----+----+----+----+----+----+----+----+
| |
+ ChaChaPoly frame (48 bytes) +
| Encrypted and authenticated |
+ Alice static key S +
| (32 bytes) |
+ +
| k defined in KDF for message 2 |
+ n = 1 +
| see KDF for associated data |
+ +
| |
+----+----+----+----+----+----+----+----+
| |
+ Length specified in message 1 +
| |
+ ChaChaPoly frame +
| Encrypted and authenticated |
+ +
| Alice RouterInfo |
+ using block format 2 +
| Alice Options (optional) |
+ using block format 1 +
| Arbitrary padding |
+ using block format 254 +
| |
+ +
| k defined in KDF for message 3 part 2 |
+ n = 0 +
| see KDF for associated data |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
S :: 32 bytes, ChaChaPoly encrypted Alice's X25519 static key, little endian
inside 48 byte ChaChaPoly frame
未加密数据(未显示 Poly1305 认证标签):
+----+----+----+----+----+----+----+----+
| |
+ +
| S |
+ Alice static key +
| (32 bytes) |
+ +
| |
+ +
+----+----+----+----+----+----+----+----+
| |
+ +
| |
+ +
| Alice RouterInfo block |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
| |
+ Optional Options block +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
| |
+ Optional Padding block +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
S :: 32 bytes, Alice's X25519 static key, little endian
1) 在线 DPI
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 一次性接收。 这也是为了提高效率并确保随机填充的有效性。
Message 3 part 2 frame length: 此帧的长度(包括 MAC)由 Alice 在 message 1 中发送。请参阅该消息以了解关于为填充留出足够空间的重要说明。
Message 3 part 2 帧内容:此帧的格式与数据阶段帧的格式相同,除了帧的长度是由 Alice 在 message 1 中发送的。数据阶段帧格式见下文。 该帧必须按以下顺序包含 1 到 3 个块:
- Alice 的 Router Info 块(必需)
- Options 块(可选)
- Padding 块(可选) 此帧绝不能包含任何其他块类型。
如果 Alice 在消息 3 的末尾附加一个数据阶段帧(可选择包含填充)并同时发送两者,则不需要消息 3 第 2 部分填充,因为对观察者来说这将显示为一个大的字节流。由于 Alice 通常(但不总是)会有一个 I2NP 消息要发送给 Bob(这就是她连接到他的原因),因此建议采用这种实现方式,以提高效率并确保随机填充的有效性。
消息 3 的两个 AEAD 帧(第 1 部分和第 2 部分)的总长度为 65535 字节; 第 1 部分是 48 字节,因此第 2 部分的最大帧长度为 65487; 第 2 部分不包括 MAC 的最大明文长度为 65471。
Key Derivation Function (KDF) (for data phase)
数据阶段使用零长度关联数据输入。
KDF 使用链式密钥 ck 生成两个密码密钥 k_ab 和 k_ba,使用 HMAC-SHA256(key, data),如 RFC-2104 中定义。这是 Split() 函数,完全按照 Noise 规范中的定义。
ck = from handshake phase
// k_ab, k_ba = HKDF(ck, zerolen)
// ask_master = HKDF(ck, zerolen, info="ask")
// zerolen is a zero-length byte array
temp_key = HMAC-SHA256(ck, zerolen)
// overwrite the chaining key in memory, no longer needed
ck = (all zeros)
// Output 1
// cipher key, for Alice transmits to Bob (Noise doesn't make clear which is which, but Java code does)
k_ab = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// cipher key, for Bob transmits to Alice (Noise doesn't make clear which is which, but Java code does)
k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02)).
KDF for SipHash for length field:
Generate an Additional Symmetric Key (ask) for SipHash
SipHash uses two 8-byte keys (big endian) and 8 byte IV for first data.
// "ask" is 3 bytes, US-ASCII, no null termination
ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01))
// sip_master = HKDF(ask_master, h || "siphash")
// "siphash" is 7 bytes, US-ASCII, no null termination
// overwrite previous temp_key in memory
// h is from KDF for message 3 part 2
temp_key = HMAC-SHA256(ask_master, h || "siphash")
// overwrite ask_master in memory, no longer needed
ask_master = (all zeros)
sip_master = HMAC-SHA256(temp_key, byte(0x01))
Alice to Bob SipHash k1, k2, IV:
// sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen)
// overwrite previous temp_key in memory
temp_key = HMAC-SHA256(sip_master, zerolen)
// overwrite sip_master in memory, no longer needed
sip_master = (all zeros)
sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)).
sipk1_ab = sipkeys_ab[0:7], little endian
sipk2_ab = sipkeys_ab[8:15], little endian
sipiv_ab = sipkeys_ab[16:23]
Bob to Alice SipHash k1, k2, IV:
sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)).
sipk1_ba = sipkeys_ba[0:7], little endian
sipk2_ba = sipkeys_ba[8:15], little endian
sipiv_ba = sipkeys_ba[16:23]
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
4) Data Phase
Noise payload: 如下定义,包含随机填充 Non-noise payload: 无
从消息 3 的第 2 部分开始,所有消息都在经过身份验证和加密的 ChaChaPoly “帧” 内,并带有前置的两字节混淆长度。所有填充都在帧内。帧内是包含零个或多个"块"的标准格式。每个块都有一个字节的类型和两个字节的长度。类型包括日期/时间、I2NP 消息、选项、终止和填充。
注意:Bob可以(但不是必须)在数据阶段向Alice发送第一条消息时,将他的RouterInfo发送给Alice。
(载荷安全属性)
XK(s, rs): Authentication Confidentiality
<- 2 5
-> 2 5
Authentication: 2.
Sender authentication resistant to key-compromise impersonation (KCI).
The sender authentication is based on an ephemeral-static DH ("es" or "se")
between the sender's static key pair and the recipient's ephemeral key pair.
Assuming the corresponding private keys are secure, this authentication cannot be forged.
Confidentiality: 5.
Encryption to a known recipient, strong forward secrecy.
This payload is encrypted based on an ephemeral-ephemeral DH as well as
an ephemeral-static DH with the recipient's static key pair.
Assuming the ephemeral private keys are secure, and the recipient is not being actively impersonated
by an attacker that has stolen its static private key, this payload cannot be decrypted.
2) 离线 DPI
为了提高效率并最大限度地减少长度字段的识别, 实现必须确保发送方缓冲然后一次性刷新数据消息的全部内容, 包括长度字段和 AEAD 帧。 这增加了数据包含在单个 TCP 数据包中的可能性 (除非被操作系统或中间盒分段),并且对方能够一次性接收全部数据。 这也是为了提高效率并确保随机填充的有效性。
路由器可以选择在 AEAD 错误时终止会话,或者可以继续尝试通信。 如果继续,路由器应该在重复错误后终止会话。
SipHash obfuscated length
参考:SipHash
一旦双方都完成了握手,它们就传输载荷,然后在 ChaChaPoly “帧"中进行加密和认证。
每个帧前面都有一个两字节的长度字段,采用大端序。这个长度指定了后续加密帧字节的数量,包括MAC。为了避免在流中传输可识别的长度字段,帧长度通过与从SipHash派生的掩码进行XOR运算来进行混淆,SipHash使用数据阶段KDF初始化。注意两个方向具有来自KDF的唯一SipHash密钥和IV。
sipk1, sipk2 = The SipHash keys from the KDF. (two 8-byte long integers)
IV[0] = sipiv = The SipHash IV from the KDF. (8 bytes)
length is big endian.
For each frame:
IV[n] = SipHash-2-4(sipk1, sipk2, IV[n-1])
Mask[n] = First 2 bytes of IV[n]
obfuscatedLength = length ^ Mask[n]
The first length output will be XORed with with IV[1].
接收方拥有相同的 SipHash 密钥和 IV。解码长度是通过推导用于混淆长度的掩码并对截断的摘要进行异或运算来获得帧的长度。帧长度是加密帧包括 MAC 在内的总长度。
未来工作
- 如果你使用返回无符号长整数的 SipHash 库函数, 使用最低有效的两个字节作为 Mask。 将长整数转换为下一个 IV,采用小端序格式。
认证加密
+----+----+----+----+----+----+----+----+
|obf size | |
+----+----+ +
| |
+ ChaChaPoly frame +
| Encrypted and authenticated |
+ key is k_ab for Alice to Bob +
| key is k_ba for Bob to Alice |
+ as defined in KDF for data phase +
| n starts at 0 and increments |
+ for each frame in that direction +
| no associated data |
+ 16 bytes minimum +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
obf size :: 2 bytes length obfuscated with SipHash
when de-obfuscated: 16 - 65535
Minimum size including length field is 18 bytes.
Maximum size including length field is 65537 bytes.
Obfuscated length is 2 bytes.
Maximum ChaChaPoly frame is 65535 bytes.
ChaCha20/Poly1305
加密帧中包含零个或多个块。每个块包含一个单字节标识符、一个双字节长度字段,以及零个或多个数据字节。
为了可扩展性,接收者必须忽略具有未知标识符的块,并将其视为填充。
加密数据最大为 65535 字节,包括 16 字节的认证头,因此未加密数据的最大值为 65519 字节。
(未显示Poly1305认证标签):
+----+----+----+----+----+----+----+----+
|blk | size | data |
+----+----+----+ +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
|blk | size | data |
+----+----+----+ +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
~ . . . ~
blk :: 1 byte
0 for datetime
1 for options
2 for RouterInfo
3 for I2NP message
4 for termination
224-253 reserved for experimental features
254 for padding
255 reserved for future extension
size :: 2 bytes, big endian, size of data to follow, 0 - 65516
data :: the data
Maximum ChaChaPoly frame is 65535 bytes.
Poly1305 tag is 16 bytes
Maximum total block size is 65519 bytes
Maximum single block size is 65519 bytes
Block type is 1 byte
Block length is 2 bytes
Maximum single block data size is 65516 bytes.
Block Ordering Rules
在握手消息 3 第 2 部分中,顺序必须是:RouterInfo,如果存在则跟随 Options,如果存在则跟随 Padding。不允许其他块。
在数据阶段,顺序未指定,除了以下要求:填充(如果存在)必须是最后一个块。终止(如果存在)必须是最后一个块,除了填充。
在单个帧中可能包含多个I2NP块。在单个帧中不允许有多个填充块。其他块类型在单个帧中可能不会有多个块,但这并不是被禁止的。
AEAD 错误处理
时间同步的特殊情况:
+----+----+----+----+----+----+----+
| 0 | 4 | timestamp |
+----+----+----+----+----+----+----+
blk :: 0
size :: 2 bytes, big endian, value = 4
timestamp :: Unix timestamp, unsigned seconds.
Wraps around in 2106
密钥派生函数 (KDF)(用于握手消息 1)
传递更新的选项。选项包括:最小和最大填充。
选项块长度是可变的。
+----+----+----+----+----+----+----+----+
| 1 | size |tmin|tmax|rmin|rmax|tdmy|
+----+----+----+----+----+----+----+----+
|tdmy| rdmy | tdelay | rdelay | |
~----+----+----+----+----+----+----+ ~
| more_options |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
blk :: 1
size :: 2 bytes, big endian, size of options to follow, 12 bytes minimum
tmin, tmax, rmin, rmax :: requested padding limits
tmin and rmin are for desired resistance to traffic analysis.
tmax and rmax are for bandwidth limits.
tmin and tmax are the transmit limits for the router sending this options block.
rmin and rmax are the receive limits for the router sending this options block.
Each is a 4.4 fixed-point float representing 0 to 15.9375
(or think of it as an unsigned 8-bit integer divided by 16.0).
This is the ratio of padding to data. Examples:
Value of 0x00 means no padding
Value of 0x01 means add 6 percent padding
Value of 0x10 means add 100 percent padding
Value of 0x80 means add 800 percent (8x) padding
Alice and Bob will negotiate the minimum and maximum in each direction.
These are guidelines, there is no enforcement.
Sender should honor receiver's maximum.
Sender may or may not honor receiver's minimum, within bandwidth constraints.
tdmy: Max dummy traffic willing to send, 2 bytes big endian, bytes/sec average
rdmy: Requested dummy traffic, 2 bytes big endian, bytes/sec average
tdelay: Max intra-message delay willing to insert, 2 bytes big endian, msec average
rdelay: Requested intra-message delay, 2 bytes big endian, msec average
Padding distribution specified as additional parameters?
Random delay specified as additional parameters?
more_options :: Format TBD
1) SessionRequest
- 选项格式待定。
- 选项协商待定。
RouterInfo
将 Alice 的 RouterInfo 传递给 Bob。用于握手消息 3 第 2 部分。将 Alice 的 RouterInfo 传递给 Bob,或将 Bob 的传递给 Alice。在数据阶段可选使用。
+----+----+----+----+----+----+----+----+
| 2 | size |flg | RouterInfo |
+----+----+----+----+ +
| (Alice RI in handshake msg 3 part 2) |
~ (Alice, Bob, or third-party ~
| RI in data phase) |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
blk :: 2
size :: 2 bytes, big endian, size of flag + router info to follow
flg :: 1 byte flags
bit order: 76543210
bit 0: 0 for local store, 1 for flood request
bits 7-1: Unused, set to 0 for future compatibility
routerinfo :: Alice's or Bob's RouterInfo
Notes
在数据阶段使用时,接收方(Alice 或 Bob)应验证这是与最初发送(对 Alice)或发送给(对 Bob)相同的 Router Hash。然后,将其视为本地 I2NP DatabaseStore Message。验证签名,验证更新的时间戳,并存储在本地 netDb 中。如果标志位 0 为 1,且接收方是 floodfill,则将其视为带有非零回复令牌的 DatabaseStore Message,并将其泛洪到最近的 floodfill。
Router Info 不使用 gzip 压缩 (与 DatabaseStore 消息中的情况不同,DatabaseStore 消息中是压缩的)
除非 RouterInfo 中有已发布的 RouterAddresses,否则不得请求泛洪。接收路由器不得泛洪 RouterInfo,除非其中包含已发布的 RouterAddresses。
实现者必须确保在读取块时, 格式错误或恶意数据不会导致读取 溢出到下一个块中。
此协议不提供 RouterInfo 已被接收、存储或泛洪的确认(无论在握手阶段还是数据阶段)。 如果需要确认,且接收方是 floodfill, 发送方应改为发送标准的 I2NP DatabaseStoreMessage 并携带回复令牌。
Issues
也可以在数据阶段使用,而不是使用I2NP DatabaseStoreMessage。 例如,Bob可以使用它来启动数据阶段。
是否允许此消息包含除发起者之外的其他 router 的 RI,作为 DatabaseStoreMessages 的通用替代,例如用于 floodfill 的泛洪?
密钥派生函数 (KDF)(用于握手消息 2 和消息 3 第 1 部分)
一个带有修改头部的单个 I2NP 消息。I2NP 消息不能在块之间或 ChaChaPoly 帧之间分片。
这使用了标准 NTCP I2NP 头部的前 9 个字节,并移除了头部的最后 7 个字节,具体如下:将过期时间从 8 字节截断为 4 字节,移除 2 字节长度(使用块大小减去 9),并移除 1 字节的 SHA256 校验和。
+----+----+----+----+----+----+----+----+
| 3 | size |type| msg id |
+----+----+----+----+----+----+----+----+
| short exp | message |
+----+----+----+----+ +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
blk :: 3
size :: 2 bytes, big endian, size of type + msg id + exp + message to follow
I2NP message body size is (size - 9).
type :: 1 byte, I2NP msg type, see I2NP spec
msg id :: 4 bytes, big endian, I2NP message ID
short exp :: 4 bytes, big endian, I2NP message expiration, Unix timestamp, unsigned seconds.
Wraps around in 2106
message :: I2NP message body
Notes
- 实现者必须确保在读取块时,格式错误或恶意数据不会导致读取操作溢出到下一个块中。
2) SessionCreated(会话已创建)
Noise 推荐使用显式终止消息。原始 NTCP 没有这种消息。断开连接。这必须是帧中最后一个非填充块。
+----+----+----+----+----+----+----+----+
| 4 | size | valid data frames |
+----+----+----+----+----+----+----+----+
received | rsn| addl data |
+----+----+----+----+ +
~ . . . ~
+----+----+----+----+----+----+----+----+
blk :: 4
size :: 2 bytes, big endian, value = 9 or more
valid data frames received :: The number of valid AEAD data phase frames received
(current receive nonce value)
0 if error occurs in handshake phase
8 bytes, big endian
rsn :: reason, 1 byte:
0: normal close or unspecified
1: termination received
2: idle timeout
3: router shutdown
4: data phase AEAD failure
5: incompatible options
6: incompatible signature type
7: clock skew
8: padding violation
9: AEAD framing error
10: payload format error
11: message 1 error
12: message 2 error
13: message 3 error
14: intra-frame read timeout
15: RI signature verification fail
16: s parameter missing, invalid, or mismatched in RouterInfo
17: banned
addl data :: optional, 0 or more bytes, for future expansion, debugging,
or reason text.
Format unspecified and may vary based on reason code.
Notes
并非所有原因都会实际使用,这取决于具体实现。握手失败通常会导致使用 TCP RST 关闭连接。请参见上述握手消息部分的说明。列出的其他原因是为了保持一致性、日志记录、调试或策略变更时使用。
Padding
这用于 AEAD 帧内的填充。消息 1 和消息 2 的填充在 AEAD 帧外。消息 3 和数据阶段的所有填充都在 AEAD 帧内。
AEAD 内部的填充应该大致遵循协商的参数。Bob 在消息 2 中发送了他请求的 tx/rx 最小/最大参数。Alice 在消息 3 中发送了她请求的 tx/rx 最小/最大参数。在数据阶段可能会发送更新的选项。请参阅上面的选项块信息。
如果存在,这必须是帧中的最后一个块。
+----+----+----+----+----+----+----+----+
|254 | size | padding |
+----+----+----+ +
| |
~ . . . ~
| |
+----+----+----+----+----+----+----+----+
blk :: 254
size :: 2 bytes, big endian, size of padding to follow
padding :: random data
Notes
- 填充策略待定。
- 最小填充待定。
- 允许仅填充帧。
- 填充默认值待定。
- 参见选项块中的填充参数协商
- 参见选项块中的最小/最大填充参数
- Noise 将消息限制为 64KB。如果需要更多填充,请发送多个帧。
- Router 对违反协商填充的响应取决于具体实现。
Other block types
为了向前兼容,实现应该忽略未知的块类型,但在消息 3 第 2 部分中除外,该部分不允许未知块。
Future work
- 填充长度应该基于每条消息决定并估算长度分布,或者应该添加随机延迟。这些对策措施旨在抵御 DPI,因为消息大小可能会暴露传输协议正在承载 I2P 流量。确切的填充方案是未来工作的一个领域,附录 A 提供了有关该主题的更多信息。
5) Termination
连接可以通过正常或异常的 TCP socket 关闭终止,或者按照 Noise 的建议,使用显式终止消息。显式终止消息在上述数据阶段中定义。
在任何正常或异常终止时,router 应当将所有内存中的临时数据清零,包括握手临时密钥、对称加密密钥以及相关信息。
Published Router Info
握手消息 3 第 1 部分的加密,使用消息 2 KDF)
已发布的 RouterAddress(RouterInfo 的一部分)将具有 “NTCP” 或 “NTCP2” 的协议标识符。
RouterAddress 必须包含"host"和"port"选项,就像当前的 NTCP 协议一样。
RouterAddress必须包含三个选项来表示NTCP2支持:
s=(Base64 key) 此 RouterAddress 的当前 Noise 静态公钥 (s)。 使用标准 I2P Base 64 字母表进行 Base 64 编码。 二进制格式 32 字节,Base 64 编码格式 44 字节, 小端序 X25519 公钥。
i=(Base64 IV) 当前用于在消息1中加密此RouterAddress的X值的IV。 使用标准I2P Base 64字母表进行Base 64编码。 二进制16字节,Base 64编码24字节, 大端序。
v=2 当前版本(2)。 当发布为"NTCP"时,暗示对版本1的额外支持。 未来版本的支持将使用逗号分隔的值, 例如 v=2,3 实现应验证兼容性,包括在存在逗号时的多个 版本。逗号分隔的版本必须按数字顺序排列。
Alice 必须在使用 NTCP2 协议连接之前验证所有三个选项都存在且有效。
当以 “NTCP” 发布并带有 “s”、“i” 和 “v” 选项时,路由器必须在该主机和端口上接受 NTCP 和 NTCP2 协议的传入连接,并自动检测协议版本。
当发布为带有"s”、“i"和"v"选项的"NTCP2"时,router 仅接受该主机和端口上 NTCP2 协议的传入连接。
如果一个 router 同时支持 NTCP1 和 NTCP2 连接,但未实现对传入连接的自动版本检测,则必须同时公布"NTCP"和"NTCP2"地址,并且仅在"NTCP2"地址中包含 NTCP2 选项。router 应该在"NTCP2"地址中设置更低的成本值(更高优先级),使 NTCP2 优先于"NTCP"地址。
如果在同一个RouterInfo中发布了多个NTCP2 RouterAddress(无论是"NTCP"还是"NTCP2”)(用于额外的IP地址或端口),所有指定相同端口的地址必须包含相同的NTCP2选项和值。特别是,所有地址都必须包含相同的静态密钥和iv。
密钥派生函数 (KDF)(用于握手消息 3 第 2 部分)
如果 Alice 不发布她的 NTCP2 地址(作为 “NTCP” 或 “NTCP2”)用于传入连接,她必须发布一个 “NTCP2” 路由器地址,该地址仅包含她的静态密钥和 NTCP2 版本,这样 Bob 就可以在消息 3 第 2 部分中收到 Alice 的 RouterInfo 后验证该密钥。
s=(Base64 key) 如上文发布地址中所定义。
v=2 如上所述,用于已发布的地址。
此 router 地址将不包含 “i”、“host” 或 “port” 选项,因为这些对于出站 NTCP2 连接并非必需。此地址的已发布成本并不严格重要,因为它仅用于入站;但是,如果成本设置得比其他地址更高(优先级更低),可能对其他 router 有帮助。建议值为 14。
Alice 也可以简单地将"s"和"v"选项添加到现有已发布的"NTCP"地址中。
3) SessionConfirmed(会话确认)
由于RouterInfo的缓存机制,router在运行期间不得轮换静态公钥或IV,无论是否在已发布的地址中。Router必须持久存储此密钥和IV以便在立即重启后重用,这样传入连接将继续工作,且重启时间不会暴露。Router必须持久存储或以其他方式确定上次关闭时间,以便在启动时计算之前的停机时间。
考虑到暴露重启时间的担忧,如果 router 之前已经停机一段时间(至少几个小时),router 可能会在启动时轮换此密钥或 IV。
如果 router 发布了任何 NTCP2 RouterAddresses(作为 NTCP 或 NTCP2),轮换前的最小停机时间应该更长,例如一个月,除非本地 IP 地址已更改或 router “重新生成密钥”。
如果路由器发布了任何SSU RouterAddresses,但没有NTCP2(作为NTCP或NTCP2),那么轮换前的最短停机时间应该更长,例如一天,除非本地IP地址已更改或路由器"重新生成密钥"。即使发布的SSU地址具有介绍者,这也适用。
如果router没有任何已发布的RouterAddresses(NTCP、NTCP2或SSU),那么轮换前的最短停机时间可能只有两小时,即使IP地址发生变化,除非router进行"重新密钥生成"。
如果 router “重新生成密钥"到不同的 Router Hash,它也应该生成新的 noise 密钥和 IV。
实现必须意识到,更改静态公钥或IV将阻止来自缓存了旧RouterInfo的router的传入NTCP2连接。RouterInfo发布、隧道对等体选择(包括OBGW和IB最接近跳点)、零跳隧道选择、传输选择以及其他实现策略都必须考虑到这一点。
IV轮换遵循与密钥轮换相同的规则,只是IV仅存在于已发布的RouterAddresses中,因此隐藏或防火墙后的router没有IV。如果有任何变化(版本、密钥、选项?),建议IV也随之变化。
注意:重新生成密钥前的最短停机时间可能会被修改以确保网络健康,并防止因适度时间停机的 router 进行重新播种。
Identity Hiding
可否认性不是目标。请参见上面的概述。
每个模式都被分配了属性,描述了提供给发起方静态公钥和响应方静态公钥的机密性。基本假设是临时私钥是安全的,并且如果各方从对方接收到他们不信任的静态公钥,会中止握手。
本节仅考虑通过握手中的静态公钥字段导致的身份泄露。当然,Noise参与者的身份可能会通过其他方式暴露,包括载荷字段、流量分析或诸如IP地址等元数据。
Alice:(8) 使用前向保密加密到已认证的一方。
Bob:(3) 不会传输,但被动攻击者可以检查响应者私钥的候选值,并确定候选值是否正确。
Bob 在 netDb 中发布他的静态公钥。Alice 可能会也可能不会?
Issues
- 如果 Bob 更改了他的静态密钥,是否可以回退到 “XX” 模式?
Noise Protocol Framework
当发布为"NTCP"时,router必须自动检测传入连接的协议版本。
这种检测依赖于具体实现,但这里提供一些一般性指导。
为了检测传入 NTCP 连接的版本,Bob 按如下步骤进行:
等待至少 64 字节(NTCP2 消息 1 的最小大小)
如果初始接收到的数据是 288 字节或更多,则传入连接是版本 1
如果少于 288 字节,则
等待一段时间以获取更多数据(在 NTCP2 广泛采用之前的良好策略),如果总共至少接收到 288 字节,则为 NTCP 1。
尝试将解码的第一阶段作为版本 2,如果失败,等待短时间获取更多数据(在 NTCP2 广泛采用后的良好策略)
- Decrypt the first 32 bytes (the X key) of the SessionRequest packet using AES-256 with key RH_B. - Verify a valid point on the curve. If it fails, wait a short time for more data for NTCP 1 - Verify the AEAD frame. If it fails, wait a short time for more data for NTCP 1
请注意,如果我们检测到针对 NTCP 1 的活跃 TCP 分段攻击,可能会建议进行更改或采用其他策略。
为了促进快速版本检测和握手,实现必须确保Alice缓冲然后一次性刷新第一条消息的全部内容,包括填充。这增加了数据被包含在单个TCP数据包中的可能性(除非被操作系统或中间盒分段),并被Bob一次性接收。这也是为了提高效率并确保随机填充的有效性。这适用于NTCP和NTCP2握手。
框架增补功能
如果 Alice 和 Bob 都支持 NTCP2,Alice 应该使用 NTCP2 进行连接。
如果 Alice 因任何原因无法使用 NTCP2 连接到 Bob,连接将失败。 Alice 不能重试使用 NTCP 1。
如果 Bob 更改了他的密钥,是否回退到 XX 模式?这需要预先添加一个类型字节?
如果Alice重新连接时"向前降级"到KK模式,假设Bob仍然拥有她的静态密钥?这样做不会节省任何往返次数,并且使用4轮DH交换,相比之下XK只需要3轮。可能不值得。
KK(s, rs):
-> s
<- s
...
-> e, es, ss
<- e, ee, se
I2P 的新加密原语
本节讨论了一种针对典型填充方案的攻击,该攻击允许攻击者仅通过观察填充消息的长度来发现未填充消息长度的概率分布。设 N 为描述未填充字节数的随机变量,P 同样为填充字节数的随机变量。那么总消息大小为 N + P。
假设对于未填充大小为 n 的情况,在填充方案中至少添加 P_min(n) >= 0 字节的填充,最多添加 P_max(n) >= P_min(n) 字节的填充。显而易见的方案是使用长度为 P 的填充,其中 P 是均匀随机选择的:
Pr[P = p | N = n] = 1 / (P_max(n) - P_min(n)) if P_min(n) <= p <= P_max(n),
0 otherwise.
一个简单的填充方案只需确保填充后消息的大小不超过 N_max:
P_max(n) = N_max - n, n <= N_max
P_min(n) = 0.
然而,这会泄露未填充长度的信息。
攻击者可以轻易估计 Pr[x <= N + P <= y],例如通过直方图的方式。
- 由此,他也可以尝试估计
Pr[n_1 <= N <= n_2],实际上:
Pr[N + P = m] = Σ_n Pr[N = n] Pr[P = m - n | N = n].
在简单方案中,
Pr[N + P = m] = Σ_{n <= m} Pr[N = n] / (N_max - n).
很明显,就像在进行上述计算之前一样,这会泄露关于 Pr[N = n] 的信息:如果数据包的长度几乎总是大于 m,那么几乎永远不会观察到 N + P <= m。不过这并不是最大的问题,尽管能够观察到最小消息长度本身就可以被认为是一个问题。
一个更大的问题是可以准确确定 Pr[N = n]:
Pr[N + P = m] - Pr[N + P = m-1] = Pr[N = m] / (N_max - m),
也就是说
Pr[N = n] = (N_max - n)(Pr[N + P = n] - Pr[N + P = n - 1])
因此,为了识别 NTCP2,攻击者可以使用以下任何方法:
估算正整数 k 的
Pr[kB <= N <= (k + 1)B - 1]。对于 NTCP2,该值始终为零。估算
Pr[N = kB]并与标准 I2P profile 进行比较。
因此,这种简单的攻击部分破坏了填充的目的,填充试图混淆未填充消息的大小分布。攻击者需要观察多少消息才能区分协议,取决于所需的准确性以及实践中出现的未填充消息的最小和最大大小。请注意,攻击者很容易收集到大量消息,因为他可以使用目标所使用的特定端口发送和接收的所有流量。
在某些形式中(例如估计 Pr[kB <= N <= (k + 1)B - 1]),攻击只需要几个字节的内存(一个整数就足够了),可以认为这种攻击可能被包含在许多稍微高级但仍然是标准的 DPI 框架中。
本提案建议使用以下对策之一:
开发一个替代填充方案,该方案考虑到N的(估计)分布,使用非均匀填充长度分布。一个好的填充方案可能需要维护每条消息块数量的直方图。
在消息的(随机大小)片段之间添加随机延迟。
第二种选择通常更受青睐,因为它可以同时用作对抗流量分析的对策。然而,这种延迟可能超出了 NTCP2 协议的范围,因此第一种选择可能更受青睐,因为它也更容易实现。
处理开销估算
基于时序的DPI抗性(消息间时序/延迟可能依赖于具体实现;消息内延迟可以在任何点引入,例如在发送随机填充之前)。人工延迟(obfs4称之为IAT或到达间隔时间)独立于协议本身。