Next: , Previous: , Up: Developer manual  


Transport protocol

[PktLen] + ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE) +
    AUTH(ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE))

All transport and handshake messages are indistinguishable from pseudo random noise, except when using TCP connections.

PktLen is used only with TCP connections. It is big-endian uint16 length of the whole packet (except PktLen itself).

SERIAL is message’s serial number. Odds are reserved for client(->server) messages, evens for server(->client) messages.

ENCn is XTEA block cipher algorithm used here as PRP (pseudo random permutation function) to obfuscate SERIAL. Plaintext SERIAL state is kept in peers internal state, but encrypted before transmission. XTEA is compact and fast enough. Salsa20 is PRF function and requires much more code to create PRP from it.

XTEA’s encryption key is the first 128-bit of Salsa20’s output with established common key and zero nonce (message nonces start from 1).

ENC is Salsa20 stream cipher, with established session KEY and obfuscated SERIAL used as a nonce. First 256 bits of Salsa20’s output is used as Poly1305 authentication key, next 256 bits are ignored. All remaining output is XORed with the data, encrypting it.

DATA_SIZE is big-endian uint16 storing length of the DATA.

NOISE is optional. It is just some junk data, intended to fill up packet to MTU size. This is useful for concealing payload packets length.

AUTH is Poly1305 authentication function. First 256 bits of Salsa20 output are used as a one-time key for AUTH.

To prevent replay attacks we must remember received SERIALs and if meet one, then drop it. Basically we could just store latest number and check if received one is greater, but because of UDP packets reordering this can lead to valid packets dropping and overall performance degradation. We store up to 256 seen nonces in hash structure, in two swapping buckets.


Next: , Previous: , Up: Developer manual