mirror of https://github.com/oxen-io/lokinet
Merge branch 'master' of https://github.com/majestrate/llarp
commit
38b0388826
@ -1,261 +0,0 @@
|
|||||||
DHT messages
|
|
||||||
|
|
||||||
these messages can be either wrapped in a LIDM message or sent anonymously over a path
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
find introduction message (FIM)
|
|
||||||
|
|
||||||
recursively find an IS.
|
|
||||||
|
|
||||||
variant 1, by SA
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "F",
|
|
||||||
R: r_counter,
|
|
||||||
S: "<32 bytes SA>",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
variant 2, by claimed name
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "F",
|
|
||||||
N: "service.name.tld",
|
|
||||||
R: r_counter,
|
|
||||||
T: transaction_id_uin64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Transactions will persist until replied to by a GIM or 60 seconds, whichever
|
|
||||||
is reached first.
|
|
||||||
|
|
||||||
If the timeout is reached before a GIM or the forwarding of the request fails:
|
|
||||||
|
|
||||||
* close transaction
|
|
||||||
* close linked transactions
|
|
||||||
|
|
||||||
if R is non-zero and less or equal to than 5:
|
|
||||||
|
|
||||||
* decrement R by 1
|
|
||||||
* open a transaction with id T for sender's RC.k
|
|
||||||
* pick random dht capable router, F
|
|
||||||
* generate new transaction id, U
|
|
||||||
* open a transaction with id U for F.k
|
|
||||||
* link transaction U to transaction T
|
|
||||||
* send FIM with transaction id U to F
|
|
||||||
|
|
||||||
if R is greater than 5 or less than 0:
|
|
||||||
|
|
||||||
* increment shitlist value of sender's RC.k by 1
|
|
||||||
* if the shitlist value for sender's RC.k is less than 10 reply with a GIM with
|
|
||||||
an X
|
|
||||||
* if the shitlist value for sender's RC.k is equal to or greater than 10 drop
|
|
||||||
the message
|
|
||||||
|
|
||||||
if R is zero and we have 1 or more IS at position S in dht keyspace:
|
|
||||||
|
|
||||||
* reply with a GIM holding the IS who contains the introducer with the highest
|
|
||||||
expiration timestamp
|
|
||||||
|
|
||||||
if R is zero and we do not have any IS at position S in dht keyspace:
|
|
||||||
|
|
||||||
* find a router who's RC.k is closest to S, N
|
|
||||||
|
|
||||||
if N is our router:
|
|
||||||
|
|
||||||
* reply with a GIM with an empty X value
|
|
||||||
|
|
||||||
if N is not our router:
|
|
||||||
|
|
||||||
* open transaction with id T for sender's RC.k
|
|
||||||
* generate new transaction id, U
|
|
||||||
* open transaction with id U for N.k
|
|
||||||
* link transaction U to transaction T
|
|
||||||
* forward request to N using transaction id U
|
|
||||||
|
|
||||||
|
|
||||||
got introduction message (GIM)
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "G",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0,
|
|
||||||
X: [ IS, IS, IS, ... ]
|
|
||||||
}
|
|
||||||
|
|
||||||
if we have a transaction with id T:
|
|
||||||
|
|
||||||
* forward the GIM to all linked transactions
|
|
||||||
* terminate transaction T
|
|
||||||
|
|
||||||
when a linked transaction gets a GIM:
|
|
||||||
|
|
||||||
* set T to the current transaction id
|
|
||||||
* foward the GIM to the requester of T
|
|
||||||
|
|
||||||
publish introduction message (PIM)
|
|
||||||
|
|
||||||
publish one or many IM into the dht at once.
|
|
||||||
each IS will be placed in the dht
|
|
||||||
|
|
||||||
version 0 uses the SA of each 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 3 +/- 2 by the sender.
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "P",
|
|
||||||
R: r_counter,
|
|
||||||
V: 0,
|
|
||||||
X: [ IS, IS, IS, ... ]
|
|
||||||
}
|
|
||||||
|
|
||||||
The following steps happen in order:
|
|
||||||
|
|
||||||
first stage: reduction
|
|
||||||
|
|
||||||
if X's length is divisble by 2:
|
|
||||||
|
|
||||||
* split X in half as J and K
|
|
||||||
* generate 2 new PIM with the same values as the parent with empty X
|
|
||||||
* put J and K into the new PIM's X values
|
|
||||||
* associate the 2 new PIM with the current PIM batch
|
|
||||||
|
|
||||||
if X's length is not divisible by 2 and greater than 1:
|
|
||||||
|
|
||||||
* pop off an IS from X as A
|
|
||||||
* generate a new PIM with the same values as the parent with an X value of A
|
|
||||||
* associate the new PIM with the current PIM batch
|
|
||||||
* associate the old PIM having A removed from X with the current PIM batch
|
|
||||||
|
|
||||||
if X's length is 1:
|
|
||||||
|
|
||||||
* associate the PIM with the current PIM batch
|
|
||||||
|
|
||||||
any other cases for X are ignored.
|
|
||||||
|
|
||||||
for each PIM in the current batch:
|
|
||||||
|
|
||||||
if R is greater than 0:
|
|
||||||
|
|
||||||
* decrement R by 1
|
|
||||||
* queue the PIM for shuffle (second stage)
|
|
||||||
|
|
||||||
if R is 0:
|
|
||||||
|
|
||||||
* queue the PIM for distribution (third stage)
|
|
||||||
|
|
||||||
if R is less than 0:
|
|
||||||
|
|
||||||
* drop the message entirely
|
|
||||||
|
|
||||||
second stage: shuffle
|
|
||||||
|
|
||||||
* The dht node waits until we have collected 10 or more PIM or for 5 seconds,
|
|
||||||
which ever comes first.
|
|
||||||
* shuffle the list of IS randomly
|
|
||||||
* re-combine the IS into new PIMs
|
|
||||||
* queue each newly shuffled PIM for distribution (third stage)
|
|
||||||
|
|
||||||
if we collected 10 or more PIM:
|
|
||||||
|
|
||||||
* X holds 5 IS at most
|
|
||||||
|
|
||||||
if we collected less than 10 but more than 1 PIM:
|
|
||||||
|
|
||||||
* X holds 2 IS at most
|
|
||||||
|
|
||||||
if we only collected 1 PIM:
|
|
||||||
|
|
||||||
* the single PIM is unmodified
|
|
||||||
|
|
||||||
|
|
||||||
third stage: distribution
|
|
||||||
|
|
||||||
if R is less than 0:
|
|
||||||
|
|
||||||
* drop message and terminate current transaction, this should never happen but
|
|
||||||
this case is left here in the event of implementation bugs.
|
|
||||||
|
|
||||||
if R is greater than 0:
|
|
||||||
|
|
||||||
* pick a random dht capable router, N
|
|
||||||
* forward the PIM to N
|
|
||||||
|
|
||||||
if R is equal to 0:
|
|
||||||
|
|
||||||
for each IS in X as A:
|
|
||||||
|
|
||||||
* find the router closest to the SA in A, N
|
|
||||||
|
|
||||||
if N is our router:
|
|
||||||
|
|
||||||
* create dht positon S from SA in A
|
|
||||||
* store A for lookup at S
|
|
||||||
|
|
||||||
if N is not our router:
|
|
||||||
|
|
||||||
* send a PIM with X value containing just A to N
|
|
||||||
|
|
||||||
In the future post random walk keyspace batching may be done here.
|
|
||||||
As of version 0, none is done.
|
|
||||||
|
|
||||||
find router contact message (FRCM)
|
|
||||||
|
|
||||||
find a router by long term RC.k public key
|
|
||||||
|
|
||||||
{
|
|
||||||
A: "F",
|
|
||||||
K: "<32 byte public key of router>",
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
find RC who's RC.k is closest to K:
|
|
||||||
|
|
||||||
if A.k is equal to K:
|
|
||||||
|
|
||||||
* reply with a GRCM with an R value of just A
|
|
||||||
|
|
||||||
if A.k is not equal to K and we are closesr to A.k than anyone we know:
|
|
||||||
|
|
||||||
* reply with a GRCM with an empty R value
|
|
||||||
|
|
||||||
find a pending transaction id for K, P
|
|
||||||
|
|
||||||
if P exists:
|
|
||||||
|
|
||||||
* link transaction T to P
|
|
||||||
|
|
||||||
if P does not exist:
|
|
||||||
|
|
||||||
* generate a new transaction id, U
|
|
||||||
* start transaction U for A.k
|
|
||||||
* link transaction U to transaction T
|
|
||||||
* send FRCM to A.k requesting K
|
|
||||||
|
|
||||||
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: "G",
|
|
||||||
R: [RC],
|
|
||||||
T: transaction_id_uint64,
|
|
||||||
V: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
* send a GRCM with R to requesters in all linked transactions
|
|
||||||
* terminate transaction with id T
|
|
||||||
|
|
||||||
notes:
|
|
||||||
|
|
||||||
if we get a GRCM with empty R on one Tx and then one with a filled R on another
|
|
||||||
with the same K, the request is terminated by the first message as not found.
|
|
||||||
A backtrack case is needed.
|
|
@ -0,0 +1,48 @@
|
|||||||
|
onion routing scheme:
|
||||||
|
|
||||||
|
constants:
|
||||||
|
|
||||||
|
K = 8
|
||||||
|
|
||||||
|
A builds a path of length N over R[0], R[1], ... R[N] where A is connected directly to R[0] and N < K
|
||||||
|
|
||||||
|
R[i] is a router on the network
|
||||||
|
R[i].e is the e value in that router's RC
|
||||||
|
R[i].e_sk is the corrisponding secret key for R[i].e
|
||||||
|
|
||||||
|
A builds an LRCM, M that has K ciphertext records in M.b
|
||||||
|
|
||||||
|
A sends M to R[0]
|
||||||
|
|
||||||
|
starting at i = 0
|
||||||
|
|
||||||
|
M is receieved by R[i]
|
||||||
|
|
||||||
|
R[i] takes M.b[0] as a_c verifies hmac and decrypts as a LRCR a_p using:
|
||||||
|
|
||||||
|
h = a_c[0:32]
|
||||||
|
n = a_c[32:64]
|
||||||
|
e_pK = a_c[64:96]
|
||||||
|
x = a_c[96:]
|
||||||
|
|
||||||
|
s_K = PKE(e_pK, R[i].e, R[i].e_sk, n)
|
||||||
|
verify MDS(x, s_K) == h
|
||||||
|
|
||||||
|
|
||||||
|
R[i] generates a response record b_p for a successful path build
|
||||||
|
|
||||||
|
b_p = BE({ c: "a", p: a_p.p, v: 0, x: RAND(512) })
|
||||||
|
|
||||||
|
and encrypts b_p using:
|
||||||
|
|
||||||
|
n = RAND(32)
|
||||||
|
s_K = PKE(a_p.k, R.e, R.e_sk, a_p.n)
|
||||||
|
x = SE(s_k, n, b_p)
|
||||||
|
h = MDS(x, s_k)
|
||||||
|
|
||||||
|
R[i] pops off the first value from M.b (such that M.b[1] is now M.b[0])
|
||||||
|
R[i] pushes to to the end of M.b the bytestring h + n + x
|
||||||
|
|
||||||
|
this is effectively setting M.b[K-1] = h + n + x
|
||||||
|
|
||||||
|
R[i] relays M to router R[i+1] who is the router with RC.k equal to a_p.i
|
Loading…
Reference in New Issue