mirror of https://github.com/oxen-io/lokinet
remove old out of date documentation
parent
5890c99a81
commit
29df7bec74
@ -1,263 +0,0 @@
|
|||||||
|
|
||||||
LLARP Traffic Routing Protocol (LTRP)
|
|
||||||
|
|
||||||
LRTP is a protocol that instructs how to route hidden service traffic on LLARP
|
|
||||||
based networks.
|
|
||||||
|
|
||||||
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].
|
|
||||||
|
|
||||||
Overview:
|
|
||||||
|
|
||||||
LRTP is a message oriented data delivery and receival protocol for hidden
|
|
||||||
service traffic. All structures are BitTorrent Encoded dictionaries sent
|
|
||||||
over TCP.
|
|
||||||
|
|
||||||
all structures are bencoded when sent over the networks.
|
|
||||||
In this document they are provided in JSON for ease of display.
|
|
||||||
|
|
||||||
message format:
|
|
||||||
|
|
||||||
<2 bytes length (N)>
|
|
||||||
<N bytes of data>
|
|
||||||
|
|
||||||
|
|
||||||
Nouns (data structures):
|
|
||||||
|
|
||||||
Path: information about a path that we have built
|
|
||||||
|
|
||||||
{
|
|
||||||
H: [router_id_32_bytes, router_id_32_bytes, router_id_32_bytes, router_id_32_bytes],
|
|
||||||
R: "<16 bytes local rxid>",
|
|
||||||
T: "<16 bytes local txid>"
|
|
||||||
}
|
|
||||||
|
|
||||||
Introduction: a hidden service introduction
|
|
||||||
|
|
||||||
{
|
|
||||||
E: expiration_ms_since_epoch_uint64,
|
|
||||||
L: advertised_latency_ms_uint64,
|
|
||||||
P: "<16 bytes pathid>",
|
|
||||||
R: "<32 bytes RouterID>"
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceInfo: public key info for hidden service address
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "<32 bytes .loki address>",
|
|
||||||
E: "<32 bytes public encryption key>",
|
|
||||||
S: "<32 bytes public signing key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
IntroSet: information about an introduction set from the network
|
|
||||||
|
|
||||||
{
|
|
||||||
E: expires_at_timestamp_ms_since_epoch_uint64,
|
|
||||||
I: [Intro0, Intro1, ... IntroN],
|
|
||||||
S: ServiceInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
Converstation: information about a loki network converstation
|
|
||||||
|
|
||||||
{
|
|
||||||
L: "<32 bytes loki address provided if a loki address>",
|
|
||||||
S: "<32 bytes snode address provided if a snode address>",
|
|
||||||
T: "<16 bytes convo tag>"
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionInfo: information about our current session
|
|
||||||
|
|
||||||
{
|
|
||||||
I: [inbound,convos,here],
|
|
||||||
O: [outbound,covos,here],
|
|
||||||
P: [Path0, Path1, .... PathN],
|
|
||||||
S: Current IntroSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
Verbs (methods):
|
|
||||||
|
|
||||||
session requset (C->S)
|
|
||||||
|
|
||||||
the first message sent by the client
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "session",
|
|
||||||
B: "<8 bytes random>",
|
|
||||||
T: milliseconds_since_epoch_client_now_uint64,
|
|
||||||
Y: 0,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
session accept (S->C)
|
|
||||||
|
|
||||||
sent in reply to a session message to indicate session accept and give
|
|
||||||
a session cookie to the client.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "session-reply",
|
|
||||||
B: "<8 bytes random from session request>",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
T: milliseconds_since_epoch_server_now_uint64,
|
|
||||||
Y: 0,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
session reject (S->C)
|
|
||||||
|
|
||||||
sent in reply to a session message to indicate session rejection
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "session-reject",
|
|
||||||
B: "<8 bytes random from session request>",
|
|
||||||
R: "<variable length utf-8 encoded bytes human readable reason here>",
|
|
||||||
T: milliseconds_since_epoch_server_now_uint64,
|
|
||||||
Y: 0,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn a hidden service (C->S)
|
|
||||||
|
|
||||||
only one hidden service can be made per session
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "spawn",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
O: config_options_dict,
|
|
||||||
Y: 1,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
inform that we have spawned a new hidden service endpoint (S->C)
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "spawn-reply",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
S: ServiceInfo,
|
|
||||||
Y: 1,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
inform that we have not spaned a new hidden service endpint (S->C)
|
|
||||||
|
|
||||||
after sending this message the server closes the connection
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "spawn-reject",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
E: "<error message goes here>",
|
|
||||||
Y: 1,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
create a new convseration on a loki/snode address (C->S)
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "start-convo",
|
|
||||||
B: "<8 bytes random>",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
R: "human readable remote address .snode/.loki",
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
sent in reply to a make-convo message to indicate rejection (S->C)
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "start-convo-reject",
|
|
||||||
B: "<8 bytes random from start-convo message>",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
S: status_bitmask_uint,
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
sent in reply to a make-convo message to indicate that we have accepted this
|
|
||||||
new conversation and gives the convo tag it uses.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "start-convo-accept",
|
|
||||||
B: "<8 bytes random from start-convo message>",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>
|
|
||||||
}
|
|
||||||
|
|
||||||
infrom the status of a converstation on a loki address (S->C)
|
|
||||||
|
|
||||||
for an outbund conversation it is sent every time the status bitmask changes.
|
|
||||||
for inbound convos it is sent immediately when a new inbound conversation is made.
|
|
||||||
|
|
||||||
S bit 0 (LSB): we found the introset/endpoint for (set by outbound)
|
|
||||||
S bit 1: we found the router to align on (set by outbound)
|
|
||||||
S bit 2: we have a path right now (set by outbound)
|
|
||||||
S bit 3: we have made the converstation (set by both)
|
|
||||||
S bit 4: we are an inbound converstation (set by inbound)
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "convo-status",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
R: "human readable address .snode/.loki",
|
|
||||||
S: bitmask_status_uint64,
|
|
||||||
T: "<16 bytes convotag>",
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
send or recieve authenticated data to or from the network (bidi)
|
|
||||||
|
|
||||||
protocol numbers are
|
|
||||||
|
|
||||||
1 for ipv4
|
|
||||||
2 for ipv6
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "data",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
T: "<16 bytes convotag>",
|
|
||||||
W: protocol_number_uint,
|
|
||||||
X: "<N bytes payload>",
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
get session information (C->S)
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "info",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
session information update (S->C)
|
|
||||||
|
|
||||||
sent in reply to a get session information message
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "info-reply",
|
|
||||||
C: "<16 bytes session cookie>",
|
|
||||||
I: hiddenserviceinfo,
|
|
||||||
Y: sequence_num_uint64,
|
|
||||||
Z: "<32 bytes keyed hash>"
|
|
||||||
}
|
|
||||||
|
|
||||||
Protocol Flow:
|
|
||||||
|
|
||||||
all messages have an A, C, Y and Z value
|
|
||||||
|
|
||||||
A is the function name being called
|
|
||||||
|
|
||||||
C is the session cookie indicating the current session
|
|
||||||
|
|
||||||
Y is the 64 bit message sequence number as an integer
|
|
||||||
|
|
||||||
Z is the keyed hash computed by MDS(BE(msg), K) where K is HS(api_password)
|
|
||||||
with the msg.Z being set to 32 bytes of \x00
|
|
||||||
|
|
||||||
both client and server MUST know a variable length string api_password used to
|
|
||||||
authenticate access to the api subsystem.
|
|
||||||
|
|
||||||
the Y value is incremented by 1 for each direction every time the sender sends
|
|
||||||
a message in that direction.
|
|
@ -1,117 +0,0 @@
|
|||||||
|
|
||||||
* lokinet components
|
|
||||||
|
|
||||||
** basic data structures
|
|
||||||
*** AlignedBuffer
|
|
||||||
*** RouterContact
|
|
||||||
**** self signed router descriptor
|
|
||||||
*** Crypto key types
|
|
||||||
|
|
||||||
|
|
||||||
** network / threading / sync utilities
|
|
||||||
*** threadpool
|
|
||||||
*** logic (single thread threadpool)
|
|
||||||
|
|
||||||
** configuration
|
|
||||||
*** ini parser
|
|
||||||
*** llarp::Configuration
|
|
||||||
|
|
||||||
** cryptography
|
|
||||||
*** llarp::Crypto interface
|
|
||||||
**** libsodium / sntrup implementation
|
|
||||||
*** llarp::CryptoManager
|
|
||||||
**** crypto implementation signleton manager
|
|
||||||
|
|
||||||
** nodedb
|
|
||||||
*** llarp_nodedb
|
|
||||||
**** holds many RouterContacts loaded from disk in memory
|
|
||||||
**** uses a filesystem skiplist for on disk storage
|
|
||||||
**** stores in a std::unordered_map addressable via public identity key
|
|
||||||
|
|
||||||
** event loop
|
|
||||||
*** llarp_event_loop
|
|
||||||
**** udp socket read/write
|
|
||||||
**** network interface packet read/write
|
|
||||||
**** stream connection (outbound stream)
|
|
||||||
**** stream acceptor (inbound stream)
|
|
||||||
|
|
||||||
** link layer message transport:
|
|
||||||
*** ILinkSession
|
|
||||||
**** the interface for an entity that is single session with relay
|
|
||||||
**** responsible for delivery recieval of link layer messages in full
|
|
||||||
*** ILinkLayer
|
|
||||||
**** bound to an address / interface
|
|
||||||
**** has a direction, inbound / outbound
|
|
||||||
**** distinctly identified by the union of interface and direction
|
|
||||||
**** Holds many ILinkSessions bound on the distinctly idenitfied direction/interface
|
|
||||||
|
|
||||||
|
|
||||||
** link layer messages
|
|
||||||
*** link layer message parser
|
|
||||||
**** parses buffers as bencoded dicts
|
|
||||||
*** link layer message handler
|
|
||||||
**** handles each type of link layer message
|
|
||||||
|
|
||||||
|
|
||||||
** IHopHandler
|
|
||||||
*** llarp::PathHopConfig
|
|
||||||
**** txid, rxid, shared secret at hop
|
|
||||||
*** llarp::path::Path
|
|
||||||
**** a built path or a path being built
|
|
||||||
**** owns a std::vector<PathHopConfig> for each hop's info
|
|
||||||
*** TransitHop
|
|
||||||
**** a single hop on a built path
|
|
||||||
**** has txid, rxid, shared secret, hash of shared secret
|
|
||||||
|
|
||||||
|
|
||||||
** pathset
|
|
||||||
*** path::Builder
|
|
||||||
**** builds and maintains a set of paths for a common use
|
|
||||||
|
|
||||||
|
|
||||||
** routing layer message router
|
|
||||||
*** routing::IMessageHandler
|
|
||||||
**** interface for routing layer message processing
|
|
||||||
**** transit hops implement this if they are an endpoint
|
|
||||||
**** path::Path implement this always
|
|
||||||
|
|
||||||
|
|
||||||
** dht "layer" / rc gossiper
|
|
||||||
*** TODO rewrite/refactor
|
|
||||||
|
|
||||||
** hidden service data structures
|
|
||||||
*** IntroSet
|
|
||||||
**** decrypted plaintext hidden service descriptor
|
|
||||||
*** EncryptedIntroSet
|
|
||||||
**** public encrpyted / signed version of IntroSet
|
|
||||||
|
|
||||||
|
|
||||||
** service endpoint / outbound context connectivitybackend
|
|
||||||
*** service::Endpoint
|
|
||||||
**** backend for sending/recieving packets over the hidden service protocol layer
|
|
||||||
**** kitchen sink
|
|
||||||
*** service::SendContext
|
|
||||||
**** interface type for sending to a resource on the network
|
|
||||||
*** service::OutboundContext
|
|
||||||
**** implements SendContext extends path::Builder and path::PathSet
|
|
||||||
**** for maintaining a pathset that aligns on an introset's intros
|
|
||||||
~
|
|
||||||
|
|
||||||
** snode / exit connectivity backend
|
|
||||||
*** exit::BaseSession
|
|
||||||
**** extends path::Builder
|
|
||||||
**** obtains an exit/snode session from the router they are aligning to
|
|
||||||
*** exit::Endpoint
|
|
||||||
**** snode/exit side of an exit::Session
|
|
||||||
|
|
||||||
** snapp / exit / mobile / null frontend handlers
|
|
||||||
*** handlers::TunEndpoint
|
|
||||||
**** extends service::Endpoint
|
|
||||||
**** provides tun interface frontend for hidden service backend
|
|
||||||
*** handlers::ExitEndpoint
|
|
||||||
**** provides tun interface frontend for exit/snode backend
|
|
||||||
|
|
||||||
|
|
||||||
** outbound message dispatcher
|
|
||||||
*** TODO tom please document these
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
cryptography:
|
|
||||||
|
|
||||||
H(x) is 512 bit blake2b digest of x
|
|
||||||
HS(x) is 256 bit blake2b digest of x
|
|
||||||
MD(x, k) is 512 bit blake2b hmac of x with secret value k
|
|
||||||
MDS(x, k) is 256 bit blake2b hmac of x with secret value k
|
|
||||||
SE(k, n, x) is chacha20 encrypt data x using symettric key k and nounce n
|
|
||||||
SD(k, n, x) is chacha20 dectypt data x using symettric key k and nounce n
|
|
||||||
S(k, x) is sign x with ed25519 using secret key k
|
|
||||||
EDKG() is generate ec keypair (p, s) public key p (32 bytes), secret key s (64 bytes)
|
|
||||||
V(k, x, sig) is verify x data using signature sig using public key k
|
|
||||||
EDDH(a, b) is curve25519 scalar multiplication of a and b
|
|
||||||
HKE(a, b, x) is hashed key exchange between a and b using a secret key x HS(a + b + EDDH(x, b))
|
|
||||||
TKE(a, b, x, n) is a transport shared secret kdf using MDS(n, HKE(a, b, x))
|
|
||||||
|
|
||||||
when A is client and B is server where n is a 32 bytes shared random
|
|
||||||
|
|
||||||
client computes TKE(A.pk, B.pk, A.sk, n)
|
|
||||||
server computes TKE(A.pk, B.pk, B.sk, n)
|
|
||||||
|
|
||||||
PDH(a, b, x) is path shared secret generation HS(a + b + EDDH(x, b))
|
|
||||||
|
|
||||||
PKE(a, b, x, n) is a path shared secret kdf using MDS(n, PDH(a, b, x))
|
|
||||||
|
|
||||||
given A is the path creator and B is a hop in the path and n is 32 bytes shared random
|
|
||||||
|
|
||||||
A computes PKE(A.pk, B.pk, A.sk, n) as S_a
|
|
||||||
B computes PKE(A.pk, B.pk, B.sk, n) as S_b
|
|
||||||
|
|
||||||
S_a is equal to S_b
|
|
||||||
|
|
||||||
RAND(n) is n random bytes
|
|
||||||
|
|
||||||
PQKG() is generate a sntrup4591761 key pair (sk, pk)
|
|
||||||
PQKE_A(pk) is alice generating (x, k) where x is sntrup4591761 ciphertext block and k is the session key
|
|
||||||
PQKE_B(x, sk) is bob calculating k where x is sntrup4591761 ciphertext block, sk is bob's sntrup4591761 secretkey and k is the session key
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
|||||||
DHT messages
|
|
||||||
|
|
||||||
DHT messages can be either wrapped in a LIDM message or sent anonymously over a path inside a DHT routing message
|
|
||||||
|
|
||||||
The distance function is A xor B (traditional kademlia)
|
|
||||||
|
|
||||||
The dht implements both iterative and recursive lookups.
|
|
||||||
|
|
||||||
Recursive lookups forward the request to a node closer if not found.
|
|
||||||
Iterative lookups return the key and of the DHT node who is closer.
|
|
||||||
|
|
||||||
In the case of iterative FRCM the RC of the closer router is provided.
|
|
||||||
In the case of iterative FIM the pubkey of a dht node who is closer is provided in the GIM.
|
|
||||||
|
|
||||||
find introduction message (FIM)
|
|
||||||
|
|
||||||
variant 1: find an IS by SA
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "F",
|
|
||||||
R: 0 for iterative and 1 for recurisve,
|
|
||||||
S: "<32 bytes SA>",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
variant 2: recursively find many IS in a tag
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "F",
|
|
||||||
E: [list, of, excluded, SA],
|
|
||||||
R: 0 for iterative and 1 for recurisve,
|
|
||||||
N: "<16 bytes topic tag>",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
exclude adding service addresses in E if present
|
|
||||||
|
|
||||||
|
|
||||||
got introduction message (GIM)
|
|
||||||
|
|
||||||
sent in reply to FIM containing the result of the search
|
|
||||||
sent in reply to PIM to acknoledge the publishing of an IS
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "G",
|
|
||||||
I: [IS, IS, IS, ...],
|
|
||||||
K: "<32 bytes public key of router who is closer, provided ONLY if FIM.R is 0>",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
The I value MUST NOT contain more than 4 IS.
|
|
||||||
The I value MUST contain either 1 or 0 IS for PIM and FIM variant 1.
|
|
||||||
|
|
||||||
|
|
||||||
publish introduction message (PIM)
|
|
||||||
|
|
||||||
publish one IS to the DHT.
|
|
||||||
|
|
||||||
version 0 uses the SA of the IS as the keyspace location.
|
|
||||||
|
|
||||||
in the future the location will be determined by the dht kdf
|
|
||||||
which uses a shared random source to obfuscate keyspace location.
|
|
||||||
|
|
||||||
|
|
||||||
R is currently set to 0 by the sender.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "I",
|
|
||||||
I: IS,
|
|
||||||
R: random_walk_counter,
|
|
||||||
S: optional member 0 for immediate store otherwise non zero,
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if R is greater than 0, decrement R and forward the request to a random DHT
|
|
||||||
node without storing the IS.
|
|
||||||
As of protocol version 0, R is always 0.
|
|
||||||
|
|
||||||
If S is provided store the IS for later lookup unconditionally, then
|
|
||||||
decrement S by 1 and forward to dht peer who is next closest to
|
|
||||||
the SA of the IS. If S is greater than 3, don't store the IS and
|
|
||||||
discard this message.
|
|
||||||
|
|
||||||
acknoledge introduction message (AIM)
|
|
||||||
|
|
||||||
acknoledge the publishing of an introduction
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "A",
|
|
||||||
P: published_to_counter,
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
increment P by 1 and forward to requester
|
|
||||||
|
|
||||||
|
|
||||||
find router contact message (FRCM)
|
|
||||||
|
|
||||||
find a router by long term RC.k public key
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "R",
|
|
||||||
E: 0 or 1 if exploritory lookup,
|
|
||||||
I: 0 or 1 if iterative lookup,
|
|
||||||
K: "<32 byte public key of router>",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if E is provided and non zero then return E dht nodes that are closest to K
|
|
||||||
if I is provided and non zero then this request is considered an iterative lookup
|
|
||||||
during an iterative lookup the response's GRCM.K is set to the pubkey of the router closer in key space.
|
|
||||||
during a recursive lookup the request is forwarded to a router who is closer in
|
|
||||||
keyspace to K.
|
|
||||||
If we have no peers that are closer to K and we don't have the RC known locally
|
|
||||||
we reply with a GRCM whose R value is emtpy.
|
|
||||||
In any case if we have a locally known RC with pubkey equal to K reply with
|
|
||||||
a GRCM with 1 RC in the R value corrisponding to that locally known RC.
|
|
||||||
|
|
||||||
got router contact message (GRCM)
|
|
||||||
|
|
||||||
R is a list containing a single RC if found or is an empty list if not found
|
|
||||||
sent in reply to FRCM only
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "S",
|
|
||||||
K: "<32 bytes public identity key of router closer, provided ONLY if FRCM.I is 1>",
|
|
||||||
R: [RC],
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
in response to an exploritory router lookup, where FRCM.E is provided and non zero.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "S",
|
|
||||||
N: [list, of, router, publickeys, near, K],
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
sent in reply to a dht request to indicate transaction timeout
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "T",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
llarp's dht is a recusrive kademlia dht with optional request proxying via paths for requester anonymization.
|
|
||||||
|
|
||||||
dht is separated into 2 different networks, one for router contacts, one for introsets.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
format for consesus propagation messages:
|
|
||||||
|
|
||||||
keys: A, H, K, N, O, T, U, V
|
|
||||||
|
|
||||||
concensus request messages
|
|
||||||
|
|
||||||
requester requests current table hash, H,N,O is set to zeros if not known
|
|
||||||
|
|
||||||
C -> S
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "C",
|
|
||||||
H: "<32 byte last hash of consensus table>",
|
|
||||||
N: uint64_number_of_entries_to_request,
|
|
||||||
O: uint64_offset_in_table,
|
|
||||||
T: uint64_txid,
|
|
||||||
V: []
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when H or N is set to zero from the requester, they are requesting the current consensus table's hash
|
|
||||||
|
|
||||||
consensus response message is as follows for a zero H or N value
|
|
||||||
|
|
||||||
S -> C
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "C",
|
|
||||||
H: "<32 byte hash of current consensus table>",
|
|
||||||
N: uint64_number_of_entries_in_table,
|
|
||||||
T: uint64_txid,
|
|
||||||
U: uint64_ms_next_update_required,
|
|
||||||
V: [proto, major, minor, patch]
|
|
||||||
}
|
|
||||||
|
|
||||||
requester requests a part of the current table for hash H
|
|
||||||
|
|
||||||
N must be less than or equal to 512
|
|
||||||
|
|
||||||
C -> S
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "C",
|
|
||||||
H: "<32 bytes current consensus table hash>",
|
|
||||||
N: 256,
|
|
||||||
O: 512,
|
|
||||||
T: uint64_txid,
|
|
||||||
V: []
|
|
||||||
}
|
|
||||||
|
|
||||||
consensus response message for routers 512 to 512 + 256
|
|
||||||
|
|
||||||
S -> C
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "C",
|
|
||||||
H: "<32 bytes current concensus table hash>",
|
|
||||||
K: [list, of, N, pubkeys, from, request, starting, at, position, O],
|
|
||||||
T: uint64_txid,
|
|
||||||
V: [proto, major, minor, patch]
|
|
||||||
}
|
|
||||||
|
|
||||||
consensus table is a concatination of all public keys in lexigraphical order.
|
|
||||||
|
|
||||||
the hash function in use is 256 bit blake2
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gossip RC message
|
|
||||||
|
|
||||||
broadcast style RC publish message. sent to all peers infrequently.
|
|
||||||
|
|
||||||
it is really an unwarrented GRCM, propagate to all peers.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "S",
|
|
||||||
R: [RC],
|
|
||||||
T: 0,
|
|
||||||
V: proto
|
|
||||||
}
|
|
||||||
|
|
||||||
replays are dropped using a decaying hashset or decaying bloom filter.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
the introset dht has 3 message: GetIntroSet Message (GIM), PutIntroSet Message (PIM), FoundIntroSet Message (FIM)
|
|
||||||
|
|
@ -1,147 +0,0 @@
|
|||||||
THIS DOCUMENT IS 3 YEARS OUT OF DATE AND NEEDS TO BE REWRITTEN
|
|
||||||
|
|
||||||
|
|
||||||
LLARP - Low Latency Anon Routing Protocol
|
|
||||||
|
|
||||||
TL;DR edition: an onion router with a tun interface for transporting ip packets
|
|
||||||
anonymously between you and the internet and internally inside itself to other users.
|
|
||||||
|
|
||||||
|
|
||||||
This document describes the high level outline of LLARP, specific all the
|
|
||||||
project's goals, non-goals and network architecture from a bird's eye view.
|
|
||||||
|
|
||||||
|
|
||||||
Preface:
|
|
||||||
|
|
||||||
|
|
||||||
Working on I2P has been a really big learning experience for everyone involved.
|
|
||||||
After much deliberation I have decided to start making a "next generation" onion
|
|
||||||
routing protocol. Specifically LLARP is (currently) a research project
|
|
||||||
to explore the question:
|
|
||||||
|
|
||||||
|
|
||||||
"What if I2P was made in the current year (2018)? What would be different?"
|
|
||||||
|
|
||||||
|
|
||||||
Project Non Goals:
|
|
||||||
|
|
||||||
|
|
||||||
This project does not attempt to solve traffic shape correlation or active nation
|
|
||||||
state sponsored network attacks. The former is an inherit property of low latency
|
|
||||||
computer networks that I personally do not think is possible to properly fully
|
|
||||||
"solve". The latter is a threat that lies outside the scope of what the current
|
|
||||||
toolset that is available to me at the moment provides.
|
|
||||||
|
|
||||||
|
|
||||||
This project does not attempt to be a magical application level cure-all for
|
|
||||||
application or end user security. At the end of the day that is a problem that
|
|
||||||
exists between chair and keyboard.
|
|
||||||
|
|
||||||
|
|
||||||
The Single Project Goal:
|
|
||||||
|
|
||||||
|
|
||||||
LLARP is a protocol suite meant to anonymize IP by providing an anonymous
|
|
||||||
network level (IPv4/IPv6) tunnel broker for both "hidden services" and
|
|
||||||
communication back to "the clearnet" (the normal internet). Both hidden service
|
|
||||||
and clearnet communication MUST permit both outbound and inbound traffic on the
|
|
||||||
network level without any NAT (except for IPv4 in which NAT is permitted due to
|
|
||||||
lack of address availability).
|
|
||||||
|
|
||||||
|
|
||||||
In short We want to permit both anonymous exit and entry network level traffic
|
|
||||||
between LLARP enabled networks and the internet.
|
|
||||||
|
|
||||||
|
|
||||||
Rationale for starting over:
|
|
||||||
|
|
||||||
|
|
||||||
Despite Tor Project's best efforts to popularize Tor use, Tor2Web seems to be
|
|
||||||
widely popular for people who do not wish to opt into the ecosystem. My proposed
|
|
||||||
solution would be to permit inbound traffic from "exit nodes" in addition to
|
|
||||||
allowing outbound exit traffic. I have no ideas on how this could be done with
|
|
||||||
the existing protocols in Tor or if it is possible or advisable to attempt such
|
|
||||||
as I am not familiar with their ecosystem.
|
|
||||||
|
|
||||||
|
|
||||||
I2P could have been used as a medium for encrypted anonymous IP transit but the
|
|
||||||
current network has issues with latency and throughput. Rebasing I2P atop more
|
|
||||||
modern cryptography has been going on internally inside I2P for at least 5 years
|
|
||||||
with less progress than desired. Like some before me, I have concluded that it
|
|
||||||
would be faster to redo the whole stack "the right way" than to wait for I2P to
|
|
||||||
finish rebasing. That being said, nothing is preventing I2P from be used for
|
|
||||||
encrypted anonymous IP transit traffic in a future where I2P finishes their
|
|
||||||
protocol migrations, I just don't want to wait.
|
|
||||||
|
|
||||||
|
|
||||||
In short, I want to take the "best parts" from Tor and I2P and make a new
|
|
||||||
protocol suite.
|
|
||||||
|
|
||||||
|
|
||||||
For both Tor and I2P I have 2 categories for the attributes they have.
|
|
||||||
|
|
||||||
|
|
||||||
the good
|
|
||||||
the bad and the ugly
|
|
||||||
|
|
||||||
|
|
||||||
The good (I2P):
|
|
||||||
|
|
||||||
|
|
||||||
I2P aims to provide an anonymous unspoofable load balanced network layer.
|
|
||||||
|
|
||||||
|
|
||||||
I want this feature.
|
|
||||||
|
|
||||||
|
|
||||||
I2P is trust agile, it does not have any hard coded trusts in its network
|
|
||||||
architecture. Even network boostrap can be done from a single router if the user
|
|
||||||
desires to (albeit this is currently ill advised).
|
|
||||||
|
|
||||||
|
|
||||||
I want this feature.
|
|
||||||
|
|
||||||
|
|
||||||
The good (Tor):
|
|
||||||
|
|
||||||
|
|
||||||
Tor embraces the reality of the current internet infrastructure by having a
|
|
||||||
client/server architecture. This allows very low barriers of entry in using the
|
|
||||||
Tor network and a higher barrier of entry for contributing routing
|
|
||||||
infrastructure. This promotes a healthy network shape of high capacity servers
|
|
||||||
serving low capacity clients that "dangle off of the side" of the network.
|
|
||||||
|
|
||||||
|
|
||||||
I want this feature.
|
|
||||||
|
|
||||||
|
|
||||||
The bad and the ugly (I2P):
|
|
||||||
|
|
||||||
|
|
||||||
Bad: I2P uses old cryptography, specially 2048 bit ElGamal using non standard primes.
|
|
||||||
The use of ElGamal is so pervasive throughout the I2P protocol stack that it
|
|
||||||
exists at every level of it. Removing it is a massive task that is taking a long
|
|
||||||
LONG time.
|
|
||||||
|
|
||||||
|
|
||||||
I don't want this feature.
|
|
||||||
|
|
||||||
|
|
||||||
Ugly: I2P cannot currently mitigate most sybil attacks with their current network
|
|
||||||
architecture. Recently I2P has added some blocklist solutions signed by release
|
|
||||||
signers but this probably won't scale in the event of a "big" attack. In
|
|
||||||
addition I2P isn't staffed for such attacks either.
|
|
||||||
|
|
||||||
|
|
||||||
This is a hard problem to solve that the Loki network may be able to help
|
|
||||||
with by creating a financial barrier to running multiple a relays.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The bad and the ugly (Tor):
|
|
||||||
|
|
||||||
|
|
||||||
Bad: Tor is strictly TCP oriented.
|
|
||||||
I don't want this feature.
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
Lokinet needs certain capabilities to run to set up a virtual network interface and provide a DNS server. The preferred approach to using this is through the linux capabilities mechanism, which allows assigning limited capabilities without needing to run the entire process as root.
|
|
||||||
|
|
||||||
There are two main ways to do this:
|
|
||||||
|
|
||||||
1. If you are running lokinet via an init system such as systemd, you can specify the capabilities in the service file by adding:
|
|
||||||
|
|
||||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
|
||||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
|
||||||
|
|
||||||
into the [Service] section of the systemd service file. This will assign the necessary permissions when running the process and allow lokinet to work while running as a non-root user.
|
|
||||||
|
|
||||||
2. You can set the capabilities on the binary by using the setcap program (if not available you may need to install libcap2-bin on Debian/Ubuntu-based systems) and running:
|
|
||||||
|
|
||||||
setcap cap_net_admin,cap_net_bind_service=+eip lokinet
|
|
||||||
|
|
||||||
This grants the permissions whenever the lokinet binary is executed.
|
|
@ -1,102 +0,0 @@
|
|||||||
digraph {
|
|
||||||
constants -> util;
|
|
||||||
|
|
||||||
crypto -> constants;
|
|
||||||
crypto -> llarp;
|
|
||||||
crypto -> util;
|
|
||||||
|
|
||||||
dht -> crypto;
|
|
||||||
dht -> messages;
|
|
||||||
dht -> llarp;
|
|
||||||
dht -> path;
|
|
||||||
dht -> routing;
|
|
||||||
dht -> service;
|
|
||||||
dht -> util;
|
|
||||||
|
|
||||||
dns -> crypto;
|
|
||||||
dns -> ev;
|
|
||||||
dns -> handlers;
|
|
||||||
dns -> llarp;
|
|
||||||
dns -> net;
|
|
||||||
dns -> service;
|
|
||||||
dns -> util;
|
|
||||||
|
|
||||||
ev -> net;
|
|
||||||
ev -> util;
|
|
||||||
|
|
||||||
exit -> crypto;
|
|
||||||
exit -> handlers;
|
|
||||||
exit -> messages;
|
|
||||||
exit -> net;
|
|
||||||
exit -> path;
|
|
||||||
exit -> routing;
|
|
||||||
exit -> util;
|
|
||||||
|
|
||||||
handlers -> dns;
|
|
||||||
handlers -> ev;
|
|
||||||
handlers -> exit;
|
|
||||||
handlers -> net;
|
|
||||||
handlers -> service;
|
|
||||||
handlers -> util;
|
|
||||||
|
|
||||||
link -> constants;
|
|
||||||
link -> crypto;
|
|
||||||
link -> ev;
|
|
||||||
link -> messages;
|
|
||||||
link -> net;
|
|
||||||
link -> util;
|
|
||||||
|
|
||||||
messages -> crypto;
|
|
||||||
messages -> dht;
|
|
||||||
messages -> exit;
|
|
||||||
messages -> link;
|
|
||||||
messages -> llarp;
|
|
||||||
messages -> path;
|
|
||||||
messages -> routing;
|
|
||||||
messages -> service;
|
|
||||||
messages -> util;
|
|
||||||
|
|
||||||
net -> crypto;
|
|
||||||
net -> util;
|
|
||||||
|
|
||||||
path -> crypto;
|
|
||||||
path -> dht;
|
|
||||||
path -> llarp;
|
|
||||||
path -> messages;
|
|
||||||
path -> routing;
|
|
||||||
path -> service;
|
|
||||||
path -> util;
|
|
||||||
|
|
||||||
routing -> llarp;
|
|
||||||
routing -> messages;
|
|
||||||
routing -> path;
|
|
||||||
routing -> util;
|
|
||||||
|
|
||||||
service -> crypto;
|
|
||||||
service -> dht;
|
|
||||||
service -> ev;
|
|
||||||
service -> exit;
|
|
||||||
service -> handlers;
|
|
||||||
service -> messages;
|
|
||||||
service -> net;
|
|
||||||
service -> path;
|
|
||||||
service -> routing;
|
|
||||||
service -> util;
|
|
||||||
|
|
||||||
util -> constants;
|
|
||||||
|
|
||||||
llarp -> constants;
|
|
||||||
llarp -> crypto;
|
|
||||||
llarp -> dht;
|
|
||||||
llarp -> dns;
|
|
||||||
llarp -> ev;
|
|
||||||
llarp -> exit;
|
|
||||||
llarp -> handlers;
|
|
||||||
llarp -> link;
|
|
||||||
llarp -> messages;
|
|
||||||
llarp -> net;
|
|
||||||
llarp -> path;
|
|
||||||
llarp -> routing;
|
|
||||||
llarp -> service;
|
|
||||||
llarp -> util;
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 1000 368" style="enable-background:new 0 0 1000 368;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:#FFFFFF;}
|
|
||||||
.st1{fill:url(#SVGID_1_);}
|
|
||||||
.st2{fill:#333333;}
|
|
||||||
.st3{fill:url(#SVGID_2_);}
|
|
||||||
.st4{fill:url(#SVGID_3_);}
|
|
||||||
.st5{fill:#00263A;}
|
|
||||||
.st6{fill:url(#SVGID_4_);}
|
|
||||||
.st7{fill:url(#SVGID_5_);}
|
|
||||||
.st8{fill:url(#SVGID_6_);}
|
|
||||||
.st9{fill:url(#SVGID_7_);}
|
|
||||||
.st10{fill:url(#SVGID_8_);}
|
|
||||||
.st11{fill:url(#SVGID_9_);}
|
|
||||||
.st12{fill:url(#SVGID_10_);}
|
|
||||||
.st13{fill:url(#SVGID_11_);}
|
|
||||||
.st14{fill:url(#SVGID_12_);}
|
|
||||||
.st15{fill:url(#SVGID_13_);}
|
|
||||||
.st16{fill:url(#SVGID_14_);}
|
|
||||||
.st17{fill:url(#SVGID_15_);}
|
|
||||||
.st18{fill:url(#SVGID_16_);}
|
|
||||||
.st19{fill:url(#SVGID_17_);}
|
|
||||||
.st20{opacity:6.000000e-02;}
|
|
||||||
.st21{opacity:4.000000e-02;fill:#FFFFFF;}
|
|
||||||
.st22{opacity:7.000000e-02;fill:#FFFFFF;}
|
|
||||||
.st23{fill:#008522;}
|
|
||||||
.st24{fill:#78BE20;}
|
|
||||||
.st25{fill:#005F61;}
|
|
||||||
.st26{fill:url(#SVGID_18_);}
|
|
||||||
</style>
|
|
||||||
<g>
|
|
||||||
<path class="st0" d="M366.6,78h37.1v178.9H497v32.7H366.6V78z"/>
|
|
||||||
<path class="st0" d="M619.8,74.5C683.3,74.5,728,120.8,728,184c0,63.1-44.7,109.5-108.2,109.5c-63.5,0-108.2-46.3-108.2-109.5
|
|
||||||
C511.6,120.8,556.3,74.5,619.8,74.5z M619.8,107.5c-42.8,0-70.1,32.7-70.1,76.5c0,43.5,27.3,76.5,70.1,76.5
|
|
||||||
c42.5,0,70.1-33,70.1-76.5C689.9,140.2,662.3,107.5,619.8,107.5z"/>
|
|
||||||
<path class="st0" d="M819.4,200.5L801,222v67.6h-37.1V78H801v100.9L883.8,78h46l-86,99.9l92.3,111.7h-45.7L819.4,200.5z"/>
|
|
||||||
<path class="st0" d="M960.9,78H998v211.6h-37.1V78z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="86.8402" y1="268.7968" x2="86.8402" y2="0.426">
|
|
||||||
<stop offset="0" style="stop-color:#78BE20"/>
|
|
||||||
<stop offset="0.1197" style="stop-color:#58AF21"/>
|
|
||||||
<stop offset="0.3682" style="stop-color:#199122"/>
|
|
||||||
<stop offset="0.486" style="stop-color:#008522"/>
|
|
||||||
<stop offset="0.6925" style="stop-color:#007242"/>
|
|
||||||
<stop offset="0.8806" style="stop-color:#006459"/>
|
|
||||||
<stop offset="1" style="stop-color:#005F61"/>
|
|
||||||
</linearGradient>
|
|
||||||
<polygon class="st1" points="132.1,268.8 0.3,137 136.9,0.4 173.3,36.8 73.1,137 168.5,232.4 "/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="212.9564" y1="367.5197" x2="212.9564" y2="99.1484">
|
|
||||||
<stop offset="0" style="stop-color:#78BE20"/>
|
|
||||||
<stop offset="0.1197" style="stop-color:#58AF21"/>
|
|
||||||
<stop offset="0.3682" style="stop-color:#199122"/>
|
|
||||||
<stop offset="0.486" style="stop-color:#008522"/>
|
|
||||||
<stop offset="0.6925" style="stop-color:#007242"/>
|
|
||||||
<stop offset="0.8806" style="stop-color:#006459"/>
|
|
||||||
<stop offset="1" style="stop-color:#005F61"/>
|
|
||||||
</linearGradient>
|
|
||||||
<polygon class="st3" points="162.9,367.5 126.5,331.1 226.7,230.9 131.3,135.6 167.7,99.1 299.5,230.9 "/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.9 KiB |
@ -1,70 +0,0 @@
|
|||||||
LokiNET admin api
|
|
||||||
|
|
||||||
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].
|
|
||||||
|
|
||||||
------
|
|
||||||
|
|
||||||
the admin api currently uses jsonrpc 2.0 over http
|
|
||||||
|
|
||||||
the methods currently provided are:
|
|
||||||
|
|
||||||
llarp.nodedb.rc.getbykey
|
|
||||||
|
|
||||||
get rc by public identity key
|
|
||||||
|
|
||||||
required parameters:
|
|
||||||
|
|
||||||
key: 32 bytes public identity key
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
a list of RCs (see protocol v0 spec) that have this public identity key
|
|
||||||
usually 0 or 1 RCs
|
|
||||||
|
|
||||||
|
|
||||||
llarp.nodedb.rc.getbycidr
|
|
||||||
|
|
||||||
get a list of RCs in an address range
|
|
||||||
|
|
||||||
required parameters:
|
|
||||||
|
|
||||||
cidr: ipv6 network cidr string, i.e. "::ffff.21.0.0.0/8" or "fc00::/7"
|
|
||||||
limit: integer max number of items to fetch, zero or positive integer,
|
|
||||||
if zero no limit.
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
a list of 0 to limit RCs that advertise themselves as being reachble via an
|
|
||||||
address in the given CIDR.
|
|
||||||
|
|
||||||
|
|
||||||
llarp.admin.sys.uptime (authentication required)
|
|
||||||
|
|
||||||
required paramters:
|
|
||||||
|
|
||||||
(none)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
an integer milliseconds since unix epoch we've been online
|
|
||||||
|
|
||||||
llarp.admin.link.neighboors
|
|
||||||
|
|
||||||
get a list of connected service nodes on all links
|
|
||||||
|
|
||||||
required parameters:
|
|
||||||
|
|
||||||
(none)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
list of 0 to N dicts in the following format:
|
|
||||||
|
|
||||||
{
|
|
||||||
"connected" : uint64_milliseconds_timestamp_connected_at
|
|
||||||
"ident" : "<64 hex encoded public identity key>",
|
|
||||||
"laddr" : "local address",
|
|
||||||
"raddr" : "remote address"
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
Wire Protocol (version ½)
|
|
||||||
|
|
||||||
|
|
||||||
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 and framed transport over UTP [1]
|
|
||||||
|
|
||||||
Handshake:
|
|
||||||
|
|
||||||
Alice establishes a UTP "connection" with Bob.
|
|
||||||
|
|
||||||
Alice sends a LIM a_L encrpyted with the initial b_K key
|
|
||||||
|
|
||||||
if Bob accepts Alice's router, Bob replies with a LIM b_L encrpyted with the
|
|
||||||
b_K key.
|
|
||||||
|
|
||||||
next the session keys are generated via:
|
|
||||||
|
|
||||||
a_h = HS(a_K + a_L.n)
|
|
||||||
b_h = HS(b_K + b_L.n)
|
|
||||||
a_K = TKE(A.p, B_a.e, sk, a_h)
|
|
||||||
b_K = TKE(A.p, B_a.e, sk, b_h)
|
|
||||||
|
|
||||||
A.tx_K = b_K
|
|
||||||
A.rx_K = a_K
|
|
||||||
B.tx_K = a_K
|
|
||||||
B.rx_K = B_K
|
|
||||||
|
|
||||||
the initial value of a_K is HS(A.k) and b_K is HS(B.k)
|
|
||||||
|
|
||||||
608 byte fragments are sent over UTP in an ordered fashion.
|
|
||||||
|
|
||||||
The each fragment F has the following structure:
|
|
||||||
|
|
||||||
[ 32 bytes blake2 keyed hash of the following 576 bytes (h)]
|
|
||||||
[ 32 bytes random nonce (n)]
|
|
||||||
[ 544 bytes encrypted payload (p)]
|
|
||||||
|
|
||||||
the recipiant verifies F.h == MDS(F.n + F.p, rx_K) and the UTP session
|
|
||||||
is reset if verification fails.
|
|
||||||
|
|
||||||
the decrypted payload P has the following structure:
|
|
||||||
|
|
||||||
[ 24 bytes random (A) ]
|
|
||||||
[ big endian unsigned 32 bit message id (I) ]
|
|
||||||
[ big endian unsigned 16 bit fragment length (N) ]
|
|
||||||
[ big endian unsigned 16 bit fragment remaining bytes (R) ]
|
|
||||||
[ N bytes of plaintext payload (X) ]
|
|
||||||
[ trailing bytes discarded ]
|
|
||||||
|
|
||||||
link layer messages fragmented and delievered in any order the sender chooses.
|
|
||||||
|
|
||||||
recipaint ensures a buffer for message number P.I exists, allocating one if it
|
|
||||||
does not exist.
|
|
||||||
|
|
||||||
recipiant appends P.X to the end of the buffer for message P.I
|
|
||||||
|
|
||||||
if P.R is zero then message number P.I is completed and processed as a link
|
|
||||||
layer messages. otherwise the recipiant expects P.R additional bytes.
|
|
||||||
P.R's value MUST decrease by P.N in the next fragment sent.
|
|
||||||
|
|
||||||
message size MUST NOT exceed 8192 bytes.
|
|
||||||
|
|
||||||
if a message is not received in 2 seconds it is discarded and any further
|
|
||||||
fragments for the message are also discarded.
|
|
||||||
|
|
||||||
P.I MUST have the initial value 0
|
|
||||||
P.I MUST be incremeneted by 1 for each new messsage transmitted
|
|
||||||
P.I MAY wrap around back to 0
|
|
||||||
|
|
||||||
|
|
||||||
after every fragment F the session key K is mutated via:
|
|
||||||
|
|
||||||
K = HS(K + P.A)
|
|
||||||
|
|
||||||
Periodically the connection initiator MUST renegotiate the session key by
|
|
||||||
sending a LIM after L.p milliseconds have elapsed.
|
|
||||||
|
|
||||||
If the local RC changes while a connection is established they MUST
|
|
||||||
renegotioate the session keys by sending a LIM to ensure the new RC is sent.
|
|
||||||
|
|
||||||
|
|
||||||
references:
|
|
||||||
|
|
||||||
[1] http://www.bittorrent.org/beps/bep_0029.html
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,903 +0,0 @@
|
|||||||
LLARP v0
|
|
||||||
|
|
||||||
LLARP (Low Latency Anon Routing Protocol) is a protocol for anonymizing senders and
|
|
||||||
recipiants of encrypted messages sent over the internet without a centralised
|
|
||||||
trusted party.
|
|
||||||
|
|
||||||
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].
|
|
||||||
|
|
||||||
basic structures:
|
|
||||||
|
|
||||||
all structures are key, value dictionaries encoded with bittorrent encoding
|
|
||||||
notation:
|
|
||||||
|
|
||||||
a + b is a concatanated with b
|
|
||||||
|
|
||||||
a ^ b is a bitwise XOR b
|
|
||||||
|
|
||||||
x[a:b] is a memory slice of x from index a to b
|
|
||||||
|
|
||||||
BE(x) is bittorrent encode x
|
|
||||||
|
|
||||||
BD(x) is bittorrent decode x
|
|
||||||
|
|
||||||
{ a: b, y: z } is a dictionary with two keys a and y
|
|
||||||
whose values are b and z respectively
|
|
||||||
|
|
||||||
[ a, b, c ... ] is a list containing a b c and more items in that order
|
|
||||||
|
|
||||||
"<description>" is a bytestring whose contents and length is described by the
|
|
||||||
quoted value <description>
|
|
||||||
|
|
||||||
"<value>" * N is a bytestring containing the <value> concatenated N times.
|
|
||||||
|
|
||||||
cryptography:
|
|
||||||
|
|
||||||
see crypto_v0.txt
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
wire protocol
|
|
||||||
|
|
||||||
see wire-protocol.txt
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
datastructures:
|
|
||||||
|
|
||||||
all datastructures are assumed version 0 if they lack a v value
|
|
||||||
otherwise version is provided by the v value
|
|
||||||
|
|
||||||
all ip addresses can be ipv4 via hybrid dual stack ipv4 mapped ipv6 addresses,
|
|
||||||
i.e ::ffff.8.8.8.8. The underlying implementation MAY implement ipv4 as native
|
|
||||||
ipv4 instead of using a hybrid dual stack.
|
|
||||||
|
|
||||||
net address:
|
|
||||||
|
|
||||||
net addresses are a variable length byte string, if between 7 and 15 bytes it's
|
|
||||||
treated as a dot notation ipv4 address (xxx.xxx.xxx.xxx)
|
|
||||||
if it's exactly 16 bytes it's treated as a big endian encoding ipv6 address.
|
|
||||||
|
|
||||||
address info (AI)
|
|
||||||
|
|
||||||
An address info (AI) defines a publically reachable endpoint
|
|
||||||
|
|
||||||
{
|
|
||||||
c: transport_rank_uint16,
|
|
||||||
d: "<transport dialect name>",
|
|
||||||
e: "<32 bytes public encryption key>",
|
|
||||||
i: "<net address>",
|
|
||||||
p: port_uint16,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
example wank address info:
|
|
||||||
|
|
||||||
{
|
|
||||||
c: 1,
|
|
||||||
d: "wank",
|
|
||||||
e: "<32 bytes of 0x61>",
|
|
||||||
i: "123.123.123.123",
|
|
||||||
p: 1234,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
bencoded form:
|
|
||||||
|
|
||||||
d1:ci1e1:d4:wank1:e32:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1:d3:iwp1:i15:123.123.123.1231:pi1234e1:vi0ee
|
|
||||||
|
|
||||||
Traffic Policy (TP)
|
|
||||||
|
|
||||||
Traffic policy (TP) defines port, protocol and QoS/drop policy.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: protocol_integer,
|
|
||||||
b: port_integeger,
|
|
||||||
d: drop_optional_integer,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
drop d is set to 1 to indicate that packets of protocol a with source port b will be dropped.
|
|
||||||
if d is 0 or not provided this traffic policy does nothing.
|
|
||||||
|
|
||||||
Exit Info (XI)
|
|
||||||
|
|
||||||
An exit info (XI) defines a exit address that can relay exit traffic to the
|
|
||||||
internet.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "<net address exit address>",
|
|
||||||
b: "<net address exit netmask>",
|
|
||||||
k: "<32 bytes public encryption/signing key>",
|
|
||||||
p: [ list, of, traffic, policies],
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Exit Route (XR)
|
|
||||||
|
|
||||||
An exit route (XR) define an allocated exit address and any additional
|
|
||||||
information required to access the internet via that exit address.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "<16 bytes big endian ipv6 gateway address>",
|
|
||||||
b: "<16 bytes big endian ipv6 netmask>",
|
|
||||||
c: "<16 bytes big endian ipv6 source address>",
|
|
||||||
l: lifetime_in_milliseconds_uint64,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
router contact (RC)
|
|
||||||
|
|
||||||
router's full identity
|
|
||||||
|
|
||||||
{
|
|
||||||
a: [ one, or, many, AI, here ... ],
|
|
||||||
e: extensions_supported,
|
|
||||||
i: "<max 8 bytes network identifier>",
|
|
||||||
k: "<32 bytes public long term identity signing key>",
|
|
||||||
n: "<optional max 32 bytes router nickname>",
|
|
||||||
p: "<32 bytes public path encryption key>",
|
|
||||||
s: [services, supported],
|
|
||||||
u: time_signed_at_milliseconds_since_epoch_uint64,
|
|
||||||
v: 0,
|
|
||||||
x: [ Exit, Infos ],
|
|
||||||
z: "<64 bytes signature using identity key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
e is a dict containing key/value of supported protocol extensions on this service node, keys are strings, values MUST be 0 or 1.
|
|
||||||
|
|
||||||
s is a list of services on this service node:
|
|
||||||
|
|
||||||
each service is a 6 item long, list of the following:
|
|
||||||
|
|
||||||
["_service", "_proto", ttl_uint, priority_uint, weight_uint, port_uint]
|
|
||||||
|
|
||||||
with the corrisponding SRV record:
|
|
||||||
|
|
||||||
<_service>.<_proto>.router_pubkey_goes_here.snode. <ttl_uint> IN SRV <priority_uint> <weight_uint> <port_uint> router_pubkey_goes_here.snode
|
|
||||||
|
|
||||||
|
|
||||||
RC.t is the timestamp of when this RC was signed.
|
|
||||||
RC is valid for a maximum of 1 hour after which it MUST be resigned with the new
|
|
||||||
timestamp.
|
|
||||||
|
|
||||||
RC.i is set to the network identifier.
|
|
||||||
|
|
||||||
only routers with the same network identifier may connect to each other.
|
|
||||||
|
|
||||||
"testnet" for testnet.
|
|
||||||
"lokinet" for the "official" lokinet mainline network.
|
|
||||||
|
|
||||||
other values of RC.i indicate the router belongs to a network fork.
|
|
||||||
|
|
||||||
|
|
||||||
service info (SI)
|
|
||||||
|
|
||||||
public information blob for a hidden service
|
|
||||||
|
|
||||||
e is the long term public encryption key
|
|
||||||
s is the long term public signing key
|
|
||||||
v is the protocol version
|
|
||||||
x is a nounce value for generating vanity addresses that can be omitted
|
|
||||||
|
|
||||||
if x is included it MUST be equal to 16 bytes
|
|
||||||
|
|
||||||
{
|
|
||||||
e: "<32 bytes public encryption key>",
|
|
||||||
s: "<32 bytes public signing key>",
|
|
||||||
v: 0,
|
|
||||||
x: "<optional 16 bytes nonce for vanity>"
|
|
||||||
}
|
|
||||||
|
|
||||||
service address (SA)
|
|
||||||
|
|
||||||
the "network address" of a hidden service, which is computed as the blake2b
|
|
||||||
256 bit hash of the public infomration blob.
|
|
||||||
|
|
||||||
HS(BE(SI))
|
|
||||||
|
|
||||||
when in string form it's encoded with z-base32 and uses the .loki tld
|
|
||||||
|
|
||||||
introduction (I)
|
|
||||||
|
|
||||||
a descriptor annoucing a path to a hidden service
|
|
||||||
|
|
||||||
k is the rc.k value of the router to contact
|
|
||||||
p is the path id on the router that is owned by the service
|
|
||||||
v is the protocol version
|
|
||||||
x is the timestamp milliseconds since epoch that this introduction expires at
|
|
||||||
|
|
||||||
{
|
|
||||||
k: "<32 bytes public identity key of router>",
|
|
||||||
l: advertised_path_latency_ms_uint64, (optional)
|
|
||||||
p: "<16 bytes path id>",
|
|
||||||
v: 0,
|
|
||||||
x: time_expires_milliseconds_since_epoch_uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
introduction set (IS)
|
|
||||||
|
|
||||||
and introset is a signed set of introductions for a hidden service
|
|
||||||
a is the service info of the publisher
|
|
||||||
h is a list of srv records in same format as in RCs
|
|
||||||
i is the list of introductions that this service is advertising with
|
|
||||||
k is the public key to use when doing encryption to this hidden service
|
|
||||||
n is a 16 byte null padded utf-8 encoded string tagging the hidden service in
|
|
||||||
a topic searchable via a lookup (optional)
|
|
||||||
v is the protocol version
|
|
||||||
w is an optinal proof of work for DoS protection (slot for future)
|
|
||||||
z is the signature of the entire IS where z is set to zero signed by the hidden
|
|
||||||
service's signing key.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: SI,
|
|
||||||
h: [list, of, advertised, services],
|
|
||||||
i: [ I, I, I, ... ],
|
|
||||||
k: "<1218 bytes sntrup4591761 public key block>",
|
|
||||||
n: "<16 bytes service topic (optional)>",
|
|
||||||
t: timestamp_uint64_milliseconds_since_epoch_published_at,
|
|
||||||
v: 0,
|
|
||||||
w: optional proof of work,
|
|
||||||
z: "<64 bytes signature using service info signing key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
h is a list of services on this endpoint
|
|
||||||
|
|
||||||
each service is a 7 item long, list of the following:
|
|
||||||
|
|
||||||
["_service", "_proto", ttl_uint, priority_uint, weight_uint, port_uint, "<32 bytes SA of the service>"]
|
|
||||||
|
|
||||||
with the corrisponding SRV record:
|
|
||||||
|
|
||||||
<_service>.<_proto>.<service_address>.loki. <ttl_uint> IN SRV <priority_uint> <weight_uint> <port_uint> <SA of sub service>.loki.
|
|
||||||
|
|
||||||
recursion on SRV records is NOT permitted.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Encrypted frames:
|
|
||||||
|
|
||||||
|
|
||||||
Encrypted frames are encrypted containers for link message records like LRCR.
|
|
||||||
|
|
||||||
32 bytes hmac, h
|
|
||||||
32 bytes nounce, n
|
|
||||||
32 bytes ephmeral sender's public encryption key, k
|
|
||||||
remaining bytes ciphertext, x
|
|
||||||
|
|
||||||
decryption:
|
|
||||||
|
|
||||||
0) verify hmac
|
|
||||||
|
|
||||||
S = PKE(n, k, our_RC.K)
|
|
||||||
verify h == MDS(n + k + x, S)
|
|
||||||
|
|
||||||
If the hmac verification fails the entire parent message is discarded
|
|
||||||
|
|
||||||
1) decrypt and decode
|
|
||||||
|
|
||||||
new_x = SD(S, n[0:24], x)
|
|
||||||
msg = BD(new_x)
|
|
||||||
|
|
||||||
If the decoding fails the entire parent message is discarded
|
|
||||||
|
|
||||||
encryption:
|
|
||||||
|
|
||||||
to encrypt a frame to a router with public key B.k
|
|
||||||
|
|
||||||
0) prepare nounce n, ephemeral keypair (A.k, s) and derive shared secret S
|
|
||||||
|
|
||||||
A.k, s = ECKG()
|
|
||||||
n = RAND(32)
|
|
||||||
S = PKE(p, A.k, B.k, n)
|
|
||||||
|
|
||||||
1) encode and encrypt
|
|
||||||
|
|
||||||
x = BE(msg)
|
|
||||||
new_x = SE(S, n[0:24], x)
|
|
||||||
|
|
||||||
2) generate message authentication
|
|
||||||
|
|
||||||
h = MDS(n + A.k + new_x, S)
|
|
||||||
|
|
||||||
resulting frame is h + n + A.k + new_x
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
link layer messages:
|
|
||||||
|
|
||||||
the link layer is responsible for anonymising the source and destination of
|
|
||||||
routing layer messages.
|
|
||||||
|
|
||||||
any link layer message without a key v is assumed to be version 0 otherwise
|
|
||||||
indicates the protocol version in use.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
link introduce message (LIM)
|
|
||||||
|
|
||||||
This message MUST be the first link message sent before any others. This message
|
|
||||||
identifies the sender as having the RC contained in r. The recipiant MUST
|
|
||||||
validate the RC's signature and ensure that the public key in use is listed in
|
|
||||||
the RC.a matching the ipv6 address it originated from.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "i",
|
|
||||||
e: "<32 bytes ephemeral public encryption key>",
|
|
||||||
n: "<32 bytes nonce for key exhcange>",
|
|
||||||
p: uint64_milliseconds_session_period,
|
|
||||||
r: RC,
|
|
||||||
v: 0,
|
|
||||||
z: "<64 bytes signature of entire message by r.k>"
|
|
||||||
}
|
|
||||||
|
|
||||||
the link session will be kept open for p milliseconds after which
|
|
||||||
the session MUST be renegotiated.
|
|
||||||
|
|
||||||
link relay commit message (LRCM)
|
|
||||||
|
|
||||||
request a commit to relay traffic to another node.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "c",
|
|
||||||
c: [ list, of, encrypted, LRCR frames ],
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
c MUST contain dummy records if the hop length is less than the maximum
|
|
||||||
hop length.
|
|
||||||
|
|
||||||
link relay commit record (LRCR)
|
|
||||||
|
|
||||||
record requesting relaying messages for 600 seconds to router
|
|
||||||
on network whose i is equal to RC.k and decrypt data any messages using
|
|
||||||
PKE(n, rc.p, c) as symmetric key for encryption and decryption.
|
|
||||||
|
|
||||||
if l is provided and is less than 600 and greater than 10 then that lifespan
|
|
||||||
is used (in seconds) instead of 600 seconds.
|
|
||||||
|
|
||||||
if w is provided and fits the required proof of work then the lifetime of
|
|
||||||
the path is extended by w.y seconds
|
|
||||||
|
|
||||||
{
|
|
||||||
c: "<32 byte public encryption key used for upstream>",
|
|
||||||
d: uint_optional_ms_delay, // TODO
|
|
||||||
i: "<32 byte RC.k of next hop>",
|
|
||||||
l: uint_optional_lifespan,
|
|
||||||
n: "<32 bytes nounce for key exchange>",
|
|
||||||
r: "<16 bytes rx path id>",
|
|
||||||
t: "<16 bytes tx path id>",
|
|
||||||
u: "<optional RC of the next hop>",
|
|
||||||
v: 0,
|
|
||||||
w: proof of work
|
|
||||||
}
|
|
||||||
|
|
||||||
w if provided is a dict with the following struct
|
|
||||||
|
|
||||||
{
|
|
||||||
t: time_created_seconds_since_epoch,
|
|
||||||
v: 0,
|
|
||||||
y: uint32_seconds_extended_lifetime,
|
|
||||||
z: "<32 bytes nonce>"
|
|
||||||
}
|
|
||||||
|
|
||||||
the validity of the proof of work is that given
|
|
||||||
|
|
||||||
h = HS(BE(w))
|
|
||||||
|
|
||||||
h has log_e(y) prefixed bytes being 0x00
|
|
||||||
|
|
||||||
this proof of work requirement is subject to change
|
|
||||||
|
|
||||||
if i is equal to RC.k then any LRDM.x values are decrypted and interpreted as
|
|
||||||
routing layer messages. This indicates that we are the farthest hop in the path.
|
|
||||||
|
|
||||||
link relay status message (LRSM)
|
|
||||||
|
|
||||||
response to path creator about build status
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "s",
|
|
||||||
c: [ list, of, encrypted, LRSR frames],
|
|
||||||
p: "<16 bytes rx path id>",
|
|
||||||
s: uint_status_flags, // for now, only set (or don't) success bit
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
the creator of the LRSM MUST include dummy LRSR records
|
|
||||||
to pad out to the maximum path length
|
|
||||||
|
|
||||||
link relay status record (LRSR)
|
|
||||||
|
|
||||||
record indicating status of path build
|
|
||||||
|
|
||||||
{
|
|
||||||
s: uint_status_flags,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
uint_status_flags (bitwise booleans):
|
|
||||||
[0] = success
|
|
||||||
[1] = fail, hop timeout
|
|
||||||
[2] = fail, congestion
|
|
||||||
[3] = fail, refusal, next hop is not known to be a snode
|
|
||||||
[4] = fail, used by hop creator when decrypting frames if decryption fails
|
|
||||||
[5] = fail, used by hop creator when record decode fails
|
|
||||||
[4-63] reserved
|
|
||||||
|
|
||||||
link relay upstream message (LRUM)
|
|
||||||
|
|
||||||
sent to relay data via upstream direction of a previously created path.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "u",
|
|
||||||
p: "<16 bytes path id>",
|
|
||||||
v: 0,
|
|
||||||
x: "<N bytes encrypted x1 value>",
|
|
||||||
y: "<32 bytes nonce>"
|
|
||||||
}
|
|
||||||
|
|
||||||
x1 = SD(k, y, x)
|
|
||||||
|
|
||||||
if we are the farthest hop, process x1 as a routing message
|
|
||||||
otherwise transmit a LRUM to the next hop
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "u",
|
|
||||||
p: p,
|
|
||||||
v: 0,
|
|
||||||
x: x1,
|
|
||||||
y: y ^ HS(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
link relay downstream message (LRDM)
|
|
||||||
|
|
||||||
sent to relay data via downstream direction of a previously created path.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "d",
|
|
||||||
p: "<16 bytes path id>",
|
|
||||||
v: 0,
|
|
||||||
x: "<N bytes encrypted x1 value>",
|
|
||||||
y: "<32 bytes nonce>"
|
|
||||||
}
|
|
||||||
|
|
||||||
if we are the creator of the path decrypt x for each hop key k
|
|
||||||
|
|
||||||
x = SD(k, y, x)
|
|
||||||
|
|
||||||
otherwise transmit LRDM to next hop
|
|
||||||
|
|
||||||
x1 = SE(k, y, x)
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "d",
|
|
||||||
p: p,
|
|
||||||
v: 0,
|
|
||||||
x: x1,
|
|
||||||
y: y ^ HS(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
link immediate dht message (LIDM):
|
|
||||||
|
|
||||||
transfer one or more dht messages directly without a previously made path.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "m",
|
|
||||||
m: [many, dht, messages],
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
link immediate state message (LISM)
|
|
||||||
|
|
||||||
transfer a state message between nodes
|
|
||||||
|
|
||||||
{
|
|
||||||
a: "s",
|
|
||||||
s: state_message,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
state message:
|
|
||||||
|
|
||||||
NOTE: this message type is currently a documentation stub and remains unimplemented.
|
|
||||||
|
|
||||||
state messages propagate changes to the service nodes concensous to thin clients.
|
|
||||||
|
|
||||||
service node joined network
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "J",
|
|
||||||
R: "<32 bytes public key>",
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
service node parted network
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "P",
|
|
||||||
R: "<32 bytes public key>",
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
service node list request
|
|
||||||
|
|
||||||
request the service node list starting at index I containing R entries
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "R",
|
|
||||||
I: starting_offset_int,
|
|
||||||
R: number_of_entires_to_request_int,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
service node list response
|
|
||||||
|
|
||||||
response to service node list request
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "L",
|
|
||||||
S: {
|
|
||||||
"<32 bytes pubkey>" : 1,
|
|
||||||
"<32 bytes pubkey>" : 1,
|
|
||||||
....
|
|
||||||
},
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
routing layer:
|
|
||||||
|
|
||||||
the routing layer provides inter network communication between the LLARP link
|
|
||||||
layer and ip (internet protocol) for exit traffic or ap (anonymous protocol) for
|
|
||||||
hidden services. replies to messages are sent back via the path they
|
|
||||||
originated from inside a LRDM. all routing messages have an S value which
|
|
||||||
provides the sequence number of the message so the messages can be ordered.
|
|
||||||
|
|
||||||
ipv4 addresses are allowed via ipv4 mapped ipv6 addresses, i.e. ::ffff.10.0.0.1
|
|
||||||
|
|
||||||
path confirm message (PCM)
|
|
||||||
|
|
||||||
sent as the first message down a path after it's built to confirm it was built
|
|
||||||
|
|
||||||
always the first message sent
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "P",
|
|
||||||
L: uint64_milliseconds_path_lifetime,
|
|
||||||
S: 0,
|
|
||||||
T: uint64_milliseconds_sent_timestamp,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
path latency message (PLM)
|
|
||||||
|
|
||||||
a latency measurement message, reply with a PLM response if we are the far end
|
|
||||||
of a path.
|
|
||||||
|
|
||||||
variant 1, request, generated by the path creator:
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "L",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
variant 2, response, generated by the endpoint that recieved the request.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "L",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: uint64_timestamp_recieved,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
obtain exit message (OXM)
|
|
||||||
|
|
||||||
sent to an exit router to obtain ip exit traffic context.
|
|
||||||
replies are sent down the path that messages originate from.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "X",
|
|
||||||
B: [list, of, permitted, blacklisted, traffic, policies],
|
|
||||||
E: 0 for snode communication or 1 for internet access,
|
|
||||||
I: "<32 bytes signing public key for future communication>",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: uint64_transaction_id,
|
|
||||||
V: 0,
|
|
||||||
W: [list, of, required, whitelisted, traffic, policies],
|
|
||||||
X: lifetime_of_address_mapping_in_seconds_uint64,
|
|
||||||
Z: "<64 bytes signature using I>"
|
|
||||||
}
|
|
||||||
|
|
||||||
grant exit messsage (GXM)
|
|
||||||
|
|
||||||
sent in response to an OXM to grant an ip for exit traffic from an external
|
|
||||||
ip address used for exit traffic.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "G",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
Y: "<16 byte nonce>",
|
|
||||||
V: 0,
|
|
||||||
Z: "<64 bytes signature>"
|
|
||||||
}
|
|
||||||
|
|
||||||
any TITM recieved on the same path will be forwarded out to the internet if
|
|
||||||
OXAM.E is not 0, otherwise it is interpreted as service node traffic.
|
|
||||||
|
|
||||||
|
|
||||||
reject exit message (RXM)
|
|
||||||
|
|
||||||
sent in response to an OXAM to indicate that exit traffic is not allowed or
|
|
||||||
was denied.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "J",
|
|
||||||
B: backoff_milliseconds_uint64,
|
|
||||||
R: [list, of, rejected, traffic, policies],
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0,
|
|
||||||
Y: "<16 byte nonce>",
|
|
||||||
Z: "<64 bytes signature>"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
discarded data fragment message (DDFM)
|
|
||||||
|
|
||||||
sent in reply to TDFM when we don't have a path locally or are doing network
|
|
||||||
congestion control from a TITM.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "D",
|
|
||||||
P: "<16 bytes path id>",
|
|
||||||
S: uint64_sequence_number_of_fragment_dropped,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
transfer data fragment message (TDFM)
|
|
||||||
|
|
||||||
transfer data between paths.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "T",
|
|
||||||
P: "<16 bytes path id>",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: hidden_service_frame,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
transfer data to another path with id P on the local router place a random 32
|
|
||||||
byte and T values into y and z values into a LRDM message (respectively) and
|
|
||||||
send it in the downstream direction. if this path does not exist on the router
|
|
||||||
it is replied to with a DDFM.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
hidden service data (HSD)
|
|
||||||
|
|
||||||
data sent anonymously over the network to a recipiant from a sender.
|
|
||||||
sent inside a HSFM encrypted with a shared secret.
|
|
||||||
|
|
||||||
{
|
|
||||||
a: protocol_number_uint,
|
|
||||||
d: "<N bytes payload>",
|
|
||||||
i: Introduction for reply,
|
|
||||||
n: uint_message_sequence_number,
|
|
||||||
o: N seconds until this converstation plans terminate,
|
|
||||||
s: SI of sender,
|
|
||||||
t: "<16 bytes converstation_tag>,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hidden service frame (HSF)
|
|
||||||
|
|
||||||
TODO: document this better
|
|
||||||
|
|
||||||
establish converstation tag message (variant 1)
|
|
||||||
|
|
||||||
generate a new convotag that is contained inside an encrypted HSD
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "H",
|
|
||||||
C: "<1048 bytes ciphertext block>",
|
|
||||||
D: "<N bytes encrypted HSD>",
|
|
||||||
F: "<16 bytes source path_id>",
|
|
||||||
N: "<32 bytes nonce for key exchange>",
|
|
||||||
V: 0,
|
|
||||||
Z: "<64 bytes signature of entire message using sender's signing key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
alice (A) wants to talk to bob (B) over the network, both have hidden services
|
|
||||||
set up and are online on the network.
|
|
||||||
|
|
||||||
A and B are both referring to alice and bob's SI respectively.
|
|
||||||
A_sk is alice's private signing key.
|
|
||||||
|
|
||||||
for alice (A) to send the string "beep" to bob (B), alice picks an introduction
|
|
||||||
to use on one of her paths (I_A) such that I_A is aligning with one of bobs's
|
|
||||||
paths (I_B)
|
|
||||||
|
|
||||||
alice generates:
|
|
||||||
|
|
||||||
T = RAND(16)
|
|
||||||
|
|
||||||
m = {
|
|
||||||
a: 0,
|
|
||||||
d: "beep",
|
|
||||||
i: I_A,
|
|
||||||
n: 0,
|
|
||||||
s: A,
|
|
||||||
t: T,
|
|
||||||
v: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
X = BE(m)
|
|
||||||
|
|
||||||
C, K = PQKE_A(I_B.k)
|
|
||||||
N = RAND(32)
|
|
||||||
D = SE(X, K, N)
|
|
||||||
|
|
||||||
path = PickSendPath()
|
|
||||||
|
|
||||||
M = {
|
|
||||||
A: "T",
|
|
||||||
P: I_B.P,
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: {
|
|
||||||
A: "H",
|
|
||||||
C: C,
|
|
||||||
D: D,
|
|
||||||
F: path.lastHop.txID,
|
|
||||||
N: N,
|
|
||||||
V: 0,
|
|
||||||
Z: "\x00" * 64
|
|
||||||
},
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Z = S(A_sk, BE(M))
|
|
||||||
|
|
||||||
alice transmits a TDFM to router with public key I_B.K via her path that ends
|
|
||||||
with router with public key I_B.k
|
|
||||||
|
|
||||||
path = PickSendPath()
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "T",
|
|
||||||
P: I_B.P,
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: {
|
|
||||||
A: "H",
|
|
||||||
C: C,
|
|
||||||
D: D,
|
|
||||||
F: path.lastHop.txID,
|
|
||||||
N: N,
|
|
||||||
V: 0,
|
|
||||||
Z: Z
|
|
||||||
},
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
the shared secret (S) for further message encryption is:
|
|
||||||
|
|
||||||
S = HS(K + PKE(A, B, sk, N))
|
|
||||||
|
|
||||||
given sk is the local secret encryption key used by the current hidden service
|
|
||||||
|
|
||||||
please note:
|
|
||||||
signature verification of the outer message can only be done after decryption
|
|
||||||
because the signing keys are inside the encrypted HSD.
|
|
||||||
|
|
||||||
data from a previously made session (variant 2)
|
|
||||||
|
|
||||||
transfer data on a converstation previously made
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "H",
|
|
||||||
D: "<N bytes encrypted HSD>",
|
|
||||||
F: "<16 bytes path id of soruce>",
|
|
||||||
N: "<32 bytes nonce for symettric cipher>",
|
|
||||||
T: "<16 bytes converstation tag>",
|
|
||||||
V: 0,
|
|
||||||
Z: "<64 bytes signature using sender's signing key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
reject a message sent on a convo tag, when a remote endpoint
|
|
||||||
sends this message a new converstation SHOULD be established.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "H",
|
|
||||||
F: "<16 bytes path id of soruce>",
|
|
||||||
R: 1,
|
|
||||||
T: "<16 bytes converstation tag>",
|
|
||||||
V: 0,
|
|
||||||
Z: "<64 bytes signature using sender's signing key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
transfer ip traffic message (TITM)
|
|
||||||
|
|
||||||
transfer ip traffic
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "I",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
V: 0,
|
|
||||||
X: [list, of, ip, packet, buffers],
|
|
||||||
}
|
|
||||||
|
|
||||||
an ip packet buffer is prefixed with a 64 bit big endian unsigned integer
|
|
||||||
denoting the sequence number for re-ordering followed by the ip packet itself.
|
|
||||||
|
|
||||||
X is parsed as a list of IP packet buffers.
|
|
||||||
for each ip packet the source addresss is extracted and sent on the
|
|
||||||
appropriate network interface.
|
|
||||||
|
|
||||||
When we receive an ip packet from the internet to an exit address, we put it
|
|
||||||
into a TITM, and send it downstream the corresponding path in an LRDM.
|
|
||||||
|
|
||||||
update exit path message (UXPM)
|
|
||||||
|
|
||||||
sent from a new path by client to indicate that a previously established exit
|
|
||||||
should use the new path that this message came from.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "U",
|
|
||||||
P: "<16 bytes previous tx path id>",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: uint64_txid,
|
|
||||||
V: 0,
|
|
||||||
Y: "<16 bytes nonce>",
|
|
||||||
Z: "<64 bytes signature using previously provided signing key>"
|
|
||||||
}
|
|
||||||
|
|
||||||
close exit path message (CXPM)
|
|
||||||
|
|
||||||
client sends a CXPM when the exit is no longer needed or by the exit if the
|
|
||||||
exit wants to close prematurely.
|
|
||||||
also sent by exit in reply to a CXPM to confirm close.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "C",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
V: 0,
|
|
||||||
Y: "<16 bytes nonce>",
|
|
||||||
Z: "<64 bytes signature>"
|
|
||||||
}
|
|
||||||
|
|
||||||
update exit verify message (EXVM)
|
|
||||||
|
|
||||||
sent in reply to a UXPM to verify that the path handover was accepted
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "V",
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
T: uint64_txid,
|
|
||||||
V: 0,
|
|
||||||
Y: "<16 bytes nonce>",
|
|
||||||
Z: "<64 bytes signature>"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DHT message holder message:
|
|
||||||
|
|
||||||
wrapper message for sending many dht messages down a path.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "M",
|
|
||||||
M: [many, dht, messages, here],
|
|
||||||
S: uint64_sequence_number,
|
|
||||||
V: 0
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
What is a RouterEvent?
|
|
||||||
|
|
||||||
A RouterEvent is a way of representing a conceptual event that took place in a "router" (relay or client).
|
|
||||||
|
|
||||||
RouterEvents are used in order to collect information about a network all in one place and preserve causality.
|
|
||||||
|
|
||||||
How do I make a new RouterEvent?
|
|
||||||
|
|
||||||
Add your event following the structure in llarp/tooling/router_event.{hpp,cpp}
|
|
||||||
|
|
||||||
Add your event to pybind in pybind/llarp/tooling/router_event.cpp
|
|
||||||
|
|
||||||
What if a class my event uses is missing members in pybind?
|
|
||||||
|
|
||||||
Find the relevant file pybind/whatever/class.cpp and remedy that!
|
|
||||||
|
|
||||||
What if I need to refer to classes which aren't available already in pybind?
|
|
||||||
|
|
||||||
Add pybind/namespace/namespace/etc/class.cpp and pybind it!
|
|
||||||
|
|
||||||
You will need to edit the following files accordingly:
|
|
||||||
pybind/common.hpp
|
|
||||||
pybind/module.cpp
|
|
||||||
pybind/CMakeLists.txt
|
|
||||||
|
|
||||||
How do I use a RouterEvent?
|
|
||||||
|
|
||||||
From the cpp side, find the place in the code where it makes the most logical sense
|
|
||||||
that the conceptual event has taken place (and you have the relevant information to
|
|
||||||
create the "event" instance) and create it there as follows:
|
|
||||||
|
|
||||||
#include <llarp/tooling/relevant_event_header.hpp>
|
|
||||||
|
|
||||||
where the event takes place, do the following:
|
|
||||||
auto event = std::make_unique<event_type_here>(constructor_args...);
|
|
||||||
somehow_get_a_router->NotifyRouterEvent(std::move(event));
|
|
||||||
|
|
||||||
From the Python side...it's a python object!
|
|
@ -1,309 +0,0 @@
|
|||||||
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>
|
|
Loading…
Reference in New Issue