mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-09 13:10:25 +00:00
295 lines
7.1 KiB
Plaintext
295 lines
7.1 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 supports by default an authenticated message transport over a
|
|
datagram based network layer.
|
|
|
|
|
|
protocol phases:
|
|
|
|
first phase: proof of flow
|
|
second phase: session handshake
|
|
thrid phase: data transmission
|
|
|
|
proof of flow:
|
|
|
|
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.
|
|
|
|
|
|
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])
|
|
|
|
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 ed25519 public 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 ed25519 public key of sender, A_k>
|
|
<0-N bytes discarded>
|
|
<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 ed25519 public key of sender, A_k>
|
|
<0-N bytes discarded>
|
|
<last 64 bytes signature of unobsfucated packet, Z>
|
|
|
|
command 'E' - establish wire session
|
|
|
|
establish an encrypted session using a flow id
|
|
|
|
<outer-header>
|
|
<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>
|
|
|
|
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 = '0x00' * 32
|
|
msg.Z = MDS(msg, tx_K)
|
|
|
|
data tranmission:
|
|
|
|
inner message format of X (after decryption):
|
|
|
|
header:
|
|
|
|
<1 byte protocol version>
|
|
<1 byte command>
|
|
|
|
|
|
command: 'k' (keep alive)
|
|
|
|
tell other side to acknoledge they are alive
|
|
|
|
<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
|
|
|
|
<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.
|
|
|
|
<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
|
|
|
|
<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
|
|
|
|
<header>
|
|
<2 bytes increase TX rate by this many 1024 bytes per second>
|
|
<4 bytes milliseconds speedup lifetime>
|
|
<remaining bytes discarded>
|
|
|
|
command: 't' (transmit data)
|
|
|
|
transmit a message to a peer
|
|
|
|
if this fragment is not addressed to us we route it to the neighboor
|
|
with the shortest route to the recipiant as advertised by all neighboors.
|
|
|
|
<header>
|
|
<32 bytes public identity key of recipiant>
|
|
<32 bytes public identity key of sender>
|
|
<24 bytes nounce, N>
|
|
<N bytes encrypted message, X>
|
|
<last 32 bytes keyed hash, Z>
|
|
|
|
encrypted via:
|
|
|
|
K = EDDH(recipiant, sender)
|
|
X = SE(msg, K, N)
|
|
Z = MDS(X, K)
|
|
|
|
encrypted message format:
|
|
|
|
<1 byte version, currently 0>
|
|
<1 byte number of acks following, aN>
|
|
<8 * aN bytes acks>
|
|
<4 byte sequence number of fragment or 0 if no fragment is included>
|
|
<2 byte 16 byte block offset in message of this fragment if it is included>
|
|
<remaining bytes fragment data aligned to 16 bytes>
|
|
<discard anything not aligned to 16 bytes>
|
|
|
|
ack format:
|
|
|
|
<4 byte message sequence number>
|
|
<1 byte reserved current set to 0>
|
|
<1 byte ack counter (number of acks sent for the corrisponding message)>
|
|
<1 byte bitmask fragments selective ack (msb is fragment 0, lsb is fragment 7)>
|
|
<1 byte bitmask fragments posative ack (msb is fragment 0, lsb is fragment 7)>
|
|
|
|
|
|
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
|
|
|
|
<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
|
|
|
|
<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
|
|
|
|
<header>
|
|
<1 byte protocol version selected>
|
|
<1 byte protocol version highest we support>
|
|
<remaining bytes discarded>
|