========================== SSU Protocol Specification ========================== Category: Transports Last updated: 2024-01 Accurate for: 0.9.61 Overview ======== DEPRECATED - SSU has been replaced by SSU2. SSU support was removed from i2pd in release 2.44.0 (API 0.9.56) 2022-11. SSU support was removed from Java I2P in release 2.4.0 (API 0.9.61) 2023-12. See [SSU] for an overview of the SSU transport. .. _dh: DH Key Exchange =============== The initial 2048-bit DH key exchange is described on the SSU page [SSU-KEYS]. This exchange uses the same shared prime as that used for I2P's ElGamal encryption [CRYPTO-ELG]. .. _header: Message Header ============== All UDP datagrams begin with a 16 byte MAC (Message Authentication Code) and a 16 byte IV (Initialization Vector) followed by a variable-size payload encrypted with the appropriate key. The MAC used is HMAC-MD5, truncated to 16 bytes, while the key is a full 32 byte AES256 key. The specific construct of the MAC is the first 16 bytes from:: HMAC-MD5(encryptedPayload + IV + (payloadLength ^ protocolVersion ^ ((netid - 2) << 8)), macKey) where '+' means append and '^' means exclusive-or. The IV is generated randomly for each packet. The encryptedPayload is the encrypted version of the message starting with the flag byte (encrypt-then-MAC). The payloadLength used in the MAC is a 2 byte unsigned integer, big endian. Note that protocolVersion is 0, so the exclusive-or is a no-op. The macKey is either the introduction key or is constructed from the exchanged DH key (see details below), as specified for each message below. **WARNING** - the HMAC-MD5-128 used here is non-standard, see [CRYPTO-HMAC] for details. The payload itself (that is, the message starting with the flag byte) is AES256/CBC encrypted with the IV and the sessionKey, with replay prevention addressed within its body, explained below. The protocolVersion is a 2 byte unsigned integer, big endian, and is currently set to 0. Peers using a different protocol version will not be able to communicate with this peer, though earlier versions not using this flag are. The exclusive OR of ((netid - 2) << 8) is used to quickly identify cross-network connections. The netid is a 2 byte unsigned integer, big endian, and is currently set to 2. As of 0.9.42. See proposal 147 for more information. As the current network ID is 2, this is a no-op for the current network and is backward compatible. Any connections from test networks should have a different ID and will fail the HMAC. HMAC Specification ------------------ * Inner padding: 0x36... * Outer padding: 0x5C... * Key: 32 bytes * Hash digest function: MD5, 16 bytes * Block size: 64 bytes * MAC size: 16 bytes * Example C implementations: * hmac.h in i2pd [I2PD-SRC] * I2PHMAC.cpp in i2pcpp [I2PCPP-SRC]. * Example Java implementation: * I2PHMac.java in I2P [I2P-SRC] Session Key Details ------------------- The 32-byte session key is created as follows: 1. Take the exchanged DH key, represented as a positive minimal-length BigInteger byte array (two's complement big-endian) 2. If the most significant bit is 1 (i.e. array[0] & 0x80 != 0), prepend a 0x00 byte, as in Java's BigInteger.toByteArray() representation 3. If the byte array is greater than or equal to 32 bytes, use the first (most significant) 32 bytes 4. If the byte array is less than 32 bytes, append 0x00 bytes to extend to 32 bytes. *Very unlikely - See note below.* MAC Key Details --------------- The 32-byte MAC key is created as follows: 1. Take the exchanged DH key byte array, prepended with a 0x00 byte if necessary, from step 2 in the Session Key Details above. 2. If that byte array is greater than or equal to 64 bytes, the MAC key is bytes 33-64 from that byte array. 3. If that byte array is less than 64 bytes, the MAC key is the SHA-256 Hash of that byte array. *As of release 0.9.8. See note below.* Important note `````````````` Code before release 0.9.8 was broken and did not correctly handle DH key byte arrays between 32 and 63 bytes (steps 3 and 4 above) and the connection will fail. As these cases didn't ever work, they were redefined as described above for release 0.9.8, and the 0-32 byte case was redefined as well. Since the nominal exchanged DH key is 256 bytes, the chances of the mininimal representation being less than 64 bytes is vanishingly small. Header Format ------------- Within the AES encrypted payload, there is a minimal common structure to the various messages - a one byte flag and a four byte sending timestamp (seconds since the unix epoch). The header format is: Header: 37+ bytes Encryption starts with the flag byte. +----+----+----+----+----+----+----+----+ | MAC | + + | | +----+----+----+----+----+----+----+----+ | IV | + + | | +----+----+----+----+----+----+----+----+ |flag| time | | +----+----+----+----+----+ + | keying material (optional) | + + | | ~ ~ | | + +----+----+----+ | |#opt| | +----+----+----+----+----+----+ + | #opt extended option bytes (optional) | ~ ~ ~ ~ +----+----+----+----+----+----+----+----+ The flag byte contains the following bitfields: Bit order: 76543210 (bit 7 is MSB) bits 7-4: payload type bit 3: If 1, rekey data is included. Always 0, unimplemented bit 2: If 1, extended options are included. Always 0 before release 0.9.24. bits 1-0: reserved, set to 0 for compatibility with future uses Without rekeying and extended options, the header size is 37 bytes. .. _rekey: Rekeying -------- If the rekey flag is set, 64 bytes of keying material follow the timestamp. When rekeying, the first 32 bytes of the keying material is fed into a SHA256 to produce the new MAC key, and the next 32 bytes are fed into a SHA256 to produce the new session key, though the keys are not immediately used. The other side should also reply with the rekey flag set and that same keying material. Once both sides have sent and received those values, the new keys should be used and the previous keys discarded. It may be useful to keep the old keys around briefly, to address packet loss and reordering. NOTE: Rekeying is currently unimplemented. .. _extend: Extended Options ---------------- If the extended options flag is set, a one byte option size value is appended, followed by that many extended option bytes. Extended options have always been part of the specification, but were unimplemented until release 0.9.24. When present, the option format is specific to the message type. See message documentation below on whether extended options are expected for the given message, and the specified format. While Java routers have always recognized the flag and options length, other implementations have not. Therefore, do not send extended options to routers older than release 0.9.24. Padding ======= All messages contain 0 or more bytes of padding. Each message must be padded to a 16 byte boundary, as required by the AES256 encryption layer [CRYPTO-AES]. Through release 0.9.7, messages were only padded to the next 16 byte boundary, and messages not a multiple of 16 bytes could possibly be invalid. As of release 0.9.7, messages may be padded to any length as long as the current MTU is honored. Any extra 1-15 padding bytes beyond the last block of 16 bytes cannot be encrypted or decrypted and will be ignored. However, the full length and all padding is included in the MAC calculation. As of release 0.9.8, transmitted messages are not necessarily a multiple of 16 bytes. The SessionConfirmed message is an exception, see below. Keys ==== Signatures in the SessionCreated and SessionConfirmed messages are generated using the [SigningPublicKey] from the [RouterIdentity] which is distributed out-of-band by publishing in the network database, and the associated [SigningPrivateKey]. Through release 0.9.15, the signature algorithm was always DSA, with a 40 byte signature. As of release 0.9.16, the signature algorithm may be specified by a a [KeyCertificate] in Bob's [RouterIdentity]. Both introduction keys and session keys are 32 bytes, and are defined by the Common structures specification [SESSIONKEY]. The key used for the MAC and encryption is specified for each message below. Introduction keys are delivered through an external channel (the network database), where they have traditionally been identical to the router Hash through release 0.9.47, but may be random as of release 0.9.48. Notes ===== IPv6 ---- The protocol specification allows both 4-byte IPv4 and 16-byte IPv6 addresses. SSU-over-IPv6 is supported as of version 0.9.8. See the documentation of individual messages below for details on IPv6 support. .. _time: Timestamps ---------- While most of I2P uses 8-byte [Date] timestamps with millisecond resolution, SSU uses 4-byte unsigned integer timestamps with one-second resolution. Because these values are unsigned, they will not roll over until February 2106. Messages ======== There are 10 messages (payload types) defined: ==== ================ ===== Type Message Notes ==== ================ ===== 0 SessionRequest 1 SessionCreated 2 SessionConfirmed 3 RelayRequest 4 RelayResponse 5 RelayIntro 6 Data 7 PeerTest 8 SessionDestroyed Implemented as of 0.8.9 n/a HolePunch ==== ================ ===== .. _sessionRequest: SessionRequest (type 0) ----------------------- This is the first message sent to establish a session. ==================== ====================================================== **Peer:** Alice to Bob **Data:** * 256 byte X, to begin the DH agreement * 1 byte IP address size * that many byte representation of Bob's IP address * N bytes, currently uninterpreted **Crypto Key used:** Bob's introKey, as retrieved from the network database **MAC Key used:** Bob's introKey, as retrieved from the network database ==================== ====================================================== Message format: +----+----+----+----+----+----+----+----+ | X, as calculated from DH | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ |size| that many byte IP address (4-16) | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| ~ . . . ~ Typical size including header, in current implementation: 304 (IPv4) or 320 (IPv6) bytes (before non-mod-16 padding) Extended options ```````````````` Note: Implemented in 0.9.24. * Minimum length: 3 (option length byte + 2 bytes) * Option length: 2 minimum * 2 bytes flags: Bit order: 15...76543210 (bit 15 is MSB) bit 0: 1 for Alice to request a relay tag from Bob in the SessionCreated response, 0 if Alice does not need a relay tag. Note that "1" is the default if no extended options are present bits 15-1: unused, set to 0 for compatibility with future uses Notes ````` * IPv4 and IPv6 addresses are supported. * The uninterpreted data could possibly be used in the future for challenges. .. _sessioncreated: SessionCreated (type 1) ----------------------- This is the response to a SessionRequest_. ==================== ========================================================== **Peer:** Bob to Alice **Data:** * 256 byte Y, to complete the DH agreement * 1 byte IP address size * that many byte representation of Alice's IP address * 2 byte Alice's port number * 4 byte relay (introduction) tag which Alice can publish (else 0x00000000) * 4 byte timestamp (seconds from the epoch) for use in the DSA signature * Bob's [Signature] of the critical exchanged data (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's new relay tag + Bob's signed on time), encrypted with another layer of encryption using the negotiated sessionKey. The IV is reused here. See notes for length information. * 0-15 bytes of padding of the signature, using random data, to a multiple of 16 bytes, so that the signature + padding may be encrypted with an additional layer of encryption using the negotiated session key as part of the DSA block. * N bytes, currently uninterpreted **Crypto Key used:** Bob's introKey, with an additional layer of encryption over the 40 byte signature and the following 8 bytes padding. **MAC Key used:** Bob's introKey ==================== ========================================================== Message format: +----+----+----+----+----+----+----+----+ | Y, as calculated from DH | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ |size| that many byte IP address (4-16) | +----+----+----+----+----+----+----+----+ | Port (A)| public relay tag | signed +----+----+----+----+----+----+----+----+ on time | | +----+----+ + | | + + | signature | + + | | + + | | + +----+----+----+----+----+----+ | | (0-15 bytes of padding) +----+----+----+----+----+----+----+----+ | | +----+----+ + | arbitrary amount | ~ of uninterpreted data ~ ~ . . . ~ Typical size including header, in current implementation: 368 bytes (IPv4 or IPv6) (before non-mod-16 padding) Notes ````` * IPv4 and IPv6 addresses are supported. * If the relay tag is nonzero, Bob is offering to act as an introducer for Alice. Alice may subsequently publish Bob's address and the relay tag in the network database. * For the signature, Bob must use his external port, as that what Alice will use to verify. If Bob's NAT/firewall has mapped his internal port to a different external port, and Bob is unaware of it, the verification by Alice will fail. * See the Keys_ section above for details on signatures. Alice already has Bob's public signing key, from the network database. * Through release 0.9.15, the signature was always a 40 byte DSA signature and the padding was always 8 bytes. As of release 0.9.16, the signature type and length are implied by the type of the [SigningPublicKey] in Bob's [RouterIdentity]. The padding is as necessary to a multiple of 16 bytes. * This is the only message that uses the sender's intro key. All others use the receiver's intro key or the established session key. * Signed-on time appears to be unused or unverified in the current implementation. * The uninterpreted data could possibly be used in the future for challenges. * Extended options in the header: Not expected, undefined. .. _sessionconfirmed: SessionConfirmed (type 2) ------------------------- This is the response to a SessionCreated_ message and the last step in establishing a session. There may be multiple SessionConfirmed messages required if the Router Identity must be fragmented. ==================== ========================================================== **Peer:** Alice to Bob **Data:** * 1 byte identity fragment info:: Bit order: 76543210 (bit 7 is MSB) bits 7-4: current identity fragment # 0-14 bits 3-0: total identity fragments (F) 1-15 * 2 byte size of the current identity fragment * that many byte fragment of Alice's [RouterIdentity] * After the last identity fragment only: * 4 byte signed-on time * N bytes padding, currently uninterpreted * After the last identity fragment only: * The remaining bytes contain Alice's [Signature] of the critical exchanged data (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's new relay tag + Alice's signed on time). See notes for length information. **Crypto Key used:** Alice/Bob sessionKey, as generated from the DH exchange **MAC Key used:** Alice/Bob MAC Key, as generated from the DH exchange ==================== ========================================================== **Fragment 0 through F-2** (only if F > 1; currently unused, see notes below): +----+----+----+----+----+----+----+----+ |info| cursize | | +----+----+----+ + | fragment of Alice's full | ~ Router Identity ~ ~ . . . ~ | | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| ~ . . . ~ **Fragment F-1 (last or only fragment):** +----+----+----+----+----+----+----+----+ |info| cursize | | +----+----+----+ + | last fragment of Alice's full | ~ Router Identity ~ ~ . . . ~ | | +----+----+----+----+----+----+----+----+ | signed on time | | +----+----+----+----+ + | arbitrary amount of uninterpreted | ~ data, until the signature at ~ ~ end of the current packet ~ | Packet length must be mult. of 16 | +----+----+----+----+----+----+----+----+ + + | | + + | signature | + + | | + + | | +----+----+----+----+----+----+----+----+ Typical size including header, in current implementation: 512 bytes (with Ed25519 signature) or 480 bytes (with DSA-SHA1 signature) (before non-mod-16 padding) Notes ````` * In the current implementation, the maximum fragment size is 512 bytes. This should be extended so that longer signatures will work without fragmentation. The current implementation does not correctly process signatures split across two fragments. * The typical [RouterIdentity] is 387 bytes, so no fragmentation is ever necessary. If new crypto extends the size of the RouterIdentity, the fragmentation scheme must be tested carefully. * There is no mechanism for requesting or redelivering missing fragments. * The total fragments field F must be set identically in all fragments. * See the Keys_ section above for details on DSA signatures. * Signed-on time appears to be unused or unverified in the current implementation. * Since the signature is at the end, the padding in the last or only packet must pad the total packet to a multiple of 16 bytes, or the signature will not get decrypted correctly. This is different from all the other message types, where the padding is at the end. * Through release 0.9.15, the signature was always a 40 byte DSA signature. As of release 0.9.16, the signature type and length are implied by the type of the [SigningPublicKey] in Alice's [RouterIdentity]. The padding is as necessary to a multiple of 16 bytes. * Extended options in the header: Not expected, undefined. .. _sessiondestroyed: SessionDestroyed (type 8) ------------------------- The SessionDestroyed message was implemented (reception only) in release 0.8.1, and is sent as of release 0.8.9. ==================== ============================ **Peer:** Alice to Bob or Bob to Alice **Data:** none **Crypto Key used:** Alice/Bob sessionKey **MAC Key used:** Alice/Bob MAC Key ==================== ============================ This message does not contain any data. Typical size including header, in current implementation: 48 bytes (before non-mod-16 padding) Notes ````` * Destroy messages received with the sender's or receiver's intro key will be ignored. * Extended options in the header: Not expected, undefined. .. _relayrequest: RelayRequest (type 3) --------------------- This is the first message sent from Alice to Bob to request an introduction to Charlie. ==================== ========================================================== **Peer:** Alice to Bob **Data:** * 4 byte relay (introduction) tag, nonzero, as received by Alice in the SessionCreated_ message from Bob * 1 byte IP address size * that many byte representation of Alice's IP address * 2 byte port number (of Alice) * 1 byte challenge size * that many bytes to be relayed to Charlie in the intro * Alice's 32-byte introduction key (so Bob can reply with Charlie's info) * 4 byte nonce of Alice's relay request * N bytes, currently uninterpreted **Crypto Key used:** Bob's introKey, as retrieved from the network database (or Alice/Bob sessionKey, if established) **MAC Key used:** Bob's introKey, as retrieved from the network database (or Alice/Bob MAC Key, if established) ==================== ========================================================== Message format: +----+----+----+----+----+----+----+----+ | relay tag |size| Alice IP addr +----+----+----+----+----+----+----+----+ | Port (A)|size| challenge bytes | +----+----+----+----+ + | to be delivered to Charlie | +----+----+----+----+----+----+----+----+ | Alice's intro key | + + | | + + | | + + | | +----+----+----+----+----+----+----+----+ | nonce | | +----+----+----+----+ + | arbitrary amount of uninterpreted data| ~ . . . ~ Typical size including header, in current implementation: 96 bytes (no Alice IP included) or 112 bytes (4-byte Alice IP included) (before non-mod-16 padding) Notes ````` * The IP address is only included if it is be different than the packet's source address and port. * This message may be sent via IPv4 or IPv6. If the message is over IPv6 for an IPv4 introduction, or (as of release 0.9.50) over IPv4 for an IPv6 introduction, Alice must include her introduction address and port. This is supported as of release 0.9.50. * If Alice includes her address/port, Bob may perform additional validation before continuing. * Prior to release 0.9.24, Java I2P rejected any address or port that was different from the connection. * Challenge is unimplemented, challenge size is always zero * Relaying for IPv6 is supported as of release 0.9.50. * Prior to release 0.9.12, Bob's intro key was always used. As of release 0.9.12, the session key is used if there is an established session between Alice and Bob. In practice, there must be an established session, as Alice will only get the nonce (introduction tag) from the session created message, and Bob will mark the introduction tag invalid once the session is destroyed. * Extended options in the header: Not expected, undefined. .. _relayresponse: RelayResponse (type 4) ---------------------- This is the response to a RelayRequest_ and is sent from Bob to Alice. ==================== ========================================================== **Peer:** Bob to Alice **Data:** * 1 byte IP address size * that many byte representation of Charlie's IP address * 2 byte Charlie's port number * 1 byte IP address size * that many byte representation of Alice's IP address * 2 byte Alice's port number * 4 byte nonce sent by Alice * N bytes, currently uninterpreted **Crypto Key used:** Alice's introKey, as received in the Relay Request (or Alice/Bob sessionKey, if established) **MAC Key used:** Alice's introKey, as received in the Relay Request (or Alice/Bob MAC Key, if established) ==================== ========================================================== Message format: +----+----+----+----+----+----+----+----+ |size| Charlie IP | Port (C)|size| +----+----+----+----+----+----+----+----+ | Alice IP | Port (A)| nonce +----+----+----+----+----+----+----+----+ | arbitrary amount of | +----+----+ + | uninterpreted data | ~ . . . ~ Typical size including header, in current implementation: 64 (Alice IPv4) or 80 (Alice IPv6) bytes (before non-mod-16 padding) Notes ````` * This message may be sent via IPv4 or IPv6. * Alice's IP address/port are the apparent IP/port that Bob received the RelayRequest on (not necessarily the IP Alice included in the RelayRequest), and may be IPv4 or IPv6. Alice currently ignores these on receive. * Charlie's IP address may be IPv4, or, as of release 0.9.50, IPv6. as that is the address that Alice will send the SessionRequest to after the Hole Punch. * Relaying for IPv6 is supported as of release 0.9.50. * Prior to release 0.9.12, Alice's intro key was always used. As of release 0.9.12, the session key is used if there is an established session between Alice and Bob. * Extended options in the header: Not expected, undefined. .. _relayintro: RelayIntro (type 5) ------------------- This is the introduction for Alice, which is sent from Bob to Charlie. ==================== ===================================================== **Peer:** Bob to Charlie **Data:** * 1 byte IP address size * that many byte representation of Alice's IP address * 2 byte port number (of Alice) * 1 byte challenge size * that many bytes relayed from Alice * N bytes, currently uninterpreted **Crypto Key used:** Bob/Charlie sessionKey **MAC Key used:** Bob/Charlie MAC Key ==================== ===================================================== Message format: +----+----+----+----+----+----+----+----+ |size| Alice IP | Port (A)|size| +----+----+----+----+----+----+----+----+ | that many bytes of challenge | + + | data relayed from Alice | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| ~ . . . ~ Typical size including header, in current implementation: 48 bytes (before non-mod-16 padding) Notes ````` * For IPv4, Alice's IP address is always 4 bytes, because Alice is trying to connect to Charlie via IPv4. As of release 0.9.xx, IPv6 is supported, and Alice's IP address may be 16 bytes. * 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_. * 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_. As of release 0.9.50, IPv6 is supported, and this message may be sent via an established IPv6 connection. * As of release 0.9.50, any SSU address published with introducers must contain "4" or "6" in the "caps" option. * Challenge is unimplemented, challenge size is always zero * Extended options in the header: Not expected, undefined. .. _data: Data (type 6) ------------- This message is used for data transport and acknowledgment. ==================== ========================================================== **Peer:** Any **Data:** * 1 byte flags:: Bit order: 76543210 (bit 7 is MSB) bit 7: explicit ACKs included bit 6: ACK bitfields included bit 5: reserved bit 4: explicit congestion notification (ECN) bit 3: request previous ACKs bit 2: want reply bit 1: extended data included (unused, never set) bit 0: reserved * if explicit ACKs are included: * a 1 byte number of ACKs * that many 4 byte MessageIds being fully ACKed * if ACK bitfields are included: * a 1 byte number of ACK bitfields * that many 4 byte MessageIds + a 1 or more byte ACK bitfield. The bitfield uses the 7 low bits of each byte, with the high bit specifying whether an additional bitfield byte follows it (1 = true, 0 = the current bitfield byte is the last). These sequence of 7 bit arrays represent whether a fragment has been received - if a bit is 1, the fragment has been received. To clarify, assuming fragments 0, 2, 5, and 9 have been received, the bitfield bytes would be as follows:: byte 0: Bit order: 76543210 (bit 7 is MSB) bit 7: 1 (further bitfield bytes follow) bit 6: 0 (fragment 6 not received) bit 5: 1 (fragment 5 received) bit 4: 0 (fragment 4 not received) bit 3: 0 (fragment 3 not received) bit 2: 1 (fragment 2 received) bit 1: 0 (fragment 1 not received) bit 0: 1 (fragment 0 received) byte 1: Bit order: 76543210 (bit 7 is MSB) bit 7: 0 (no further bitfield bytes) bit 6: 0 (fragment 13 not received) bit 5: 0 (fragment 12 not received) bit 4: 0 (fragment 11 not received) bit 3: 0 (fragment 10 not received) bit 2: 1 (fragment 9 received) bit 1: 0 (fragment 8 not received) bit 0: 0 (fragment 7 not received) * If extended data included: * 1 byte data size * that many bytes of extended data (currently uninterpreted) * 1 byte number of fragments (can be zero) * If nonzero, that many message fragments. Each fragment contains: * 4 byte messageId * 3 byte fragment info:: Bit order: 76543210 (bit 7 is MSB) bits 23-17: fragment # 0 - 127 bit 16: isLast (1 = true) bits 15-14: unused, set to 0 for compatibility with future uses bits 13-0: fragment size 0 - 16383 * that many bytes * N bytes padding, uninterpreted **Crypto Key used:** Alice/Bob sessionKey **MAC Key used:** Alice/Bob MAC Key ==================== ========================================================== Message format: +----+----+----+----+----+----+----+----+ |flag| (additional headers, determined | +----+ + ~ by the flags, such as ACKs or ~ | bitfields | +----+----+----+----+----+----+----+----+ |#frg| messageId | frag info | +----+----+----+----+----+----+----+----+ | that many bytes of fragment data | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ | messageId | frag info | | +----+----+----+----+----+----+----+ + | that many bytes of fragment data | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ | messageId | frag info | | +----+----+----+----+----+----+----+ + | that many bytes of fragment data | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| ~ . . . ~ Notes ````` * The current implementation adds a limited number of duplicate acks for messages previously acked, if space is available. * If the number of fragments is zero, this is an ack-only or keepalive message. * The ECN feature is unimplemented, and the bit is never set. * In the current implementation, the want reply bit is set when the number of fragments is greater then zero, and not set when there are no fragments. * Extended data is unimplemented and never present. * Reception of multiple fragments is supported in all releases. Transmission of multiple fragments is implemented in release 0.9.16. * As currently implemented, maximum fragments is 64 (maximum fragment number = 63). * As currently implemented, maximum fragment size is of course less than the MTU. * Take care not to exceed the maximum MTU even if there is a large number of ACKs to send. * The protocol allows zero-length fragments but there's no reason to send them. * In SSU, the data uses a short 5-byte I2NP header followed by the payload of the I2NP message instead of the standard 16-byte I2NP header. The short I2NP header consists only of the one-byte I2NP type and 4-byte expiration in seconds. The I2NP message ID is used as the message ID for the fragment. The I2NP size is assembled from the fragment sizes. The I2NP checksum is not required as UDP message integrity is ensured by decryption. * Message IDs are not sequence numbers and are not consecutive. SSU does not guarantee in-order delivery. While we use the I2NP message ID as the SSU message ID, from the SSU protocol view, they are random numbers. In fact, since the router uses a single Bloom filter for all peers, the message ID must be an actual random number. * Because there are no sequence numbers, there is no way to be sure an ACK was received. The current implementation routinely sends a large amount of duplicate ACKs. Duplicate ACKs should not be taken as an indication of congestion. * ACK Bitfield notes: The receiver of a data packet does not know how many fragments are in the message unless it has received the last fragment. Therefore, the number of bitfield bytes sent in response may be less or more than the number of fragments divided by 7. For example, if the highest fragment the receiver has seen is number 4, only one byte is required to be sent, even if there may be 13 fragments total. Up to 10 bytes (i.e. (64 / 7) + 1) may be included for each message ID acked. * Extended options in the header: Not expected, undefined. .. _peertest: PeerTest (type 7) ----------------- See [SSU-PEERTEST] for details. Note: IPv6 peer testing is supported as of release 0.9.27. ==================== ========================================================== **Peer:** Any **Data:** See notes section below for details * 4 byte nonce * 1 byte IP address size (may be zero) * that many byte representation of Alice's IP address, if size > 0 * 2 byte Alice's port number * Alice's or Charlie's 32-byte introduction key * N bytes, currently uninterpreted **Crypto Key used:** Listed in order of occurrence: 1. When sent from Alice to Bob: Alice/Bob sessionKey (The protocol also permits Bob's introKey if Alice and Bob do not have an established session, but in the current implementation Alice always selects a Bob that is established. As of release 0.9.15, Bob will reject PeerTests from peers without an established session.) 2. When sent from Bob to Charlie: Bob/Charlie sessionKey 3. When sent from Charlie to Bob: Bob/Charlie sessionKey 4. When sent from Bob to Alice: Alice/Bob sessionKey (or for Bob prior to 0.9.52, Alice's introKey, as received in the PeerTest message from Alice, see note below) 5. When sent from Charlie to Alice: Alice's introKey, as received in the PeerTest message from Bob 6. When sent from Alice to Charlie: Charlie's introKey, as received in the PeerTest message from Charlie **MAC Key used:** Listed in order of occurrence: 1. When sent from Alice to Bob: Alice/Bob MAC Key (The protocol also permits Bob's introKey if Alice and Bob do not have an established session, but in the current implementation Alice always selects a Bob that is established. As of release 0.9.15, Bob will reject PeerTests from peers without an established session.) 2. When sent from Bob to Charlie: Bob/Charlie MAC Key 3. When sent from Charlie to Bob: Bob/Charlie MAC Key 4. When sent from Bob to Alice: Alice's introKey, as received in the PeerTest message from Alice 5. When sent from Charlie to Alice: Alice's introKey, as received in the PeerTest message from Bob 6. When sent from Alice to Charlie: Charlie's introKey, as received in the PeerTest message from Charlie ==================== ========================================================== Message format: +----+----+----+----+----+----+----+----+ | test nonce |size| Alice IP addr +----+----+----+----+----+----+----+----+ | Port (A)| | +----+----+----+ + | Alice or Charlie's | + introduction key (Alice's is sent to + | Bob and Charlie, while Charlie's is | + sent to Alice) + | | + +----+----+----+----+----+ | | arbitrary amount of | +----+----+----+ | | uninterpreted data | ~ . . . ~ Typical size including header, in current implementation: 80 bytes (before non-mod-16 padding) Notes ````` * When sent by Alice, IP address size is 0, IP address is not present, and port is 0, as Bob and Charlie do not use the data; the point is to determine Alice's true IP address/port and tell Alice; Bob and Charlie don't care what Alice thinks her address is. * When sent by Bob or Charlie, IP and port are present, and IP address is 4 or 16 bytes. IPv6 testing is supported as of release 0.9.27. * When sent by Charlie to Alice, the IP and port are as follows: First time (message 5): Alice's requested IP and port as received in message 2. Second time (message 7): Alice's actual IP and port that message 6 was received from. * IPv6 Notes: Through release 0.9.26, only testing of IPv4 addresses is supported. Therefore, all Alice-Bob and Alice-Charlie communication must be via IPv4. Bob-Charlie communication, however, may be via IPv4 or IPv6. Alice's address, when specified in the PeerTest message, must be 4 bytes. As of release 0.9.27, 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). * A peer must maintain a table of active test states (nonces). On reception of a PeerTest message, look up the nonce in the table. If found, it's an existing test and you know your role (Alice, Bob, or Charlie). Otherwise, if the IP is not present and the port is 0, this is a new test and you are Bob. Otherwise, this is a new test and you are Charlie. * As of release 0.9.15, Alice must have an established session with Bob and use the session key. * Prior to API version 0.9.52, in some implementations, Bob replied to Alice using Alice's intro key rather than the Alice/Bob session key, even though Alice and Bob have an established session (since 0.9.15). As of API version 0.9.52, Bob will correctly use the session key in all implementations, and Alice should reject a message received from Bob with Alice's intro key if Bob is API version 0.9.52 or higher. * Extended options in the header: Not expected, undefined. HolePunch --------- A HolePunch is simply a UDP packet with no data. It is unauthenticated and unencrypted. It does not contain a SSU header, so it does not have a message type number. It is sent from Charlie to Alice as a part of the Introduction sequence. .. _sampledatagrams: Sample datagrams ================ Minimal data message -------------------- * no fragments, no ACKs, no NACKs, etc * Size: 39 bytes +----+----+----+----+----+----+----+----+ | MAC | + + | | +----+----+----+----+----+----+----+----+ | IV | + + | | +----+----+----+----+----+----+----+----+ |flag| time |flag|#frg| | +----+----+----+----+----+----+----+ + | padding to fit a full AES256 block | +----+----+----+----+----+----+----+----+ Minimal data message with payload --------------------------------- * Size: 46+fragmentSize bytes +----+----+----+----+----+----+----+----+ | MAC | + + | | +----+----+----+----+----+----+----+----+ | IV | + + | | +----+----+----+----+----+----+----+----+ |flag| time |flag|#frg| +----+----+----+----+----+----+----+----+ messageId | frag info | | ----+----+----+----+----+----+ + | that many bytes of fragment data | ~ . . . ~ | | +----+----+----+----+----+----+----+----+ References ========== [CRYPTO-AES] https://geti2p.net/en/docs/how/cryptography#AES [CRYPTO-ELG] https://geti2p.net/en/docs/how/cryptography#elgamal [CRYPTO-HMAC] https://geti2p.net/en/docs/how/cryptography#udp [Date] https://geti2p.net/spec/common-structures#type-date [I2P-SRC] https://github.com/i2p/i2p.i2p [I2PCPP-SRC] http://git.repo.i2p.xyz/w/i2pcpp.git [I2PD-SRC] https://github.com/PurpleI2P/i2pd [KeyCertificate] https://geti2p.net/spec/common-structures#key-certificates [RouterIdentity] https://geti2p.net/spec/common-structures#struct-routeridentity [SESSIONKEY] https://geti2p.net/spec/common-structures#type-sessionkey [Signature] https://geti2p.net/spec/common-structures#type-signature [SigningPrivateKey] https://geti2p.net/spec/common-structures#type-signingprivatekey [SigningPublicKey] https://geti2p.net/spec/common-structures#type-signingpublickey [SSU] https://geti2p.net/en/docs/transport/ssu [SSU-KEYS] https://geti2p.net/en/docs/transport/ssu#keys [SSU-PEERTEST] https://geti2p.net/en/docs/transport/ssu#peerTesting