mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-19 09:25:28 +00:00
268 lines
5.9 KiB
Plaintext
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>
|