class PacketBuilder extends Object
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(payload || IV || (payloadLength ^ protocolVersion), macKey)
The protocol version is currently 0.
The payload itself 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 in 2s complement.
The protocolVersion is a 2 byte unsigned integer in 2s complement, and 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.
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:
Bit order: 76543210 bits 7-4: payload type bit 3: rekey? bit 2: extended options included bits 1-0: 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.
NOTE: Rekeying is currently unimplemented.
Header: 37+ bytes +----+----+----+----+----+----+----+----+ | MAC | | | +----+----+----+----+----+----+----+----+ | IV | | | +----+----+----+----+----+----+----+----+ |flag| time | (optionally | +----+----+----+----+----+ | | this may have 64 byte keying material | | and/or a one+N byte extended options) | +---------------------------------------|
Modifier and Type | Class and Description |
---|---|
static class |
PacketBuilder.Fragment
Class for passing multiple fragments to buildPacket()
|
Modifier and Type | Field and Description |
---|---|
static int |
ABSOLUTE_MAX_ACKS
one byte field
|
static int |
DATA_HEADER_SIZE
not including acks.
|
static int |
FRAGMENT_HEADER_SIZE
4 byte msg ID + 3 byte fragment info
|
static int |
HEADER_SIZE
if no extended options or rekey data, which we don't support = 37
|
static int |
IP_HEADER_SIZE
IPv4 only
|
static int |
IPV6_HEADER_SIZE |
static int |
MIN_DATA_PACKET_OVERHEAD
74
|
static int |
MIN_IPV6_DATA_PACKET_OVERHEAD
94
|
(package private) static int |
TYPE_ACK |
(package private) static int |
TYPE_CONF |
(package private) static int |
TYPE_CREAT |
(package private) static int |
TYPE_FIRST
For debugging and stats only - does not go out on the wire.
|
(package private) static int |
TYPE_INTRO |
(package private) static int |
TYPE_PUNCH |
(package private) static int |
TYPE_RESP |
(package private) static int |
TYPE_RREQ |
(package private) static int |
TYPE_SREQ |
(package private) static int |
TYPE_TBC |
(package private) static int |
TYPE_TCB |
(package private) static int |
TYPE_TFA |
(package private) static int |
TYPE_TTA |
static int |
UDP_HEADER_SIZE
Same for IPv4 and IPv6
|
Constructor and Description |
---|
PacketBuilder(RouterContext ctx,
UDPTransport transport) |
Modifier and Type | Method and Description |
---|---|
UDPPacket |
buildACK(PeerState peer,
List<ACKBitfield> ackBitfields)
Build the ack packet.
|
UDPPacket |
buildHolePunch(InetAddress to,
int port)
Creates an empty unauthenticated packet for hole punching.
|
UDPPacket |
buildPacket(byte[] data,
InetAddress to,
int port)
TESTING ONLY.
|
UDPPacket |
buildPacket(List<PacketBuilder.Fragment> fragments,
PeerState peer,
List<Long> ackIdsRemaining,
int newAckCount,
List<ACKBitfield> partialACKsRemaining) |
UDPPacket |
buildPacket(OutboundMessageState state,
int fragment,
PeerState peer,
List<Long> ackIdsRemaining,
int newAckCount,
List<ACKBitfield> partialACKsRemaining)
This builds a data packet (PAYLOAD_TYPE_DATA).
|
UDPPacket |
buildPeerTestFromAlice(InetAddress toIP,
int toPort,
SessionKey toIntroKey,
long nonce,
SessionKey aliceIntroKey)
Build a packet as if we are Alice and we either want Bob to begin a
peer test or Charlie to finish a peer test.
|
UDPPacket |
buildPeerTestFromAlice(InetAddress toIP,
int toPort,
SessionKey toCipherKey,
SessionKey toMACKey,
long nonce,
SessionKey aliceIntroKey)
Build a packet as if we are Alice and we either want Bob to begin a
peer test or Charlie to finish a peer test.
|
UDPPacket |
buildPeerTestToAlice(InetAddress aliceIP,
int alicePort,
SessionKey aliceIntroKey,
SessionKey charlieIntroKey,
long nonce)
Build a packet as if we are either Bob or Charlie and we are helping test Alice.
|
UDPPacket |
buildPeerTestToBob(InetAddress bobIP,
int bobPort,
InetAddress aliceIP,
int alicePort,
SessionKey aliceIntroKey,
long nonce,
SessionKey bobCipherKey,
SessionKey bobMACKey)
Build a packet as if we are Charlie sending Bob a packet verifying that we will help test Alice.
|
UDPPacket |
buildPeerTestToCharlie(InetAddress aliceIP,
int alicePort,
SessionKey aliceIntroKey,
long nonce,
InetAddress charlieIP,
int charliePort,
SessionKey charlieCipherKey,
SessionKey charlieMACKey)
Build a packet as if we are Bob sending Charlie a packet to help test Alice.
|
UDPPacket |
buildPing(PeerState peer)
An ACK packet with no acks.
|
(package private) UDPPacket |
buildRelayIntro(RemoteHostId alice,
PeerState charlie,
UDPPacketReader.RelayRequestReader request) |
List<UDPPacket> |
buildRelayRequest(UDPTransport transport,
OutboundEstablishState state,
SessionKey ourIntroKey)
build intro packets for each of the published introducers
|
(package private) UDPPacket |
buildRelayResponse(RemoteHostId alice,
PeerState charlie,
long nonce,
SessionKey cipherKey,
SessionKey macKey) |
UDPPacket[] |
buildSessionConfirmedPackets(OutboundEstablishState state,
RouterIdentity ourIdentity)
Build a new series of SessionConfirmed packets for the given peer,
encrypting it as necessary.
|
UDPPacket |
buildSessionCreatedPacket(InboundEstablishState state,
int externalPort,
SessionKey ourIntroKey)
Build a new SessionCreated packet for the given peer, encrypting it
as necessary.
|
UDPPacket |
buildSessionDestroyPacket(InboundEstablishState peer)
Build a destroy packet, which contains a header but no body.
|
UDPPacket |
buildSessionDestroyPacket(OutboundEstablishState peer)
Build a destroy packet, which contains a header but no body.
|
UDPPacket |
buildSessionDestroyPacket(PeerState peer)
Build a destroy packet, which contains a header but no body.
|
UDPPacket |
buildSessionRequestPacket(OutboundEstablishState state)
Build a new SessionRequest packet for the given peer, encrypting it
as necessary.
|
static int |
getMaxAdditionalFragmentSize(PeerState peer,
int numFragments,
int curDataSize)
Will a packet to 'peer' that already has 'numFragments' fragments
totalling 'curDataSize' bytes fit another fragment of size 'newFragSize' ??
This doesn't leave anything for acks.
|
static final int TYPE_FIRST
static final int TYPE_ACK
static final int TYPE_PUNCH
static final int TYPE_RESP
static final int TYPE_INTRO
static final int TYPE_RREQ
static final int TYPE_TCB
static final int TYPE_TBC
static final int TYPE_TTA
static final int TYPE_TFA
static final int TYPE_CONF
static final int TYPE_SREQ
static final int TYPE_CREAT
public static final int HEADER_SIZE
public static final int FRAGMENT_HEADER_SIZE
public static final int DATA_HEADER_SIZE
public static final int IP_HEADER_SIZE
public static final int UDP_HEADER_SIZE
public static final int MIN_DATA_PACKET_OVERHEAD
public static final int IPV6_HEADER_SIZE
public static final int MIN_IPV6_DATA_PACKET_OVERHEAD
public static final int ABSOLUTE_MAX_ACKS
public PacketBuilder(RouterContext ctx, UDPTransport transport)
transport
- may be null for unit testing onlypublic static int getMaxAdditionalFragmentSize(PeerState peer, int numFragments, int curDataSize)
numFragments
- >= 1public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, List<Long> ackIdsRemaining, int newAckCount, List<ACKBitfield> partialACKsRemaining)
16 byte MAC 16 byte IV 1 byte flag 4 byte date 1 byte flag 1 byte explicit ack count IF included 4*n byte explict acks IF included 1 byte ack bitfield count IF included 4*n + ?? ack bitfields IF included 1 byte fragment count (always 1) 4 byte message ID 3 byte fragment info n byte fragment 0-15 bytes paddingSo ignoring the ack bitfields, and assuming we have explicit acks, it's (47 + 4*explict acks + padding) added to the fragment length.
ackIdsRemaining
- list of messageIds (Long) that should be acked by this packet.
The list itself is passed by reference, and if a messageId is
transmitted it will be removed from the list.
Not all message IDs will necessarily be sent, there may not be room.
non-null.newAckCount
- the number of ackIdsRemaining entries that are new. These must be the first
ones in the listpartialACKsRemaining
- list of messageIds (ACKBitfield) that should be acked by this packet.
The list itself is passed by reference, and if a messageId is
included, it should be removed from the list.
Full acks in this list are skipped, they are NOT transmitted.
non-null.
Not all acks will necessarily be sent, there may not be room.public UDPPacket buildPacket(List<PacketBuilder.Fragment> fragments, PeerState peer, List<Long> ackIdsRemaining, int newAckCount, List<ACKBitfield> partialACKsRemaining)
public UDPPacket buildPing(PeerState peer)
public UDPPacket buildACK(PeerState peer, List<ACKBitfield> ackBitfields)
ackBitfields
- list of ACKBitfield instances to either fully or partially ACKpublic UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey)
public UDPPacket buildSessionRequestPacket(OutboundEstablishState state)
public UDPPacket[] buildSessionConfirmedPackets(OutboundEstablishState state, RouterIdentity ourIdentity)
public UDPPacket buildSessionDestroyPacket(PeerState peer)
public UDPPacket buildSessionDestroyPacket(OutboundEstablishState peer)
public UDPPacket buildSessionDestroyPacket(InboundEstablishState peer)
public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toIntroKey, long nonce, SessionKey aliceIntroKey)
public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey)
public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce)
public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, InetAddress charlieIP, int charliePort, SessionKey charlieCipherKey, SessionKey charlieMACKey)
public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey)
public List<UDPPacket> buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey)
UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request)
UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, SessionKey cipherKey, SessionKey macKey)
public UDPPacket buildHolePunch(InetAddress to, int port)
public UDPPacket buildPacket(byte[] data, InetAddress to, int port)