$Id: udp.html,v 1.7 2005/03/29 19:20:07 jrandom Exp $
The goal of this protocol is to provide secure, authenticated, semireliable, and unordered message delivery, exposing only a minimal amount of data easily discernible to third parties. It should support high degree communication as well as TCP-friendly congestion control, and may include PMTU detection. It should be capable of efficiently moving bulk data at rates sufficient for home users. In addition, it should support techniques for addressing network obstacles, like most NATs or firewalls.
To contact an SSU peer, one of two sets of information is necessary: a direct address, for when the peer is publicly reachable, or an indirect address, for using a third party to introduce the peer. There is no restriction on the number of addresses a peer may have.
Direct: udp://host:port/introKey Indirect: udp://tag@relayhost:port/relayIntroKey/targetIntroKey
These introduction keys are delivered through an external channel and must be used when establishing a session key. For the indirect address, the peer must first contact the relayhost and ask them for an introduction to the peer known at that relayhost under the given tag. If possible, the relayhost sends a message to the addressed peer telling them to contact the requesting peer, and also gives the requesting peer the IP and port on which the addressed peer is located. In addition, the peer establishing the connection must already know the public keys of the peer they are connecting to (but not necessary to any intermediary relay peer).
All UDP datagrams begin with a MAC and an IV, followed by a variable size payload encrypted with the appropriate key. The MAC used is HMAC-SHA256, truncated to 16 bytes, while the key is a full AES256 key. The specific construct of the MAC is the first 16 bytes from:
HMAC-SHA256(payload || IV || payloadLength, macKey)
The payload itself is AES256/CBC encrypted with the IV and the sessionKey, with replay prevention addressed within its body, explained below.
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:
bits 0-3: payload type bit 4: rekey? bit 5: extended options included bits 6-7: reserved
If the rekey flag is set, 64 bytes of keying material follow the timestamp. If the extended options flag is set, a one byte option size value is appended to, followed by that many extended option bytes, which are currently uninterpreted.
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.
Header: 37+ bytes +----+----+----+----+----+----+----+----+ | MAC | | | +----+----+----+----+----+----+----+----+ | IV | | | +----+----+----+----+----+----+----+----+ |flag| time | (optionally | +----+----+----+----+----+ | | this may have 64 byte keying material | | and/or a one+N byte extended options) | +---------------------------------------|
Peer: | Alice to Bob |
Data: |
|
Key used: | introKey |
+----+----+----+----+----+----+----+----+ | X, as calculated from DH | | | . . . | | +----+----+----+----+----+----+----+----+ |size| that many byte IP address (4-16) | +----+----+----+----+----+----+----+----+ | arbitrary amount | | of uninterpreted data | . . . | | +----+----+----+----+----+----+----+----+
Peer: | Bob to Alice |
Data: |
|
Key used: | introKey for the data through the pad bytes, and the sessionKey for the DSA signature |
+----+----+----+----+----+----+----+----+ | Y, as calculated from DH | | | . . . | | +----+----+----+----+----+----+----+----+ |size| that many byte IP address (4-16) | +----+----+----+----+----+----+----+----+ | Port (A)| (pad to 16 byte boundary) | +----+----+----+----+----+----+----+----+ | public relay tag | DSA signature | +----+----+----+----+ | | | | | | | | | + +----+----+----+----+ | | arbitrary amount | +----+----+----+----+ | | of uninterpreted data | +----+----+----+----+----+----+----+----+
Peer: | Bob to Alice |
Data: |
|
Key used: | sessionKey |
Fragment 1 through N-1 +----+----+----+----+----+----+----+----+ |info| fragment of Alice's full | +----+ | | identity keys | . . . | | +----+----+----+----+----+----+----+----+ Fragment N: +----+----+----+----+----+----+----+----+ |info| fragment of Alice's full | +----+ | | identity keys | . . . | | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted | | data, up from the end of the | | identity key to 40 bytes prior to | | end of the current packet | +----+----+----+----+----+----+----+----+ | DSA signature | | | | | | | | | +----+----+----+----+----+----+----+----+
Peer: | Alice to Bob |
Data: |
|
Key used: | introKey (or sessionKey, if Alice/Bob is established) |
+----+----+----+----+----+----+----+----+ | relay tag |size| that many | +----+----+----+----+----+ +----| | bytes making up Bob's IP address |size| +----+----+----+----+----+----+----+----+ | that many bytes making up Alice's IP | +----+----+----+----+----+----+----+----+ | Port (A)|size| that many challenge | +----+----+----+ | | bytes to be delivered to Charlie | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| +----+----+----+----+----+----+----+----+
Peer: | Bob to Alice |
Data: |
|
Key used: | introKey (or sessionKey, if Alice/Bob is established) |
+----+----+----+----+----+----+----+----+ |size| that many bytes making up | +----+ +----+----+ | Charlie's IP address | Port (C)| +----+----+----+----+----+----+----+----+ |size| that many bytes making up | +----+ +----+----+ | Alice's IP address | Port (A)| +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| +----+----+----+----+----+----+----+----+
Peer: | Bob to Charlie |
Data: |
|
Key used: | sessionKey |
+----+----+----+----+----+----+----+----+ |size| that many bytes making up | +----+ +----+----+ | Charlie's IP address | Port (C)| +----+----+----+----+----+----+----+----+ |size| that many bytes of challenge | +----+ | | data relayed from Alice | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| +----+----+----+----+----+----+----+----+
Peer: | Any |
Data: |
|
Key used: | sessionKey |
+----+----+----+----+----+----+----+----+ |flag| (additional headers, determined | +----+ | | by the flags, such as ACKs, NACKs, or | | simple rate of full ACKs) | +----+----+----+----+----+----+----+----+ |#frg| messageId |info|fragSize | +----+----+----+----+----+----+----+----+ | that many bytes of fragment data | . . . | | +----+----+----+----+----+----+----+----+ | messageId |info|fragSize | | +----+----+----+----+----+----+----+ | | that many bytes of fragment data | . . . | | +----+----+----+----+----+----+----+----+ | messageId |info|fragSize | | +----+----+----+----+----+----+----+ | | that many bytes of fragment data | . . . | | +----+----+----+----+----+----+----+----+ | arbitrary amount of uninterpreted data| +----+----+----+----+----+----+----+----+
SSU's need for only semireliable delivery, TCP-friendly operation, and the capacity for high throughput allows a great deal of latitude in congestion control. The congestion control algorithm outlined below is meant to be both efficient in bandwidth as well as simple to implement.
Data is transmitted in volleys of up to 1 second, sending N bytes within P packets. The volley a packet is a part of is defined by the second field in the encrypted payload. The receiver of a volley should send back a full set of ACKs and NACKs for message IDs received in the previous volley - these ACKs and NACKs should be included in all messages sent until either the volley changes again or the the receiver gets a message in the current volley stating that the previous ACKs are no longer required. Subsequent responses from the receiver in the current volley should not contain the ACKs.
After receiving a volley with at least one data message fragment, the receiver should send back at least one message with the ACKs. Each time subsequent messages arrive on the current volley with the "request previous ACKs" flag set, if no messages in the current volley have arrived without that being set the receiver should send back a data message with the ACKs, if the receiver has the bandwidth to do so.
The number of bytes sent in each volley (N) should be initialized as 8192 bytes (an arbitrarily high value). At the beginning of a volley, if the ACKs/NACKs received for the volley two periods back contain any NACKs, N should be set to the minimum of N and the number of bytes fully ACKed, though no less than 1/2 of N. If there were no NACKs and all of the messages sent were ACKed, N is increased by the average packet size. If a message is received in a volley with the explicit congestion notification bit set, at the beginning of the next volley N is set to 1/2 N.
Messages which are partially sent or NACKed have the unsent fragments transmitted in the next volley, unless the message expiration occurs, in which case it is dropped entirely.
The simplest possible implementation does not need to pad the packets to any particular size, but instead just places a single message fragment into a packet and sends it off (careful not to exceed the MTU). A more efficient strategy would be to bundle multiple message fragments into the same packet, so long as it doesn't exceed the MTU, but this is not necessary. Eventually, a set of fixed packet sizes may be appropriate to further hide the data fragmentation to external adversaries, but the tunnel, garlic, and end to end padding should be sufficient for most needs until then.
Alice Bob Data 1, volley 1, no ACKs---------> Data 2, volley 1, no ACKs---------> Data 3, volley 1, no ACKs---------> Data 4, volley 1, no ACKs---------> Data 5, volley 2, want ACKs-------> Data 6, volley 2, want ACKs-------> // want ACK since ACKs not received <------------------ACK 1, 2, 3, 4 // automatically sent <------------------ACK 1, 2, 3, 4 // sent due to Data 6 Data 7, volley 2, no ACKs---------> // no further ACKs required Data 8, volley 2, no ACKs---------> Data 9, volley 3, want ACKs-------> // new volley, we want ACKs! <------------------ACK 5, 6, 7, 8 // automatically sent Data 10, volley 3, no ACKs---------> Data 11, volley 3, no ACKs---------> Data 12, volley 3, no ACKs---------> <------------------ACK 9, 10, 11, 12 // automatically sentBidirectional transfer
Alice Bob Data 1, volley 1, no ACKs-------------------------> <-----------------------------Data 1, volley 1, no ACKs Data 2, volley 1, no ACKs-------------------------> <-----------------------------Data 2, volley 1, no ACKs Data 3, volley 1, no ACKs-------------------------> <-----------------------------Data 3, volley 1, no ACKs Data 4, volley 1, no ACKs-------------------------> <-----------------------------Data 4, volley 1, no ACKs Data 5, volley 2, want ACKs, ACK 1, 2, 3, 4-------> // new volley, send ACKs <---------------Data 5, volley 2, no ACKs, ACK 1, 2, 3, 4 // received ACKs, no need to ask for them Data 6, volley 2, no ACKs-------------------------> <-----------------------------Data 6, volley 2, no ACKs Data 7, volley 2, no ACKs-------------------------> <-----------------------------Data 8, volley 2, no ACKs Data 8, volley 2, no ACKs-------------------------> <-----------------------------Data 9, volley 2, no ACKs ACK 5, 6, 7, 8, 9---------------------------------> <-----------------------------------ACK 5, 6, 7, 8
All encryption used is AES256/CBC with 32 byte keys and 16 byte IVs. The MAC and session keys are negotiated as part of the DH exchange, used for the HMAC and encryption, respectively. Prior to the DH exchange, the publicly knowable introKey is used for the MAC and encryption.
When using the introKey, both the initial message and any subsequent reply use the introKey of the responder (Bob) - the responder does not need to know the introKey of the requestor (Alice). The DSA signing key used by Bob should already be known to Alice when she contacts him, though Alice's DSA key may not already be known by Bob.
Upon receiving a message, the receiver checks the from IP address with any established sessions - if there is one or more matches, those session's MAC keys are tested sequentially in the HMAC. If none of those verify or if there are no matching IP addresses, the receiver tries their introKey in the MAC. If that does not verify, the packet is dropped. If it does verify, it is interpreted according to the message type, though if the receiver is overloaded, it may be dropped anyway.
If Alice and Bob have an established session, but Alice loses the keys for some reason and she wants to contact Bob, she may at any time simply establish a new session through the SessionRequest and related messages. If Bob has lost the key but Alice does not know that, she will first attempt to prod him to reply, by sending a DataMessage with the wantReply flag set, and if Bob continually fails to reply, she will assume the key is lost and reestablish a new one.
For the DH key agreement, RFC3526 2048bit MODP group (#14) is used:
p = 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } g = 2
The DSA p, q, and g are shared according to the scope of the identity which created them.
Replay prevention at the SSU layer occurs by rejecting packets with exceedingly old timestamps or those which reuse an IV. To detect duplicate IVs, a sequence of Bloom filters are employed to "decay" periodically so that only recently added IVs are detected.
The messageIds used in DataMessages are defined at layers above the SSU transport and are passed through transparently. These IDs are not in any particular order - in fact, they are likely to be entirely random. The SSU layer makes no attempt at messageId replay prevention - higher layers should take that into account.
Alice Bob SessionRequest---------------------> <---------------------SessionCreated SessionConfirmed-------------------> SessionConfirmed-------------------> SessionConfirmed-------------------> SessionConfirmed-------------------> <--------------------------Data
Alice Bob Charlie RelayRequest ----------------------> <--------------RelayResponse RelayIntro-----------> <--------------------------------------------Data (ignored) SessionRequest--------------------------------------------> <--------------------------------------------SessionCreated SessionConfirmed------------------------------------------> SessionConfirmed------------------------------------------> SessionConfirmed------------------------------------------> SessionConfirmed------------------------------------------> <---------------------------------------------------Data
+----+----+----+----+----+----+----+----+ | MAC | | | +----+----+----+----+----+----+----+----+ | IV | | | +----+----+----+----+----+----+----+----+ |flag| time |flag|#frg| | +----+----+----+----+----+----+----+ | | padding to fit a full AES256 block | +----+----+----+----+----+----+----+----+Minimal data message with payload
+----+----+----+----+----+----+----+----+ | MAC | | | +----+----+----+----+----+----+----+----+ | IV | | | +----+----+----+----+----+----+----+----+ |flag| time |flag|#frg| +----+----+----+----+----+----+----+----+ messageId |info| fragSize| | +----+----+----+----+----+----+ | | that many bytes of fragment data | . . . | | +----+----+----+----+----+----+----+----+