mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-17 15:25:35 +00:00
310 lines
7.8 KiB
Plaintext
310 lines
7.8 KiB
Plaintext
Wire Protocol (version 1)
|
|
|
|
|
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
|
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
|
document are to be interpreted as described in RFC 2119 [RFC2119].
|
|
|
|
LLARP's wire protocol is Internet Wire Protocol (IWP)
|
|
|
|
The main goal of iwp is to provide an authenticated encrypted
|
|
reliable semi-ordered durable datagram transfer protocol supporting
|
|
datagrams of larger size than link mtu.
|
|
|
|
in iwp there is an initiator who initiates a session to a recipiant.
|
|
|
|
iwp has 3 phases. the first phase is the proof of flow phase.
|
|
the second is a session handshake phase, the third is data transmission.
|
|
|
|
proof of flow:
|
|
|
|
the purpose of the proof of flow phase is to verify the existence
|
|
of the initiator's endpoint.
|
|
|
|
At any time before the data transfer phase a reject message
|
|
is sent the session is reset.
|
|
|
|
Alice (A) is the sender and Bob (B) is the recipiant.
|
|
|
|
A asks for a flow id from B.
|
|
|
|
B MAY send a flow id to A or MAY reject the message from A.
|
|
|
|
session handshake:
|
|
|
|
an encrypted session is established using establish wire session messages
|
|
using a newly created flow id.
|
|
|
|
message format:
|
|
|
|
there are 2 layers in this protocol, outer messages and inner messages.
|
|
|
|
outer messages are sent in plaintext and / or obfsucated with symettric
|
|
encryption using a preshared key.
|
|
|
|
inner messages are inside an encrypted and authenticated envelope
|
|
wrapped by an outer messages, which is always a data tranmssion message.
|
|
|
|
outer message format:
|
|
|
|
every outer message MAY be obfsucated via symettric encryption for dpi
|
|
resistance reasons, this is not authenticated encryption.
|
|
|
|
the message is first assumed to be sent in clear first.
|
|
if parsing of clear variant fails then the recipiant MUST fall back to assuming
|
|
the protocol is in obfuscated mode.
|
|
|
|
|
|
<16 bytes nounce, n>
|
|
<remaining bytes obsfucated, m>
|
|
|
|
obfuscated via:
|
|
|
|
K = HS(B_k)
|
|
N = HS(n + K)
|
|
X = SD(K, m, N[0:24])
|
|
|
|
where
|
|
B_k is the long term identity public key of the recipient.
|
|
HS is blake2 256 bit non keyed hash
|
|
SD is xchacha20 symettric stream cipher (decryption)
|
|
|
|
outer-header:
|
|
|
|
<1 byte command>
|
|
<1 byte reserved set to 0x3d>
|
|
|
|
command 'O' - obtain flow id
|
|
|
|
obtain a flow id
|
|
|
|
<outer-header>
|
|
<6 magic bytes "netid?">
|
|
<8 bytes netid, I>
|
|
<8 bytes timestamp milliseconds since epoch, T>
|
|
<32 bytes public identity key of sender, A_k>
|
|
<0-N bytes discarded>
|
|
<last 64 bytes signature of unobfuscated packet, Z>
|
|
|
|
the if the network id differs from the current network's id a reject message
|
|
MUST be sent
|
|
|
|
MUST be replied to with a message rejected or a give handshake cookie
|
|
|
|
command 'G' - give flow id
|
|
|
|
<outer-header>
|
|
<6 magic bytes "netid!">
|
|
<16 bytes new flow id>
|
|
<32 bytes public identiy key of sender, A_k>
|
|
<0-N bytes ignored but included in signature>
|
|
<last 64 bytes signature of unobfsucated packet, Z>
|
|
|
|
after recieving a give flow id message a session negotiation can happen with that flow id.
|
|
|
|
command 'R' - flow rejected
|
|
|
|
reject new flow
|
|
|
|
<outer-header>
|
|
<14 ascii bytes reason for rejection null padded>
|
|
<8 bytes timestamp>
|
|
<32 bytes public identity key of sender, A_k>
|
|
<0-N bytes ignored but included in signature>
|
|
<last 64 bytes signature of unobsfucated packet, Z>
|
|
|
|
command 'E' - establish wire session
|
|
|
|
establish an encrypted session using a flow id
|
|
|
|
<outer-header>
|
|
<2 bytes 0x0a 0x0d>
|
|
<4 bytes flags, F>
|
|
<16 bytes flow id, B>
|
|
<32 bytes ephemeral public encryption key, E>
|
|
<8 bytes packet counter starting at 0>
|
|
<optional 32 bytes authenticated credentials, A>
|
|
<last 64 bytes signature of unobfuscated packet using identity key, Z>
|
|
|
|
|
|
F is currently set to all zeros
|
|
|
|
every time we try establishing a wire session we increment the counter
|
|
by 1 for the next message we send.
|
|
|
|
when we get an establish wire session message
|
|
we reply with an establish wire session message with counter being counter + 1
|
|
|
|
if A is provided that is interpreted as being generated via:
|
|
|
|
h0 = HS('<insert some password here>')
|
|
h1 = EDDH(us, them)
|
|
A = HS(B + h0 + h1)
|
|
|
|
each side establishes their own rx key using this message.
|
|
when each side has both established thier rx key data can be transmitted.
|
|
|
|
command 'D' - encrypted data transmission
|
|
|
|
transmit encrypted data on a wire session
|
|
|
|
<outer-header>
|
|
<16 bytes flow-id, F>
|
|
<24 bytes nonce, N>
|
|
<N encrypted data, X>
|
|
<last 32 bytes keyed hash of entire payload, Z>
|
|
|
|
|
|
B is the flow id from the recipiant (from outer header)
|
|
N is a random nounce
|
|
X is encrypted data
|
|
Z is keyed hash of entire message
|
|
|
|
Z is generated via:
|
|
|
|
msg.Z = MDS(outer-header + F + N + X, tx_K)
|
|
|
|
data tranmission:
|
|
|
|
inner message format of X (after decryption):
|
|
|
|
inner header:
|
|
|
|
<1 byte protocol version>
|
|
<1 byte command>
|
|
|
|
|
|
command: 'k' (keep alive)
|
|
|
|
tell other side to acknoledge they are alive
|
|
|
|
<inner header>
|
|
<2 bytes resevered, set to 0>
|
|
<2 bytes attempt counter, set to 0 and incremented every retransmit, reset when we get a keepalive ack>
|
|
<2 bytes milliseconds ping timeout>
|
|
<8 bytes current session TX limit in bytes per second>
|
|
<8 bytes current session RX use in bytes per second>
|
|
<8 bytes milliseconds since epoch our current time>
|
|
<remaining bytes discarded>
|
|
|
|
command: 'l' (keep alive ack)
|
|
|
|
acknolege keep alive message
|
|
|
|
<inner header>
|
|
<6 bytes reserved, set to 0>
|
|
<8 bytes current session RX limit in bytes per second>
|
|
<8 bytes current session TX use in bytes per second>
|
|
<8 bytes milliseconds since epoch our current time>
|
|
<remaining bytes discarded>
|
|
|
|
|
|
command: 'n' (advertise neighboors)
|
|
|
|
tell peer about neighboors, only sent by non service nodes to other non service
|
|
nodes.
|
|
|
|
<inner header>
|
|
<route between us and them>
|
|
<0 or more intermediate routes>
|
|
<route from a service node>
|
|
|
|
route:
|
|
|
|
<1 byte route version (currently 0)>
|
|
<1 byte flags, lsb set indicates src is a service node>
|
|
<2 bytes latency in ms>
|
|
<2 bytes backpressure>
|
|
<2 bytes number of connected peers>
|
|
<8 bytes publish timestamp ms since epoch>
|
|
<32 bytes pubkey neighboor>
|
|
<32 bytes pubkey src>
|
|
<64 bytes signature of entire route signed by src>
|
|
|
|
command: 'c' (congestion)
|
|
|
|
tell other side to slow down
|
|
|
|
<inner header>
|
|
<2 bytes reduce TX rate by this many 1024 bytes per second>
|
|
<4 bytes milliseconds slowdown lifetime>
|
|
<remaining bytes discarded>
|
|
|
|
command: 'd' (anti-congestion)
|
|
|
|
tell other side to speed up
|
|
|
|
<inner header>
|
|
<2 bytes increase TX rate by this many 1024 bytes per second>
|
|
<4 bytes milliseconds speedup lifetime>
|
|
<remaining bytes discarded>
|
|
|
|
|
|
command: 's' (start transmission)
|
|
|
|
initate the transmission of a message to the remote peer
|
|
|
|
<inner header>
|
|
<1 byte flags F>
|
|
<1 byte reserved R set to zero>
|
|
<2 bytes total size of full message>
|
|
<4 bytes sequence number S>
|
|
<32 bytes blake2 hash of full message>
|
|
<N remaining bytes first fragment of message>
|
|
|
|
if F lsb is set then there is no further fragments
|
|
|
|
command: 't' (continued transmission)
|
|
|
|
continue transmission of a bigger message
|
|
|
|
<inner header>
|
|
<1 byte flags F>
|
|
<1 bytes reserved R set to zero>
|
|
<2 bytes 16 byte block offset in message>
|
|
<4 bytes sequence number S>
|
|
<N remaining bytes fragment of message aligned to 16 bytes>
|
|
<remaining bytes not aligned to 16 bytes discarded>
|
|
|
|
command: 'q' (acknoledge transmission)
|
|
|
|
acknoledges a transmitted message
|
|
|
|
|
|
|
|
command: 'r' (rotate keys)
|
|
|
|
inform remote that their RX key should be rotated
|
|
|
|
given alice(A) sends this message to bob(B) the new keys are computed as such:
|
|
|
|
n_K = TKE(K, B_e, K_seed, N)
|
|
|
|
A.tx_K = n_K
|
|
B.rx_K = n_K
|
|
|
|
<inner header>
|
|
<2 bytes milliseconds lifetime of old keys, retain them for this long and then discard>
|
|
<4 bytes reserved, set to 0>
|
|
<32 bytes key exchange nounce, N>
|
|
<32 bytes next public encryption key, K>
|
|
<remaining bytes discarded>
|
|
|
|
command: 'u' (upgrade)
|
|
|
|
request protocol upgrade
|
|
|
|
<inner header>
|
|
<1 byte protocol min version to upgrade to>
|
|
<1 byte protocol max version to upgrade to>
|
|
<remaining bytes discarded>
|
|
|
|
command: 'v' (version upgrade)
|
|
|
|
sent in response to upgrade message
|
|
|
|
<inner header>
|
|
<1 byte protocol version selected>
|
|
<1 byte protocol version highest we support>
|
|
<remaining bytes discarded>
|