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

NTCP2 传输

基于噪声协议的 TCP 传输用于 router 到 router 链接

概述

NTCP2 是一个经过身份验证的密钥协商协议,它提高了 NTCP 对各种形式的自动化识别和攻击的抵抗能力。

NTCP2 设计为具有灵活性并能与 NTCP 共存。它可以在与 NTCP 相同的端口上支持,也可以在不同的端口上支持,或者完全不同时支持 NTCP。详细信息请参见下面的已发布 router 信息部分。

与其他I2P传输协议一样,NTCP2仅用于I2NP消息的点对点(router到router)传输。它不是通用的数据管道。

从版本 0.9.36 开始支持 NTCP2。请参阅 Prop111 获取原始提案,包括背景讨论和附加信息。

Noise 协议框架

NTCP2 使用 Noise Protocol Framework NOISE (修订版 33,2017-10-04)。Noise 具有与 Station-To-Station 协议 STS 类似的特性,后者是 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 字节随机数,前 4 字节设置为零。
  • Hash Function: SHA256 标准 32 字节哈希,在 I2P 中已广泛使用。

框架的新增功能

NTCP2 定义了对 Noise_XK_25519_ChaChaPoly_SHA256 的以下增强。这些通常遵循 NOISE 第13节中的指导原则。

  1. 明文临时密钥使用已知密钥和IV通过AES加密进行混淆。2) 向消息1和消息2添加随机明文填充。明文填充包含在握手哈希(MixHash)计算中。请参见下面消息2和消息3第1部分的KDF部分。向消息3和数据阶段消息添加随机AEAD填充。3) 添加两字节帧长度字段,这是TCP上Noise协议所必需的,也如obfs4中一样。这仅用于数据阶段消息。消息1和消息2的AEAD帧为固定长度。消息3第1部分AEAD帧为固定长度。消息3第2部分AEAD帧长度在消息1中指定。4) 两字节帧长度字段使用SipHash-2-4进行混淆,如obfs4中一样。5) 为消息1、2、3和数据阶段定义载荷格式。当然,这些在框架中并未定义。

消息

所有 NTCP2 消息长度都小于或等于 65537 字节。消息格式基于 Noise 消息,但为了帧结构和不可区分性进行了修改。使用标准 Noise 库的实现可能需要对接收到的消息进行预处理,使其符合 Noise 消息格式。所有加密字段都是 AEAD 密文。

建立序列如下:

Alice Bob

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

使用 Noise 术语,建立和数据序列如下:(载荷安全属性来自 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 = Router Hash for Alice (32 bytes)
- RH_B = Router Hash for Bob (32 bytes)

认证加密

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

加密/认证数据将表示为

+----+----+----+----+----+----+----+----+

|                                       |

    + + | Encrypted and authenticated data | ~ . . . ~ | | +----+----+----+----+----+----+----+----+

ChaCha20/Poly1305

加密和认证的数据格式。

加密/解密函数的输入:

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 |](##SUBST##|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 中也有类似的使用。

注意事项

  • 由于 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应该设置一个随机超时(范围待定)然后读取随机数量的字节(范围待定)再关闭socket。Bob应该维护一个重复失败IP的黑名单。
  • 在数据阶段,AEAD消息大小是用SipHash"加密"(混淆)的。必须注意避免创建解密预言机。当数据阶段AEAD认证失败时,接收方应该设置一个随机超时(范围待定)然后读取随机数量的字节(范围待定)。读取后,或读取超时时,接收方应该发送一个包含"AEAD失败"原因码的终止块的载荷,然后关闭连接。
  • 对于数据阶段中无效长度字段值,采取相同的错误处理行为。

密钥派生函数 (KDF)(用于握手消息 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.

1) SessionRequest

Alice 发送给 Bob。

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

(载荷安全属性来自 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 作为密钥,以及 Bob 在网络数据库中发布的 IV。

AES加密仅用于抵抗DPI(深度包检测)。任何知道Bob的router哈希值和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.

选项块:注意:所有字段都是大端序。

+----+----+----+----+----+----+----+----+

| 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 is implementation-dependent)

    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

注释

  • 当发布的地址为"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 字节的随机大小?(分布取决于实现)Java 实现目前将填充限制为最大 256 字节。

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

  • Bob 可以在尝试解密之前对有效密钥进行快速 MSB 检查(X[31] & 0x80 == 0)。如果高位被设置,则像处理 AEAD 失败一样实施探测抗性。

  • DoS 缓解:DH 是一个相对昂贵的操作。与之前的 NTCP 协议一样,router 应该采取所有必要措施来防止 CPU 或连接耗尽。对最大活动连接数和正在进行的最大连接建立数设置限制。强制执行读取超时(包括单次读取和"slowloris"攻击的总超时)。限制来自同一源的重复或同时连接。为重复失败的源维护黑名单。不要响应 AEAD 失败。

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

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

  • 消息3第2部分长度:这是第二个AEAD帧的大小(包括16字节MAC),包含Alice的Router Info和可选填充,将在SessionConfirmed消息中发送。由于router会定期重新生成和重新发布其Router Info,当前Router Info的大小可能在发送消息3之前发生变化。实现必须选择以下两种策略之一:

a) 保存当前要在消息3中发送的 Router Info,以便知道大小,并可选择性地为填充添加空间;

