diff --git a/i2p2www/spec/proposals/159-ssu2.rst b/i2p2www/spec/proposals/159-ssu2.rst index 8be341fcb827c0bc70f503098f450b3240f7da73..5cdf8d6158a5a27c86042d6d8917183ca3f6bd1c 100644 --- a/i2p2www/spec/proposals/159-ssu2.rst +++ b/i2p2www/spec/proposals/159-ssu2.rst @@ -5,7 +5,7 @@ SSU2 :author: eyedeekay, orignal, zlatinb, zzz :created: 2021-09-12 :thread: http://zzz.i2p/topics/2612 - :lastupdated: 2022-08-31 + :lastupdated: 2022-10-12 :status: Open :target: 0.9.56 @@ -16,7 +16,7 @@ SSU2 Status ======== -Preliminary rollout plan: +Rollout plan: ========================== ===================== ==================== @@ -33,11 +33,13 @@ New Token 0.9.55 2022-08 0.9.57 2022-11 Freeze extended protocol 0.9.55 2022-08 Relay 0.9.55 2022-08 0.9.56 2022-11 Peer Test 0.9.55 2022-08 0.9.56 2022-11 +Enable for random 2% 0.9.55 2022-08 Path Validation 0.9.55+ dev 0.9.56 2022-11 Connection Migration 0.9.55+ dev 0.9.56 2022-11 Immediate ACK flag 0.9.55+ dev 0.9.56 2022-11 Key Rotation 0.9.57 2023-02 0.9.58 2023-05 -Disable SSU 1 0.9.58 2023-05 0.9.59 2023-08 +Disable SSU 1 (i2pd) 0.9.56 2022-11 +Disable SSU 1 (Java I2P) 0.9.58 2023-05 0.9.59 2023-08 ========================== ===================== ==================== Basic Session includes the handshake and data phase. diff --git a/i2p2www/spec/ssu2.rst b/i2p2www/spec/ssu2.rst index fd8c90fae36783006d83e0abcc9e87abf0305601..808c1cc68a77467309155ddc8f2a4fc5c9c26e1d 100644 --- a/i2p2www/spec/ssu2.rst +++ b/i2p2www/spec/ssu2.rst @@ -3,8 +3,8 @@ SSU2 ====== .. meta:: :category: Transports - :lastupdated: 2022-08 - :accuratefor: 0.9.55 + :lastupdated: 2022-10 + :accuratefor: 0.9.56 .. contents:: @@ -13,7 +13,6004 @@ SSU2 Status ======== -Development and testing in progress. See [Prop159]_. +Testing in progress. See [Prop159]_ for additional background and goals, +including security analysis, threat models, a review of SSU 1 security and issues, +and excerpts of the QUIC specifications. + +Rollout plan: + + +========================== ===================== ==================== + Feature Testing (not default) Enabled by default +========================== ===================== ==================== +Local test code 2022-02 +Joint test code 2022-03 +Joint test in-net 0.9.54 2022-05 +Freeze basic protocol 0.9.54 2022-05 +Basic Session 0.9.55 2022-08 0.9.56 2022-11 +Address Validation (Retry) 0.9.55 2022-08 0.9.56 2022-11 +Fragmented RI in handshake 0.9.55 2022-08 0.9.56 2022-11 +New Token 0.9.55 2022-08 0.9.57 2022-11 +Freeze extended protocol 0.9.55 2022-08 +Relay 0.9.55 2022-08 0.9.56 2022-11 +Peer Test 0.9.55 2022-08 0.9.56 2022-11 +Enable for random 2% 0.9.55 2022-08 +Path Validation 0.9.55+ dev 0.9.56 2022-11 +Connection Migration 0.9.55+ dev 0.9.56 2022-11 +Immediate ACK flag 0.9.55+ dev 0.9.56 2022-11 +Key Rotation 0.9.57 2023-02 0.9.58 2023-05 +Disable SSU 1 (i2pd) 0.9.56 2022-11 +Disable SSU 1 (Java I2P) 0.9.58 2023-05 0.9.59 2023-08 +========================== ===================== ==================== + +Basic Session includes the handshake and data phase. +Extended protocol includes relay and peer test. + + + +Overview +======== + +This proposal describes an authenticated key agreement protocol to improve the +resistance of [SSU]_ to various forms of automated identification and attacks. + +The proposal is organized as follows: the security goals are presented, +followed by a discussion of the basic protocol. Next, a complete specification +of all protocol messages is given. Finally, router addresses and version +identification are discussed. + +As with other I2P transports, SSU2 is defined +for point-to-point (router-to-router) transport of I2NP messages. +It is not a general-purpose data pipe. +Like [SSU]_, it also provides two additional services: +Relaying for NAT traversal, and Peer Testing for determination of inbound reachability. +It also provides a third service, not in SSU, for connection migration +when a peer changes IP or port. + + + +Design Overview +==================== + +Summary +-------- + +We rely on several existing protocols, both within I2P and outside standards, +for inspiration, guidance, and code reuse: + +* Threat models: From NTCP2 [NTCP2]_, with significant additional threats + relevant to UDP transport as analyzed by QUIC [RFC-9000]_ [RFC-9001]_. + +* Cryptographic choices: From [NTCP2]_. + +* Handshake: Noise XK from [NTCP2]_ and [NOISE]_. Significant simplifications + to NTCP2 are possible due to the encapsulation (inherent message boundaries) + provided by UDP. + +* Handshake ephemeral key obfuscation: Adapted from [NTCP2]_ + but using ChaCha20 from [ECIES]_ instead of AES. + +* Packet headers: Adapted from WireGuard [WireGuard]_ and QUIC [RFC-9000]_ [RFC-9001]_. + +* Packet header obfuscation: Adapted from [NTCP2]_ + but using ChaCha20 from [ECIES]_ instead of AES. + +* Packet header protection: Adapted from QUIC [RFC-9001]_ and [Nonces]_ + +* Headers used as AEAD associated data as in [ECIES]_. + +* Packet numbering: Adapted from WireGuard [WireGuard]_ and QUIC [RFC-9000]_ [RFC-9001]_. + +* Messages: Adapted from [SSU]_ + +* I2NP Fragmentation: Adapted from [SSU]_ + +* Relay and Peer Testing: Adapted from [SSU]_ + +* Signatures of Relay and Peer Test data: From the common structures spec [Common]_ + +* Block format: From [NTCP2]_ and [ECIES]_. + +* Padding and options: From [NTCP2]_ and [ECIES]_. + +* Acks, nacks: Adapted from QUIC [RFC-9000]_. + +* Flow control: TBD + + +There are no new cryptographic primitives that have not been used in I2P before. + + + +Delivery Guarantees +---------------------- + +As with other I2P transports NTCP, NTCP2, and SSU 1, this transport is not a general-purpose +facility for delivery of an in-order stream of bytes. It is designed for +transport of I2NP messages. There is no "stream" abstraction provided. + +In addition, as for SSU, it contains additional facilities for peer-facilitated NAT traversal +and testing of reachability (inbound connections). + +As for SSU 1, it does NOT provide in-order delivery of I2NP messages. +Nor does it provide guaranteed delivery of I2NP messages. +For efficiency, or because of out-of order delivery of UDP datagrams +or loss of those datagrams, I2NP messages may be delivered to the +far-end out-of-order, or may not be delivered at all. +An I2NP message may be retransmitted multiple times if necessary, +but delivery may eventually fail without causing the full connection to be +disconnected. Also, new I2NP messages may continue to be sent even +while retransmission (loss recovery) is occurring for other I2NP messages. + +This protocol does NOT completely prevent duplicate delivery of I2NP messages. +The router should enforce I2NP expiration and use a Bloom filter or other +mechanism based on the I2NP message ID. +See the I2NP Message Duplication section below. + + +Noise Protocol Framework +------------------------- + +This proposal provides the requirements based on the Noise Protocol Framework +[NOISE]_ (Revision 33, 2017-10-04). +Noise has similar properties to the Station-To-Station protocol +[STS]_, which is the basis for the [SSU]_ protocol. In Noise parlance, Alice +is the initiator, and Bob is the responder. + +SSU2 is based on the Noise protocol Noise_XK_25519_ChaChaPoly_SHA256. +(The actual identifier for the initial key derivation function +is "Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256" +to indicate I2P extensions - see KDF 1 section below) + +NOTE: This identifier is different than that used for NTCP2, because +all three handshake messages use the header as associated data. + +This Noise protocol uses the following primitives: + +- Handshake Pattern: XK + Alice transmits her key to Bob (X) + Alice knows Bob's static key already (K) + +- DH Function: X25519 + X25519 DH with a key length of 32 bytes as specified in [RFC-7748]_. + +- Cipher Function: ChaChaPoly + AEAD_CHACHA20_POLY1305 as specified in [RFC-7539]_ section 2.8. + 12 byte nonce, with the first 4 bytes set to zero. + +- Hash Function: SHA256 + Standard 32-byte hash, already used extensively in I2P. + + +Additions to the Framework +------------------------------- + +This proposal defines the following enhancements to +Noise_XK_25519_ChaChaPoly_SHA256. These generally follow the guidelines in +[NOISE]_ section 13. + +1) Handshake messages (Session Request, Created, Confirmed) include + a 16 or 32 byte header. + +2) The headers for the handshake messages (Session Request, Created, Confirmed) + are used as input to mixHash() before encryption/decryption + to bind the headers to the message. + +3) Headers are encrypted and protected. + +4) Cleartext ephemeral keys are obfuscated with ChaCha20 encryption using a known + key and IV. This is quicker than elligator2. + +5) The payload format is defined for messages 1, 2, and the data phase. + Of course, this is not defined in Noise. + +The data phase uses encryption similar to, but not compatible with, the Noise data phase. + + + +Definitions +=============== + +We define the following functions corresponding to the cryptographic building blocks used. + +ZEROLEN + zero-length byte array + +H(p, d) + SHA-256 hash function that takes a personalization string p and data d, and + produces an output of length 32 bytes. + As defined in [NOISE]_. + || below means append. + + Use SHA-256 as follows:: + + H(p, d) := SHA-256(p || d) + +MixHash(d) + SHA-256 hash function that takes a previous hash h and new data d, + and produces an output of length 32 bytes. + || below means append. + + Use SHA-256 as follows:: + + MixHash(d) := h = SHA-256(h || d) + +STREAM + The ChaCha20/Poly1305 AEAD as specified in [RFC-7539]_. + S_KEY_LEN = 32 and S_IV_LEN = 12. + + ENCRYPT(k, n, plaintext, ad) + Encrypts plaintext using the cipher key k, and nonce n which MUST be unique for + the key k. + Associated data ad is optional. + Returns a ciphertext that is the size of the plaintext + 16 bytes for the HMAC. + + The entire ciphertext must be indistinguishable from random if the key is secret. + + DECRYPT(k, n, ciphertext, ad) + Decrypts ciphertext using the cipher key k, and nonce n. + Associated data ad is optional. + Returns the plaintext. + +DH + X25519 public key agreement system. Private keys of 32 bytes, public keys of 32 + bytes, produces outputs of 32 bytes. It has the following + functions: + + GENERATE_PRIVATE() + Generates a new private key. + + DERIVE_PUBLIC(privkey) + Returns the public key corresponding to the given private key. + + DH(privkey, pubkey) + Generates a shared secret from the given private and public keys. + +HKDF(salt, ikm, info, n) + A cryptographic key derivation function which takes some input key material ikm (which + should have good entropy but is not required to be a uniformly random string), a salt + of length 32 bytes, and a context-specific 'info' value, and produces an output + of n bytes suitable for use as key material. + + Use HKDF as specified in [RFC-5869]_, using the HMAC hash function SHA-256 + as specified in [RFC-2104]_. This means that SALT_LEN is 32 bytes max. + +MixKey(d) + Use HKDF() with a previous chainKey and new data d, and + sets the new chainKey and k. + As defined in [NOISE]_. + + Use HKDF as follows:: + + MixKey(d) := output = HKDF(chainKey, d, "", 64) + chainKey = output[0:31] + k = output[32:63] + + + + +Messages +======== + +Each UDP datagram contains exactly one message. +The length of the datagram (after the IP and UDP headers) is the length of the message. +Padding, if any, is contained in a padding block inside the message. +In this document, we use the terms "datagram" and "packet" mostly interchangeably. +Each datagram (or packet) contains a single message (unlike QUIC, where +a datagram may contain multiple QUIC packets). +The "packet header" is the part after the IP/UDP header. + +Exception: +The Session Confirmed message is unique in that it may be fragmented across multiple packets. +See the Session Confirmed Fragmentation section below for more information. + +All SSU2 messages are at least 40 bytes in length. +Any message of length 1-39 bytes is invalid. +All SSU2 messages are less than or equal to 1472 (IPv4) or 1452 (IPv6) bytes in length. The message +format is based on Noise messages, with modifications for framing and indistinguishability. +Implementations using standard Noise libraries must pre-process received +messages to the standard Noise message format. All encrypted fields are AEAD +ciphertexts. + + +The following messages are defined: + +==== ================ ============= ============= +Type Message Header Length Header Encr. Length +==== ================ ============= ============= + 0 SessionRequest 32 64 + 1 SessionCreated 32 64 + 2 SessionConfirmed 16 16 + 6 Data 16 16 + 7 PeerTest 32 32 + 9 Retry 32 32 + 10 Token Request 32 32 + 11 HolePunch 32 32 +==== ================ ============= ============= + + + +Session Establishment +----------------------- + +The standard establishment sequence, when Alice has a valid token previously received from Bob, is as follows: + +.. raw:: html + + {% highlight %} +Alice Bob + + SessionRequest -------------------> + <------------------- SessionCreated + SessionConfirmed -----------------> +{% endhighlight %} + + +When Alice does not have a valid token, the establishment sequence is as follows: + +.. raw:: html + + {% highlight %} +Alice Bob + + TokenRequest ---------------------> + <--------------------------- Retry + SessionRequest -------------------> + <------------------- SessionCreated + SessionConfirmed -----------------> +{% endhighlight %} + + +When Alice thinks she has a valid token, +but Bob rejects it (perhaps because Bob restarted), +the establishment sequence is as follows: + +.. raw:: html + + {% highlight %} +Alice Bob + + SessionRequest -------------------> + <--------------------------- Retry + SessionRequest -------------------> + <------------------- SessionCreated + SessionConfirmed -----------------> +{% endhighlight %} + + +Bob may reject a Session or Token Request by replying with a Retry message +containing a Termination block with a reason code. +Based on the reason code, Alice should not attempt another +request for some period of time: + + +.. raw:: html + + {% highlight %} +Alice Bob + + SessionRequest -------------------> + <--------------------------- Retry containing a Termination block + + or + + TokenRequest ---------------------> + <--------------------------- Retry containing a Termination block +{% endhighlight %} + + +Using Noise terminology, the establishment and data sequence is as follows: +(Payload Security Properties) + +.. raw:: html + + {% highlight lang='text' %} +XK(s, rs): Authentication Confidentiality + <- s + ... + -> e, es 0 2 + <- e, ee 2 1 + -> s, se 2 5 + <- 2 5 +{% endhighlight %} + + +Once a session has been established, Alice and Bob can exchange Data messages. + + +Packet Header +--------------- + +All packets start with an obfuscated (encrypted) header. +There are two header types, long and short. +Note that the first 13 bytes (Destination Connection ID, packet number, and type) +are the same for all headers. + +Long Header +````````````` +The long header is 32 bytes. It is used before a session is created, for Token Request, SessionRequest, SessionCreated, and Retry. +It is also used for out-of-session Peer Test and Hole Punch messages. + +Before header encryption: + +.. raw:: html + + {% highlight lang='dataspec' %} + ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + + Destination Connection ID :: 8 bytes, unsigned big endian integer + + Packet Number :: 4 bytes, unsigned big endian integer + + type :: The message type = 0, 1, 7, 9, 10, or 11 + + ver :: The protocol version, equal to 2 + + id :: 1 byte, the network ID (currently 2, except for test networks) + + flag :: 1 byte, unused, set to 0 for future compatibility + + Source Connection ID :: 8 bytes, unsigned big endian integer + + Token :: 8 bytes, unsigned big endian integer + +{% endhighlight %} + + +Short Header +````````````` +The short header is 16 bytes. It is used for Session Created and for Data messages. +Uauthenticated messages such as Session Request, Retry, and Peer Test will +always use the long header. + +16 bytes is required, because +the receiver must decrypt the first 16 bytes to get the message type, +and then must decrypt an additional 16 bytes if it's actually a long header, +as indicated by the message type. + +For Session Confirmed, before header encryption: + +.. raw:: html + + {% highlight lang='dataspec' %} + ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type|frag| flags | + +----+----+----+----+----+----+----+----+ + + Destination Connection ID :: 8 bytes, unsigned big endian integer + + Packet Number :: 4 bytes, all zeros + + type :: The message type = 2 + + frag :: 1 byte fragment info: + bit order: 76543210 (bit 7 is MSB) + bits 7-4: fragment number 0-14, big endian + bits 3-0: total fragments 1-15, big endian + + flags :: 2 bytes, unused, set to 0 for future compatibility + +{% endhighlight %} + +See the Session Confirmed Fragmentation section below for more information +about the frag field. + + +For Data messages, before header encryption: + +.. raw:: html + + {% highlight lang='dataspec' %} + ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type|flag|moreflags| + +----+----+----+----+----+----+----+----+ + + Destination Connection ID :: 8 bytes, unsigned big endian integer + + Packet Number :: 4 bytes, unsigned big endian integer + + type :: The message type = 6 + + flag :: 1 byte flags: + bit order: 76543210 (bit 7 is MSB) + bits 7-1: unused, set to 0 for future compatibility + bits 0: when set to 1, immediate ack requested + + moreflags :: 2 bytes, unused, set to 0 for future compatibility + +{% endhighlight %} + + + +Connection ID Numbering +``````````````````````````` + +Connection IDs must be randomly generated. +Source and Destination IDs must NOT be identical, +so that an on-path attacker cannot capture and send a packet +back to the originator that looks valid. +Do NOT use a counter to generate connection IDs, so that an on-path +attacker cannot generate a packet that looks valid. + +Unlike in QUIC, we do not change the connection IDs during or after the handshake, +even after a Retry message. The IDs remain constant from the first message +(Token Request or Session Request) to the last message (Data with Termination). +Additionally, connection IDs do not change during or after +path challenge or connection migration. + +Also different than QUIC is that connection IDs in the headers +are always header-encrypted. See below. + + + +Packet Numbering +````````````````` +If no First Packet Number block is sent in the handshake, +packets are numbered within a single session, for each direction, starting from 0, to a max of (2**32 -1). +A session must be terminated, and a new session created, well before the max +number of packets is sent. + +If a First Packet Number block is sent in the handshake, +packets are numbered within a single session, for that direction, starting from that packet number. +The packet number may wrap around during the session. +When a max of 2**32 packets have been sent, wrapping the packet number back +to the first packet number, that session is no longer valid. +A session must be terminated, and a new session created, well before the max +number of packets is sent. + + +TODO key rotation, reduce max packet number? + + +Handshake packets that are determined to be lost are retransmitted +whole, with the identical header including packet number. +The handshake messages Session Request, Session Created, and Session Confirmed +MUST be retransmitted with the same packet number and identical encrypted contents, +so that the same chained hash will be used to encrypt the response. +The Retry message is never transmitted. + +Data phase packets that are determined to be lost are never retransmitted +whole (except termination, see below). The same applies to the blocks that are contained within lost +packets. Instead, the information that might be carried in blocks is +sent again in new packets as needed. +Data Packets are never retransmitted with the same packet number. +Any retransmission of packet contents (whether or not the contents remain the same) +must use the next unused packet number. + +Retransmitting an unchanged whole packet as-is, with the same packet number, +is not allowed for several reasons. For background see QUIC [RFC-9000]_ section 12.3. + +- It's inefficient to store packets for retransmission +- A new packet data looks different to an on-path observer, can't tell it's retransmitted +- A new packet gets an updated ack block sent with it, not the old ack block +- You only retransmit what's necessary. some fragments could have been already retransmitted once and been acked +- You can fit as much as you need into each retransmitted packet if more is pending +- Endpoints that track all individual packets for the purposes of + detecting duplicates are at risk of accumulating excessive state. + The data required for detecting duplicates can be limited by + maintaining a minimum packet number below which all packets are + immediately dropped. +- This scheme is much more flexible + + +New packets are used to carry information that is +determined to have been lost. In general, information is sent again +when a packet containing that information is determined to be lost, +and sending ceases when a packet containing that information is remain the same) +acknowledged. + +Exception: A data phase packet containing a Termination block may, +but is not required to be, retransmitted whole, as-is. +See the Session Termination section below. + + +The following packets contain a random packet number that is ignored: + +- Session Request +- Session Created +- Token Request +- Retry +- Peer Test +- Hole Punch + +For Alice, outbound packet numbering starts at 0 with Session Confirmed. +For Bob, outbound packet numbering starts at 0 with first Data packet, +which should be an ACK of the Session Confirmed. +The packet numbers +in an example standard handshake will be: + +.. raw:: html + + {% highlight %} +Alice Bob + + SessionRequest (r) ------------> + <------------- SessionCreated (r) + SessionConfirmed (0) ------------> + <------------- Data (0) (Ack-only) + Data (1) ------------> (May be sent before Ack is received) + <------------- Data (1) + Data (2) ------------> + Data (3) ------------> + Data (4) ------------> + <------------- Data (2) + + r = random packet number (ignored) + Token Request, Retry, and Peer Test + also have random packet numbers. +{% endhighlight %} + + +Any retransmission of handshake messages +(SessionRequest, SessionCreated, or SessionConfirmed) +must be resent unchanged, with the same packet number. +Do not use different ephemeral keys or change the payload +when retransmitting these messages. + + +Header Binding +```````````````` +The header (before obfuscation and protection) is always included in the associated +data for the AEAD function, to cryptographically bind the header to the data. + + +Header Encryption +``````````````````` + +Header encryption has several goals. +See the "Additional DPI Discussion" section above for background and assumptions. + +- Prevent online DPI from identifying the protocol +- Prevent patterns in a series of messages in the same connection, + except for handshake retransmissions +- Prevent patterns in messages of the same type in different connections +- Prevent decryption of handshake headers + without knowledge of the introduction key found in the netdb +- Prevent identification of X25519 ephemeral keys + without knowledge of the introduction key found in the netdb +- Prevent decryption of data phase packet number and type + by any online or offline attacker +- Prevent injection of valid handshake packets by an on-path or off-path observer + without knowledge of the introduction key found in the netdb +- Prevent injection of valid data packets by an on-path or off-path observer +- Allow rapid and efficient classification of incoming packets +- Provide "probing" resistance so that there is no response to a bad + Session Request, or if there is a Retry response, + the response is not identifiable as I2P + without knowledge of the introduction key found in the netdb +- The Destination Connection ID is not critical data, + and it's ok if it can be decrypted by an observer + with knowledge of the introduction key found in the netdb +- The packet number of a data phase packet is an AEAD nonce and is critical data. + It must not be decryptable by an observer even + with knowledge of the introduction key found in the netdb. + See [Nonces]_. + +Headers are encrypted with known keys published in the network database +or calculated later. +In the handshake phase, this is for DPI resistance only, as the key is public and the +key and nonces are reused, so it is effectively just obfuscation. +Note that the header encryption is also used to obfuscate +the ephemeral keys X (in Session Request) and Y (in Session Created). + +See the Inbound Packet Handling section below for additional guidance. + +Bytes 0-15 of all headers +are encrypted using a header protection scheme by XORing with data calculated from known keys, +using ChaCha20, similar to QUIC [RFC-9001]_ and [Nonces]_. +This ensures that the encrypted short header and the first part of the long header +will appear to be random. + +For Session Request and Session Created, bytes 16-31 of the long header and the 32-byte Noise ephemeral key +are encrypted using ChaCha20. +The unencrypted data is random, so the encrypted data will appear to be random. + +For Retry, bytes 16-31 of the long header +are encrypted using ChaCha20. +The unencrypted data is random, so the encrypted data will appear to be random. + +Unlike the QUIC [RFC-9001]_ header protection scheme, +ALL parts of all headers, including destination and source connection IDs, +are encrypted. +QUIC [RFC-9001]_ and [Nonces]_ are primarily focused on encrypting +the "critical" part of the header, i.e. the packet number (ChaCha20 nonce). +While encrypting the session ID makes incoming packet classification a little more complex, +it makes some attacks more difficult. QUIC defines different connection IDs +for different phases, and for path challenge and connection migration. +Here we use the same connection IDs throughout, as they are encrypted. + +There are seven header protection key phases: + +- Session Request and Token Request +- Session Created +- Retry +- Session Confirmed +- Data Phase +- Peer Test +- Hole Punch + + +================= =================== ==================== + Message Key k_header_1 Key k_header_2 +================= =================== ==================== +Token Request Bob Intro Key Bob Intro Key +Session Request Bob Intro Key Bob Intro Key +Session Created Bob Intro Key See Session Request KDF +Session Confirmed Bob Intro Key See Session Created KDF +Retry Bob Intro Key Bob Intro Key +Data Alice/Bob Intro Key See data phase KDF +Peer Test 5,7 Alice Intro Key Alice Intro Key +Peer Test 6 Charlie Intro Key Charlie Intro Key +Hole Punch Alice Intro Key Alice Intro Key +================= =================== ==================== + + + +Header encryption is designed to allow rapid classification of +inbound packets, without complex heuristics or fallbacks. +This is accomplished by using the same k_header_1 key +for almost all inbound messages. +Even when the source IP or port of a connection changes +due to an actual IP change or NAT behavior, the packet may be +rapidly mapped to a session with a single lookup of the connection ID. + +Note that Session Created and Retry are the ONLY messages that require fallback processing +for k_header_1 to decrypt the Connection ID, because they use the sender's (Bob's) intro key. +ALL other messages use the receiver's intro key for k_header_1. +The fallback processing need only look up pending outbound connections by +source IP/port. + +If the fallback processing by source IP/port fails to find a pending +outbound connection, there could be several causes: + +- Not an SSU2 message +- A corrupted SSU2 message +- The reply is spoofed or modified by an attacker +- Bob has a symmetric NAT +- Bob changed IP or port during processing of the message +- Bob sent the reply out a different interface + +While additional fallback processing is possible to attempt to find +the pending outbound connection and decrypt the connection ID +using the k_header_1 for that connection, it is probably not necessary. +If Bob has issues with his NAT or packet routing, it is probably +better to let the connection fail. +This design relies on endpoints retaining a stable address for the duration of the handshake. + +See the Inbound Packet Handling sesion below for additional guidelines. + +See the individual KDF sections below for the derivation of the header encryption keys for that phase. + + + +Header Encryption KDF +```````````````````````` + +.. raw:: html + + {% highlight lang='dataspec' %} +// incoming encrypted packet + packet = incoming encrypted packet + len = packet.length + + // take the next-to-last 12 bytes of the packet + iv = packet[len-24:len-13] + k_header_1 = header encryption key 1 + data = {0, 0, 0, 0, 0, 0, 0, 0} + mask = ChaCha20.encrypt(k_header_1, iv, data) + + // encrypt the first part of the header by XORing with the mask + packet[0:7] ^= mask[0:7] + + // take the last 12 bytes of the packet + iv = packet[len-12:len-1] + k_header_2 = header encryption key 2 + data = {0, 0, 0, 0, 0, 0, 0, 0} + mask = ChaCha20.encrypt(k_header_2, iv, data) + + // encrypt the second part of the header by XORing with the mask + packet[8:15] ^= mask[0:7] + + + // For Session Request and Session Created only: + iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + + // encrypt the third part of the header and the ephemeral key + packet[16:63] = ChaCha20.encrypt(k_header_2, iv, packet[16:63]) + + + // For Retry, Token Request, Peer Test, and Hole Punch only: + iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + + // encrypt the third part of the header + packet[16:31] = ChaCha20.encrypt(k_header_2, iv, packet[16:31]) + + +{% endhighlight %} + +This KDF uses the last 24 bytes of the packet as the IV for the two +ChaCha20 operations. As all packets end with a 16 byte MAC, +this requires that all packet payloads are a minimum of 8 bytes. +This requirement is additionally documented in the message sections below. + + + +Header Validation +``````````````````` +After decrypting the first 8 bytes of the header, +the receiver will know the Destination Connection ID. From there, +the receiver knows what header encryption key to use for +the remainder of the header, based on the key phase of the session. + +Decrypting the next 8 bytes of the header will then reveal the message type and be able to determine +if it is a short or long header. +If it is a long header, the receiver must validate the version and netid fields. +If the version is != 2, or the netid is != the expected value (generally 2, except in test networks), +the receiver should drop the message. + + +Packet Integrity +------------------------ + +All message contain either three or four parts: + +- The message header +- For Session Request and Session Created only, an ephemeral key +- A ChaCha20-encrypted payload +- A Poly1305 MAC + +In all cases, the header (and if present, the ephemeral key) is bound +to the authentication MAC to ensure that the entire message is intact. + +- For handshake messages Session Request, Session Created, and Session Confirmed, + the message header is mixHash()ed before the Noise processing phase +- The ephemeral key, if present, is covered by a standard Noise misHash() +- For messages outside the Noise handshake, the header is used + as Associated Data for the ChaCha20/Poly1305 encryption. + +Inbound packet handlers must always decrypt the ChaCha20 payload and validate +the MAC before processing the message, with one exception: +To mitigate DoS attacks from address-spoofed packets containing +apparent Session Request messages with an invalid token, a handler need NOT +attempt to decrypt and validate the full message +(requiring an expensive DH operation in addition to the ChaCha2o/Poly1305 decryption). +The handler may respond with a Retry message using the values found in the header +of the Session Request message. + + +Authenticated Encryption +------------------------ + +There are three separate authenticated encryption instances (CipherStates). +One during the handshake phase, and two (transmit and receive) for the data phase. +Each has its own key from a KDF. + +Encrypted/authenticated data will be represented as + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | | + + + + | Encrypted and authenticated data | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ +{% endhighlight %} + + +ChaCha20/Poly1305 +````````````````` + +Encrypted and authenticated data format. + +Inputs to the encryption/decryption functions: + +.. raw:: html + + {% highlight lang='dataspec' %} + +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: + The packet header, 16 bytes. + + data :: Plaintext data, 0 or more bytes + +{% endhighlight %} + +Output of the encryption function, input to the decryption function: + +.. raw:: html + + {% highlight lang='dataspec' %} + ++----+----+----+----+----+----+----+----+ + | | + + + + | ChaCha20 encrypted data | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + | Poly1305 Message Authentication Code | + + (MAC) + + | 16 bytes | + +----+----+----+----+----+----+----+----+ + + encrypted data :: Same size as plaintext data, 0 - 65519 bytes + + MAC :: Poly1305 message authentication code, 16 bytes + +{% endhighlight %} + +For ChaCha20, what is described here corresponds to [RFC-7539]_, which is also +used similarly in TLS [RFC-7905]_. + +Notes +````` +- Since ChaCha20 is a stream cipher, plaintexts need not be padded. + Additional keystream bytes are discarded. + +- The key for the cipher (256 bits) is agreed upon by means of the SHA256 KDF. + The details of the KDF for each message are in separate sections below. + + +AEAD Error Handling +``````````````````` +- In all messages, the AEAD message size is known in advance. + On an AEAD authentication failure, recipient must halt further message processing and + discard the message. + +- Bob should maintain a blacklist of IPs with + repeated failures. + + +KDF for Session Request +------------------------------------------------------- + +The Key Derivation Function (KDF) generates a handshake phase cipher key k from the DH result, +using HMAC-SHA256(key, data) as defined in [RFC-2104]_. +These are the InitializeSymmetric(), MixHash(), and MixKey() functions, +exactly as defined in the Noise spec. + +KDF for Initial ChainKey +```````````````````````` + +.. raw:: html + + {% highlight lang='text' %} + +// Define protocol_name. + Set protocol_name = "Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256" + (52 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 + + // MixHash(null prologue) + h = SHA256(h); + + // up until here, can all be precalculated by Alice for all outgoing connections + + // Bob's X25519 static keys + // bpk is published in routerinfo + bsk = GENERATE_PRIVATE() + bpk = DERIVE_PUBLIC(bsk) + + // Bob static key + // MixHash(bpk) + // || below means append + h = SHA256(h || bpk); + + // Bob introduction key + // bik is published in routerinfo + bik = RANDOM(32) + + // up until here, can all be precalculated by Bob for all incoming connections + +{% endhighlight %} + + +KDF for Session Request +````````````````````````` + +.. raw:: html + + {% highlight lang='text' %} + +// MixHash(header) + h = SHA256(h || header) + + This is the "e" message pattern: + + // Alice's X25519 ephemeral keys + aesk = GENERATE_PRIVATE() + aepk = DERIVE_PUBLIC(aesk) + + // Alice ephemeral key X + // MixHash(aepk) + h = SHA256(h || aepk); + + // h is used as the associated data for the AEAD in Session Request + // Retain the Hash h for the Session Created KDF + + + End of "e" message pattern. + + This is the "es" message pattern: + + // DH(e, rs) == DH(s, re) + sharedSecret = DH(aesk, bpk) = DH(bsk, aepk) + + // MixKey(DH()) + //[chainKey, k] = MixKey(sharedSecret) + // ChaChaPoly parameters to encrypt/decrypt + keydata = HKDF(chainKey, sharedSecret, "", 64) + chainKey = keydata[0:31] + + // AEAD parameters + k = keydata[32:64] + n = 0 + ad = h + ciphertext = ENCRYPT(k, n, payload, ad) + + // retain the chainKey for Session Created KDF + + + End of "es" message pattern. + + // Header encryption keys for this message + // bik = Bob's intro key + k_header_1 = bik + k_header_2 = bik + + // Header encryption keys for next message (Session Created) + k_header_1 = bik + k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) + + // Header encryption keys for next message (Retry) + k_header_1 = bik + k_header_2 = bik + + +{% endhighlight %} + + + + +SessionRequest (Type 0) +------------------------ + +Alice sends to Bob, either as the first message in the handshake, +or in response to a Retry message. +Bob responds with a Session Created message. +Size: 80 + payload size. +Minimum Size: 88 + +If Alice does not have a valid token, Alice should send a Token Request message +instead of a Session Request, to avoid the asymmetric encryption +overhead in generating a Session Request. + +Long header. +Noise content: Alice's ephemeral key X +Noise payload: DateTime and other blocks +Max payload size: MTU - 108 (IPv4) or MTU - 128 (IPv6). +For 1280 MTU: Max payload is 1172 (IPv4) or 1152 (IPv6). +For 1500 MTU: Max payload is 1392 (IPv4) or 1372 (IPv6). + +Payload Security Properties: + +.. raw:: html + + {% highlight lang='text' %} +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. + + +{% endhighlight %} + +The X value is encrypted to ensure payload indistinguishably +and uniqueness, which are necessary DPI countermeasures. +We use ChaCha20 encryption to achieve this, +rather than more complex and slower alternatives such as elligator2. +Asymmetric encryption to Bob's router public key would be far too slow. +ChaCha20 encryption uses Bob's intro key as published +in the network database. + +ChaCha20 encryption is for DPI resistance only. +Any party knowing Bob's introduction key, which is published in the network database, +may decrypt the header and X value in this message. + + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Long Header bytes 0-15, ChaCha20 | + + encrypted with Bob intro key + + | See Header Encryption KDF | + +----+----+----+----+----+----+----+----+ + | Long Header bytes 16-31, ChaCha20 | + + encrypted with Bob intro key n=0 + + | | + +----+----+----+----+----+----+----+----+ + | | + + X, ChaCha20 encrypted + + | with Bob intro key n=0 | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | | + + + + | ChaCha20 encrypted data | + + (length varies) + + | k defined in KDF for Session Request | + + n = 0 + + | see KDF for associated data | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + X :: 32 bytes, ChaCha20 encrypted X25519 ephemeral key, little endian + key: Bob's intro key + n: 1 + data: 48 bytes (bytes 16-31 of the header, followed by encrypted X) + +{% endhighlight %} + +Unencrypted data (Poly1305 authentication tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + | | + + + + | X | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | Noise payload (block data) | + + (length varies) + + | see below for allowed blocks | + +----+----+----+----+----+----+----+----+ + + + Destination Connection ID :: Randomly generated by Alice + + id :: 1 byte, the network ID (currently 2, except for test networks) + + ver :: 2 + + type :: 0 + + flag :: 1 byte, unused, set to 0 for future compatibility + + Packet Number :: Random 4 byte number generated by Alice, ignored + + Source Connection ID :: Randomly generated by Alice, + must not be equal to Destination Connection ID + + Token :: 0 if not previously received from Bob + + X :: 32 bytes, X25519 ephemeral key, little endian + +{% endhighlight %} + + +Payload +``````` + +- DateTime block +- Options block (optional) +- Relay Tag Request block (optional) +- Padding block (optional) + +The minimum payload size is 8 bytes. Since the DateTime block is +only 7 bytes, at least one other block must be present. + + +Notes +````` +- The unique X value in the initial ChaCha20 block ensure that the ciphertext is + different for every session. + +- To provide probing resistance, Bob should not send a Retry message + in response to a Session Request message unless the + message type, protocol version, and network ID fields in the Session Request message + are valid. + +- Bob must reject connections where the timestamp value is too far off from the + current time. Call the maximum delta time "D". Bob must maintain a local + cache of previously-used handshake values and reject duplicates, to prevent + replay attacks. Values in the cache must have a lifetime of at least 2*D. + The cache values are implementation-dependent, however the 32-byte X value + (or its encrypted equivalent) may be used. + Reject by sending a Retry message containing a zero token and a termination block. + +- Diffie-Hellman ephemeral keys may never be reused, to prevent cryptographic attacks, + and reuse will be rejected as a replay attack. + +- The "KE" and "auth" options must be compatible, i.e. the shared secret K must + be of the appropriate size. If more "auth" options are added, this could + implicitly change the meaning of the "KE" flag to use a different KDF or a + different truncation size. + +- Bob must validate that Alice's ephemeral key is a valid point on the curve + here. + +- Padding should be limited to a reasonable amount. Bob may reject connections + with excessive padding. Bob will specify his padding options in Session Created. + Min/max guidelines TBD. Random size from 0 to 31 bytes minimum? + (Distribution to be determined, see Appendix A.) + +- On most errors, including AEAD, DH, apparent replay, or key + validation failure, Bob should halt further message processing and + drop the message without responding. + +- Bob MAY send a Retry message containing a zero token and a Termination block with a + clock skew reason code if the timestamp in the DateTime block is too + far skewed. + +- DoS Mitigation: DH is a relatively expensive operation. As with the previous NTCP protocol, + routers should take all necessary measures to prevent CPU or connection exhaustion. + Place limits on maximum active connections and maximum connection setups in progress. + Enforce read timeouts (both per-read and total for "slowloris"). + Limit repeated or simultaneous connections from the same source. + Maintain blacklists for sources that repeatedly fail. + Do not respond to AEAD failure. Alternatively, respond with a Retry message + before the DH operation and AEAD validation. + +- "ver" field: The overall Noise protocol, extensions, and SSU2 protocol + including payload specifications, indicating SSU2. + This field may be used to indicate support for future changes. + +- The network ID field is used to quickly identify cross-network connections. + If this field is does not match Bob's network ID, + Bob should disconnect and block future connections. + +- Bob must drop the message if the Source Connection ID equals + the Destination Connection ID. + + + +KDF for Session Created and Session Confirmed part 1 +---------------------------------------------------------------------------------- + +.. raw:: html + + {% highlight lang='text' %} + +// take h saved from Session Request KDF + // MixHash(ciphertext) + h = SHA256(h || encrypted Noise payload from Session Request) + + // MixHash(header) + h = SHA256(h || header) + + This is the "e" message pattern: + + // Bob's X25519 ephemeral keys + besk = GENERATE_PRIVATE() + bepk = DERIVE_PUBLIC(besk) + + // h is from KDF for Session Request + // Bob ephemeral key Y + // MixHash(bepk) + h = SHA256(h || bepk); + + // h is used as the associated data for the AEAD in Session Created + // Retain the Hash h for the Session Confirmed KDF + + End of "e" message pattern. + + This is the "ee" message pattern: + + // MixKey(DH()) + //[chainKey, k] = MixKey(sharedSecret) + sharedSecret = DH(aesk, bepk) = DH(besk, aepk) + keydata = HKDF(chainKey, sharedSecret, "", 64) + chainKey = keydata[0:31] + + // AEAD parameters + k = keydata[32:64] + n = 0 + ad = h + ciphertext = ENCRYPT(k, n, payload, ad) + + // retain the chaining key ck for Session Confirmed KDF + + End of "ee" message pattern. + + // Header encryption keys for this message + // bik = Bob's intro key + k_header_1 = bik + k_header_2: See Session Request KDF above + + // Header protection keys for next message (Session Confirmed) + k_header_1 = bik + k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) + +{% endhighlight %} + + +SessionCreated (Type 1) +------------------------ + +Bob sends to Alice, in response to a Session Request message. +Alice responds with a Session Confirmed message. +Size: 80 + payload size. +Minimum Size: 88 + +Noise content: Bob's ephemeral key Y +Noise payload: DateTime, Address, and other blocks +Max payload size: MTU - 108 (IPv4) or MTU - 128 (IPv6). +For 1280 MTU: Max payload is 1172 (IPv4) or 1152 (IPv6). +For 1500 MTU: Max payload is 1392 (IPv4) or 1372 (IPv6). + +Payload Security Properties: + +.. raw:: html + + {% highlight lang='text' %} +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. + +{% endhighlight %} + +The Y value is encrypted to ensure payload indistinguishably and uniqueness, +which are necessary DPI countermeasures. We use ChaCha20 encryption to achieve +this, rather than more complex and slower alternatives such as elligator2. +Asymmetric encryption to Alice's router public key would be far too slow. ChaCha20 +encryption uses Bob's intro key, +as published in the network database. + +ChaCha20 encryption is for DPI resistance only. Any party knowing Bob's intro key, +which is published in the network database, and captured the first 32 +bytes of Session Request, may decrypt the Y value in this message. + + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Long Header bytes 0-15, ChaCha20 | + + encrypted with Bob intro key and + + | derived key, see Header Encryption KDF| + +----+----+----+----+----+----+----+----+ + | Long Header bytes 16-31, ChaCha20 | + + encrypted with derived key n=0 + + | See Header Encryption KDF | + +----+----+----+----+----+----+----+----+ + | | + + Y, ChaCha20 encrypted + + | with derived key n=0 | + + (32 bytes) + + | See Header Encryption KDF | + + + + | | + +----+----+----+----+----+----+----+----+ + | ChaCha20 data | + + Encrypted and authenticated data + + | length varies | + + k defined in KDF for Session Created + + | n = 0; see KDF for associated data | + + + + | | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + Y :: 32 bytes, ChaCha20 encrypted X25519 ephemeral key, little endian + key: Bob's intro key + n: 1 + data: 48 bytes (bytes 16-31 of the header, followed by encrypted Y) + +{% endhighlight %} + +Unencrypted data (Poly1305 auth tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + | | + + + + | Y | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | Noise payload (block data) | + + (length varies) + + | see below for allowed blocks | + +----+----+----+----+----+----+----+----+ + + Destination Connection ID :: The Source Connection ID + received from Alice in Session Request + + id :: 1 byte, the network ID (currently 2, except for test networks) + + ver :: 2 + + type :: 0 + + flag :: 1 byte, unused, set to 0 for future compatibility + + Packet Number :: Random 4 byte number generated by Bob, ignored + + Source Connection ID :: The Destination Connection ID + received from Alice in Session Request + + Token :: 0 (unused) + + Y :: 32 bytes, X25519 ephemeral key, little endian + +{% endhighlight %} + + +Payload +``````` +- DateTime block +- Address block +- Relay Tag block (optional) +- New Token block (optional) +- First Packet Number block (optional) +- Options block (optional) +- Termination block (not recommended, send in a retry message instead) +- Padding block (optional) + +The minimum payload size is 8 bytes. Since the DateTime and Address blocks +total more than that, the requirement is met with only those two blocks. + +Notes +````` + +- Alice must validate that Bob's ephemeral key is a valid point on the curve + here. + +- Padding should be limited to a reasonable amount. + Alice may reject connections with excessive padding. + Alice will specify her padding options in Session Confirmed. + Min/max guidelines TBD. Random size from 0 to 31 bytes minimum? + (Distribution to be determined, see Appendix A.) + +- On any error, including AEAD, DH, timestamp, apparent replay, or key + validation failure, Alice must halt further message processing and close the + connection without responding. + +- Alice must reject connections where the timestamp value is too far off from + the current time. Call the maximum delta time "D". Alice must maintain a + local cache of previously-used handshake values and reject duplicates, to + prevent replay attacks. Values in the cache must have a lifetime of at least + 2*D. The cache values are implementation-dependent, however the 32-byte Y + value (or its encrypted equivalent) may be used. + +- Alice must drop the message if the source IP and port do not match + the destination IP and port of the Session Request. + +- Alice must drop the message if the Destination and Source Connection IDs + do not match the Source and Destination Connection IDs of the Session Request. + +- Bob sends a relay tag block if requested by Alice in the Session Request. + + +Issues +`````` +- Include min/max padding options here? + + + +KDF for Session Confirmed part 1, using Session Created KDF +--------------------------------------------------------------------------- + +.. raw:: html + + {% highlight lang='text' %} + +// take h saved from Session Created KDF + // MixHash(ciphertext) + h = SHA256(h || encrypted Noise payload from Session Created) + + // MixHash(header) + h = SHA256(h || header) + // h is used as the associated data for the AEAD in Session Confirmed part 1, below + + This is the "s" message pattern: + + // Alice's X25519 static keys + ask = GENERATE_PRIVATE() + apk = DERIVE_PUBLIC(ask) + + // AEAD parameters + // k is from Session Request + n = 1 + ad = h + ciphertext = ENCRYPT(k, n++, apk, ad) + + // MixHash(ciphertext) + h = SHA256(h || ciphertext); + + // h is used as the associated data for the AEAD in Session Confirmed part 2 + + End of "s" message pattern. + + // Header encryption keys for this message + See Session Confirmed part 2 below + +{% endhighlight %} + + +KDF for Session Confirmed part 2 +-------------------------------------------------------------- + +.. raw:: html + + {% highlight lang='text' %} + +This is the "se" message pattern: + + // DH(ask, bepk) == DH(besk, apk) + sharedSecret = DH(ask, bepk) = DH(besk, apk) + + // MixKey(DH()) + //[chainKey, k] = MixKey(sharedSecret) + keydata = HKDF(chainKey, sharedSecret, "", 64) + chainKey = keydata[0:31] + + // AEAD parameters + k = keydata[32:64] + n = 0 + ad = h + ciphertext = ENCRYPT(k, n, payload, ad) + + // h from Session Confirmed part 1 is used as the associated data for the AEAD in Session Confirmed part 2 + // MixHash(ciphertext) + h = SHA256(h || ciphertext); + + // retain the chaining key ck for the data phase KDF + // retain the hash h for the data phase KDF + + End of "se" message pattern. + + // Header encryption keys for this message + // bik = Bob's intro key + k_header_1 = bik + k_header_2: See Session Created KDF above + + // Header protection keys for data phase + See data phase KDF below + +{% endhighlight %} + + +SessionConfirmed (Type 2) +----------------------------- + +Alice sends to Bob, in response to a Session Created message. +Bob responds immediately with a Data message containing an ACK block. +Size: 80 + payload size. +Minimum Size: About 500 (minimum router info block size is about 420 bytes) + +Noise content: Alice's static key +Noise payload part 1: None +Noise payload part 2: Alice's RouterInfo, and other blocks +Max payload size: MTU - 108 (IPv4) or MTU - 128 (IPv6). +For 1280 MTU: Max payload is 1172 (IPv4) or 1152 (IPv6). +For 1500 MTU: Max payload is 1392 (IPv4) or 1372 (IPv6). + +Payload Security Properties: + + +.. raw:: html + + {% highlight lang='text' %} +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. + +{% endhighlight %} + +This contains two ChaChaPoly frames. +The first is Alice's encrypted static public key. +The second is the Noise payload: Alice's encrypted RouterInfo, optional +options, and optional padding. They use different keys, because the MixKey() +function is called in between. + + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Short Header 16 bytes, ChaCha20 | + + encrypted with Bob intro key and + + | derived key, see Header Encryption KDF| + +----+----+----+----+----+----+----+----+ + | ChaCha20 frame (32 bytes) | + + Encrypted and authenticated data + + + Alice static key S + + | k defined in KDF for Session Created | + + n = 1 + + | | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + | | + + Length varies (remainder of packet) + + | | + + ChaChaPoly frame + + | Encrypted and authenticated | + + see below for allowed blocks + + | | + + k defined in KDF for + + | Session Confirmed part 2 | + + n = 0 + + | see KDF for associated data | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + S :: 32 bytes, ChaChaPoly encrypted Alice's X25519 static key, little endian + inside 48 byte ChaChaPoly frame + +{% endhighlight %} + +Unencrypted data (Poly1305 auth tags not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type|frag| flags | + +----+----+----+----+----+----+----+----+ + | | + + + + | S | + + Alice static key + + | (32 bytes) | + + + + | | + + + + +----+----+----+----+----+----+----+----+ + | | + + + + | Noise Payload | + + (length varies) + + | see below for allowed blocks | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + Destination Connection ID :: As sent in Session Request, + or one received in Session Confirmed? + + Packet Number :: 0 always, for all fragments, even if retransmitted + + type :: 2 + + frag :: 1 byte fragment info: + bit order: 76543210 (bit 7 is MSB) + bits 7-4: fragment number 0-14, big endian + bits 3-0: total fragments 1-15, big endian + + flags :: 2 bytes, unused, set to 0 for future compatibility + + S :: 32 bytes, Alice's X25519 static key, little endian + +{% endhighlight %} + + +Payload +``````` +- RouterInfo block (must be the first block) +- Options block (optional) +- New Token block (optional) +- Relay Request block (optional) +- Peer Test block (optional) +- First Packet Number block (optional) +- I2NP, First Fragment, or Follow-on Fragment blocks (optional, but probably no room) +- Padding block (optional) + +The minimum payload size is 8 bytes. Since the RouterInfo block +will be well more than that, the requirement is met with only that block. + + +Notes +````` +- Bob must perform the usual Router Info validation. + Ensure the signature type is supported, verify the signature, + verify the timestamp is within bounds, and any other checks necessary. + See below for notes on handling fragmented Router Infos. + +- Bob must verify that Alice's static key received in the first frame matches + the static key in the Router Info. Bob must first search the Router Info for + a NTCP or SSU2 Router Address with a matching version (v) option. + See Published Router Info and Unpublished Router Info sections below. + See below for notes on handling fragmented Router Infos. + +- If Bob has an older version of Alice's RouterInfo in his netdb, verify + that the static key in the router info is the same in both, if present, + and if the older version is less than XXX old (see key rotate time below) + +- Bob must validate that Alice's static key is a valid point on the curve here. + +- Options should be included, to specify padding parameters. + +- On any error, including AEAD, RI, DH, timestamp, or key validation failure, + Bob must halt further message processing and close the connection without + responding. + +- Message 3 part 2 frame content: This format of this frame is the same as the + format of data phase frames, except that the length of the frame is sent + by Alice in Session Request. See below for the data phase frame format. + The frame must contain 1 to 4 blocks in the following order: + 1) Alice's Router Info block (required) + 2) Options block (optional) + 3) I2NP blocks (optional) + 4) Padding block (optional) + This frame must never contain any other block type. + TODO: what about relay and peer test? + +- Message 3 part 2 padding block is recommended. + +- There may be no space, or only a small amount of space, available for + I2NP blocks, depending on the MTU and the Router Info size. + Do NOT include I2NP blocks if the Router Info is fragmented. + The simplest implementation may be to never include I2NP blocks in + the Session Confirmed message, and send all I2NP blocks in + subsequent Data messages. + See Router Info block section below for maximum block size. + + +Session Confirmed Fragmentation +````````````````````````````````````` + +The Session Confirmed message must contain the full signed Router Info from Alice so that +Bob may perform several required checks: + +- The static key "s" in the RI matches the static key in the handshake +- The introduction key "i" in the RI must be extracted and valid, to be used + in the data phase +- The RI signature is valid + +Unfortunately, the Router Info, even when gzip compressed in the RI block, may exceed the MTU. +Therefore, the Session Confirmed may be fragmented across two or more packets. +This is the ONLY case in the SSU2 protcol where an AEAD-protected payload is fragmented +across two or more packets. + +The headers for each packet are constructed as follows: + +- ALL headers are short headers with the same packet number 0 +- ALL headers contain a "frag" field, with the fragment number and + total number of fragments +- The unencrypted header of fragment 0 is the associated data (AD) for the "jumbo" message +- Each header is encrypted using the last 24 bytes of data in THAT packet + +Construct the series of packets as follows: + +- Create a single RI block (fragment 0 of 1 in the RI block frag field). + We do not use RI block fragmentation, that was for an alternate method + of solving the same problem. +- Create a "jumbo" payload with the RI block and any other blocks to be included +- Calculate the total data size (not including the header), + which is the payload size + 64 bytes for the static key and two MACs +- Calculate the space available in each packet, which is + the MTU minus the IP header (20 or 40), minus the UDP header (8), + minus the SSU2 short header (16). Total per-packet overhead is + 44 (IPv4) or 64 (IPv6). +- Calculate the number of packets. +- Calculate the size of the data in the last packet. It must be greater than + or equal to 24 bytes, so that header encryption will work. + If it is too small, either add a padding block, OR increase the size of the + padding block if already present, OR reduce the size of one of the other packets + so that the last packet will be big enough. +- Create the unencrypted header for the first packet, with the total number of + fragments in the frag field, and encrypt the "jumbo" + payload with Noise, using the header as AD, as usual. +- Split up the encrypted jumbo packet into fragments +- Add an unencrypted header for each fragment 1-n +- Encrypt the header for each fragment 0-n. Each header uses the SAME + k_header_1 and k_header_2 as defined above in the Session Confirmed KDF. +- Transmit all fragments + +Reassembly process: + +When Bob receives any Session Confirmed message, he decrypts the header, +inspects the frag field, and determines that the Session Confirmed is fragmented. +He does not (and cannot) decrypt the message until all fragments are received +and reassembled. + +- Preserve the header for fragment 0, as it is used as the Noise AD +- Discard the headers for other fragments before reassembly +- Reassemble the "jumbo" payload, with the header for fragment 0 as AD, + and decrypt with Noise +- Validate the RI block as usual +- Proceed to the data phase and send ACK 0, as usual + +There is no mechanism for Bob to ack individual fragments. When Bob receives all +fragments, reassembles, decrypts, and validates the contents, Bob does a split() +as usual, enters the data phase, and sends an ACK of packet number 0. + +If Alice does not receive an ACK of packet number 0, she must retransmit all +session confirmed packets as-is. + +Examples: + +For 1500 MTU over IPv6, max payload is 1372, RI block overhead is 5, +max (gzip compressed) RI data size is 1367 (assuming no other blocks). +With two packets, the overhead of the 2nd packet is 64, so it can hold +another 1436 bytes of payload. So two packets is enough for a compressed +RI up to 2803 bytes. + +The largest compressed RI seen in the current network is about 1400 bytes; +therefore, in practice, two fragments should be enough, even with +a minimum 1280 MTU. The protocol allows for 15 fragments max. + +Security analysis: + +The integrity and security of a fragmented Session Confirmed is the same as that +of an unfragmented one. Any alteration of any fragment will cause the +Noise AEAD to fail after reassembly. The headers of the fragments after fragment +0 are only used to identify the fragment. Even if an on-path attacker had the +k_header_2 key used to encrypt the header (unlikely, derived from the handshake), +this would not allow the attacker to substitute a valid fragment. + + + +KDF for data phase +---------------------------------------------- + +The data phase uses the header for associated data. + +The KDF generates two cipher keys k_ab and k_ba from the chaining key ck, +using HMAC-SHA256(key, data) as defined in [RFC-2104]_. +This is the split() function, exactly as defined in the Noise spec. + +.. raw:: html + + {% highlight lang='text' %} +// split() + // chainKey = from handshake phase + keydata = HKDF(chainKey, ZEROLEN, "", 64) + k_ab = keydata[0:31] + k_ba = keydata[32:63] + + // key is k_ab for Alice to Bob + // key is k_ba for Bob to Alice + + keydata = HKDF(key, ZEROLEN, "HKDFSSU2DataKeys", 64) + k_data = keydata[0:31] + k_header_2 = keydata[32:63] + + + // AEAD parameters + k = k_data + n = 4 byte packet number from header + ad = 16 byte header, before header encryption + ciphertext = ENCRYPT(k, n, payload, ad) + + // Header encryption keys for data phase + // aik = Alice's intro key + // bik = Bob's intro key + k_header_1 = Receiver's intro key (aik or bik) + k_header_2: from above + +{% endhighlight %} + + + + + +Data Message (Type 6) +--------------------------- + +Noise payload: All block types are allowed +Max payload size: MTU - 60 (IPv4) or MTU - 80 (IPv6). +For 1500 MTU: Max payload is 1440 (IPv4) or 1420 (IPv6). + +Starting with the 2nd part of Session Confirmed, all messages are inside +an authenticated and encrypted ChaChaPoly payload. +All padding is inside the message. +Inside the payload is a standard format with zero or more "blocks". +Each block has a one-byte type and a two-byte length. +Types include date/time, I2NP message, options, termination, and padding. + +Note: Bob may, but is not required, to send his RouterInfo to Alice as +his first message to Alice in the data phase. + +Payload Security Properties: + + +.. raw:: html + + {% highlight lang='text' %} +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. + +{% endhighlight %} + +Notes +````` +- The router must drop a message with an AEAD error. + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Short Header 16 bytes, ChaCha20 | + + encrypted with intro key and + + | derived key, see Data Phase KDF | + +----+----+----+----+----+----+----+----+ + | ChaCha20 data | + + Encrypted and authenticated data + + | length varies | + + k defined in Data Phase KDF + + | n = packet number from header | + + + + | | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + +{% endhighlight %} + +Unencrypted data (Poly1305 auth tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| flags | + +----+----+----+----+----+----+----+----+ + | Noise payload (block data) | + + (length varies) + + | | + +----+----+----+----+----+----+----+----+ + + Destination Connection ID :: As specified in session setup + + Packet Number :: 4 byte big endian integer + + type :: 6 + + flags :: 3 bytes, unused, set to 0 for future compatibility + +{% endhighlight %} + + +Notes +````` +- The minimum payload size is 8 bytes. This requirement will be met + by any ACK, I2NP, First Fragment, or Follow-on Fragment block. + If the requirement is not met, a Padding block must be included. + +- Each packet number may only be used once. + When retransmitting I2NP messages or fragments, + a new packet number must be used. + + +KDF for Peer Test +-------------------- + +.. raw:: html + + {% highlight lang='text' %} + +// AEAD parameters + // bik = Bob's intro key + k = bik + n = 4 byte packet number from header + ad = 32 byte header, before header encryption + ciphertext = ENCRYPT(k, n, payload, ad) + + // Header encryption keys for this message + k_header_1 = bik + k_header_2 = bik + +{% endhighlight %} + + +Peer Test (Type 7) +------------------------ + +Charlie sends to Alice, and Alice Sends to Charlie, +for Peer Test phases 5-7 only. +Peer Test phases 1-4 must be sent in-session using a Peer Test block in a Data message. +See the Peer Test Block and Peer Test Process sections below for more information. + +Size: 48 + payload size. + +Noise payload: See below. + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Long Header bytes 0-15, ChaCha20 | + + encrypted with Alice or Charlie + + | intro key | + +----+----+----+----+----+----+----+----+ + | Long Header bytes 16-31, ChaCha20 | + + encrypted with Alice or Charlie + + | intro key | + +----+----+----+----+----+----+----+----+ + | | + + + + | ChaCha20 encrypted data | + + (length varies) + + | | + + see KDF for key and n + + | see KDF for associated data | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + +{% endhighlight %} + +Unencrypted data (Poly1305 authentication tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + | ChaCha20 payload (block data) | + + (length varies) + + | see below for allowed blocks | + +----+----+----+----+----+----+----+----+ + + + Destination Connection ID :: See below + + type :: 7 + + ver :: 2 + + id :: 1 byte, the network ID (currently 2, except for test networks) + + flag :: 1 byte, unused, set to 0 for future compatibility + + Packet Number :: Random number generated by Alice or Charlie + + Source Connection ID :: See below + + Token :: Randomly generated by Alice or Charlie, ignored + +{% endhighlight %} + +Payload +``````` +- DateTime block +- Address block (required for messages 6 and 7, see note below) +- Peer Test block +- Padding block (optional) + +The minimum payload size is 8 bytes. Since the Peer Test block +totals more than that, the requirement is met with only this block. + +In messages 5 and 7, the Peer Test block may be identical to +the block from in-session messages 3 and 4, +containing the agreement signed by Charlie, +or it may be regenerated. Signature is optional. + +In message 6, the Peer Test block may be identical to +the block from in-session messages 1 and 2, +containing the request signed by Alice, +or it may be regenerated. Signature is optional. + +Connection IDs: The two connection IDs are derived from the test nonce. +For messages 5 and 7 sent from Charlie to Alice, the Destination Connection ID +is two copies of the 4-byte big-endian test nonce, i.e. ((nonce << 32) | nonce). +The Source Connection ID is the inverse of the Destination Connection ID, +i.e. ~((nonce << 32) | nonce). +For message 6 sent from Alice to Charlie, swap the two connection IDs. + +Address block contents: + +- In message 5: Not required. +- In message 6: Charlie's IP and port as selected from Charlie's RI. +- In message 7: Alice's actual IP and port message 6 was received from. + + + +KDF for Retry +---------------- + +The requirement for the Retry message is that Bob is not required to +decrypt the Session Request message to generate a Retry message in response. +Also, this message must be fast to generate, using symmetric encryption only. + +.. raw:: html + + {% highlight lang='text' %} + +// AEAD parameters + // bik = Bob's intro key + k = bik + n = 4 byte packet number from header + ad = 32 byte header, before header encryption + ciphertext = ENCRYPT(k, n, payload, ad) + + // Header encryption keys for this message + k_header_1 = bik + k_header_2 = bik + +{% endhighlight %} + + +Retry (Type 9) +------------------------------- + +Bob sends to Alice, in response to a Session Request or Token Request message. +Alice responds with a new Session Request. +Size: 48 + payload size. + +Also serves as a Termination message (i.e., "Don't Retry") +if a Termination block is included. + + +Noise payload: See below. + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Long Header bytes 0-15, ChaCha20 | + + encrypted with Bob intro key + + | | + +----+----+----+----+----+----+----+----+ + | Long Header bytes 16-31, ChaCha20 | + + encrypted with Bob intro key + + | | + +----+----+----+----+----+----+----+----+ + | | + + + + | ChaCha20 encrypted data | + + (length varies) + + | | + + see KDF for key and n + + | see KDF for associated data | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + +{% endhighlight %} + +Unencrypted data (Poly1305 authentication tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + | ChaCha20 payload (block data) | + + (length varies) + + | see below for allowed blocks | + +----+----+----+----+----+----+----+----+ + + + Destination Connection ID :: The Source Connection ID + received from Alice in Token Request + or Session Request + + Packet Number :: Random number generated by Bob + + type :: 9 + + ver :: 2 + + id :: 1 byte, the network ID (currently 2, except for test networks) + + flag :: 1 byte, unused, set to 0 for future compatibility + + Source Connection ID :: The Destination Connection ID + received from Alice in Token Request + or Session Request + + Token :: 8 byte unsigned integer, randomly generated by Bob, nonzero, + or zero if session is rejected and a termination block is included + +{% endhighlight %} + +Payload +``````` +- DateTime block +- Address block +- Options block (optional) +- Termination block (optional, if session is rejected) +- Padding block (optional) + +The minimum payload size is 8 bytes. Since the DateTime and Address blocks +total more than that, the requirement is met with only those two blocks. + + +Notes +````` +- To provide probing resistance, a router should not send a Retry message + in response to a Session Request or Token Request message unless the + message type, protocol version, and network ID fields in the Request message + are valid. + +- To limit the magnitude of any amplification attack that can be mounted using spoofed source addresses, + the Retry message must not contain large amounts of padding. + It is recommended that the Retry message be no larger than three times the size + of the message it is responding to. + Alternatively, use a simple method such as adding a random amount of padding + in the range 1-64 bytes. + + +KDF for Token Request +-------------------------- + +This message must be fast to generate, using symmetric encryption only. + +.. raw:: html + + {% highlight lang='text' %} + +// AEAD parameters + // bik = Bob's intro key + k = bik + n = 4 byte packet number from header + ad = 32 byte header, before header encryption + ciphertext = ENCRYPT(k, n, payload, ad) + + // Header encryption keys for this message + k_header_1 = bik + k_header_2 = bik + +{% endhighlight %} + + +Token Request (Type 10) +------------------------------- + +Alice sends to Bob. Bob response with a Retry message. +Size: 48 + payload size. + +If Alice does not have a valid token, Alice should send this message +instead of a Session Request, to avoid the asymmetric encryption +overhead in generating a Session Request. + + +Noise payload: See below. + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Long Header bytes 0-15, ChaCha20 | + + encrypted with Bob intro key + + | | + +----+----+----+----+----+----+----+----+ + | Long Header bytes 16-31, ChaCha20 | + + encrypted with Bob intro key + + | | + +----+----+----+----+----+----+----+----+ + | | + + + + | ChaCha20 encrypted data | + + (length varies) + + | | + + see KDF for key and n + + | see KDF for associated data | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + +{% endhighlight %} + +Unencrypted data (Poly1305 authentication tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + | ChaCha20 payload (block data) | + + (length varies) + + | see below for allowed blocks | + +----+----+----+----+----+----+----+----+ + + + Destination Connection ID :: Randomly generated by Alice + + Packet Number :: Random number generated by Alice + + type :: 10 + + ver :: 2 + + id :: 1 byte, the network ID (currently 2, except for test networks) + + flag :: 1 byte, unused, set to 0 for future compatibility + + Source Connection ID :: Randomly generated by Alice, + must not be equal to Destination Connection ID + + Token :: zero + +{% endhighlight %} + + +Payload +``````` +- DateTime block +- Padding block + +The minimum payload size is 8 bytes. + + +Notes +````` +- To provide probing resistance, a router should not send a Retry message + in response to a Token Request message unless the + message type, protocol version, and network ID fields in the Token Request message + are valid. + +- This is NOT a standard Noise message and is not part of the handshake. + It is not bound to the Session Request message other than by connection IDs. + +- On most errors, including AEAD, or apparent replay + Bob should halt further message processing and + drop the message without responding. + +- Bob must reject connections where the timestamp value is too far off from the + current time. Call the maximum delta time "D". Bob must maintain a local + cache of previously-used handshake values and reject duplicates, to prevent + replay attacks. Values in the cache must have a lifetime of at least 2*D. + The cache values are implementation-dependent, however the 32-byte X value + (or its encrypted equivalent) may be used. + +- Bob MAY send a Retry message containing a zero token and a Termination block with a + clock skew reason code if the timestamp in the DateTime block is too + far skewed. + +- Minimum size: TBD, same rules as for Session Created? + + + +KDF for Hole Punch +-------------------------- + +This message must be fast to generate, using symmetric encryption only. + +.. raw:: html + + {% highlight lang='text' %} + +// AEAD parameters + // aik = Alice's intro key + k = aik + n = 4 byte packet number from header + ad = 32 byte header, before header encryption + ciphertext = ENCRYPT(k, n, payload, ad) + + // Header encryption keys for this message + k_header_1 = aik + k_header_2 = aik + +{% endhighlight %} + + + + +Hole Punch (Type 11) +------------------------------- + +Charlie sends to Alice, in response to a Relay Intro received from Bob. +Alice responds with a new Session Request. +Size: 48 + payload size. + +Noise payload: See below. + +Raw contents: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Long Header bytes 0-15, ChaCha20 | + + encrypted with Alice intro key + + | | + +----+----+----+----+----+----+----+----+ + | Long Header bytes 16-31, ChaCha20 | + + encrypted with Alice intro key + + | | + +----+----+----+----+----+----+----+----+ + | | + + + + | ChaCha20 encrypted data | + + (length varies) + + | | + + see KDF for key and n + + | see KDF for associated data | + +----+----+----+----+----+----+----+----+ + | | + + Poly1305 MAC (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + + +{% endhighlight %} + +Unencrypted data (Poly1305 authentication tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | Destination Connection ID | + +----+----+----+----+----+----+----+----+ + | Packet Number |type| ver| id |flag| + +----+----+----+----+----+----+----+----+ + | Source Connection ID | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + | ChaCha20 payload (block data) | + + (length varies) + + | see below for allowed blocks | + +----+----+----+----+----+----+----+----+ + + + Destination Connection ID :: See below + + Packet Number :: Random number generated by Charlie + + type :: 11 + + ver :: 2 + + id :: 1 byte, the network ID (currently 2, except for test networks) + + flag :: 1 byte, unused, set to 0 for future compatibility + + Source Connection ID :: See below + + Token :: 8 byte unsigned integer, randomly generated by Charlie, nonzero. + +{% endhighlight %} + +Payload +``````` +- DateTime block +- Address block +- Relay Response block +- Padding block (optional) + +The minimum payload size is 8 bytes. Since the DateTime and Address blocks +total more than that, the requirement is met with only those two blocks. + +Connection IDs: The two connection IDs are derived from the relay nonce. +The Destination Connection ID +is two copies of the 4-byte big-endian relay nonce, i.e. ((nonce << 32) | nonce). +The Source Connection ID is the inverse of the Destination Connection ID, +i.e. ~((nonce << 32) | nonce). + +Alice should ignore the token in the header. The token to be used in +the Session Request is in the Relay Response block. + + + +Noise Payload +=============== + +Each Noise payload contains zero or more "blocks". + +This uses the same block format as defined in the [NTCP2]_ and [ECIES]_ specifications. +Individual block types are defined differently. +The equivalent term in QUIC [RFC-9000]_ is "frames". + +There are concerns that encouraging implementers to share code +may lead to parsing issues. Implementers should carefully consider +the benefits and risks of sharing code, and ensure that the +ordering and valid block rules are different for the two contexts. + + +Payload Format +---------------- + +There are one or more blocks in the encrypted payload. +A block is a simple Tag-Length-Value (TLV) format. +Each block contains a one-byte identifier, a two-byte length, +and zero or more bytes of data. +This format is identical to that in [NTCP2]_ and [ECIES]_, +however the block definitions are different. + +For extensibility, receivers must ignore blocks with unknown identifiers, +and treat them as padding. + + +(Poly1305 auth tag not shown): + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + |blk | size | data | + +----+----+----+ + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + |blk | size | data | + +----+----+----+ + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + ~ . . . ~ + + blk :: 1 byte, see below + size :: 2 bytes, big endian, size of data to follow, 0 - TBD + data :: the data + +{% endhighlight %} + +Header encryption uses the last 24 bytes of the packet as the IV for the two +ChaCha20 operations. As all packets end with a 16 byte MAC, +this requires that all packet payloads are a minimum of 8 bytes. +If a payload would not otherwise meet this requirement, +a Padding block must be included. + +Maximum ChaChaPoly payload varies based on message type, MTU, +and IPv4 or IPv6 address type. +Maximum payload is MTU - 60 for IPv4 and MTU - 80 for IPv6. +Maximum payload data is MTU - 63 for IPv4 and MTU - 83 for IPv6. +Upper limit is about 1440 bytes for IPv4, 1500 MTU, Data message. +Maximum total block size is the maximum payload size. +Maximum single block size is the maximum total block size. +Block type is 1 byte. +Block length is 2 bytes. +Maximum single block data size is the maximum single block size minus 3. + +Notes: + +- Implementers must ensure that when reading a block, + malformed or malicious data will not cause reads to + overrun into the next block or beyond the payload boundary. + +- Implementations should ignore unknown block types for + forward compatibility. + + + +Block types: + +==================================== ============= ============ + Payload Block Type Type Number Block Length +==================================== ============= ============ +DateTime 0 7 +Options 1 15+ +Router Info 2 varies +I2NP Message 3 varies +First Fragment 4 varies +Follow-on Fragment 5 varies +Termination 6 9 typ. +Relay Request 7 varies +Relay Response 8 varies +Relay Intro 9 varies +Peer Test 10 varies +Next Nonce 11 TBD +ACK 12 varies +Address 13 9 or 21 +reserved 14 -- +Relay Tag Request 15 3 +Relay Tag 16 7 +New Token 17 15 +Path Challenge 18 varies +Path Response 19 varies +First Packet Number 20 7 +Congestion 21 4 +reserved for experimental features 224-253 +Padding 254 varies +reserved for future extension 255 +==================================== ============= ============ + + +Block Ordering Rules +---------------------- + +In the Session Confirmed, Router Info must be the first block. + +In all other messages, order is unspecified, except for the +following requirements: +Padding, if present, must be the last block. +Termination, if present, must be the last block except for Padding. +Multiple Padding blocks are not allowed in a single payload. + + +Block Specifications +---------------------- + +DateTime +```````` +For time synchronization: + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+ + | 0 | 4 | timestamp | + +----+----+----+----+----+----+----+ + + blk :: 0 + size :: 2 bytes, big endian, value = 4 + timestamp :: Unix timestamp, unsigned seconds. + Wraps around in 2106 + +{% endhighlight %} + +Notes: + +Unlike in SSU 1, there is no timestamp in the packet header +for the data phase in SSU 2. +Implementations should periodically send DateTime blocks +in the data phase. + + +Options +``````` +Pass updated options. +Options include: Min and max padding. + +Options block will be variable length. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 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 + +{% endhighlight %} + + +Options Issues: + +- Options negotiation is TBD. + + +RouterInfo +`````````` +Pass Alice's RouterInfo to Bob. +Used in Session Confirmed part 2 payload only. +Not to be used in the data phase; use an +I2NP DatabaseStore Message instead. + +Minimum Size: About 420 bytes, unless the router identity and +signature in the router info are compressible, which is unlikely. + +NOTE: The Router Info block is never fragmented. +The frag field is always 0/1. +See the Session Confirmed Fragmentation section above for more information. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 2 | size |flag|frag| | + +----+----+----+----+----+ + + | | + + Router Info fragment + + | (Alice RI in Session Confirmed) | + + (Alice, Bob, or third-party + + | RI in data phase) | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 2 + size :: 2 bytes, big endian, 2 + fragment size + flag :: 1 byte flags + bit order: 76543210 (bit 7 is MSB) + bit 0: 0 for local store, 1 for flood request + bit 1: 0 for uncompressed, 1 for gzip compressed + bits 7-2: Unused, set to 0 for future compatibility + frag :: 1 byte fragment info: + bit order: 76543210 (bit 7 is MSB) + bits 7-4: fragment number, always 0 + bits 3-0: total fragments, always 1, big endian + + routerinfo :: Alice's or Bob's RouterInfo + + +{% endhighlight %} + +Notes: + +- The Router Info is optionally compressed with gzip, + as indicated by flag bit 1. + This is different from NTCP2, where it is never compressed, + and from a DatabaseStore Message, where it always is compressed. + Compression is optional because it usually is of little benefit + for small Router Infos, where there is little compressible content, + but is very beneficial for large Router Infos with several + compressible Router Addresses. + Compression is recommended if it allows a Router Info to fit + in a single Session Confirmed packet without fragmentation. + +- Maximum size of first or only fragment in the Session Confirmed message: + MTU - 113 for IPv4 or MTU - 133 for IPv6. + Assuming 1500 byte default MTU, and no other blocks in the message, + 1387 for IPv4 or 1367 for IPv6. + 97% of current router infos are smaller than 1367 witout gzipping. + 99.9% of current router infos are smaller than 1367 when gzipped. + Assuming 1280 byte minimum MTU, and no other blocks in the message, + 1167 for IPv4 or 1147 for IPv6. + 94% of current router infos are smaller than 1147 witout gzipping. + 97% of current router infos are smaller than 1147 when gzipped. + +- The frag byte is now unused, the Router Info block is never fragmented. + The frag byte must be set to fragment 0, total fragments 1. + See the Session Confirmed Fragmentation section above for more information. + +- Flooding must not be requested unless there are published + RouterAddresses in the RouterInfo. The receiving router + must not flood the RouterInfo unless there are published + RouterAddresses in it. + +- This protocol does not provide an acknowledgment that the RouterInfo + was stored or flooded. + If acknowledgment is desired, and the receiver is floodfill, + the sender should instead send a standard I2NP DatabaseStoreMessage + with a reply token. + + + +I2NP Message +```````````` +A complete I2NP message with a modified header. + +This uses the same 9 bytes for the I2NP header +as in [NTCP2]_ (type, message id, short expiration). + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 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 + +{% endhighlight %} + +Notes: + +- This is the same 9-byte I2NP header format used in NTCP2. + +- This is exactly the same format as the First Fragment block, + but the block type indicates that this is a complete message. + +- Maximum size including 9-byte I2NP header is MTU - 63 for IPv4 and MTU - 83 for IPv6. + + +First Fragment +``````````````` +The first fragment (fragment #0) of an I2NP message with a modified header. + +This uses the same 9 bytes for the I2NP header +as in [NTCP2]_ (type, message id, short expiration). + +Total number of fragments is not specified. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 4 | size |type| msg id | + +----+----+----+----+----+----+----+----+ + | short exp | | + +----+----+----+----+ + + | partial message | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 4 + size :: 2 bytes, big endian, size of data to follow + Fragment 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 :: Partial I2NP message body, bytes 0 - (size - 10) + +{% endhighlight %} + +Notes: + +- This is the same 9-byte I2NP header format used in NTCP2. + +- This is exactly the same format as the I2NP Message block, + but the block type indicates that this is a the first fragment of a message. + +- Partial message length must be greater than zero. + +- As in SSU 1, it is recommended to send the last fragment first, + so that the receiver knows the total number of fragments and can + efficiently allocate receive buffers. + +- Maximum size including 9-byte I2NP header is MTU - 63 for IPv4 and MTU - 83 for IPv6. + + +Follow-on Fragment +```````````````````````` +An additional fragment (fragment number greater than zero) of an I2NP message. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 5 | size |frag| msg id | + +----+----+----+----+----+----+----+----+ + | | + + + + | partial message | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 5 + size :: 2 bytes, big endian, size of data to follow + Fragment size is (size - 5). + frag :: Fragment info: + Bit order: 76543210 (bit 7 is MSB) + bits 7-1: fragment number 1 - 127 (0 not allowed) + bit 0: isLast (1 = true) + msg id :: 4 bytes, big endian, I2NP message ID + message :: Partial I2NP message body + +{% endhighlight %} + +Notes: + +- Partial message length must be greater than zero. + +- As in SSU 1, it is recommended to send the last fragment first, + so that the receiver knows the total number of fragments and can + efficiently allocate receive buffers. + +- As in SSU 1, the maximum fragment number is 127, but the practical + limit is 63 or less. Implementations may limit the maximum to + what is practical for a maximum I2NP message size of about 64 KB, + which is about 55 fragments with a 1280 minimum MTU. + See the Max I2NP Message Size section below. + +- Maximum partial message size (not including frag and message id) is MTU - 68 for IPv4 and MTU - 88 for IPv6. + + + +Termination +``````````` +Drop the connection. +This must be the last non-padding block in the payload. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 6 | size | valid data packets | + +----+----+----+----+----+----+----+----+ + received | rsn| addl data | + +----+----+----+----+ + + ~ . . . ~ + +----+----+----+----+----+----+----+----+ + + blk :: 6 + size :: 2 bytes, big endian, value = 9 or more + valid data packets received :: The number of valid packets 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: Session Request error + 12: Session Created error + 13: Session Confirmed error + 14: Timeout + 15: RI signature verification fail + 16: s parameter missing, invalid, or mismatched in RouterInfo + 17: banned + 18: bad token + 19: connection limits + 20: incompatible version + 21: wrong net ID + 22: replaced by new session + addl data :: optional, 0 or more bytes, for future expansion, debugging, + or reason text. + Format unspecified and may vary based on reason code. + +{% endhighlight %} + +Notes: + +- Not all reasons may actually be used, implementation dependent. + Most failures will generally result in the message being dropped, not a termination. + See notes in handshake message sections above. + Additional reasons listed are for consistency, logging, debugging, or if policy changes. +- It is recommended that an ACK block be included with the Termination block. +- In the data phase, for any reason other than "termination received", + the peer should respond with a termination block with the reason "termination received". + + +RelayRequest +`````````````` + +Sent in a Data message in-session, from Alice to Bob. +See Relay Process section below. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 7 | size |flag| nonce | + +----+----+----+----+----+----+----+----+ + | relay tag | timestamp | + +----+----+----+----+----+----+----+----+ + | ver| asz|AlicePort| Alice IP address | + +----+----+----+----+----+----+----+----+ + | signature | + + length varies + + | 64 bytes for Ed25519 | + ~ ~ + | . . . | + +----+----+----+----+----+----+----+----+ + + blk :: 7 + size :: 2 bytes, big endian, size of data to follow + flag :: 1 byte flags, Unused, set to 0 for future compatibility + + The data below here is covered + by the signature, and Bob forwards it unmodified. + + nonce :: 4 bytes, randomly generated by Alice + relay tag :: 4 bytes, the itag from Charlie's RI + timestamp :: Unix timestamp, unsigned seconds. + Wraps around in 2106 + ver :: 1 byte SSU version to be used for the introduction: + 1: SSU 1 + 2: SSU 2 + asz :: 1 byte endpoint (port + IP) size (6 or 18) + AlicePort :: 2 byte Alice's port number, big endian + Alice IP :: (asz - 2) byte representation of Alice's IP address, + network byte order + signature :: length varies, 64 bytes for Ed25519. + Signature of prologue, Bob's hash, + and signed data above, as signed by + Alice. + +{% endhighlight %} + +Notes: + +* The IP address is always included (unlike in SSU 1) + and may be different than the IP used for the session. + + +Signature: + +Alice signs the request and includes it in this block; Bob forwards it in the Relay Intro block to Charlie. +Signature algorithm: Sign the following data with the Alice's router signing key: + +- prologue: 16 bytes "RelayRequestData", not null-terminated (not included in the message) +- bhash: Bob's 32-byte router hash (not included in the message) +- chash: Charlie's 32-byte router hash (not included in the message) +- nonce: 4 byte nonce +- relay tag: 4 byte relay tag +- timestamp: 4 byte timestamp (seconds) +- ver: 1 byte SSU version +- asz: 1 byte endpoint (port + IP) size (6 or 18) +- AlicePort: 2 byte Alice's port number +- Alice IP: (asz - 2) byte Alice IP address + + +RelayResponse +`````````````` + +Sent in a Data message in-session, from Charlie to Bob +or from Bob to Alice, AND in the Hole Punch message +from Charlie to Alice. +See Relay Process section below. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 8 | size |flag|code| nonce + +----+----+----+----+----+----+----+----+ + | timestamp | ver| csz|Char + +----+----+----+----+----+----+----+----+ + Port| Charlie IP addr | | + +----+----+----+----+----+ + + | signature | + + length varies + + | 64 bytes for Ed25519 | + ~ ~ + | . . . | + +----+----+----+----+----+----+----+----+ + | Token | + +----+----+----+----+----+----+----+----+ + + blk :: 8 + size :: 2 bytes, 6 + flag :: 1 byte flags, Unused, set to 0 for future compatibility + code :: 1 byte status code: + 0: accept + 1: rejected by Bob, reason unspecified + 2: rejected by Bob, Charlie is banned + 3: rejected by Bob, limit exceeded + 4: rejected by Bob, signature failure + 5: rejected by Bob, relay tag not found + 6: rejected by Bob, Alice RI not found + 7-63: other rejected by Bob codes TBD + 64: rejected by Charlie, reason unspecified + 65: rejected by Charlie, unsupported address + 66: rejected by Charlie, limit exceeded + 67: rejected by Charlie, signature failure + 68: rejected by Charlie, Alice is already connected + 69: rejected by Charlie, Alice is banned + 70: rejected by Charlie, Alice is unknown + 71-127: other rejected by Charlie codes TBD + 128: reject, source and reason unspecified + 129-255: other reject codes TBD + + The data below is covered by the signature if the code is 0 (accept). + Bob forwards it unmodified. + + nonce :: 4 bytes, as received from Bob or Alice + + The data below is present only if the code is 0 (accept). + + timestamp :: Unix timestamp, unsigned seconds. + Wraps around in 2106 + ver :: 1 byte SSU version to be used for the introduction: + 1: SSU 1 + 2: SSU 2 + csz :: 1 byte endpoint (port + IP) size (0 or 6 or 18) + may be 0 for some rejection codes + CharliePort :: 2 byte Charlie's port number, big endian + not present if csz is 0 + Charlie IP :: (csz - 2) byte representation of Charlie's IP address, + network byte order + not present if csz is 0 + signature :: length varies, 64 bytes for Ed25519. + Signature of prologue, Bob's hash, + and signed data above, as signed by + Charlie. + Not present if rejected by Bob. + token :: Token generated by Charlie for Alice to use + in the Session Request. + Only present if code is 0 (accept) + +{% endhighlight %} + + + +Notes: + +The token must be used immediately by Alice in the Session Request. + + + +Signature: + +If Charlie agrees (response code 0) or rejects (response code 64 or higher), +Charlie signs the response and includes it in this block; Bob forwards it in the Relay Response block to Alice. +Signature algorithm: Sign the following data with the Charlie's router signing key: + +- prologue: 16 bytes "RelayAgreementOK", not null-terminated (not included in the message) +- bhash: Bob's 32-byte router hash (not included in the message) +- nonce: 4 byte nonce +- timestamp: 4 byte timestamp (seconds) +- ver: 1 byte SSU version +- csz: 1 byte endpoint (port + IP) size (0 or 6 or 18) +- CharliePort: 2 byte Charlie's port number (not present if csz is 0) +- Charlie IP: (csz - 2) byte Charlie IP address (not present if csz is 0) + +If Bob rejects (response code 1-63), +Bob signs the response and includes it in this block. +Signature algorithm: Sign the following data with the Bob's router signing key: + +- prologue: 16 bytes "RelayAgreementOK", not null-terminated (not included in the message) +- bhash: Bob's 32-byte router hash (not included in the message) +- nonce: 4 byte nonce +- timestamp: 4 byte timestamp (seconds) +- ver: 1 byte SSU version +- csz: 1 byte = 0 + + +RelayIntro +`````````````` + +Sent in a Data message in-session, from Bob to Charlie. +See Relay Process section below. + +Must be preceded by a RouterInfo block, or I2NP DatabaseStore message block (or fragment), +containing Alice's Router Info, +either in the same payload (if there's room), or in a previous message. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 9 | size |flag| | + +----+----+----+----+ + + | | + + + + | Alice Router Hash | + + 32 bytes + + | | + + +----+----+----+----+ + | | nonce | + +----+----+----+----+----+----+----+----+ + | relay tag | timestamp | + +----+----+----+----+----+----+----+----+ + | ver| asz|AlicePort| Alice IP address | + +----+----+----+----+----+----+----+----+ + | signature | + + length varies + + | 64 bytes for Ed25519 | + ~ ~ + | . . . | + +----+----+----+----+----+----+----+----+ + + blk :: 9 + size :: 2 bytes, big endian, size of data to follow + flag :: 1 byte flags, Unused, set to 0 for future compatibility + hash :: Alice's 32-byte router hash, + + The data below here is covered + by the signature, as received from Alice in the Relay Request, + and Bob forwards it unmodified. + + nonce :: 4 bytes, as received from Alice + relay tag :: 4 bytes, the itag from Charlie's RI + timestamp :: Unix timestamp, unsigned seconds. + Wraps around in 2106 + ver :: 1 byte SSU version to be used for the introduction: + 1: SSU 1 + 2: SSU 2 + asz :: 1 byte endpoint (port + IP) size (6 or 18) + AlicePort :: 2 byte Alice's port number, big endian + Alice IP :: (asz - 2) byte representation of Alice's IP address, + network byte order + signature :: length varies, 64 bytes for Ed25519. + Signature of prologue, Bob's hash, + and signed data above, as signed by + Alice. + +{% endhighlight %} + + + +Notes: + +* For IPv4, Alice's IP address is always 4 bytes, because Alice is trying to connect to Charlie via IPv4. + IPv6 is supported, and Alice's IP address may be 16 bytes. + +* For IPv4, this message must be sent via an established IPv4 connection, + as that's the only way that Bob knows Charlie's IPv4 address to return to Alice in the RelayResponse_. + IPv6 is supported, and this message may be sent via an established IPv6 connection. + +* Any SSU address published with introducers must contain "4" or "6" in the "caps" option. + + +Signature: + +Alice signs the request and Bob forwards it in this block to Charlie. +Verification algorithm: Verify the following data with the Alice's router signing key: + +- prologue: 16 bytes "RelayRequestData", not null-terminated (not included in the message) +- bhash: Bob's 32-byte router hash (not included in the message) +- chash: Charlie's 32-byte router hash (not included in the message) +- nonce: 4 byte nonce +- relay tag: 4 byte relay tag +- timestamp: 4 byte timestamp (seconds) +- ver: 1 byte SSU version +- asz: 1 byte endpoint (port + IP) size (6 or 18) +- AlicePort: 2 byte Alice's port number +- Alice IP: (asz - 2) byte Alice IP address + + +PeerTest +`````````````` + +Sent either in a Data message in-session, +or a Peer Test message out-of-session. +See Peer Test Process section below. + +For message 2, +must be preceded by a RouterInfo block, or I2NP DatabaseStore message block (or fragment), +containing Alice's Router Info, +either in the same payload (if there's room), or in a previous message. + +For message 4, if the relay is accepted (reason code 0), +must be preceded by a RouterInfo block, or I2NP DatabaseStore message block (or fragment), +containing Charlie's Router Info, +either in the same payload (if there's room), or in a previous message. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 10 | size | msg|code|flag| | + +----+----+----+----+----+----+ + + | Alice router hash (message 2 only) | + + or + + | Charlie router hash (message 4 only) | + + or all zeros if rejected by Bob + + | Not present in messages 1,3,5,6,7 | + + +----+----+ + | | ver| + +----+----+----+----+----+----+----+----+ + nonce | timestamp | asz| + +----+----+----+----+----+----+----+----+ + |AlicePort| Alice IP address | | + +----+----+----+----+----+----+ + + | signature | + + length varies + + | 64 bytes for Ed25519 | + ~ ~ + | . . . | + +----+----+----+----+----+----+----+----+ + + blk :: 10 + size :: 2 bytes, big endian, size of data to follow + msg :: 1 byte message number 1-7 + code :: 1 byte status code: + 0: accept + 1: rejected by Bob, reason unspecified + 2: rejected by Bob, no Charlie available + 3: rejected by Bob, limit exceeded + 4: rejected by Bob, signature failure + 5: rejected by Bob, address unsupported + 6-63: other rejected by Bob codes TBD + 64: rejected by Charlie, reason unspecified + 65: rejected by Charlie, unsupported address + 66: rejected by Charlie, limit exceeded + 67: rejected by Charlie, signature failure + 68: rejected by Charlie, Alice is already connected + 69: rejected by Charlie, Alice is banned + 70: rejected by Charlie, Alice is unknown + 70-127: other rejected by Charlie codes TBD + 128: reject, source and reason unspecified + 129-255: other reject codes TBD + reject codes only allowed in messages 3 and 4 + flag :: 1 byte flags, Unused, set to 0 for future compatibility + hash :: Alice's or Charlie's 32-byte router hash, + only present in messages 2 and 4. + All zeros (fake hash) in message 4 if rejected by Bob. + + For messages 1-4, the data below here is covered + by the signature, if present, and Bob forwards it unmodified. + + ver :: 1 byte SSU version: + 1: SSU 1 (not supported) + 2: SSU 2 (required) + nonce :: 4 byte test nonce, big endian + timestamp :: Unix timestamp, unsigned seconds. + Wraps around in 2106 + asz :: 1 byte endpoint (port + IP) size (6 or 18) + AlicePort :: 2 byte Alice's port number, big endian + Alice IP :: (asz - 2) byte representation of Alice's IP address, + network byte order + signature :: length varies, 64 bytes for Ed25519. + Signature of prologue, Bob's hash, + and signed data above, as signed by + Alice or Charlie. + Only present for messages 1-4. + Optional in message 5-7. + + +{% endhighlight %} + + +Notes: + +* Unlike in SSU 1, message 1 must include Alice's IP address and port. + +* Testing of IPv6 addresses is supported, + and Alice-Bob and Alice-Charlie communication may be via IPv6, + if Bob and Charlie indicate support with a 'B' capability in their published IPv6 address. + See Proposal 126 for details. + + Alice sends the request to Bob using an existing session over the transport (IPv4 or IPv6) that she wishes to test. + When Bob receives a request from Alice via IPv4, Bob must select a Charlie that advertises an IPv4 address. + When Bob receives a request from Alice via IPv6, Bob must select a Charlie that advertises an IPv6 address. + The actual Bob-Charlie communication may be via IPv4 or IPv6 (i.e., independent of Alice's address type). + +* Messages 1-4 must be contained in a Data message in an existing session. + +* Bob must send Alice's RI to Charlie prior to sending message 2. + +* Bob must send Charlie's RI to Alice prior to sending message 4, if accepted (reason code 0). + +* Messages 5-7 must be contained in a Peer Test message out-of-session. + +* Messages 5 and 7 may contain the same signed data as sent in messages 3 and 4, or it may + be regenerated with a new timestamp. Signature is optional. + +* Message 6 may contain the same signed data as sent in messages 1 and 2, or it may + be regenerated with a new timestamp. Signature is optional. + + +Signatures: + +Alice signs the request and includes it in message 1; Bob forwards it in message 2 to Charlie. +Charlie signs the response and includes it in message 3; Bob forwards it in message 4 to Alice. +Signature algorithm: Sign or verify the following data with the Alice's or Charlie's signing key: + +- prologue: 16 bytes "PeerTestValidate", not null-terminated (not included in the message) +- bhash: Bob's 32-byte router hash (not included in the message) +- ahash: Alice's 32-byte router hash + (Only used in the signature for messages 3 and 4; not included in message 3 or 4) +- ver: 1 byte SSU version +- nonce: 4 byte test nonce +- timestamp: 4 byte timestamp (seconds) +- asz: 1 byte endpoint (port + IP) size (6 or 18) +- AlicePort: 2 byte Alice's port number +- Alice IP: (asz - 2) byte Alice IP address + + + +NextNonce +`````````````` + +TODO only if we rotate keys + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 11 | size | TBD | + +----+----+----+ + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 11 + size :: 2 bytes, big endian, size of data to follow + +{% endhighlight %} + + +Ack +`````````````` +4 byte ack through, followed by an ack count +and zero or more nack/ack ranges. + +This design is adapted and simplified from QUIC. +The design goals are as follows: + +- We want to efficiently encode a "bitfield", which is a + sequence of bits representing acked packets. +- The bitfield is mostly 1's. Both the 1's and the 0's + generally come in sequential "clumps". +- The amount of room in the packet available for acks varies. +- The most important bit is the highest numbered one. + Lower numbered ones are less important. + Below a certain distance from the highest bit, the oldest + bits will be "forgotten" and never sent again. + +The encoding specified below accomplishes these design goals, +by sending the number of the highest bit that is set to 1, +together with additional consecutive bits lower than that +which are also set to 1. +After that, if there is room, one or more "ranges" specifying +the number of consectutive 0 bits and consecutive 1 bits +lower than that. +See QUIC [RFC-9000]_ section 13.2.3 for more background. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 12 | size | Ack Through |acnt| + +----+----+----+----+----+----+----+----+ + | range | range | . . . | + +----+----+----+----+ + + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 12 + size :: 2 bytes, big endian, size of data to follow, + 5 minimum + ack through :: highest packet number acked + acnt :: number of acks lower than ack through also acked, + 0-255 + range :: If present, + 1 byte nack count followed by 1 byte ack count, + 0-255 each + +{% endhighlight %} + +Examples: + +We want to ACK packet 10 only: + +- Ack Through: 10 +- acnt: 0 +- no ranges are included + +We want to ACK packets 8-10 only: + +- Ack Through: 10 +- acnt: 2 +- no ranges are included + +We want to ACK 10 9 8 6 5 2 1 0, and NACK 7 4 3. +The encoding of the ACK Block is: + +- Ack Through: 10 +- acnt: 2 (ack 9 8) +- range: 1 2 (nack 7, ack 6 5) +- range: 2 3 (nack 4 3, ack 2 1 0) + + +Notes: + +- Ranges may not be present. Max number of ranges is not specified, + may be as many as will fit in the packet. +- Range nack may be zero if acking more than 255 consecutive packets. +- Range ack may be zero if nacking more than 255 consecutive packets. +- Range nack and ack may not both be zero. +- After the last range, packets are neither acked nor nacked. + Length of the ack block and how old acks/nacks are handled + is up to the sender of the ack block. + See ack sections below for discussion. +- The ack through should be the highest packet number received, + and any packets higher have not been received. + However, in limited situations, it could be lower, such as + acking a single packet that "fills in a hole", or a simplified + implementation that does not maintain the state of all received packets. + Above the highest received, packets are neither acked nor nacked, + but after several ack blocks, it may be appropriate to go + into fast retransmit mode. +- This format is a simplified version of that in QUIC. + It is designed to efficiently encode a large number of ACKs, + together with bursts of NACKs. +- ACK blocks are used to acknowledge data phase packets. + They are only to be included for in-session data phase packets. + + +Address +`````````````` +2 byte port and 4 or 16 byte IP address. +Alice's address, sent to Alice by Bob, +or Bob's address, sent to Bob by Alice. + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 13 | 6 or 18 | Port | IP Address + +----+----+----+----+----+----+----+----+ + | + +----+ + + blk :: 13 + size :: 2 bytes, big endian, 6 or 18 + port :: 2 bytes, big endian + ip :: 4 byte IPv4 or 16 byte IPv6 address, + big endian (network byte order) + +{% endhighlight %} + + + +Relay Tag Request +``````````````````````` +This may be sent by Alice in a Session Request, Session Confirmed, or Data message. +Not supported in the Session Created message, as Bob doesn't have Alice's RI yet, +and doesn't know if Alice supports relay. +Also, if Bob is getting an incoming connection, he probably doesn't need introducers +(except perhaps for the other type ipv4/ipv6). + +When sent in the Session Request, +Bob may respond with a Relay Tag in the Session Created message, +or may choose to wait until receiving Alice's RouterInfo in the +Session Confirmed to validate Alice's identity before responding in a Data message. +If Bob does not wish to relay for Alice, he does not send a Relay Tag block. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+ + | 15 | 0 | + +----+----+----+ + + blk :: 15 + size :: 2 bytes, big endian, value = 0 + +{% endhighlight %} + + +Relay Tag +``````````` +This may be sent by Bob in a Session Confirmed or Data message, +in response to a Relay Tag Request from Alice. + +When the Relay Tag Request is sent in the Session Request, +Bob may respond with a Relay Tag in the Session Created message, +or may choose to wait until receiving Alice's RouterInfo in the +Session Confirmed to validate Alice's identity before responding in a Data message. +If Bob does not wish to relay for Alice, he does not send a Relay Tag block. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+ + | 16 | 4 | relay tag | + +----+----+----+----+----+----+----+ + + blk :: 16 + size :: 2 bytes, big endian, value = 4 + relay tag :: 4 bytes, big endian, nonzero + +{% endhighlight %} + + +New Token +``````````````` +For a subsequent connection. +Generally included in the Session Created and Session Confirmed messages. +May also be sent again in the Data message of a long-lived session +if the previous token expires. + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 17 | 12 | expires | + +----+----+----+----+----+----+----+----+ + token | + +----+----+----+----+----+----+----+ + + blk :: 17 + size :: 2 bytes, big endian, value = 12 + expires :: Unix timestamp, unsigned seconds. + Wraps around in 2106 + token :: 8 bytes, big endian + +{% endhighlight %} + + +Path Challenge +`````````````` +A Ping with arbitrary data to be returned in a Path Response, +used as a keep-alive or to validate an IP/Port change. + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 18 | size | Arbitrary Data | + +----+----+----+ + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 18 + size :: 2 bytes, big endian, size of data to follow + data :: Arbitrary data to be returned in a Path Response + length as selected by sender + +{% endhighlight %} + +Notes: + +A minimum data size of 8 bytes, containing random data, +is recommended but not required. + + + +Path Response +`````````````` +A Pong with the data received in the Path Challenge, as reply to the Path Challenge, +used as a keep-alive or to validate an IP/Port change. + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + | 19 | size | | + +----+----+----+ + + | Data received in Path Challenge | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 19 + size :: 2 bytes, big endian, size of data to follow + data :: As received in a Path Challenge + +{% endhighlight %} + + + + +First Packet Number +`````````````````````` +Optionally included in the handshake in each direction, +to specify the first packet number that will be sent. +This provides more security for header encryption, +similar to TCP. + +Not fully specified, not currently supported. + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+ + | 20 | size | First pkt number | + +----+----+----+----+----+----+----+ + + blk :: 20 + size :: 4 + pkt num :: The first packet number to be sent in the data phase + +{% endhighlight %} + + + +Congestion +`````````````````````` +This is block is designed to be an extensible method +to exchange congestion control information. +Congestion control can be complex and may evolve as +we get more experience with the protocol in live testing, +or after full rollout. + +This keeps any congestion information out of the high-usage +I2NP, First Fragment, Followon Fragment, and ACK blocks, +where there is no space for flags allocated. +While there are three bytes of unused flags in the Data packet header, +that also provides limited space for extensibility, +and weaker encryption protection. + +While it is somewhat wasteful to use a 4-byte block +for two bits of information, by putting this in a separate block, +we can easily extend it with additional data such as +current window sizes, measured RTT, or other flags. +Experience has shown that flag bits alone is often insufficient +and awkward for implementation of advanced congestion control schemes. +Trying to add support for any possible congestion control feature +in, for example, the ACK block, would waste space and add complexity +to the parsing of that block. + +Implementations should not assume that the other router supports +any particular flag bit or feature included here, +unless implementation is required by a future version of this specification. + +This block should probably be the last non-padding block in the payload. + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+ + | 21 | size |flag| + +----+----+----+----+ + + blk :: 21 + size :: 1 (or more if extended) + flag :: 1 byte flags + bit order: 76543210 (bit 7 is MSB) + bit 0: 1 to request immediate ack + bit 1: 1 for explicit congestion notification (ECN) + bits 7-2: Unused, set to 0 for future compatibility + +{% endhighlight %} + + + + +Padding +``````` +This is for padding inside AEAD payloads. +Padding for all messages are inside AEAD payloads. + +Padding should roughly adhere to the negotiated parameters. +Bob sent his requested tx/rx min/max parameters in Session Created. +Alice sent her requested tx/rx min/max parameters in Session Confirmed. +Updated options may be sent during the data phase. +See options block information above. + +If present, this must be the last block in the payload. + + + +.. raw:: html + + {% highlight lang='dataspec' %} ++----+----+----+----+----+----+----+----+ + |254 | size | padding | + +----+----+----+ + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + blk :: 254 + size :: 2 bytes, big endian, size of padding to follow + padding :: random data + +{% endhighlight %} + +Notes: + +- Padding strategies TBD. +- Minimum padding TBD. +- Padding-only payloads are allowed. +- Padding defaults TBD. +- See options block for padding parameter negotiation +- See options block for min/max padding parameters +- Do not exceed the MTU. If more padding is necessary, send multiple messages. +- Router response on violation of negotiated padding is implementation-dependent. + +- The padding length is either to be decided on a per-message basis and + estimates of the length distribution, or random delays should be added. + These countermeasures are to be included to resist DPI, as message sizes + would otherwise reveal that I2P traffic is being carried by the transport + protocol. The exact padding scheme is an area of future work, Appendix A + of [NTCP2]_ provides more information on the topic. + + + + + +Replay Prevention +===================== + +SSU2 is designed to minimize the impact of messages replayed by an attacker. + +Token Request, Retry, Session Request, Session Created, Hole Punch, +and out-of-session Peer Test messages must contain DateTime blocks. + +Both Alice and Bob validate that the time for these messages is within a valid skew (recommended +/- 2 minutes). +For "probing resistance", Bob should not reply to Token Request or Session Request +messages if the skew is invalid, as these messages may be a replay or probing attack. + +Bob may choose to reject duplicate Token Request and Retry messages, +even if the skew is valid, via a Bloom filter or other mechanism. +However, the size and CPU cost of replying to these messages is low. +At worst, a replayed Token Request message may invalidate a previously-sent +token. + +The token system greatly minimizes the impact of replayed Session Request messages. +Since tokens may only be used once, a replayed Session Request message +will never have a valid token. +Bob may choose to reject duplicate Session Request messages, +even if the skew is valid, via a Bloom filter or other mechanism. +However, the size and CPU cost of replying with a Retry message is low. +At worst, sending a Retry message may invalidate a previously-sent +token. + + +Duplicate Session Created and Session Confirmed messages will +not validate because the Noise handshake state will not be in the correct state to decrypt them. +At worst, a peer may retransmit a Session Confirmed in response to an apparent +duplicate Session Created. + +Replayed Hole Punch and Peer Test messages should have little or no impact. + +Routers must use the data message packet number to +detect and drop duplicate data phase messages. +Each packet number should only be used once. +Replayed messages must be ignored. + + + + +Handshake Retransmission +=========================== + +Session Request +---------------- +If no Session Created or Retry is received by Alice: + +Maintain same source and connection IDs, ephemeral key, and packet number 0. +Or, just retain and retransmit the same encrypted packet. +Packet number must not be incremented, because that would change +the chained hash value used to encrypt the Session Created message. + +Recommended retransmission intervals: 1.25, 2.5, and 5 seconds (1.25, 3.75, and 8.75 seconds after first sent). +Recommended timeout: 15 seconds total + + +Session Created +---------------- +If no Session Confirmed is received by Bob: + +Maintain same source and connection IDs, ephemeral key, and packet number 0. +Or, just retain the encrypted packet. +Packet number must not be incremented, because that would change +the chained hash value used to encrypt the Session Confirmed message. + +Recommended retransmission intervals: 1, 2, and 4 seconds (1, 3, and 7 seconds after first sent). +Recommended timeout: 12 seconds total + + +Session Confirmed +------------------ +In SSU 1, Alice does not shift to the data phase until the first data packet is +received from Bob. This makes SSU 1 a two-round-trip setup. + +For SSU 2, +Recommended Session Confirmed retransmission intervals: 1.25, 2.5, and 5 seconds (1.25, 3.75, and 8.75 seconds after first sent). + +There are several alternatives. All are 1 RTT: + +1) Alice assumes Session Confirmed was received, sends data messages immediately, + never retransmit Session Confirmed. Data packets received out-of-order + (before Session Confirmed) will be undecryptable, but will get retransmitted. + If Session Confirmed is lost, all sent data messages will be dropped. + +2) As in 1), send data messages immediately, but also retransmit Session Confirmed + until a data message is received. + +3) We could use IK instead of XK, as it has only two messages in the handshake, but + it uses an extra DH (4 instead of 3). + +The recommeded implementation is option 2). +Alice must retain the information required to retransmit the Session Confirmed message. +Alice should also retransmit all Data messages after the Session Confirmed +message is retransmitted. + +When retransmitting Session Confirmed, +maintain same source and connection IDs, ephemeral key, and packet number 1. +Or, just retain the encrypted packet. +Packet number must not be incremented, because that would change +the chained hash value which is an input for the split() function. + +Bob may retain (queue) the data messages received before the Session Confirmed message. +Neither the header protection keys nor the decryption keys are available +before the Session Confirmed message is received, so Bob does not know +that they are data messages, but that can be presumed. +After the Session Confirmed message is received, Bob is able to +decrypt and process the queued Data messages. +If this is too complex, Bob may just drop the undecryptable Data messages, +as Alice will retransmit them. + +Note: If the session confirmed packets are lost, Bob will retransmit +session created. The session created header will not be decryptable +with Alice's intro key, as it is set with Bob's intro key +(unless fallback decryption is performed with Bob's intro key). +Bob may immediately retransmit the session confirmed packets +if not previously acked, and an undecryptable packet is received. + + +Token Request +---------------- +If no Retry is received by Alice: + +Maintain same source and connection IDs. +An implementation may generate a new random packet number and encrypt a new packet; +Or it may reuse the same packet number or just retain and retransmit the same encrypted packet. +Packet number must not be incremented, because that would change +the chained hash value used to encrypt the Session Created message. + +Recommended retransmission intervals: 3 and 6 seconds (3 and 9 seconds after first sent). +Recommended timeout: 15 seconds total + + +Retry +--------- +If no Session Confirmed is received by Bob: + +A Retry message is not retransmitted on timeout, to reduce the impacts +of spoofed source addresses. + +However, a Retry message may be retransmitted in response to a repeated +Session Request message being received with the original (invalid) token, +or in response to a repeated Token Request message. +In either case, this indicates that the Retry message was lost. + +If a second Session Request message is received with a different +but still-invalid token, drop the pending session and do not respond. + +If resending the Retry message: +Maintain same source and connection IDs and token. +An implementation may generate a new random packet number and encrypt a new packet; +Or it may reuse the same packet number or just retain and retransmit the same encrypted packet. + + + +Total Timeout +-------------- +Recommended total timeout for the handshake is 20 seconds. + + + +Duplicates and Error Handling +----------------------------- +Duplicates of the three Noise handshake messages +Session Request, Session Created, and Session Confirmed +must be detected before MixHash() of the header. +While the Noise AEAD processing will presumably fail after that, +the handshake hash would already be corrupted. + +If any of the three messages is corrupted and fails AEAD, +the handshake cannot subsequently be recovered even with retransmission, +because MixHash() was already called on the corrupted message. + + + +Tokens +============= + +The Token in the Session Request header is used for DoS mitigation, +to prevent source address spoofing, and as resistance to replay attacks. + +If Bob does not accept the token in the Session Request message, Bob does NOT decrypt +the message, as it requires an expensive DH operation. +Bob simply sends a Retry message with a new token. + +If a subsequent Session Request message then is received with that token, +Bob proceeds to decrypt that message and proceed with the handshake. + +The token must be a randomly-generated 8 byte value, if the generator of the token +stores the values and associated IP and port (in-memory or persistently). +The generator may not generate an opaque value, for example, +using the SipHash (with a secret seed K0, K1) of the IP, port, and current hour or day, +to create tokens that do not need to be saved in-memory, +because this method make it difficult to reject reused tokens and replay attacks. + +Tokens may only be used once. +A token sent from Bob to Alice in a Retry message must be used immediately, and expires +in a few seconds. +A token sent in a New Token block in an established session +may be used in a subsequent connection, and it +expires at the time specified in that block. +Expiration is specified by the sender; recommended values are +one hour minimum, several hours maximum. + +If a router's IP or port changes, it must delete all saved tokens +(both inbound and outbound) for the old IP or port, as they are no longer valid. +Tokens may optionally be persisted across router restarts, implementation dependent. +Acceptance of an unexpired token is not guaranteed; if Bob has forgotten or deleted +his saved tokens, he will send a Retry to Alice. +A router may choose to limit token storage, and remove the oldest stored tokens +even if they have not expired. + +New Token blocks may be sent from Alice to Bob or Bob to Alice. +They would typically be sent once, during or soon after session establishment. +The token may be resent before or after expiration with a new expiration time, +or a new token may be sent. +Routers should assume that only the last token received is valid; +there is no requirement to store multiple inbound or outbound tokens for the same IP/port. + +A token is bound to the combination of source IP/port and destination IP/port. +A token received on IPv4 may not be used for IPv6 or vice versa. + +If either peer migrates to a new IP or port during the session +(see the Connection Migration section), any previously-exchanged tokens are invalited, +and new tokens must be exchanged. + +Implementations may, but are not required to, save tokens on disk and +reload them on restart. If persisted, the implementation must +ensure that the IP and port have not changed since shutdown +before reloading them. + + + +I2NP Message Fragmentation +=========================== + +Differences from SSU 1 + +Note: As in SSU 1, the initial fragment does not contain information +on the total number of fragments or the total length. +Follow-on fragments do not contain information on their offset. +This provides the sender the flexibility of fragmenting "on the fly" +based on available space in the packet. +(Java I2P does not do this; it "pre-fragments" before the first fragment is sent) +However, it does burden the receiver to store fragments +received out-of-order and delay reassembly until all fragments are received. + +As in SSU 1, any retransmission of fragments must preserve the length (and implicit offset) +of the fragment's previous transmission. + +SSU 2 does separate the three cases (full message, initial fragment, and follow-on fragment) +into three different block types, to improve processing efficiency. + + + +I2NP Message Duplication +=========================== + +This protocol does NOT completely prevent duplicate delivery of I2NP messages. +IP-layer duplicates or replay attacks will be detected at the SSU2 layer, +because each packet number may only be used once. + +When I2NP messages or fragments are retransmitted in new packets, however, +this is not detectable at the SSU2 layer. +The router should enforce I2NP expiration (both too old and too far in the future) +and use a Bloom filter or other mechanism based on the I2NP message ID. + +Additional mechanisms may be used by the router, or in the SSU2 implementation, +to detect duplicates. +For example, SSU2 could maintain a cache of recently-received message IDs. +This is implementation-dependent. + + + +Congestion Control +==================== + +This proposal specifies the protocol for packet numbering and +ACK blocks. This provides sufficient real-time information for a +transmitter to implement an efficient and responsive congestion control algorithm, +while allowing flexibility and innovation in that implementation. +This section discusses implementation goals and provides suggestions. +General guidance may be found in [RFC-9002]_. +See also [RFC-6298]_ for guidance on retransmission timers. + +ACK-only data packets should not count for bytes or packets in-flight +and are not congestion-controlled. +Unlike in TCP, SSU2 can detect the loss of these packets and +that information may be used to adjust the congestion state. +However, this document does not specify a mechanism for doing so. + +Packets containing some other non-data blocks may also be excluded from congestion control +if desired, implementation-dependent. For example: + +- Peer Test +- Relay request/intro/response +- Path challenge/response + +It is recommended that the congestion control be based on byte count, not +packet count, following the guidance in TCP RFCs and QUIC [RFC-9002]_. +An additional packet count limit may be useful as well to prevent +buffer overflow in the kernel or in middleboxes, implementation dependent, +although this may add significant complexity. +If per-session and/or total packet output is bandwidth-limited and/or paced, +this may mitigate the need for packet count ilmiting. + + + + +Packet Numbers +-------------- + +In SSU 1, ACKs and NACKs contained I2NP message numbers and fragment bitmasks. +Transmitters tracked the ACK status of outbound messages (and their fragments) +and retransmitted fragments as required. + +In SSU 2, ACKs and NACKs contain packet numbers. +Transmitters must maintain a data structure with a mapping of packet numbers to their contents. +When a packet is ACKed or NACKed, the transmitter must determine what +I2NP messages and fragments were in that packet, to decide what to retransmit. + + +Session Confirmed ACK +------------------------ + +Bob sends an ACK of packet 0, which acknowledges the Session Confirmed message and allows +Alice to proceed to the data phase, and discard the large Session Confirmed message +being saved for possible retransmission. +This replaces the DeliveryStatusMessage sent by Bob in SSU 1. + +Bob should send an ACK as soon as possible after receiving the Session Confirmed message. +A small delay (no more than 50 ms) is acceptable, since at least one Data message should arrive almost +immediately after the Session Confirmed message, so that the ACK may acknowledge both +the Session Confirmed and the Data message. +This will prevent Bob from having to retransmit the Session Confirmed message. + + +Generating ACKs +-------------------- + +Definition: Ack-eliciting packets: +Packets that contain ack-eliciting blocks elicit an ACK from the receiver +within the maximum acknowledgment delay and are called ack-eliciting packets. + +Routers acknowledge all packets they receive and process. However, +only ack-eliciting packets cause an ACK block to be sent within the +maximum ack delay. Packets that are not ack-eliciting are only +acknowledged when an ACK block is sent for other reasons. + +When sending a packet for any reason, an endpoint should attempt to +include an ACK block if one has not been sent recently. Doing so +helps with timely loss detection at the peer. + +In general, frequent feedback from a receiver improves loss and +congestion response, but this has to be balanced against excessive +load generated by a receiver that sends an ACK block in response to +every ack-eliciting packet. The guidance offered below seeks to +strike this balance. + +In-session data packets containing any block +EXCEPT for the following are ack-eliciting: + +- ACK block +- Address block +- DateTime block +- Padding block +- Termination block +- Others? + +Out-of session packets, including handshake messages +and peer test messages 5-7, have their own acknowledgement mechanisms. +See below. + + +Handshake ACKs +-------------- + +These are special cases: + + +- Token Request is implicitly acked by Retry +- Session Request is implicitly acked by Session Created or Retry +- Retry is implicitly acked by Session Request +- Session Created is implicitly acked by Session Confirmed +- Session Confirmed should be acked immediately + + + +Sending ACK Blocks +--------------------- + +ACK blocks are used to acknowledge data phase packets. +They are only to be included for in-session data phase packets. + +Every packet should be acknowledged at least once, and ack-eliciting +packets must be acknowledged at least once within a maximum delay. + +An endpoint must acknowledge all ack-eliciting handshake +packets immediately +within its maximum delay, with the following exception. +Prior to handshake confirmation, an endpoint might not have packet +header encryption keys for decrypting the packets +when they are received. It might therefore buffer them and +acknowledge them when the requisite keys become available. + +Since packets containing only ACK blocks are not congestion +controlled, an endpoint must not send more than one such packet in +response to receiving an ack-eliciting packet. + +An endpoint must not send a non-ack-eliciting packet in response to a +non-ack-eliciting packet, even if there are packet gaps that precede +the received packet. This avoids an infinite feedback loop of +acknowledgments, which could prevent the connection from ever +becoming idle. Non-ack-eliciting packets are eventually acknowledged +when the endpoint sends an ACK block in response to other events. + +An endpoint that is only sending ACK blocks will not receive +acknowledgments from its peer unless those acknowledgments are +included in packets with ack-eliciting blocks. An endpoint should +send an ACK block with other blocks when there are new ack-eliciting +packets to acknowledge. When only non-ack-eliciting packets need to +be acknowledged, an endpoint MAY choose not to send an ACK block with +outgoing blocks until an ack-eliciting packet has been received. + +An endpoint that is only sending non-ack-eliciting packets might +choose to occasionally add an ack-eliciting block to those packets to +ensure that it receives an acknowledgment. In +that case, an endpoint MUST NOT send an ack-eliciting block in all +packets that would otherwise be non-ack-eliciting, to avoid an +infinite feedback loop of acknowledgments. + +In order to assist loss detection at the sender, an endpoint should +generate and send an ACK block without delay when it receives an ack- +eliciting packet in any of these cases: + +* When the received packet has a packet number less than another + ack-eliciting packet that has been received + +* When the packet has a packet number larger than the highest- + numbered ack-eliciting packet that has been received and there are + missing packets between that packet and this packet. + +* When the ack-immediate flag in the packet header is set + +The algorithms are expected to be resilient to +receivers that do not follow the guidance offered above. However, an +implementation should only deviate from these requirements after +careful consideration of the performance implications of a change, +for connections made by the endpoint and for other users of the +network. + + +ACK Frequency +----------------- + +A receiver determines how frequently to send acknowledgments in +response to ack-eliciting packets. This determination involves a +trade-off. + +Endpoints rely on timely acknowledgment to detect loss. +Window-based congestion controllers rely on +acknowledgments to manage their congestion window. In both cases, +delaying acknowledgments can adversely affect performance. + +On the other hand, reducing the frequency of packets that carry only +acknowledgments reduces packet transmission and processing cost at +both endpoints. It can improve connection throughput on severely +asymmetric links and reduce the volume of acknowledgment traffic +using return path capacity; see Section 3 of [RFC-3449]_. + +A receiver should send an ACK block after receiving at least two ack-eliciting packets. +This recommendation is general in nature and +consistent with recommendations for TCP endpoint behavior [RFC-5681]_. +Knowledge of network conditions, knowledge of the peer's congestion +controller, or further research and experimentation might suggest +alternative acknowledgment strategies with better performance +characteristics. + +A receiver may process multiple available packets before determining +whether to send an ACK block in response. +In general, the receiver should not delay an ACK by more than RTT / 6, +or 150 ms max. + +The ack-immediate flag in the data packet header is a request that +the receiver send an ack soon after reception, probably within +a few ms. +In general, the receiver should not delay an immediate ACK by more than RTT / 16, +or 5 ms max. + + +Immediate ACK Flag +------------------- + +The receiver does not know the sender's send window size, +and so does not know how long to delay before sending an ACK. +The immediate ACK flag in the data packet header is an important way to +maintain maximum throughput by minimizing effective RTT. +The immediate ACK flag is header byte 13, bit 0, i.e. (header[13] & 0x01). +When set, an immediate ACK is requested. +See the short header section above for details. + +There are several possible strategies a sender may use to determine +when to set the immediate-ack flag: + +- Set once every N packets, for some small N +- Set on the last in a burst of packet +- Set whenver the send window is almost full, for example over 2/3 full +- Set on all packets with retransmitted fragments + +Immediate ACK flags should only be necessary on data packets containing +I2NP messages or message fragments. + + + +ACK Block Size +-------------------- + +When an ACK block is sent, one or more ranges of acknowledged packets +are included. Including acknowledgments for older packets reduces +the chance of spurious retransmissions caused by losing previously +sent ACK blocks, at the cost of larger ACK blocks. + +ACK blocks should always acknowledge the most recently received +packets, and the more out of order the packets are, the more +important it is to send an updated ACK block quickly, to prevent the +peer from declaring a packet as lost and spuriously retransmitting +the blocks it contains. An ACK block must fit within a +single packet. If it does not, then older ranges (those with +the smallest packet numbers) are omitted. + +A receiver limits the number of ACK ranges it +remembers and sends in ACK blocks, both to limit the size of ACK +blocks and to avoid resource exhaustion. After receiving +acknowledgments for an ACK block, the receiver should stop tracking +those acknowledged ACK ranges. Senders can expect acknowledgments +for most packets, but this protocol does not guarantee receipt of an +acknowledgment for every packet that the receiver processes. + +It is possible that retaining many ACK ranges could cause an ACK +block to become too large. A receiver can discard unacknowledged ACK +Ranges to limit ACK block size, at the cost of increased +retransmissions from the sender. This is necessary if an ACK block +would be too large to fit in a packet. Receivers may also limit ACK +block size further to preserve space for other blocks or to limit the +bandwidth that acknowledgments consume. + +A receiver must retain an ACK range unless it can ensure that it will +not subsequently accept packets with numbers in that range. +Maintaining a minimum packet number that increases as ranges are +discarded is one way to achieve this with minimal state. + +Receivers can discard all ACK ranges, but they must retain the +largest packet number that has been successfully processed, as that +is used to recover packet numbers from subsequent packets. + +The following section describes an exemplary approach for determining what +packets to acknowledge in each ACK block. Though the goal of this +algorithm is to generate an acknowledgment for every packet that is +processed, it is still possible for acknowledgments to be lost. + + +Limiting Ranges by Tracking ACK Blocks +------------------------------------------- + +When a packet containing an ACK block is sent, the Ack Through +field in that block can be saved. When a packet +containing an ACK block is acknowledged, the receiver can stop +acknowledging packets less than or equal to the Ack Through +field in the sent ACK block. + +A receiver that sends only non-ack-eliciting packets, such as ACK +blocks, might not receive an acknowledgment for a long period of +time. This could cause the receiver to maintain state for a large +number of ACK blocks for a long period of time, and ACK blocks it +sends could be unnecessarily large. In such a case, a receiver could +send a PING or other small ack-eliciting block occasionally, such as +once per round trip, to elicit an ACK from the peer. + +In cases without ACK block loss, this algorithm allows for a minimum +of 1 RTT of reordering. In cases with ACK block loss and reordering, +this approach does not guarantee that every acknowledgment is seen by +the sender before it is no longer included in the ACK block. Packets +could be received out of order, and all subsequent ACK blocks +containing them could be lost. In this case, the loss recovery +algorithm could cause spurious retransmissions, but the sender will +continue making forward progress. + + +Congestion +---------- + +I2P transports do not guarantee in-order delivery of I2NP messages. +Therefore, loss of a Data message containing one or more I2NP messages or fragments +does NOT prevent other I2NP messages from being delivered; +there is no head-of-line blocking. +Implementations should continue to send new messages during the loss recovery +phase if the send window allows it. + + +Retransmission +--------------- + +A sender should not retain the full contents of a message, to be retransmitted +identically (except for handshake messages, see above). +A sender must assemble messages containing up-to-date information +(ACKs, NACKs, and unacknowledged data) every time it sends a message. +A sender should avoid retransmitting information from messages once they are acknowledged. +This includes messages that are acknowledged after being declared lost, +which can happen in the presence of network reordering. + +Window +------- + +TBD. +General guidance may be found in [RFC-9002]_. + + +Connection Migration +===================== + +A peer's IP or port may change during the lifetime of a session. +An IP change may be caused by IPv6 temporary address rotation, +ISP-driven periodic IP change, a mobile client transitioning +between WiFi and cellular IPs, or other local network changes. +A port change may be caused by a NAT rebinding after +the previous binding timed out. + +A peer's IP or port may appear to change due to various +on- and off-path attacks, including modifying or injecting +packets. + +Connection migration is the process by which a new source endpoint +(IP+port) is validated, while preventing changes that are not validated. +This process is a simplified version of that defined in QUIC [RFC-9000]_. +This process is defined only for the data phase of a session. +Migration is not permitted during the handshake. All handshake packets +must be verified to be from the same IP and port as previously +sent and received packets. In other words, a peer's IP and port +must be constant during the handshake. + +Threat Model +------------- +(Adapted from QUIC [RFC-9000]_) + +Peer Address Spoofing +````````````````````````` +A peer may spoofing its source address to cause an endpoint to send excessive amounts of data to an unwilling host. +If the endpoint sends significantly more data than the spoofing peer, +connection migration might be used to amplify the volume of data that an attacker can generate toward a victim. + +On-Path Address Spoofing +````````````````````````` +An on-path attacker could cause a spurious connection migration by copying and forwarding a packet +with a spoofed address such that it arrives before the original packet. +The packet with the spoofed address will be seen to come from a migrating connection, +and the original packet will be seen as a duplicate and dropped. +After a spurious migration, validation of the source address will fail because the entity at the source address +does not have the necessary cryptographic keys to read or respond to the Path Challenge that is sent to it even if it wanted to. + +Off-Path Packet Forwarding +```````````````````````````` +An off-path attacker that can observe packets might forward copies of genuine packets to endpoints. +If the copied packet arrives before the genuine packet, this will appear as a NAT rebinding. +Any genuine packet will be discarded as a duplicate. +If the attacker is able to continue forwarding packets, it might be able to cause migration to a path via the attacker. +This places the attacker on-path, giving it the ability to observe or drop all subsequent packets. + +Privacy Implications +````````````````````` +QUIC [RFC-9000]_ specified changing connection IDs when changing network paths. +Using a stable connection ID on multiple network paths would allow a passive observer to correlate activity between those paths. +An endpoint that moves between networks might not wish to have their activity correlated by any entity other than their peer. +However, QUIC does not encrypt the connection IDs in the header. +SSU2 does do that, so the privacy leak would require the passive observer to also +have access to the network database to get the introduction key required to decrypt the connection ID. +Even with the introduction key, this is not a strong attack, and we do not +change connection IDs after migration in SSU2, as this would be a significant complication. + + +Initiating Path Validation +--------------------------- + +During the data phase, peers must check that the source IP and port +of each received data packet. If the IP or port is different than +previously received, AND the packet is not a duplicate packet number, +AND the packet successfully decrypts, the session enters +the path validation phase. + +Additionally, a peer must verify that the new IP and port +are valid according to local validation rules +(not blocked, not illegal ports, etc.). +Peers are NOT required to support migration between IPv4 and IPv6, +and may treat a new IP in the other address family as invalid, +since this is not expected behavior and may add significant implementation complexity. +On receiving a packet from an invalid IP/port, an implementation +may simply drop it, or may initiate a path validation with the old IP/port. + +Upon entering the path validation phase, take the following steps: + +- Start a path validation timeout timer of several seconds, + or several times the current RTO (TBD) +- Reduce the congestion window to the minimum +- Reduce the PMTU to the minimum (1280) +- Send a data packet containing a Path Challenge block, + an Address block (containing the new IP/port), + and, typically, an ACK block, to the new IP and port. + This packet uses the same connection ID and encryption keys + as the current session. + The Path Challenge block data must contain sufficient entropy + (at least 8 bytes) so that it cannot be spoofed. +- Optionally, also send a Path Challenge to the old IP/port, + with different block data. See below. +- Start a Path Response timeout timer based on the current + RTO (typically RTT + a multiple of RTTdev) + +While in the path validation phase, the session may continue to +process incoming packets. Whether from the old or new IP/port. +The session may also continue to send and acknowledge data packets. +However, the congestion window and PMTU must remain at the minimum +values during the path validation phase, to prevent +being used for deinal of service attacks by +sending large amounts of traffic to a spoofed address. + +An implementation may, but is not required, to attempt to validate +multiple paths simultaneously. This is probably not worth the complexity. +It may, but is not required, to +remember a previous IP/port as being already validated, and to +skip path validation if a peer returns to its previous IP/port. + +If a Path Response is received, containing the identical data +sent in the Path Challenge, the Path Validation has succeeded. +The source IP/port of the Path Response message is +not required to be the same as the Path Challenge was sent to. + +If a Path Response is not received before the Path Response timer +expires, send another Path Challenge and double the Path Response timer. + +If a Path Response is not received before the Path Validation timer +expires, the Path Validation has failed. + +Message Contents +-------------------------- +The Data messages should contain the following blocks. +Order is not specified except that Padding must be last: + +- Path Validation or Path Response block. + Path Validation contains opaque data, recommended 8 bytes minimum. + Path Response contains the data from the Path Validation. +- Address block containing the recipient's apparent IP +- DateTime block +- ACK block +- Padding block + +It is not recommended to include any other blocks +(for example, I2NP) in the message. + +It is allowed to include a Path Validation block in the message +containing the Path Response, to initiate a validation +in the other direction. + +Path Challenge and Path Response blocks are ACK-eliciting. +The Path Challenge will be ACKed by a Data message containing +the Path Response and ACK blocks. +The Path Response should be ACKed by a Data message containing an ACK block. + + +Routing during Path Validation +------------------------------- +The QUIC specification is not clear on where to send data packets +during path validation - to the old or new IP/port? +There is a balance to be struck between rapidly responding to +IP/port changes, and not sending traffic to spoofed addresses. +Also, spoofed packets must not be allowed to substantially impact +an existing session. +Port-only changes are likely to be caused by NAT rebinding after +an idle period; IP changes could happen during high-traffic phases +in one or both directions. + +Strategies are subject to research and refinement. +Possibilities include: + +- Not sending data packets to the new IP/port until validated +- Continuing to send data packets to the old IP/port until + the new IP/port is validated +- Simultaneously revalidating the old IP/port +- Not sending any data until either the old or new IP/port is validated +- Different strategies for port-only change than for IP change +- Different strategies for an IPv6 change in the same /32, likely caused + by temporary address rotation + + +Responding to Path Challenge +------------------------------ +Upon receiving a Path Challenge, the peer must respond +with a data packet containing a Path Response, with the data +from the Path Challenge. + +The Path Response must be sent to the IP/port from which the +Path Challenge was received. This is NOT NECESSARILY +the IP/port that was previously established for the peer. +This ensures that path validation by a peer only succeeds if the path is functional in both directions. +See the Validation after Local Change section below. + +Unless the IP/port is different from the previously-known IP/port for the peer, +treat a Path Challenge as a simple ping, and simply respond unconditionally with a Path Response. +The receiver does not keep or change any state based on a received Path Challenge. +If the IP/port is different, a peer must verify that the new IP and port +are valid according to local validation rules +(not blocked, not illegal ports, etc.). +Peers are NOT required to support cross-address-family responses between IPv4 and IPv6, +and may treat a new IP in the other address family as invalid, +since this is not expected behavior. + +Unless constrained by congestion control, the Path Response should be sent immediately. +Implementations should take measures to rate limit Path Responses or the bandwidth used +if necessary. + +A Path Challenge block generally is accompanied by an Address block in the same message. +If the address block contains a new IP/port, a peer may +validate that IP/port and initiate peer testing of that new IP/port, with +the session peer or any other peer. +If the peer thinks it is firewalled, and only the port changed, this change is probably +due to NAT rebinding, and further peer testing is probably not required. + + +Successful Path Validation +--------------------------- +On successful path validation, the connection is fully migrated to the new IP/port. +On success: + +- Exit the path validation phase +- All packets are sent to the new IP and port. +- The restrictions on congestion window and PMTU are removed, and they + are allowed to increase. Do not simply restore them to the + old values, as the new path may have different characteristics. +- If the IP changed, set calculated RTT and RTO to initial values. + Because port-only changes are commonly the result of NAT rebinding or other middlebox activity, + the peer may instead retain its congestion control state and round-trip estimate in those cases + instead of reverting to initial values. +- Delete (invalidate) any tokens sent or received for the old IP/port (optional) +- Send a new token block for the new IP/port (optional) + + +Cancelling Path Validation +--------------------------- +While in the path validation phase, any valid, non-duplicate packets +that are received from the old IP/port and are successfully decrypted +will cause Path Validation to be cancelled. +It is important that a cancelled path validation, caused by a spoofed packet, +does not cause a valid session to be terminated or significantly disrupted. + +On cancelled path validation: + +- Exit the path validation phase +- All packets are sent to the old IP and port. +- The restrictions on congestion window and PMTU are removed, and they + are allowed to increase, or, optionally, restore the previous values +- Retransmit any data packets that were previously sent to the new IP/port + to the old IP/port. + + +Failed Path Validation +--------------------------- +It is important that a failed path validation, caused by a spoofed packet, +does not cause a valid session to be terminated or significantly disrupted. + +On failed path validation: + +- Exit the path validation phase +- All packets are sent to the old IP and port. +- The restrictions on congestion window and PMTU are removed, and they + are allowed to increase. +- Optionally, start a path validation on the old IP and port. + If it fails, terminate the session. +- Otherwise, follow standard session timeout and termination rules. +- Retransmit any data packets that were previously sent to the new IP/port + to the old IP/port. + + +Validation After Local Change +------------------------------ +The above process is defined for peers who receive a packet from +a changed IP/port. However, it may also be initiated in the other direction, +by a peer who detects that his IP or port have changed. +A peer may be able to detect that his local IP changed; however, it is much less +likely to detect that his port changed because of a NAT rebinding. +Therefore, this is optional. + +On receiving a path challenge from a peer whose IP or port has changed, +the other peer should initiate a path challenge in the other direction. + + +Use as Ping/Pong +----------------- +Path Validation and Path Response blocks may be used at any time as Ping/Pong packets. +Reception of a Path Validation block does not change any state at the receiver, +unless received from a different IP/port. + + + +Multiple Sessions +================== + +Peers should not establish multiple sessions with the same peer, +whether SSU 1 or 2, or with the same or different IP addresses. +However, this could happen, either due to bugs, or a previous +session termination message being lost, or in a race where the +termination message has not arrived yet. + +If Bob has an existing session with Alice, +when Bob receives the Session Confirmed from Alice, completing the +handshake and establishing a new session, Bob should: + +- Migrate any unsent or unacknowledged outbound I2NP messages from the + old session to the new one +- Send a termination with reason code 22 on the old session +- Remove the old session and replace it with the new one + + + +Session Termination +===================== + +Handshake phase +------------------ +Sessions in the handshake phase are generally terminated simply +by timing out, or not responding further. Optionally, they may be terminated +by including a Termination block in the response, but +most errors are not possible to respond to due to a lack of cryptographic keys. +Even if keys are available for a response including a termination block, +it is usually not worth the CPU to perform the DH for the response. +An exception MAY be a Termination block in a retry message, which +is inexpensive to generate. + + +Data phase +------------------ +Sessions in the data phase are terminated by sending a data +message that includes a Termination block. +This message should also include an ACK block. +It may, if the session has been up long enough that a previously +sent token has expired or is about to expire, +a New Token block. +This message is not ack-eliciting. +When receiving a Termination block with any reason except "Termination Received", +the peer responds with a data message containing a +Termination block with the reason "Termination Received". + +After sending or receiving a Termination block, +the session should enter the closing phase for some maximum period of time TBD. +The closing state is necessary to protect against the +packet containing the Termination block being lost, +and packets in-flight in the other direction. +While in the closing phase, there is no requirement to process +any additional received packets. +A session in the closing state sends a packet containing a Termination block in response +to any incoming packet that it attributes to the session. +A sesssion should limit the rate at which it generates packets in +the closing state. For instance, an session could wait for a +progressively increasing number of received packets or amount of time +before responding to received packets. + +To minimize the state that a router +maintains for a closing session, sessions may, but are not required to, send the exact same +packet with the same packet number as-is in response to any received packet. +Note: Allowing retransmission of a termination packet is an +exception to the requirement that a new packet number be used +for each packet. Sending new packet numbers +is primarily of advantage to loss recovery and congestion +control, which are not expected to be relevant for a closed connection. +Retransmitting the final packet requires less state. + +After receiving a Termination block with the reason "Termination Received", +the session may exit the closing phase. + + +Cleanup +------------------ +Upon any normal or abnormal termination, routers should +zero-out any in-memory ephemeral data, including handshake ephemeral keys, +symmetric crypto keys, and related information. + + +MTU +======== + +Requirements vary, based on whether the published address is shared with SSU 1. +Current SSU 1 IPv4 minimum is 620, which is definitely too small. + +The minimum SSU2 MTU is 1280 for both IPv4 and IPv6, +which is the same as specified in [RFC-9000]_. +See below. +By increasing the minimum MTU, 1 KB tunnel messages +and short tunnel build messages will fit in one datagram, greatly reducing the +typical amount of fragmentation. This also allows +an increase in the maximum I2NP message size. +1820-byte streaming messages should fit in two datagrams. + +A router must not enable SSU2 or publish an SSU2 address unless +the MTU for that address is at least 1280. + +Routers must publish a non-default MTU in each SSU or SSU2 router address. + + +SSU Address +------------ +Shared address with SSU 1, must follow SSU 1 rules. +IPv4: Default and max is 1484. Min is 1292. +(IPv4 MTU + 4) must be a multiple of 16. +IPv6: Must be published, min is 1280 and the max is 1488. +IPv6 MTU must be a multiple of 16. + +SSU2 Address +------------ +IPv4: Default and max is 1500. Min is 1280. +IPv6: Default and max is 1500. Min is 1280. +No multiple of 16 rules, but should probably be a multiple of 2 at least. + +PMTU Discovery +--------------- +For SSU 1, current Java I2P performs PMTU discovery by starting with small packets and +gradually increasing the size, or increasing based on received packet size. +This is crude and greatly reduces the efficiency. +Continuing this feature in SSU 2 is TBD. + +Recent studies [PMTU]_ suggest that a minimum for IPv4 of 1200 or more would work +for more than 99% of connections. QUIC [RFC-9000]_ requires a minimum IP +packet size of 1280 bytes. + +quote [RFC-9000]_: + +The maximum datagram size is defined as the largest size of UDP +payload that can be sent across a network path using a single UDP +datagram. QUIC MUST NOT be used if the network path cannot support a +maximum datagram size of at least 1200 bytes. + +QUIC assumes a minimum IP packet size of at least 1280 bytes. This +is the IPv6 minimum size [IPv6] and is also supported by most modern +IPv4 networks. Assuming the minimum IP header size of 40 bytes for +IPv6 and 20 bytes for IPv4 and a UDP header size of 8 bytes, this +results in a maximum datagram size of 1232 bytes for IPv6 and 1252 +bytes for IPv4. Thus, modern IPv4 and all IPv6 network paths are +expected to be able to support QUIC. + +Note: This requirement to support a UDP payload of 1200 bytes +limits the space available for IPv6 extension headers to 32 +bytes or IPv4 options to 52 bytes if the path only supports the +IPv6 minimum MTU of 1280 bytes. This affects Initial packets +and path validation. + +end quote + + +Handshake Min Size +------------------------- + +QUIC requires that Initial datagrams in both directions be at least 1200 bytes, +to prevent amplification attacks. and ensure the PMTU supports it in both directions. + +We could require this for Session Request and Session Created, +at substantial cost in bandwidth. +Perhaps we could do this only if we don't have a token, +or after a Retry message is received. +TBD + +QUIC requires that Bob send no more than three times the amount of data +received until the client address is validated. +SSU2 meets this requirement inherently, because the Retry message +is about the same size as the Token Request message, and is +smaller than the Session Request message. +Also, the Retry message is only sent once. + + +Path Message Min Size +------------------------- + +QUIC requires that messages containing PATH_CHALLENGE or PATH_RESPONSE blocks be at least 1200 bytes, +to prevent amplification attacks. and ensure the PMTU supports it in both directions. + +We could require this as well, at substantial cost in bandwidth. +However, these cases should be rare. +TBD + + + +Max I2NP Message Size +----------------------- + +IPv4: +No IP fragmentation is assumed. +IP + datagram header is 28 bytes. +This assumes no IPv4 options. +Max message size is MTU - 28. +Data phase header is 16 bytes and MAC is 16 bytes, totaling 32 bytes. +Payload size is MTU - 60. +Max data phase payload is 1440 for a max 1500 MTU. +Max data phase payload is 1220 for a min 1280 MTU. + + +IPv6: +No IP fragmentation is allowed. +IP + datagram header is 48 bytes. +This assumes no IPv6 extension headers. +Max message size is MTU - 48. +Data phase header is 16 bytes and MAC is 16 bytes, totaling 32 bytes. +Payload size is MTU - 80. +Max data phase payload is 1420 for a max 1500 MTU. +Max data phase payload is 1200 for a min 1280 MTU. + +In SSU 1, the guidelines were a strict maximum of about 32 KB for +a I2NP message based on 64 maximum fragments and a 620 minimum MTU. +Due to overhead for bundled LeaseSets and session keys, the practical limit +at the application level was about 6KB lower, or about 26KB. +The SSU 1 protocol allows for 128 fragments but current implementations +limit it to 64 fragments. + +By raising the minimum MTU to 1280, with a data phase payload of +approximately 1200, an SSU 2 message of about 76 KB is possible in 64 fragments +and 152 KB in 128 fragments. This easily allows a maximum of 64 KB. + +Due to fragmentation in tunnels, and fragmentation in SSU 2, +the chance of message loss increases exponentially with message size. +We continue to recommend a practical limit of about 10 KB at the +application layer for I2NP datagrams. + + +Peer Test Process +======================== + +See Peer Test Security above for an analysis of SSU1 Peer Test and +the goals for SSU2 Peer Test. + +.. raw:: html + + {% highlight %} +Alice Bob Charlie + 1. PeerTest -------------------> + Alice RI -------------------> + 2. PeerTest -------------------> + 3. <------------------ PeerTest + <---------------- Charlie RI + 4. <------------------ PeerTest + + 5. <----------------------------------------- PeerTest + 6. PeerTest -----------------------------------------> + 7. <----------------------------------------- PeerTest +{% endhighlight %} + + + +When rejected by Bob: + +.. raw:: html + + {% highlight %} +Alice Bob Charlie + 1. PeerTest -------------------> + 4. <------------------ PeerTest (reject) +{% endhighlight %} + + + +When rejected by Charlie: + +.. raw:: html + + {% highlight %} +Alice Bob Charlie + 1. PeerTest -------------------> + Alice RI -------------------> + 2. PeerTest -------------------> + 3. <------------------ PeerTest (reject) + (optional: Bob could try another Charlie here) + 4. <------------------ PeerTest (reject) +{% endhighlight %} + + +NOTE: RI may be sent either I2NP Database Store messages in I2NP blocks, +or as RI blocks (if small enough). These may be contained in the +same packets as the peer test blocks, if small enough. + +Messages 1-4 are in-session using Peer Test blocks in a Data message. +Messages 5-7 are out-of-session using Peer Test blocks in a Peer Test message. + +NOTE: As in SSU 1, messages 4 and 5 may arrive in either order. +Message 5 and/or 7 may not be received at all if Alice is firewalled. +When message 5 arrives before message 4, +Alice cannot immediately send message 6, because she does not +yet have Charlie's intro key to encrypt the header. +When message 4 arrives before message 5, +Alice should not immediately send message 6, because she should wait +to see if message 5 arrives without opening the firewall with message 6. + + +========= ============ ============= +Message Path Intro Key +========= ============ ============= +1 A->B session in-session +2 B->C session in-session +3 C->B session in-session +4 B->A session in-session +5 C->A Alice +6 A->C Charlie +7 C->A Alice +========= ============ ============= + + + +Versions +------------------ +Cross-version peer testing is not supported. +The only allowed version combination is where all peers are version 2. + +========= =========== ============= ============= +Alice/Bob Bob/Charlie Alice/Charlie Supported +========= =========== ============= ============= +1 1 1 SSU 1 +1 1 2 no, use 1/1/1 +1 2 1 no, Bob must select a version 1 Charlie +1 2 2 no, Bob must select a version 1 Charlie +2 1 1 no, Bob must select a version 2 Charlie +2 1 2 no, Bob must select a version 2 Charlie +2 2 1 no, use 2/2/2 +2 2 2 yes +========= =========== ============= ============= + + +Retransmissions +------------------ +Messages 1-4 are in-session and are covered by the +data phase ACK and retransmission processes. +Peer Test blocks are ack-eliciting. + +Messages 5-7 may be retransmitted, unchanged. + + +IPv6 Notes +------------------ +As in SSU 1, testing of IPv6 addresses is supported, +and Alice-Bob and Alice-Charlie communication may be via IPv6, +if Bob and Charlie indicate support with a 'B' capability in their published IPv6 address. +See Proposal 126 for details. + +As in SSU 1 prior to 0.9.50, +Alice sends the request to Bob using an existing session over the transport (IPv4 or IPv6) that she wishes to test. +When Bob receives a request from Alice via IPv4, Bob must select a Charlie that advertises an IPv4 address. +When Bob receives a request from Alice via IPv6, Bob must select a Charlie that advertises an IPv6 address. +The actual Bob-Charlie communication may be via IPv4 or IPv6 (i.e., independent of Alice's address type). +This is NOT the behavior of SSU 1 as of 0.9.50, where mixed IPv4/v6 requests are allowed. + + + +Processing by Bob +----------------------------- +Unlike in SSU 1, Alice specifies the requested test IP and port in message 1. +Bob should validate this IP and port, and reject with code 5 if invalid. +Recommended IP validation is that, for IPv4, it matches Alice's IP, +and for IPv6, at least the first 8 bytes of the IP match. +Port validation should reject privileged ports and ports for well-known protocols. + + + +Relay Process +======================== + +See Relay Security above for an analysis of SSU1 Relay and +the goals for SSU2 Relay. + + +.. raw:: html + + {% highlight %} +Alice Bob Charlie + lookup Bob RI + + SessionRequest --------------------> + <------------ SessionCreated + SessionConfirmed -----------------> + + 1. RelayRequest ----------------------> + Alice RI ------------> + 2. RelayIntro -----------> + 3. <-------------- RelayResponse + 4. <-------------- RelayResponse + + 5. <-------------------------------------------- HolePunch + 6. SessionRequest --------------------------------------------> + 7. <-------------------------------------------- SessionCreated + 8. SessionConfirmed ------------------------------------------> + +{% endhighlight %} + + +When rejected by Bob: + +.. raw:: html + + {% highlight %} +Alice Bob Charlie + lookup Bob RI + + SessionRequest --------------------> + <------------ SessionCreated + SessionConfirmed -----------------> + + 1. RelayRequest ----------------------> + 4. <-------------- RelayResponse + +{% endhighlight %} + + +When rejected by Charlie: + + +.. raw:: html + + {% highlight %} +Alice Bob Charlie + lookup Bob RI + + SessionRequest --------------------> + <------------ SessionCreated + SessionConfirmed -----------------> + + 1. RelayRequest ----------------------> + Alice RI ------------> + 2. RelayIntro -----------> + 3. <-------------- RelayResponse + 4. <-------------- RelayResponse + +{% endhighlight %} + +NOTE: RI may be sent either I2NP Database Store messages in I2NP blocks, +or as RI blocks (if small enough). These may be contained in the +same packets as the ralay blocks, if small enough. + +In SSU 1, Charlie's router info contains the IP, port, intro key, relay tag, and expiration of each introducer. + +In SSU 2, Charlie's router info contains the router hash, relay tag, and expiration of each introducer. + +Alice should reduce the number of round trips required by first +selecting an introducer (Bob) that she already has a connection to. +Second, if none, select an introducer she already has the router info for. + +Cross-version relaying should also be supported if possible. +This will facilitate a gradual transition from SSU 1 to SSU 2. +The allowed version combinations are (TODO): + +========= =========== ============= ============= +Alice/Bob Bob/Charlie Alice/Charlie Supported +========= =========== ============= ============= +1 1 1 SSU 1 +1 1 2 no, use 1/1/1 +1 2 1 yes? +1 2 2 no, use 1/2/1 +2 1 1 yes? +2 1 2 yes? +2 2 1 no, use 2/2/2 +2 2 2 yes +========= =========== ============= ============= + + +Retransmissions +----------------- +Relay Request, Relay Intro, and Relay Response +are all in-session and are covered by the +data phase ACK and retransmission processes. +Relay Request, Relay Intro, and Relay Response blocks are ack-eliciting. + +Hole punch may be retransmitted, as in SSU 1. + +IPv4/v6 +---------- +All features of SSU 1 relay are supported, including those documented in +[Prop158]_ and supported as of 0.9.50. +IPv4 and IPv6 introductions are supported. +A Relay Request may be sent over an IPv4 session for an IPv6 introduction, +and a Relay Request may be sent over an IPv6 session for an IPv4 introduction. + +Processing by Alice +----------------------------- +Following are differences from SSU 1 and recommendations for SSU 2 implementation. + +Introducer Selection +````````````````````` +In SSU 1, introduction is relatively inexpensive, and Alice generally sends Relay Requests to all introducers. +In SSU 2, introduction is more expensive, as a connection must first be established with an introducer. +To minimize introduction latency and overhead, the recommended processing steps are as follows: + +- Ignore any introducers that are expired based on the iexp value in the address +- If an SSU2 connection is already established to one or more introducers, + pick one and send the Relay Request to that introducer only. +- Otherwise, if a Router Info is locally known for one or more introducers, + pick one and connect to that introducer only. +- Otherwise, lookup the Router Infos for all introducers, + connect to the introducer whose Router Info is received first. + +Response Handling +``````````````````````` +In both SSU 1 and SSU 2, +the Relay Response and Hole Punch may be received in either order, +or may not be received at all. + +In SSU 1, Alice usually receives the Relay Response (1 RTT) +before the Hole Punch (1 1/2 RTT). +It may not be well-documented in those specifications, but +Alice must receive the Relay Response from Bob before continuing, +to receive Charlie's IP. +If the Hole Punch is received first, Alice will not recognize it, +because it contains no data and the source IP is not recognized. +After receiving the Relay Response, Alice should wait for +EITHER receiving the Hole Punch from Charlie, OR +a short delay (recommended 500 ms) before initiating the handshake with Charlie. + +In SSU 2, Alice will usually receive the Hole Punch (1 1/2 RTT) +before the Relay Response (2 RTT). +The SSU 2 Hole Punch is easier to process than in SSU 1, because it is a full +message with defined connection IDs (derived from the relay nonce) and contents including Charlie's IP. +The Relay Response (Data message) and Hole Punch message contain the identical +signed Relay Response block. +Therefore, Alice may initiate the handshake with Charlie after +EITHER receiving the Hole Punch from Charlie, OR receiving the Relay Response from Bob. + +The signature verification of the Hole Punch includes the introducer's (Bob's) router hash. +If Relay Requests have been sent to more than one introducer, +there are several options to validate the signature: + +- Try each hash to which a request was sent +- Use different nonces for each introducer, and use that to determine which + introducer this Hole Punch was in response to +- Don't re-validate the signature if the contents are identical + to that in the Relay Response, if already received +- Don't validate the signature at all + + +Tag Requests by Bob +------------------------ +In SSU 1, only Alice could request a tag, in the Session Request. +Bob could never request a tag, and Alice could not relay for Bob. + +In SSU2, Alice generally requests a tag in the Session Request, +but either Alice or Bob may also request a tag in the data phase. +Bob generally is not firewalled after receiving an inbound request, +but it could be after a relay, or Bob's state may change, +or he may request an introducer for the other address type (IPv4/v6). +So, in SSU2, it is possible for both Alice and Bob to simultaneously be relays for the other party. + + + +Published Router Info +===================== + +Address Properties +------------------- + +The following address properties may be published, unchanged from SSU 1, +including changes in [Prop158]_ supported as of API 0.9.50: + +- caps: [B,C,4,6] capabilities + +- host: IP (IPv4 or IPv6). + Shortened IPv6 address (with "::") is allowed. + May or may not be present if firewalled. + Host names are not allowed. + +- iexp[0-2]: Expiration of this introducer. + ASCII digits, in seconds since the epoch. + Only present if firewalled, and introducers are required. + Optional (even if other properties for this introducer are present). + +- ihost[0-2]: Introducer's IP (IPv4 or IPv6). + Shortened IPv6 address (with "::") is allowed. + Only present if firewalled, and introducers are required. + Host names are not allowed. + SSU address only. + +- ikey[0-2]: Introducer's Base 64 introduction key. + Only present if firewalled, and introducers are required. + SSU address only. + +- iport[0-2]: Introducer's port 1024 - 65535. + Only present if firewalled, and introducers are required. + SSU address only. + +- itag[0-2]: Introducer's tag 1 - (2**32 - 1) + ASCII digits. + Only present if firewalled, and introducers are required. + +- key: Base 64 introduction key. + +- mtu: Optional. See MTU section above. + +- port: 1024 - 65535 + May or may not be present if firewalled. + + + +Published Addresses +------------------- + +The published RouterAddress (part of the RouterInfo) will have a +protocol identifier of either "SSU" or "SSU2". + +The RouterAddress must contain three options +to indicate SSU2 support: + +- s=(Base64 key) + The current Noise static public key (s) for this RouterAddress. + Base 64 encoded using the standard I2P Base 64 alphabet. + 32 bytes in binary, 44 bytes as Base 64 encoded, + little-endian X25519 public key. + +- i=(Base64 key) + The current introduction key for encrypting the headers for this RouterAddress. + Base 64 encoded using the standard I2P Base 64 alphabet. + 32 bytes in binary, 44 bytes as Base 64 encoded, + big-endian ChaCha20 key. + +- v=2 + The current version (2). + When published as "SSU", additional support for version 1 is implied. + Support for future versions will be with comma-separated values, + e.g. v=2,3 + Implementation should verify compatibility, including multiple + versions if a comma is present. Comma-separated versions must + be in numerical order. + + +Alice must verify that all three options are present and valid +before connecting using the SSU2 protocol. + +When published as "SSU" with "s", "i", and "v" options, +and with "host" and "port" options, +the router must accept incoming connections on that host and port +for both SSU and SSU2 protocols, and automatically detect the protocol +version. + +When published as "SSU2" with "s", "i", and "v" options, +and with "host" and "port" options, +the router accepts incoming connections on that host and port +for the SSU2 protocol only. + +If a router supports both SSU1 and SSU2 connections but +does not implement automatic version detection for incoming connections, +it must advertise both "SSU" and "SSU2" addresses, and include +the SSU2 options in the "SSU2" address only. +The router should set a lower cost value (higher priority) +in the "SSU2" address than the "SSU" address, so SSU2 is preferred. + +If multiple SSU2 RouterAddresses (either as "SSU" or "SSU2") are published +in the same RouterInfo (for additional IP addresses or ports), +all addresses specifying the same port must contain the identical SSU2 options and values. +In particular, all must contain the same static key "s" and introduction key "i". + + +Introducers +``````````` +When published as SSU or SSU2 with introducers, the following options are present: + +- ih[0-2]=(Base64 hash) + A router hash for an introducer. + Base 64 encoded using the standard I2P Base 64 alphabet. + 32 bytes in binary, 44 bytes as Base 64 encoded + +- iexp[0-2]: Expiration of this introducer. + Unchanged from SSU 1. + +- itag[0-2]: Introducer's tag 1 - (2**32 - 1) + Unchanged from SSU 1. + +The following options are for SSU only and are not used for SSU2. +In SSU2, Alice gets this information from Charlie's RI instead. + +- ihost[0-2] +- ikey[0-2] +- itag[0-2] + +A router must not publish host or port in the address when publishing introducers. +A router must publish 4 and/or 6 caps in the address when publishing introducers +to indicate support for IPv4 and/or IPv6. +This is the same as the current practice for recent SSU 1 addresses. + +Note: If published as SSU, and there is a mix of SSU 1 and SSU2 introducers, +the SSU 1 introducers should be at the lower indexes and +the SSU2 introducers should be at the higher indexes, +for compatibility with older routers. + + + +Unpublished SSU2 Address +------------------------- + +If Alice does not publish her SSU2 address (as "SSU" or "SSU2") for incoming connections, +she must publish a "SSU2" router address containing only her static key and SSU2 version, +so that Bob may validate the key after receiving Alice's RouterInfo in Session Confirmed part 2. + +- s=(Base64 key) + As defined above for published addresses. + +- i=(Base64 key) + As defined above for published addresses. + +- v=2 + As defined above for published addresses. + +This router address will not contain "host" or "port" options, +as these are not required for outbound SSU2 connections. +The published cost for this address does not strictly matter, as it is inbound only; +however, it may be helpful to other routers if the cost is set higher (lower priority) +than other addresses. The suggested value is 14. + +Alice may also simply add the "i" "s" and "v" options to an existing published "SSU" address. + + + +Public Key and IV Rotation +-------------------------- + +Using the same static keys for NTCP2 and SSU2 is allowed, but not recommended. + +Due to caching of RouterInfos, routers must not rotate the static public key or IV +while the router is up, whether in a published address or not. Routers must +persistently store this key and IV for reuse after an immediate restart, so incoming +connections will continue to work, and restart times are not exposed. Routers +must persistently store, or otherwise determine, last-shutdown time, so that +the previous downtime may be calculated at startup. + +Subject to concerns about exposing restart times, routers may rotate this key or IV +at startup if the router was previously down for some time (several days at +least). + +If the router has any published SSU2 RouterAddresses (as SSU or SSU2), the +minimum downtime before rotation should be much longer, for example one month, +unless the local IP address has changed or the router "rekeys". + +If the router has any published SSU RouterAddresses, but not SSU2 (as SSU or +SSU2) the minimum downtime before rotation should be longer, for example one +day, unless the local IP address has changed or the router "rekeys". This +applies even if the published SSU address has introducers. + +If the router does not have any published RouterAddresses (SSU, SSU2, or +SSU), the minimum downtime before rotation may be as short as two hours, even +if the IP address changes, unless the router "rekeys". + +If the router "rekeys" to a different Router Hash, it should generate a new +noise key and intro key as well. + +Implementations must be aware that changing the static public key or IV will prohibit +incoming SSU2 connections from routers that have cached an older RouterInfo. +RouterInfo publishing, tunnel peer selection (including both OBGW and IB +closest hop), zero-hop tunnel selection, transport selection, and other +implementation strategies must take this into account. + +Intro key rotation is subject to identical rules as key rotation. + +Note: The minimum downtime before rekeying may be modified to ensure network +health, and to prevent reseeding by a router down for a moderate amount of +time. + + + + +Identity Hiding +``````````````` +Deniability is not a goal. See overview above. + +Each pattern is assigned properties describing the confidentiality supplied to +the initiator's static public key, and to the responder's static public key. +The underlying assumptions are that ephemeral private keys are secure, and that +parties abort the handshake if they receive a static public key from the other +party which they don't trust. + +This section only considers identity leakage through static public key fields +in handshakes. Of course, the identities of Noise participants might be +exposed through other means, including payload fields, traffic analysis, or +metadata such as IP addresses. + +Alice: (8) Encrypted with forward secrecy to an authenticated party. + +Bob: (3) Not transmitted, but a passive attacker can check candidates for the +responder's private key and determine whether the candidate is correct. + +Bob publishes his static public key in the netdb. Alice may not, but must include it in the RI +sent to Bob. + + +Packet Guidelines +========================== + + +Outbound Packet Creation +----------------------------- + +Handshake messages (Session Request/Created/Confirmed, Retry) basic steps, in order: + +- Create 16 or 32 byte header +- Create payload +- mixHash() the header (except for Retry) +- Encrypt the payload using Noise (except for Retry, use ChaChaPoly with the header as AD) +- Encrypt the header, and for Session Request/Created, the ephemeral key + + +Data phase messages basic steps, in order: + +- Create 16-byte header +- Create payload +- Encrypt the payload using ChaChaPoly using the header as AD +- Encrypt the header + + + +Inbound Packet Handling +----------------------------- + +Summary +``````````` + +Initial processing of all inbound messages: + +- Decrypt the first 8 bytes of the header (the Destination Connection ID) + with the intro key +- Lookup the connection by the Destination Connection ID +- If the connection is found and is in the data phase, go to the + data phase section +- If the connection is not found, go to the handshake section +- Note: Peer Test and Hole Punch messages may also be looked up + by the Destination Connection ID created from the test or relay nonce. + + +Handshake messages (Session Request/Created/Confirmed, Retry, Token Request) +and other out-of-session messages (Peer Test, Hole Punch) +processing: + +- Decrypt bytes 8-15 of the header + (the packet type, version, and net ID) with the intro key. If it is a + valid Session Request, Token Request, Peer Test, or Hole Punch, continue +- If not a valid message, lookup a pending outbound connection by the packet + source IP/port, treat the packet as a Session Created or Retry. + Re-decrypt the first 8 bytes of the header with the correct key, + and the bytes 8-15 of the header + (the packet type, version, and net ID). If it is a + valid Session Created or Retry, continue +- If not a valid message, fail, or queue as a possible out-of-order data phase packet +- For Session Request/Created, Retry, Token Request, Peer Test, and Hole Punch, decrypt bytes 16-31 of the header +- For Session Request/Created, decrypt the ephemeral key +- Validate all header fields, stop if not valid +- mixHash() the header +- For Session Request/Created/Confirmed, decrypt the payload using Noise +- For Retry and data phase, decrypt the payload using ChaChaPoly +- Process the header and payload + + +Data phase messages processing: + +- Decrypt bytes 8-15 of the header + (the packet type, version, and net ID) with the correct key +- Decrypt the payload using ChaChaPoly using the header as AD +- Process the header and payload + + +Details +````````` + +In SSU 1, inbound packet classification is difficult, because there is no +header to indicate session number. Routers must first match the source IP and port +to an existing peer state, and if not found, attempt multiple decryptions with different +keys to find the appropriate peer state or start a new one. +In the event that the source IP or port for an existing session changes, +possibly due to NAT behavior, +the router may use expensive heuristics to attempt to match the packet to an existing session +and recover the contents. + +SSU 2 is designed to minimize the inbound packet classification effort while maintaining +DPI resistance and other on-path threats. The Connection ID number is included in the header +for all message types, and encrypted (obfuscated) using ChaCha20 with a known key and nonce. +Additionally, the message type is also included in the header +(encrypted with header protection to a known key and then obfuscated with ChaCha20) +and may be used for additional classification. +In no case is a trial DH or other asymmetric crypto operation necessary to classify a packet. + +For almost all messages from all peers, the ChaCha20 key for the Connection ID encryption is the destination router's +introduction key as published in the netdb. + +The only exceptions are the first messages sent from Bob to Alice (Session Created or Retry) +where Alice's introduction key is not yet known to Bob. In these cases, Bob's introduction key +is used as the key. + +The protocol is designed to minimize packet classification processing that +might require additional crypto operations in multiple +fallback steps or complex heuristics. +Additionally, the vast majority of received packets will not require +a (possibly expensive) fallback lookup by source IP/port +and a second header decryption. +Only Session Created and Retry (and possibly others TBD) will require +the fallback processing. +If an endpoint changes IP or port after session creation, +the connection ID is still used to lookup the session. +It is never necessary to use heuristics to find the session, +for example by looking for a different session with the same +IP but a different port. + + +Therefore, the recommended processing steps in the receiver loop logic are: + +1) Decrypt the first 8 bytes with ChaCha20 using the local introduction key, + to recover the Destination Connection ID. + If the Connection ID matches a current or pending inbound session: + + a) Using the appropriate key, decrypt the header bytes 8-15 + to recover the version, net ID, and message type. + b) If the message type is Session Confirmed, it is a long header. + Verify the net ID and protocol version are valid. + Decrypt the bytes 15-31 of the header with ChaCha20 + using the local intro key. Then MixHash() the + decrypted 32 byte header and decrypt the message with Noise. + c) If the message type is valid but not Session Confirmed, + it is a short header. + Verify the net ID and protocol version are valid. + decrypt the rest of the message with ChaCha20/Poly1305 + using the session key, using the decrypted 16-byte header + as the AD. + d) (optional) If connection ID is a pending inbound session + awaiting a Session Confirmed message, + but the net ID, protocol, or message type is not valid, + it could be a Data message received out-of-order before the + Session Confirmed, so the data phase header protection keys are not yet known, + and the header bytes 8-15 were incorrectly decrypted. + Queue the message, and attempt to decrypt it once the + Session Confirmed message is received. + e) If b) or c) fails, drop the message. + +2) If the connection ID does not match a current session: + Check the plaintext header at bytes 8-15 are valid + (without doing any header protection operation). + Verify the net ID and protocol version are valid, and + the message type is Session Request, or other message type + allowed out-of-session (TBD). + + a) If all is valid and the message type is Session Request, + decrypt bytes 16-31 of the header and the 32-byte X value + with ChaCha20 using the local intro key. + + - If the token at header bytes 24-31 is accepted, + then MixHash() the decrypted 32 byte header and + decrypt the message with Noise. + Send a Session Created in response. + - If the token is not accepted, send a Retry message to the + source IP/port with a token. Do not attempt to + decrypt the message with Noise to avoid DDoS attacks. + + b) If the message type is some other message that is valid + out-of-session, presumably with a short header, + decrypt the rest of the message with ChaCha20/Poly1305 + using the intro key, and using the decrypted 16-byte header + as the AD. Process the message. + c) If a) or b) fails, go to step 3) + + +3) Look up a pending outbound session by the source IP/port of the packet. + + a) If found, re-decrypt the first 8 bytes with ChaCha20 using Bob's introduction key + to recover the Destination Connection ID. + b) If the connection ID matches the pending session: + Using the correct key, decrypt bytes 8-15 of the header + to recover the version, net ID, and message type. + Verify the net ID and protocol version are valid, and + the message type is Session Created or Retry, or other message type + allowed out-of-session (TBD). + + - If all is valid and the message type is Session Created, + decrypt the next 16 bytes of the header and the 32-byte Y value + with ChaCha20 using Bob's intro key. + Then MixHash() the decrypted 32 byte header and + decrypt the message with Noise. + Send a Session Confirmed in response. + - If all is valid and the message type is Retry, + decrypt bytes 16-31 of the header + with ChaCha20 using Bob's intro key. + Decrypt and validate the message using ChaCha20/Poly1305 using + TBD as the key and TBD as the nonce and the decrypted 32-byte header as the AD. + Resend a Session Request with the received token in response. + - If the message type is some other message that is valid + out-of-session, presumably with a short header, + decrypt the rest of the message with ChaCha20/Poly1305 + using the intro key, and using the decrypted 16-byte header + as the AD. Process the message. + + c) If a pending outbound session is not found, + or the connection ID does not match the pending session, drop the message, + unless the port is shared with SSU 1. + +4) If running SSU 1 on the same port, attempt to process the message as an SSU 1 packet. + + +Error Handling +``````````````` +In general, a session (in the handshake or data phase) should never be destroyed +after receiving a packet with an unexpected message type. This prevents +packet injection attacks. These packets will also commonly be received +after retransmission of a handshake packet, when the header decryption keys +are no longer valid. + +In most cases, simply drop the packet. An implementation may, but is not required to, +retransmit the previously-sent packet (handshake message or ACK 0) in response. + +After sending Session Created as Bob, unexpected packets are commonly Data packets +that cannot be decrypted because the Session Confirmed packets were lost or out-of-order. +Queue the packets and attempt to decrypt them after receiving the Session Confirmed packets. + +After receiving Session Confirmed as Bob, unexpected packets are commonly retransmitted +Session Confirmed packets, because the ACK 0 of the Sesssion Confirmed was lost. +The unexpected packets may be dropped. +An implementation may, but is not required to, send a Data packet containing an ACK block in response. + + +Notes +------- + +For Session Created and Session Confirmed, implementations must carefully validate +all decrypted header fields (Connection IDs, packet number, packet type, version, id, frag, and flags) +BEFORE calling mixHash() on the header and attempting to decrypt the +payload with Noise AEAD. If the Noise AEAD decryption fails, no further processing +may be done, because mixHash() will have corrupted the handshake state, +unless an implementation stores and "backs out" the hash state. + + +Version Detection +-------------------- + +It may not be possible to efficiently detect if incoming packets are version 1 or 2 on the same inbound port. +The steps above may make sense to do before SSU 1 processing, to avoid attempting trial DH operations +using both protocol versions. + +TBD if required. + + +Recommended Constants +======================= + +- Outbound handshake retransmission timeout: 1.25 seconds, with exponential backoff + (retransmissions at 1.25, 3.75, and 8.75 seconds) +- Total outbound handshake timeout: 15 seconds +- Inbound handshake retransmission timeout: 1 second, with exponential backoff + (retransmissions at 1, 3, and 7 seconds) +- Total inbound handshake timeout: 12 seconds +- Timeout after sending retry: 9 seconds +- ACK delay: max(10, min(rtt/6, 150)) ms +- Immediate ACK delay: min(rtt/16, 5) ms +- Max ACK ranges: 256? +- Max ACK depth: 512? +- Padding distribution: 0-15 bytes, or greater + + +Packet Overhead Analysis +========================= + +Assumes IPv4, not including extra padding, not including IP and UDP header sizes. +Padding is mod-16 padding for SSU 1 only. + +SSU 1 + +================== =========== ===== ====== ======= ====== ===== +Message Header+MAC Keys Data Padding Total Notes +================== =========== ===== ====== ======= ====== ===== +Session Request 40 256 5 3 304 Incl. extended options +Session Created 37 256 79 1 336 Incl. 64 byte Ed25519 sig +Session Confirmed 37 462 13 512 Incl. 391 byte ident and 64 byte sig +Data (RI) 37 1014 1051 Incl. 5 byte I2NP header, 1000 byte RI +Data (1 full msg) 37 14 51 Incl. 5 byte I2NP header +Total 2254 +================== =========== ===== ====== ======= ====== ===== + + +SSU 2 + +================== =========== ===== ====== ======= ====== ===== +Message Header+MACs Keys Data Padding Total Notes +================== =========== ===== ====== ======= ====== ===== +Session Request 48 32 7 87 DateTime block +Session Created 48 32 16 96 DateTime, Address blocks +Session Confirmed 48 32 1005 1085 1000 byte compressed RI block +Data (1 full msg) 32 14 46 +Total 1314 +================== =========== ===== ====== ======= ====== ===== + @@ -21,5 +6018,105 @@ Development and testing in progress. See [Prop159]_. References ========== +.. [Common] + {{ spec_url('common-structures') }} + +.. [ECIES] + {{ site_url('docs/spec/ecies', True) }} + +.. [NetDB] + {{ site_url('docs/how/network-database', True) }} + +.. [NOISE] + https://noiseprotocol.org/noise.html + +.. [Nonces] + https://eprint.iacr.org/2019/624.pdf + +.. [NTCP] + {{ site_url('docs/transport/ntcp', True) }} + +.. [NTCP2] + {{ site_url('docs/spec/ntcp2', True) }} + +.. [PMTU] + https://aura.abdn.ac.uk/bitstream/handle/2164/11693/tma2018_paper57.pdf + +.. [Prop104] + {{ proposal_url('104') }} + +.. [Prop109] + {{ proposal_url('109') }} + +.. [Prop158] + {{ proposal_url('158') }} + .. [Prop159] {{ proposal_url('159') }} + +.. [RFC-2104] + https://tools.ietf.org/html/rfc2104 + +.. [RFC-3449] + https://tools.ietf.org/html/rfc3449 + +.. [RFC-3526] + https://tools.ietf.org/html/rfc3526 + +.. [RFC-5681] + https://tools.ietf.org/html/rfc3681 + +.. [RFC-5869] + https://tools.ietf.org/html/rfc5869 + +.. [RFC-6151] + https://tools.ietf.org/html/rfc6151 + +.. [RFC-6298] + https://tools.ietf.org/html/rfc6298 + +.. [RFC-6437] + https://tools.ietf.org/html/rfc6437 + +.. [RFC-7539] + https://tools.ietf.org/html/rfc7539 + +.. [RFC-7748] + https://tools.ietf.org/html/rfc7748 + +.. [RFC-7905] + https://tools.ietf.org/html/rfc7905 + +.. [RFC-9000] + https://datatracker.ietf.org/doc/html/rfc9000 + +.. [RFC-9001] + https://datatracker.ietf.org/doc/html/rfc9001 + +.. [RFC-9002] + https://datatracker.ietf.org/doc/html/rfc9002 + +.. [RouterAddress] + {{ ctags_url('RouterAddress') }} + +.. [RouterIdentity] + {{ ctags_url('RouterIdentity') }} + +.. [SigningPublicKey] + {{ ctags_url('SigningPublicKey') }} + +.. [SSU] + {{ site_url('docs/transport/ssu', True) }} + +.. [STS] + Diffie, W.; van Oorschot P. C.; Wiener M. J., Authentication and + Authenticated Key Exchanges + +.. [Ticket1112] + https://{{ i2pconv('trac.i2p2.i2p') }}/ticket/1112 + +.. [Ticket1849] + https://{{ i2pconv('trac.i2p2.i2p') }}/ticket/1849 + +.. [WireGuard] + https://www.wireguard.com/protocol/