|
|
|
@ -9,132 +9,128 @@ LLARP supports by default an authenticated message transport over a
|
|
|
|
|
datagram based network layer.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
outer message format:
|
|
|
|
|
|
|
|
|
|
outer-header:
|
|
|
|
|
|
|
|
|
|
<1 byte command>
|
|
|
|
|
<1 byte reserved, R, set to '=' (0x3d)>
|
|
|
|
|
<32 bytes flow id, B>
|
|
|
|
|
protocol phases:
|
|
|
|
|
|
|
|
|
|
first phase: proof of flow
|
|
|
|
|
second phase: session handshake
|
|
|
|
|
thrid phase: data transmission
|
|
|
|
|
|
|
|
|
|
command 'O' - obtain flow id
|
|
|
|
|
proof of flow:
|
|
|
|
|
|
|
|
|
|
obtain a handshake cookie
|
|
|
|
|
At any time before the data transfer phase a reject message
|
|
|
|
|
is sent the session is reset.
|
|
|
|
|
|
|
|
|
|
<outer-header>
|
|
|
|
|
<32 bytes random>
|
|
|
|
|
<8 bytes net id>
|
|
|
|
|
<remaining discarded>
|
|
|
|
|
Alice (A) is the sender and Bob (B) is the recipiant.
|
|
|
|
|
|
|
|
|
|
the if the network id differs from the current network's id a reject message
|
|
|
|
|
MUST be sent
|
|
|
|
|
A asks for a flow id from B.
|
|
|
|
|
|
|
|
|
|
MUST be replied to with a message rejected or a give handshake cookie
|
|
|
|
|
B MAY send a flow id to A or MAY reject the message from A.
|
|
|
|
|
|
|
|
|
|
command 'G' - give flow id
|
|
|
|
|
|
|
|
|
|
<outer-header>
|
|
|
|
|
<32 byte new flow-id, X>
|
|
|
|
|
<remaining discarded>
|
|
|
|
|
session handshake:
|
|
|
|
|
|
|
|
|
|
give a flow id to a remote endpoint that asks for one
|
|
|
|
|
an encrypted session is established using establish wire session messages
|
|
|
|
|
using a newly created flow id.
|
|
|
|
|
|
|
|
|
|
B is the B value from the request flow id message
|
|
|
|
|
X is a 32 byte the flow id, calcuated via:
|
|
|
|
|
|
|
|
|
|
r = RAND(32)
|
|
|
|
|
a = "::ffff.<ascii representation of ipv4 address>" + ":" + "<port number>"
|
|
|
|
|
X = HS(a + B + r + net id)
|
|
|
|
|
outer message format:
|
|
|
|
|
|
|
|
|
|
resulting message:
|
|
|
|
|
|
|
|
|
|
"G=<32 byte flowid><32 bytes new flowid>"
|
|
|
|
|
every outer message MAY be obfsucated via symettric encryption for dpi
|
|
|
|
|
resistance reasons, this is not authenticated encryption.
|
|
|
|
|
|
|
|
|
|
after recieving a give flow id message a session negotiation can happen
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
command 'R' - message rejected
|
|
|
|
|
|
|
|
|
|
<outer-header>
|
|
|
|
|
<N arbitrary bytes reason for rejection>
|
|
|
|
|
<16 bytes nounce, n>
|
|
|
|
|
<remaining bytes obsfucated, m>
|
|
|
|
|
|
|
|
|
|
reject a message on a flow id
|
|
|
|
|
obfuscated via:
|
|
|
|
|
|
|
|
|
|
B is the flow id from the recipiant
|
|
|
|
|
K = HS(B_k)
|
|
|
|
|
N = HS(n + K)
|
|
|
|
|
X = SD(K, m, N[0:24])
|
|
|
|
|
|
|
|
|
|
resulting message of reject with reason "no you"
|
|
|
|
|
outer-header:
|
|
|
|
|
|
|
|
|
|
"R=<32 byte flow-id>no you"
|
|
|
|
|
<1 byte command>
|
|
|
|
|
<1 byte reserved set to 0x3d>
|
|
|
|
|
|
|
|
|
|
command 'S' - session negotiation
|
|
|
|
|
command 'O' - obtain flow id
|
|
|
|
|
|
|
|
|
|
negotiate encrypted session
|
|
|
|
|
obtain a flow id
|
|
|
|
|
|
|
|
|
|
<outer-header>
|
|
|
|
|
<24 bytes nounce, N>
|
|
|
|
|
<encrypted session negotiation data, X>
|
|
|
|
|
<last 32 bytes keyed hash, Z>
|
|
|
|
|
|
|
|
|
|
B is the flow id from the recipiant generated by the give flow id message (from outer header)
|
|
|
|
|
N is a random nounce
|
|
|
|
|
X is encrypted session negotiation data
|
|
|
|
|
Z is a keyed hash
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
Z is generated via:
|
|
|
|
|
|
|
|
|
|
msg.Z = '0x00' * 32
|
|
|
|
|
msg.Z = MDS(msg, tx_K)
|
|
|
|
|
|
|
|
|
|
session negotiation:
|
|
|
|
|
|
|
|
|
|
The session starts out with each side having 2 session keys rx_K and tx_K for
|
|
|
|
|
decrypting inbound messages and encrypting outbound messages respectively.
|
|
|
|
|
the if the network id differs from the current network's id a reject message
|
|
|
|
|
MUST be sent
|
|
|
|
|
|
|
|
|
|
The initiator (alice) and the recipiant (bob) start out with static session keys
|
|
|
|
|
MUST be replied to with a message rejected or a give handshake cookie
|
|
|
|
|
|
|
|
|
|
k_a = HS(a.k)
|
|
|
|
|
k_b = HS(b.k)
|
|
|
|
|
command 'G' - give flow id
|
|
|
|
|
|
|
|
|
|
a.rx_K = k_a
|
|
|
|
|
b.rx_K = k_b
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
a.tx_K = k_b
|
|
|
|
|
b.tx_K = k_a
|
|
|
|
|
after recieving a give flow id message a session negotiation can happen with that flow id.
|
|
|
|
|
|
|
|
|
|
command 'R' - flow rejected
|
|
|
|
|
|
|
|
|
|
decryption is done via:
|
|
|
|
|
reject new flow
|
|
|
|
|
|
|
|
|
|
SD(msg.X, rx_K, msg.N)
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
encrypted payload is bencoded LIM (see proto_v0.txt)
|
|
|
|
|
command 'E' - establish wire session
|
|
|
|
|
|
|
|
|
|
the initiator starts out by sending a LIM a_LIM to the recipiant.
|
|
|
|
|
establish an encrypted session using a flow id
|
|
|
|
|
|
|
|
|
|
the recipiant replies with a LIM b_LIM to the initiator.
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
when the initiator gets a valid LIM from the recipiant the session keys for data
|
|
|
|
|
transmission are set to:
|
|
|
|
|
every time we try establishing a wire session we increment the counter
|
|
|
|
|
by 1 for the next message we send.
|
|
|
|
|
|
|
|
|
|
k_a = TKE(a.k, b.k, a.sk, a_LIM.n)
|
|
|
|
|
k_b = TKE(b.k, a.k, b.sk, b_LIM.n)
|
|
|
|
|
when we get an establish wire session message
|
|
|
|
|
we reply with an establish wire session message with counter being counter + 1
|
|
|
|
|
|
|
|
|
|
a.rx_K = k_a
|
|
|
|
|
b.rx_K = k_b
|
|
|
|
|
if A is provided that is interpreted as being generated via:
|
|
|
|
|
|
|
|
|
|
a.tx_K = k_b
|
|
|
|
|
b.tx_K = k_a
|
|
|
|
|
h0 = HS('<insert some password here>')
|
|
|
|
|
h1 = EDDH(us, them)
|
|
|
|
|
A = HS(B + h0 + h1)
|
|
|
|
|
|
|
|
|
|
afterwards data transmission may happen.
|
|
|
|
|
the intiator's remote address is permitted to change during data transmission.
|
|
|
|
|
remote address of the last successfully
|
|
|
|
|
each side establishes their own rx key using this message.
|
|
|
|
|
when each side has both established thier rx key data can be transmitted.
|
|
|
|
|
|
|
|
|
|
D - encrypted data transmission
|
|
|
|
|
command 'D' - encrypted data transmission
|
|
|
|
|
|
|
|
|
|
transmit encrypted data on session
|
|
|
|
|
transmit encrypted data on a wire session
|
|
|
|
|
|
|
|
|
|
<outer-header>
|
|
|
|
|
<16 bytes flow-id, F>
|
|
|
|
|
<24 bytes nonce, N>
|
|
|
|
|
<encrypted data, X>
|
|
|
|
|
<N encrypted data, X>
|
|
|
|
|
<last 32 bytes keyed hash of entire payload, Z>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -158,7 +154,7 @@ header:
|
|
|
|
|
<1 byte command>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
command: 'K' (keep alive)
|
|
|
|
|
command: 'k' (keep alive)
|
|
|
|
|
|
|
|
|
|
tell other side to acknoledge they are alive
|
|
|
|
|
|
|
|
|
@ -171,7 +167,7 @@ tell other side to acknoledge they are alive
|
|
|
|
|
<8 bytes milliseconds since epoch our current time>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
|
|
|
|
|
command: 'L' (keep alive ack)
|
|
|
|
|
command: 'l' (keep alive ack)
|
|
|
|
|
|
|
|
|
|
acknolege keep alive message
|
|
|
|
|
|
|
|
|
@ -183,7 +179,29 @@ acknolege keep alive message
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
command: 'C' (congestion)
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
@ -192,7 +210,7 @@ tell other side to slow down
|
|
|
|
|
<4 bytes milliseconds slowdown lifetime>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
|
|
|
|
|
command: 'D' (anti-congestion)
|
|
|
|
|
command: 'd' (anti-congestion)
|
|
|
|
|
|
|
|
|
|
tell other side to speed up
|
|
|
|
|
|
|
|
|
@ -201,26 +219,35 @@ tell other side to speed up
|
|
|
|
|
<4 bytes milliseconds speedup lifetime>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
|
|
|
|
|
command: 'T' (transmit)
|
|
|
|
|
command: 't' (transmit data)
|
|
|
|
|
|
|
|
|
|
transmit a message to a peer
|
|
|
|
|
|
|
|
|
|
transit fragment
|
|
|
|
|
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>
|
|
|
|
|
<1 byte number of 16 byte blocks offset from beginning of message, O>
|
|
|
|
|
<1 byte number of 16 byte blocks size of fragment data, N>
|
|
|
|
|
<4 bytes sequence number>
|
|
|
|
|
<32 bytes expected digest of message, present if O is 0, otherwise omitted>
|
|
|
|
|
<16 * N bytes of data>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
command: 'A' (ack)
|
|
|
|
|
encrypted via:
|
|
|
|
|
|
|
|
|
|
acknoledge fragments
|
|
|
|
|
K = EDDH(recipiant, sender)
|
|
|
|
|
X = SE(msg, K, N)
|
|
|
|
|
Z = MDS(X, K)
|
|
|
|
|
|
|
|
|
|
<header>
|
|
|
|
|
<1 byte number of acks following, N>
|
|
|
|
|
<8 * N bytes acks>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
@ -231,7 +258,7 @@ ack format:
|
|
|
|
|
<1 byte bitmask fragments posative ack (msb is fragment 0, lsb is fragment 7)>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
command: 'R' (rotate keys)
|
|
|
|
|
command: 'r' (rotate keys)
|
|
|
|
|
|
|
|
|
|
inform remote that their RX key should be rotated
|
|
|
|
|
|
|
|
|
@ -248,7 +275,7 @@ B.rx_K = n_K
|
|
|
|
|
<32 bytes next public encryption key, K>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
|
|
|
|
|
command: 'U' (upgrade)
|
|
|
|
|
command: 'u' (upgrade)
|
|
|
|
|
|
|
|
|
|
request protocol upgrade
|
|
|
|
|
|
|
|
|
@ -257,7 +284,7 @@ request protocol upgrade
|
|
|
|
|
<1 byte protocol max version to upgrade to>
|
|
|
|
|
<remaining bytes discarded>
|
|
|
|
|
|
|
|
|
|
command: 'V' (version upgrade)
|
|
|
|
|
command: 'v' (version upgrade)
|
|
|
|
|
|
|
|
|
|
sent in response to upgrade message
|
|
|
|
|
|
|
|
|
|