b) 增加指定的大小以容纳 Router Info 大小可能的增长,并且在实际发送消息3时始终添加填充。无论哪种情况,消息1中包含的"m3p2len"长度必须与消息3中发送该帧时的大小完全一致。

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

  • network ID 字段用于快速识别跨网络连接。如果此字段非零,且与 Bob 的 network ID 不匹配,Bob 应断开连接并阻止未来的连接。来自测试网络的任何连接都应具有不同的 ID,并将无法通过测试。自 0.9.42 版本起生效。更多信息请参见提案 147。

密钥派生函数(KDF)(用于握手消息2和消息3第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]_
// 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 载荷:随机填充

(载荷安全属性来自 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 哈希作为密钥,以及来自消息 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.

注释

  • Alice必须验证Bob的临时密钥是曲线上的有效点。
  • 填充应限制在合理范围内。Alice可以拒绝包含过度填充的连接。Alice将在消息3中指定她的填充选项。最小/最大准则待定。最少从0到31字节的随机大小?(分布取决于实现)
  • 在任何错误情况下,包括AEAD、DH、时间戳、明显重放或密钥验证失败,Alice必须停止进一步的消息处理并关闭连接而不响应。这应该是异常关闭(TCP RST)。
  • 为了促进快速握手,实现必须确保Bob缓冲然后一次性刷新第一条消息的全部内容,包括填充。这增加了数据包含在单个TCP数据包中的可能性(除非被操作系统或中间设备分段),并被Alice一次性接收。这也是为了效率并确保随机填充的有效性。
  • 如果在验证消息2和读取填充后仍有任何传入数据,Alice必须使连接失败。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 is implementation-dependent)

    tsB :: 4 bytes, big endian, Unix timestamp, unsigned seconds.

    :   Wraps around in 2106

注意事项

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

问题

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

使用 message 2 KDF 对握手消息 3 第 1 部分进行加密

// 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.

密钥派生函数 (KDF) (用于握手消息 3 第 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 载荷:无

(来自 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

注释

  • 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 第 2 部分帧长度:此帧的长度(包括 MAC)由 Alice 在消息 1 中发送。请参阅该消息中关于为填充预留足够空间的重要说明。

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

  1. Alice 的 Router Info 块(必需) 2) 选项块(可选)

3) 填充块(可选)此帧绝不能包含任何其他块类型。

  • 如果 Alice 在消息 3 的末尾附加一个数据阶段帧(可选择包含填充)并同时发送两者,则不需要消息 3 第 2 部分填充,因为对观察者来说这将显示为一个大的字节流。由于 Alice 通常(但不总是)会有一个 I2NP 消息要发送给 Bob(这就是她连接他的原因),为了效率和确保随机填充的有效性,这是推荐的实现方式。

  • Message 3 两个 AEAD 帧(第1部分和第2部分)的总长度为 65535 字节;第1部分为 48 字节,所以第2部分最大帧长度为 65487;第2部分不包括 MAC 的最大明文长度为 65471。

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

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

