930 lines
35 KiB
HTML
930 lines
35 KiB
HTML
{% extends "global/layout.html" %}
|
|
{% block title %}SSU Protocol Specification{% endblock %}
|
|
{% block lastupdated %}July 2013{% endblock %}
|
|
{% block accuratefor %}0.9.7{% endblock %}
|
|
{% block content %}
|
|
|
|
Note: IPv6 information is preliminary.
|
|
|
|
<p>
|
|
<a href="{{ site_url('docs/transport/ssu') }}">See the SSU page for an overview of the SSU transport</a>.
|
|
|
|
<h1>Specification</h1>
|
|
|
|
|
|
<h2 id="DH">DH Key Exchange</h2>
|
|
<p>
|
|
The initial 2048-bit DH key exchange is described on the
|
|
<a href="{{ site_url('docs/transport/ssu') }}#keys">SSU page</a>.
|
|
This exchange uses the same shared prime as that used for I2P's
|
|
<a href="{{ site_url('docs/how/cryptography') }}#elgamal">ElGamal encryption</a>.
|
|
</p>
|
|
|
|
|
|
<h2 id="header">Message Header</h2>
|
|
|
|
<p>
|
|
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:</p>
|
|
<pre>
|
|
HMAC-MD5(payload || IV || (payloadLength ^ protocolVersion), macKey)
|
|
</pre>
|
|
where '||' means append.
|
|
The payload is the message starting with the flag byte.
|
|
The macKey is either the introduction key or is constructed from the
|
|
exchanged DH key (see details below), as specified for each message below.
|
|
Note that protocolVersion is 0, so the exclusive or is a no-op.
|
|
<b>WARNING</b> - the HMAC-MD5-128 used here is non-standard,
|
|
see <a href="{{ site_url('docs/how/cryptography') }}#udp">the cryptography page</a> for details.
|
|
|
|
|
|
<p>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 payloadLength in the MAC is a 2 byte unsigned
|
|
integer.</p>
|
|
|
|
<p>The protocolVersion is a 2 byte unsigned integer
|
|
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.</p>
|
|
|
|
<h3>Session Key Details</h3>
|
|
The 32-byte session key is created as follows:
|
|
<ol><li>
|
|
Take the exchanged DH key, represented as a positive minimal-length BigInteger byte array (two's complement big-endian)
|
|
</li><li>
|
|
If the most significant bit is 1 (i.e. array[0] & 0x80 != 0),
|
|
prepend a 0x00 byte, as in Java's BigInteger.toByteArray() representation
|
|
</li><li>
|
|
If the byte array is greater than or equal to 32 bytes, use the first (most significant) 32 bytes
|
|
</li><li>
|
|
If the byte array is less than 32 bytes, append 0x00 bytes to extend to 32 bytes.
|
|
<i>Won't happen - See note below</i>
|
|
</li></ol>
|
|
|
|
<h3>MAC Key Details</h3>
|
|
The 32-byte MAC key is created as follows:
|
|
<ol><li>
|
|
Take the exchanged DH key byte array, prepended with a 0x00 byte if necessary,
|
|
from step 2 in the Session Key Details above.
|
|
</li><li>
|
|
If that byte array is greater than or equal to 64 bytes, the MAC key is
|
|
bytes 33-64 from that byte array.
|
|
</li><li>
|
|
If that byte array is greater than 32 bytes but less than 64 bytes, the MAC key is formed from
|
|
the bytes starting at byte 33, followed by 0x00 bytes to extend the MAC key to 32 bytes.
|
|
<i>Broken - See note below</i>
|
|
</li><li>
|
|
If that byte array is equal to 32 bytes, the MAC key is all zeros.
|
|
<i>Broken - See note below</i>
|
|
</li><li>
|
|
If that byte array is less than 32 bytes, the MAC key is the SHA-256 Hash of the 32-bytes
|
|
from step 3 in the Session Key Details above.
|
|
<i>See note below</i>
|
|
</li></ol>
|
|
Important note: It appears that the existing code is buggy and does 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 won't ever work, they are subject to change (probably using SHA-256).
|
|
Since the nominal exchanged DH key is 256 bytes, the chances of the mininimal representation
|
|
being less than 64 bytes is vanishingly small.
|
|
|
|
|
|
<h3>Header Format</h3>
|
|
<p>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 flag byte contains
|
|
the following bitfields:</p>
|
|
{% highlight %}
|
|
Bit order: 76543210 (bit 7 is MSB)
|
|
|
|
bits 7-4: payload type
|
|
bit 3: rekey?
|
|
bit 2: extended options included
|
|
bits 1-0: reserved
|
|
{% endhighlight %}
|
|
{% highlight lang='dataspec' %}
|
|
Header: 37+ bytes
|
|
Encryption starts with the flag byte.
|
|
+----+----+----+----+----+----+----+----+
|
|
| MAC |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| IV |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|flag| time | (optionally |
|
|
+----+----+----+----+----+ |
|
|
| this may have 64 byte keying material |
|
|
| and/or a one+N byte extended options) |
|
|
+---------------------------------------|
|
|
{% endhighlight %}
|
|
|
|
|
|
<h3 id="rekey">Rekeying</h3>
|
|
<p>If the rekey flag is set, 64 bytes of keying material follow the
|
|
timestamp.
|
|
|
|
<p>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.</p>
|
|
|
|
<p>NOTE: Rekeying is currently unimplemented.</p>
|
|
|
|
<h3 id="extend">Extended Options</h3>
|
|
<p>
|
|
If the extended options flag is set, a one byte option
|
|
size value is appended, followed by that many extended option
|
|
bytes.</p>
|
|
|
|
<p>NOTE: Extended options is currently unimplemented.</p>
|
|
|
|
<h2 id="padding">Padding</h2>
|
|
<p>
|
|
All messages contain 0 or more bytes of padding.
|
|
Each message must be padded to a 16 byte boundary, as required by the <a href="{{ site_url('docs/how/cryptography') }}#AES">AES256 encryption layer</a>.
|
|
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.
|
|
</p>
|
|
|
|
|
|
<h2 id="keys">Keys</h2>
|
|
<p>
|
|
DSA signatures in the SessionCreated and SessionConfirmed messages are generated using
|
|
the
|
|
<a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPublicKey">signing public key</a>
|
|
from the
|
|
<a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">router identity</a>
|
|
which is distributed out-of-band by publishing in the network database, and the associated
|
|
<a href="{{ site_url('docs/spec/common-structures') }}#type_SigningPrivateKey">signing private key</a>.
|
|
</p><p>
|
|
Both introduction keys and session keys are 32 bytes,
|
|
and are defined by the
|
|
<a href="{{ site_url('docs/spec/common-structures') }}#type_SessionKey">Common structures specification</a>.
|
|
The key used for the MAC and encryption is specified for each message below.
|
|
</p>
|
|
<p>Introduction keys are delivered through an external channel
|
|
(the network database, where they are identical to the router Hash for now).
|
|
</p>
|
|
|
|
|
|
<h2 id="notes">Notes</h2>
|
|
|
|
<h3 id="ipv6">IPv6 Notes</h3>
|
|
While the protocol specification supports 16-byte IPv6 addresses,
|
|
IPv6 addressing is not currently supported within I2P.
|
|
All IP addresses are currently 4 bytes.
|
|
|
|
<h3 id="time">Timestamps</h3>
|
|
While most of I2P uses 8-byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Date">Date</a> timestamps with
|
|
millisecond resolution, SSU uses a 4-byte timestamp with one-second resolution.
|
|
|
|
|
|
|
|
|
|
<h2 id="messages">Messages</h2>
|
|
<p>
|
|
There are 10 messages (payload types) defined:
|
|
</p><p>
|
|
<table border="1">
|
|
<tr><th>Type<th>Message<th>Notes
|
|
<tr><td align="center">0<td>SessionRequest<td>
|
|
<tr><td align="center">1<td>SessionCreated<td>
|
|
<tr><td align="center">2<td>SessionConfirmed<td>
|
|
<tr><td align="center">3<td>RelayRequest<td>
|
|
<tr><td align="center">4<td>RelayResponse<td>
|
|
<tr><td align="center">5<td>RelayIntro<td>
|
|
<tr><td align="center">6<td>Data<td>
|
|
<tr><td align="center">7<td>PeerTest<td>
|
|
<tr><td align="center">8<td>SessionDestroyed<td>Implemented as of 0.8.9
|
|
<tr><td align="center">n/a<td>HolePunch<td>
|
|
</table>
|
|
</p>
|
|
|
|
<h3 id="sessionRequest">SessionRequest (type 0)</h3>
|
|
<p>
|
|
This is the first message sent to establish a session.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Alice to Bob</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>256 byte X, to begin the DH agreement</li>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Bob's IP address</li>
|
|
<li>N bytes, currently uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>introKey</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| X, as calculated from DH |
|
|
| |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|size| that many byte IP address (4-16) |
|
|
+----+----+----+----+----+----+----+----+
|
|
| arbitrary amount |
|
|
| of uninterpreted data |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 304 (IPv4) or 320 (IPv6) bytes
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
IPv4 and IPv6 addresses are supported.
|
|
</li><li>
|
|
The uninterpreted data could possibly be used in the future for challenges.
|
|
</li></ul>
|
|
|
|
|
|
|
|
<h3 id="sessionCreated">SessionCreated (type 1)</h3>
|
|
<p>
|
|
This is the response to a Session Request.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Bob to Alice</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>256 byte Y, to complete the DH agreement</li>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Alice's IP address</li>
|
|
<li>2 byte Alice's port number</li>
|
|
<li>4 byte relay (introduction) tag which Alice can publish (else 0x00000000)</li>
|
|
<li>4 byte timestamp (seconds from the epoch) for use in the DSA
|
|
signature</li>
|
|
<li>40 byte <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">DSA signature</a> 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.</li>
|
|
<li>8 bytes padding, encrypted with an additional layer of encryption
|
|
using the negotiated session key as part of the DSA block</li>
|
|
<li>N bytes, currently uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>introKey, with an additional layer of encryption over the 40 byte
|
|
signature and the following 8 bytes padding.</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| Y, as calculated from DH |
|
|
| |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|size| that many byte IP address (4-16) |
|
|
+----+----+----+----+----+----+----+----+
|
|
| Port (A)| public relay tag | signed
|
|
+----+----+----+----+----+----+----+----+
|
|
on time | |
|
|
+----+----+ |
|
|
| DSA signature |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+ +----+----+----+----+----+----+
|
|
| | (8 bytes of padding)
|
|
+----+----+----+----+----+----+----+----+
|
|
| |
|
|
+----+----+ |
|
|
| arbitrary amount |
|
|
| of uninterpreted data |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 368 bytes (IPv4 or IPv6)
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
IPv4 and IPv6 addresses are supported.
|
|
</li><li>
|
|
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.
|
|
</li><li>
|
|
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.
|
|
</li><li>
|
|
See <a href="#keys">the Keys section above</a> for details on DSA signatures.
|
|
Alice already has Bob's public signing key, from the network database.
|
|
</li><li>
|
|
Signed-on time appears to be unused or unverified in the current implementation.
|
|
</li><li>
|
|
The uninterpreted data could possibly be used in the future for challenges.
|
|
</li></ul>
|
|
|
|
|
|
|
|
<h3 id="sessionConfirmed">SessionConfirmed (type 2)</h3>
|
|
<p>
|
|
This is the response to a Session Created message and the last step in establishing a session.
|
|
There may be multiple Session Confirmed messages required if the Router Identity must be fragmented.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Alice to Bob</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>1 byte identity fragment info:<pre>
|
|
Bit order: 76543210 (bit 7 is MSB)
|
|
bits 7-4: current identity fragment # 0-14
|
|
bits 3-0: total identity fragments (F) 1-15</pre></li>
|
|
<li>2 byte size of the current identity fragment</li>
|
|
<li>that many byte fragment of Alice's
|
|
<a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>
|
|
</li>
|
|
<li>After the last identity fragment only:
|
|
<ul><li>4 byte signed-on time
|
|
</li></ul>
|
|
<li>N bytes padding, currently uninterpreted
|
|
<li>After the last identity fragment only:
|
|
<ul><li>The last 40
|
|
bytes contain the <a href="{{ site_url('docs/spec/common-structures') }}#type_Signature">DSA signature</a> of the critical exchanged
|
|
data (X + Y + Alice's IP + Alice's port + Bob's IP + Bob's port
|
|
+ Alice's new relay key + Alice's signed on time)</li>
|
|
</li></ul>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>sessionKey</td></tr>
|
|
</table>
|
|
|
|
<b>Fragment 0 through F-2 (if F > 1):</b>
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
|info| cursize | |
|
|
+----+----+----+ |
|
|
| fragment of Alice's full |
|
|
| Router Identity |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| arbitrary amount of uninterpreted |
|
|
| data |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<b>Fragment F-1 (last or only fragment):</b>
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
|info| cursize | |
|
|
+----+----+----+ |
|
|
| last fragment of Alice's full |
|
|
| Router Identity |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| signed on time | |
|
|
+----+----+----+----+ |
|
|
| arbitrary amount of uninterpreted |
|
|
| data, to 40 bytes prior to |
|
|
| end of the current packet |
|
|
| Packet length must be mult. of 16 |
|
|
+----+----+----+----+----+----+----+----+
|
|
| DSA signature |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 480 bytes
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
In the current implementation, the maximum fragment size is 512 bytes.
|
|
</li><li>
|
|
The typical <a href="{{ site_url('docs/spec/common-structures') }}#struct_RouterIdentity">Router Identity</a>
|
|
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.
|
|
</li><li>
|
|
There is no mechanism for requesting or redelivering missing fragments.
|
|
</li><li>
|
|
The total fragments field F must be set identically in all fragments.
|
|
</li><li>
|
|
See <a href="#keys">the Keys section above</a> for details on DSA signatures.
|
|
</li><li>
|
|
Signed-on time appears to be unused or unverified in the current implementation.
|
|
</li><li>
|
|
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.
|
|
</li></ul>
|
|
|
|
|
|
|
|
|
|
<h3 id="sessionDestroyed">SessionDestroyed (type 8)</h3>
|
|
<p>
|
|
The Session Destroyed message was implemented (reception only) in release 0.8.1,
|
|
and is sent as of release 0.8.9.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Alice to Bob or Bob to Alice</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td>none
|
|
</td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>sessionKey or introKey</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| no data |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 48 bytes
|
|
</p>
|
|
|
|
<h3 id="relayRequest">RelayRequest (type 3)</h3>
|
|
<p>
|
|
This is the first message sent from Alice to Bob to request an introduction to Charlie.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Alice to Bob</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>4 byte relay (introduction) tag, nonzero</li>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Alice's IP address</li>
|
|
<li>2 byte port number (of Alice)</li>
|
|
<li>1 byte challenge size</li>
|
|
<li>that many bytes to be relayed to Charlie in the intro</li>
|
|
<li>Alice's 32-byte introduction key (so Bob can reply with Charlie's info)</li>
|
|
<li>4 byte nonce of Alice's relay request</li>
|
|
<li>N bytes, currently uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>introKey (or sessionKey, if Alice/Bob is established)</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| 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|
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 96 bytes (no Alice IP included) or 112 bytes (4-byte Alice IP included)
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
The IP address is only included if it is be different than the
|
|
packet's source address and port. In the current implementation, the
|
|
IP length is always 0 and the port is always 0, and the receiver should
|
|
use the packet's source address and port.
|
|
</li><li>
|
|
This message may be sent via IPv4 or IPv6. If IPv6, Alice must include her IPv4 address and port.
|
|
</li><li>
|
|
If Alice includes her address/port, Bob may perform additional validation before continuing.
|
|
</li><li>
|
|
Challenge is unimplemented, challenge size is always zero
|
|
</li><li>
|
|
There are no plans to implement relaying for IPv6.
|
|
</li></ul>
|
|
|
|
|
|
<h3 id="relayResponse">RelayResponse (type 4)</h3>
|
|
<p>
|
|
This is the response to a Relay Request and is sent from Bob to Alice.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Bob to Alice</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Charlie's IP address</li>
|
|
<li>2 byte Charlie's port number</li>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Alice's IP address</li>
|
|
<li>2 byte Alice's port number</li>
|
|
<li>4 byte nonce sent by Alice</li>
|
|
<li>N bytes, currently uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>introKey (or sessionKey, if Alice/Bob is established)</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
|size| Charlie IP | Port (C)|size|
|
|
+----+----+----+----+----+----+----+----+
|
|
| Alice IP | Port (A)| nonce
|
|
+----+----+----+----+----+----+----+----+
|
|
| arbitrary amount of |
|
|
+----+----+ |
|
|
| uninterpreted data |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 64 (Alice IPv4) or 80 (Alice IPv6) bytes
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
This message may be sent via IPv4 or IPv6.
|
|
</li><li>
|
|
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.
|
|
</li><li>
|
|
Charlie's IP address must be IPv4, as that is the address that Alice will send
|
|
the SessionRequest to after the Hole Punch.
|
|
</li><li>
|
|
There are no plans to implement relaying for IPv6.
|
|
</li></ul>
|
|
|
|
|
|
|
|
<h3 id="relayIntro">RelayIntro (type 5)</h3>
|
|
<p>
|
|
This is the introduction for Alice, which is sent from Bob to Charlie.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Bob to Charlie</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Alice's IP address</li>
|
|
<li>2 byte port number (of Alice)</li>
|
|
<li>1 byte challenge size</li>
|
|
<li>that many bytes relayed from Alice</li>
|
|
<li>N bytes, currently uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>sessionKey</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
|size| Alice IP | Port (A)|size|
|
|
+----+----+----+----+----+----+----+----+
|
|
| that many bytes of challenge |
|
|
+ |
|
|
| data relayed from Alice |
|
|
+----+----+----+----+----+----+----+----+
|
|
| arbitrary amount of uninterpreted data|
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 48 bytes
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
Alice's IP address is always 4 bytes in the current implementation, because Alice is trying to connect
|
|
to Charlie via IPv4.
|
|
</li><li>
|
|
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.
|
|
</li><li>
|
|
Challenge is unimplemented, challenge size is always zero
|
|
</li></ul>
|
|
|
|
|
|
|
|
|
|
<h3 id="data">Data (type 6)</h3>
|
|
<p>
|
|
This message is used for data transport and acknowledgment.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Any</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>1 byte flags:<pre>
|
|
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</pre></li>
|
|
<li>if explicit ACKs are included:<ul>
|
|
<li>a 1 byte number of ACKs</li>
|
|
<li>that many 4 byte MessageIds being fully ACKed</li>
|
|
</ul></li>
|
|
<li>if ACK bitfields are included:<ul>
|
|
<li>a 1 byte number of ACK bitfields</li>
|
|
<li>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:<pre>
|
|
byte 0
|
|
Bit order: 76543210 (bit 7 is MSB)
|
|
bit 7: 1 (further bitfield bytes follow)
|
|
bit 6: 1 (fragment 0 received)
|
|
bit 5: 0 (fragment 1 not received)
|
|
bit 4: 1 (fragment 2 received)
|
|
bit 3: 0 (fragment 3 not received)
|
|
bit 2: 0 (fragment 4 not received)
|
|
bit 1: 1 (fragment 5 received)
|
|
bit 0: 0 (fragment 6 not received)
|
|
byte 1
|
|
Bit order: 76543210 (bit 7 is MSB)
|
|
bit 7: 0 (no further bitfield bytes)
|
|
bit 6: 0 (fragment 7 not received)
|
|
bit 5: 0 (fragment 8 not received)
|
|
bit 4: 1 (fragment 9 received)
|
|
bit 3: 0 (fragment 10 not received)
|
|
bit 2: 0 (fragment 11 not received)
|
|
bit 1: 0 (fragment 12 not received)
|
|
bit 0: 0 (fragment 13 not received)</pre></li>
|
|
</ul></li>
|
|
<li>If extended data included:<ul>
|
|
<li>1 byte data size</li>
|
|
<li>that many bytes of extended data (currently uninterpreted)</li></ul></li>
|
|
<li>1 byte number of fragments (can be zero)</li>
|
|
<li>If nonzero, that many message fragments. Each fragment contains:<ul>
|
|
<li>4 byte messageId</li>
|
|
<li>3 byte fragment info:<pre>
|
|
Bit order: 76543210 (bit 7 is MSB)
|
|
bits 23-17: fragment # 0 - 127
|
|
bit 16: isLast (1 = true)
|
|
bits 15-14: unused
|
|
bits 13-0: fragment size 0 - 16383</pre></li>
|
|
<li>that many bytes</li></ul>
|
|
<li>N bytes padding, uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>sessionKey</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
|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|
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
The current implementation adds a limited number of duplicate acks for
|
|
messages previously acked, if space is available.
|
|
</li><li>
|
|
If the number of fragments is zero, this is an ack-only or keepalive message.
|
|
</li><li>
|
|
The ECN feature is unimplemented, and the bit is never set.
|
|
</li><li>
|
|
The want reply bit is always set in the current implementation.
|
|
</li><li>
|
|
Extended data is unimplemented and never present.
|
|
</li><li>
|
|
The current implementation does not pack multiple fragments into a single packet;
|
|
the number of fragments is always 0 or 1.
|
|
</li><li>
|
|
As currently implemented, maximum fragments is 64
|
|
(maximum fragment number = 63).
|
|
</li><li>
|
|
As currently implemented, maximum fragment size is of course
|
|
less than the MTU.
|
|
</li><li>
|
|
Take care not to exceed the maximum MTU even if there is a large number of
|
|
ACKs to send.
|
|
</li><li>
|
|
The protocol allows zero-length fragments but there's no reason to send them.
|
|
</li><li>
|
|
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.
|
|
</li><li>
|
|
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.
|
|
</li><li>
|
|
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.
|
|
</li></ul>
|
|
|
|
|
|
|
|
<h3 id="peerTest">PeerTest (type 7)</h3>
|
|
<p>
|
|
See <a href="{{ site_url('docs/transport/ssu') }}#peerTesting">the SSU overview page</a> for details.
|
|
</p>
|
|
|
|
<table border="1">
|
|
<tr><td align="right" valign="top"><b>Peer:</b></td>
|
|
<td>Any</td></tr>
|
|
<tr><td align="right" valign="top"><b>Data:</b></td>
|
|
<td><ul>
|
|
<li>4 byte nonce</li>
|
|
<li>1 byte IP address size</li>
|
|
<li>that many byte representation of Alice's IP address</li>
|
|
<li>2 byte Alice's port number</li>
|
|
<li>Alice's 32-byte introduction key</li>
|
|
<li>N bytes, currently uninterpreted</li>
|
|
</ul></td></tr>
|
|
<tr><td align="right" valign="top"><b>Key used:</b></td>
|
|
<td>introKey (or sessionKey if the connection has already been established)</td></tr>
|
|
</table>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| 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 |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<p>
|
|
Typical size including header, in current implementation: 80 bytes
|
|
</p>
|
|
|
|
<h4>Notes</h4>
|
|
<ul><li>
|
|
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.
|
|
</li><li>
|
|
When sent by Bob or Charlie, IP and port are present, and
|
|
IP address is always 4 bytes in the current implementation.
|
|
IPv6 testing is not currently supported.
|
|
</li><li>
|
|
IPv6 Notes:
|
|
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.
|
|
</li></ul>
|
|
|
|
<h3 id="holePunch">HolePunch</h3>
|
|
<p>
|
|
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.
|
|
</p>
|
|
|
|
|
|
<h2><a name="sampleDatagrams">Sample datagrams</a></h2>
|
|
|
|
<b>Minimal data message (no fragments, no ACKs, no NACKs, etc)</b><br />
|
|
<i>(Size: 39 bytes)</i>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| MAC |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| IV |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|flag| time |flag|#frg| |
|
|
+----+----+----+----+----+----+----+ |
|
|
| padding to fit a full AES256 block |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
<b>Minimal data message with payload</b><br />
|
|
<i>(Size: 46+fragmentSize bytes)</i>
|
|
|
|
{% highlight lang='dataspec' %}
|
|
+----+----+----+----+----+----+----+----+
|
|
| MAC |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| IV |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|flag| time |flag|#frg|
|
|
+----+----+----+----+----+----+----+----+
|
|
messageId | frag info | |
|
|
+----+----+----+----+----+----+ |
|
|
| that many bytes of fragment data |
|
|
. . .
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
{% endhighlight %}
|
|
|
|
|
|
{% endblock %}
|