lokinet/docs/wire-protocol.txt
2019-02-14 13:13:45 -05:00

268 lines
5.9 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.
outer message format:
outer-header:
<1 byte command>
<1 byte reserved, R, set to '=' (0x3d)>
<32 bytes flow id, B>
command 'O' - obtain flow id
obtain a handshake cookie
<outer-header>
<32 bytes random>
<8 bytes net id>
<remaining discarded>
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>
<32 byte new flow-id, X>
<remaining discarded>
give a flow id to a remote endpoint that asks for one
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)
resulting message:
"G=<32 byte flowid><32 bytes new flowid>"
after recieving a give flow id message a session negotiation can happen
command 'R' - message rejected
<outer-header>
<N arbitrary bytes reason for rejection>
reject a message on a flow id
B is the flow id from the recipiant
resulting message of reject with reason "no you"
"R=<32 byte flow-id>no you"
command 'S' - session negotiation
negotiate encrypted session
<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
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 initiator (alice) and the recipiant (bob) start out with static session keys
k_a = HS(a.k)
k_b = HS(b.k)
a.rx_K = k_a
b.rx_K = k_b
a.tx_K = k_b
b.tx_K = k_a
decryption is done via:
SD(msg.X, rx_K, msg.N)
encrypted payload is bencoded LIM (see proto_v0.txt)
the initiator starts out by sending a LIM a_LIM to the recipiant.
the recipiant replies with a LIM b_LIM to the initiator.
when the initiator gets a valid LIM from the recipiant the session keys for data
transmission are set to:
k_a = TKE(a.k, b.k, a.sk, a_LIM.n)
k_b = TKE(b.k, a.k, b.sk, b_LIM.n)
a.rx_K = k_a
b.rx_K = k_b
a.tx_K = k_b
b.tx_K = k_a
afterwards data transmission may happen.
the intiator's remote address is permitted to change during data transmission.
remote address of the last successfully
D - encrypted data transmission
transmit encrypted data on session
<outer-header>
<24 bytes nonce, 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: '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)
transit fragment
<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>
command: 'A' (ack)
acknoledge fragments
<header>
<1 byte number of acks following, N>
<8 * N bytes acks>
<remaining bytes discarded>
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>