KDF 从链式密钥 ck 生成两个密码密钥 k_ab 和 k_ba,使用 RFC-2104 中定义的 HMAC-SHA256(key, data)。这是 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) 数据阶段

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

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

注意:Bob 可以但不是必须在数据阶段向 Alice 发送的第一条消息中包含他的 RouterInfo。

(来自 Noise 的有效载荷安全属性)

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.

注释

  • 为了提高效率并最小化长度字段的识别可能性,实现必须确保发送方缓冲然后一次性刷新数据消息的全部内容,包括长度字段和 AEAD 帧。这增加了数据包含在单个 TCP 数据包中的可能性(除非被操作系统或中间件分段),并被对方一次性接收。这也是为了提高效率并确保随机填充的有效性。
  • router 可以选择在 AEAD 错误时终止会话,或者可以继续尝试通信。如果继续,router 应该在重复错误后终止。

SipHash 混淆长度

参考: SipHash

一旦双方完成握手,它们就会传输负载,然后在 ChaChaPoly “帧"中进行加密和身份验证。

每个帧都以一个两字节的长度字段开头,采用大端序。该长度指定了后续加密帧字节的数量,包括MAC。为了避免在流中传输可识别的长度字段,帧长度通过与从数据阶段KDF初始化的SipHash派生的掩码进行异或运算来混淆。请注意,两个方向都有来自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 |](##SUBST##|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.

注释

  • 由于接收方必须获得整个帧才能检查MAC,建议发送方将帧限制在几KB而不是最大化帧大小。这将最小化接收方的延迟。

未加密数据

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

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

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

(未显示 Poly1305 认证标签):

+----+----+----+----+----+----+----+----+

[|blk |](##SUBST##|blk |) size | data | +----+----+----+ + | | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ [|blk |](##SUBST##|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.

区块排序规则

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

在数据阶段,顺序未指定,但有以下要求:如果存在填充,它必须是最后一个块。如果存在终止,它必须是除填充之外的最后一个块。

单个帧中可能包含多个 I2NP 块。单个帧中不允许有多个填充块。其他块类型在单个帧中可能不会有多个块,但这并不被禁止。

日期时间

时间同步的特殊情况:

+----+----+----+----+----+----+----+

| 0 | 4 | timestamp |

    +----+----+----+----+----+----+----+

    blk :: 0 size :: 2 bytes, big endian, value = 4 timestamp :: Unix timestamp, unsigned seconds. Wraps around in 2106

注意:实现必须舍入到最近的秒,以防止网络中的时钟偏差。

选项

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

选项块将是可变长度的。

+----+----+----+----+----+----+----+----+

| 1 | size [|tmin|](##SUBST##|tmin|)tmax[|rmin|](##SUBST##|rmin|)rmax[|tdmy|](##SUBST##|tdmy|)

    +----+----+----+----+----+----+----+----+ [|tdmy|](##SUBST##|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

选项问题

  • 选项格式待定。
  • 选项协商待定。

RouterInfo

将 Alice 的 RouterInfo 传递给 Bob。用于握手消息 3 第 2 部分。将 Alice 的 RouterInfo 传递给 Bob,或将 Bob 的传递给 Alice。在数据阶段可选使用。

+----+----+----+----+----+----+----+----+

| 2 | size [|flg |](##SUBST##|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

注释

  • 当在数据阶段使用时,接收方(Alice 或 Bob)应验证这与最初发送的(对于 Alice)或发送给的(对于 Bob)Router Hash 相同。然后,将其视为本地 I2NP DatabaseStore Message。验证签名,验证更新的时间戳,并存储在本地 netDb 中。如果标志位 0 为 1,且接收方是 floodfill,则将其视为带有非零回复令牌的 DatabaseStore Message,并将其泛洪到最近的 floodfill。
  • Router Info 不使用 gzip 压缩(与 DatabaseStore Message 中的不同,后者是压缩的)
  • 除非 RouterInfo 中有已发布的 RouterAddresses,否则不得请求泛洪。接收方 router 除非其中有已发布的 RouterAddresses,否则不得泛洪 RouterInfo。
  • 实现者必须确保在读取块时,格式错误或恶意数据不会导致读取溢出到下一个块。
  • 此协议不提供 RouterInfo 已接收、存储或泛洪的确认(无论是在握手阶段还是数据阶段)。如果需要确认,且接收方是 floodfill,发送方应改为发送带有回复令牌的标准 I2NP DatabaseStoreMessage。

问题

  • 也可以在数据阶段使用,而不是 I2NP DatabaseStoreMessage。例如,Bob 可以使用它来启动数据阶段。
  • 是否允许包含除发起者之外的其他 router 的 RI,作为 DatabaseStoreMessage 的通用替代品,例如用于 floodfill 的泛洪?

I2NP 消息

一个带有修改过头部的单个 I2NP 消息。I2NP 消息不能跨块或跨 ChaChaPoly 帧进行分片。

这使用了标准 NTCP I2NP 头部的前9个字节,并移除了头部的最后7个字节,具体如下:将过期时间从8字节缩短到4字节(使用秒而不是毫秒,与 SSU 相同),移除2字节的长度字段(使用块大小减去9),并移除1字节的 SHA256 校验和。

+----+----+----+----+----+----+----+----+

| 3 | size [|type|](##SUBST##|type|) msg id |

    +-------------------------------+
    | > short exp                   |
    +-------------------------------+

    ~ . . . ~ | | +----+----+----+----+----+----+----+----+

    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

注意事项

  • 实现者必须确保在读取块时,格式错误或恶意数据不会导致读取越界到下一个块。

终止

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.

说明

不是所有原因都会被实际使用,这取决于具体实现。握手失败通常会导致使用 TCP RST 关闭连接。请参阅上述握手消息部分的说明。列出的额外原因是为了保持一致性、日志记录、调试或应对策略变更。

填充

这是用于 AEAD 帧内部的填充。消息 1 和消息 2 的填充位于 AEAD 帧外部。消息 3 和数据阶段的所有填充都位于 AEAD 帧内部。

AEAD 内部的填充应该大致遵循协商的参数。Bob 在消息 2 中发送了他请求的 tx/rx 最小/最大参数。Alice 在消息 3 中发送了她请求的 tx/rx 最小/最大参数。更新的选项可以在数据阶段发送。请参阅上面的选项块信息。

如果存在,这必须是帧中的最后一个块。

+----+----+----+----+----+----+----+----+

[|254 |](##SUBST##|254 |) size | padding | +----+----+----+ + | | ~ . . . ~ | | +----+----+----+----+----+----+----+----+

    blk :: 254 size :: 2 bytes, big endian, size of padding to follow padding :: random data

注意事项

  • 允许大小 = 0。
  • 填充策略待定。
  • 最小填充待定。
  • 允许仅填充帧。
  • 填充默认值待定。
  • 参见选项块中的填充参数协商
  • 参见选项块中的最小/最大填充参数
  • Noise 将消息限制为 64KB。如果需要更多填充,请发送多个帧。
  • router 对违反协商填充的响应取决于实现。

其他区块类型

实现应忽略未知的块类型以保证向前兼容性,但在消息3第2部分中除外,该部分不允许未知块。

未来工作

  • 填充长度应该根据每条消息的具体情况和长度分布的估算来决定,或者应该添加随机延迟。这些对抗措施需要包含在内以抵御深度包检测(DPI),因为消息大小会暴露传输协议正在承载I2P流量。确切的填充方案是未来工作的一个领域。

5) 终止

连接可以通过正常或异常的 TCP 套接字关闭来终止,或者如 Noise 建议的那样,使用显式终止消息。显式终止消息在上述数据阶段中定义。

在任何正常或异常终止时,router 应当将所有内存中的临时数据清零,包括握手临时密钥、对称加密密钥和相关信息。

已发布的 Router 信息

功能特性

从 0.9.50 版本开始,NTCP2 地址支持 “caps” 选项,类似于 SSU。可以在 “caps” 选项中发布一个或多个功能。功能可以按任意顺序排列,但建议使用 “46” 顺序,以保持各实现间的一致性。定义了两种功能:

4:表示出站IPv4能力。如果在主机字段中发布了IP地址,则不需要此能力。如果router是隐藏的,或者NTCP2仅用于出站,‘4’和'6’可以在单个地址中组合使用。

6:表示出站 IPv6 能力。如果在主机字段中发布了 IP 地址,则不需要此能力标识。如果 router 处于隐藏状态,或者 NTCP2 仅支持出站连接,则可以在单个地址中组合使用 ‘4’ 和 ‘6’。

已发布地址

已发布的 RouterAddress(RouterInfo 的一部分)将具有 “NTCP” 或 “NTCP2” 的协议标识符。

RouterAddress 必须包含 “host” 和 “port” 选项,就像当前的 NTCP 协议一样。

RouterAddress 必须包含三个选项以表明支持 NTCP2:

  • s=(Base64 密钥) 此 RouterAddress 当前的 Noise 静态公钥 (s)。使用标准 I2P Base 64 字母表进行 Base 64 编码。二进制 32 字节,Base 64 编码 44 字节,小端序 X25519 公钥。
  • i=(Base64 IV) 此 RouterAddress 在消息 1 中加密 X 值的当前 IV。使用标准 I2P Base 64 字母表进行 Base 64 编码。二进制 16 字节,Base 64 编码 24 字节,大端序。
  • v=2 当前版本 (2)。当发布为 “NTCP” 时,暗示对版本 1 的额外支持。对未来版本的支持将使用逗号分隔的值,例如 v=2,3 实现应该验证兼容性,包括存在逗号时的多个版本。逗号分隔的版本必须按数字顺序排列。

Alice 必须验证所有三个选项都存在且有效,然后才能使用 NTCP2 协议进行连接。

当发布为带有"s”、“i"和"v"选项的"NTCP"时,router必须在该主机和端口上接受NTCP和NTCP2协议的传入连接,并自动检测协议版本。

当以"NTCP2"发布并带有"s”、“i"和"v"选项时,router仅在该主机和端口上接受NTCP2协议的传入连接。

如果一个 router 同时支持 NTCP1 和 NTCP2 连接,但没有实现对传入连接的自动版本检测,它必须同时通告 “NTCP” 和 “NTCP2” 地址,并且仅在 “NTCP2” 地址中包含 NTCP2 选项。router 应该在 “NTCP2” 地址中设置比 “NTCP” 地址更低的成本值(更高优先级),以便优先选择 NTCP2。

如果在同一个 RouterInfo 中发布了多个 NTCP2 RouterAddresses(无论是作为"NTCP"还是"NTCP2”)(用于额外的 IP 地址或端口),所有指定相同端口的地址必须包含相同的 NTCP2 选项和值。特别是,所有地址都必须包含相同的静态密钥和 iv。

未发布的 NTCP2 地址

如果 Alice 不发布她的 NTCP2 地址(作为"NTCP"或"NTCP2")用于传入连接,她必须发布一个"NTCP2" router 地址,该地址仅包含她的静态密钥和 NTCP2 版本,以便 Bob 在消息 3 第 2 部分中接收到 Alice 的 RouterInfo 后能够验证密钥。

  • s=(Base64密钥) 如上面发布地址中所定义。
  • v=2 如上面发布地址中所定义。

此 router 地址将不包含 “i”、“host” 或 “port” 选项,因为出站 NTCP2 连接不需要这些选项。此地址的发布成本并不严格重要,因为它仅用于入站连接;但是,如果将成本设置得比其他地址更高(优先级更低),可能对其他 router 有帮助。建议值为 14。

Alice 也可以简单地将"s"和"v"选项添加到现有已发布的"NTCP"地址中。

公钥和IV轮换

由于RouterInfos的缓存机制,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 发布、tunnel 对等点选择(包括 OBGW 和 IB 最近跳)、零跳 tunnel 选择、传输选择和其他实现策略都必须考虑到这一点。

IV轮换遵循与密钥轮换相同的规则,除了IV仅存在于已发布的RouterAddresses中,因此隐藏或防火墙后的router没有IV。如果有任何变化(版本、密钥、选项?),建议IV也要随之改变。

注意:重新生成密钥前的最短停机时间可能会被修改,以确保网络健康,并防止停机时间适中的 router 重新获取种子。

版本检测

当发布为"NTCP"时,router必须自动检测传入连接的协议版本。

这种检测是依赖于具体实现的,但这里有一些通用指导。

为了检测传入 NTCP 连接的版本,Bob 按以下步骤进行:

  • 等待至少 64 字节(最小 NTCP2 消息 1 大小)

  • 如果初始接收到的数据是 288 字节或更多,则传入连接是版本 1。

  • 如果小于 288 字节,则

  • 等待短时间以获取更多数据(在NTCP2广泛采用之前的良好策略),如果总共接收到至少288字节,则为NTCP 1。 > > - 尝试按版本2进行解码的前几个阶段,如果失败,等待短时间以获取更多数据(在NTCP2广泛采用之后的良好策略) > > > - 使用AES-256和密钥RH_B解密SessionRequest数据包的前32字节(X密钥)。 > > - 验证曲线上的有效点。如果失败,等待短时间以获取更多NTCP 1数据 > > - 验证AEAD帧。如果失败,等待短时间以获取更多NTCP 1数据

请注意,如果我们检测到对 NTCP 1 的主动 TCP 分段攻击,可能会建议进行更改或采用其他策略。

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

变体、后备方案和通用问题

  • 如果Alice和Bob都支持NTCP2,Alice应该使用NTCP2进行连接。
  • 如果Alice因任何原因无法使用NTCP2连接到Bob,连接将失败。Alice不得使用NTCP 1重试。

时钟偏差指南

节点时间戳包含在前两个握手消息中,即 Session Request 和 Session Created。两个节点之间大于 +/- 60 秒的时钟偏差通常是致命的。如果 Bob 认为他的本地时钟有问题,他可以使用计算出的偏差或某些外部源来调整他的时钟。否则,即使超出了最大偏差,Bob 也应该回复 Session Created,而不是简单地关闭连接。这允许 Alice 获取 Bob 的时间戳并计算偏差,必要时采取行动。此时 Bob 没有 Alice 的 router 身份,但为了节省资源,Bob 可能希望在一段时间内禁止来自 Alice IP 的传入连接,或者在多次连接尝试都存在过大偏差后进行禁止。

Alice应该通过减去一半的RTT来调整计算出的时钟偏差。如果Alice认为她的本地时钟有问题,她可以使用计算出的偏差或某些外部源来调整她的时钟。如果Alice认为Bob的时钟有问题,她可以在一段时间内禁止Bob。无论哪种情况,Alice都应该关闭连接。

如果Alice确实回复了Session Confirmed(可能是因为时钟偏差非常接近60秒的限制,而且由于RTT的影响,Alice和Bob的计算结果并不完全相同),Bob应该通过减去RTT的一半来调整计算出的时钟偏差。如果调整后的时钟偏差超过最大值,Bob应该回复一个包含时钟偏差原因代码的Disconnect消息,并关闭连接。此时,Bob已经获得了Alice的router身份,可以在一段时间内封禁Alice。

参考资料

Was this page helpful?