tox icon indicating copy to clipboard operation
tox copied to clipboard

Onion human documentation

Open kurnevsky opened this issue 6 years ago • 5 comments

Onion module allows nodes to announce their long term public keys and find friends by their long term public keys. There are two basic onion requests - AnnounceRequest and OnionDataRequest. They are enclosed to OnionRequest packets and sent though the onion path to prevent nodes finding out long term public key when they know only temporary DHT public key. There are three types of OnionRequest packets: OnionRequest0, OnionRequest1 and OnionRequest2. AnnounceRequest and OnionDataRequest when created are enclosed to OnionRequest2, OnionRequest2 is enclosed to OnionRequest1 and OnionRequest1 is enclosed to OnionRequest0. When DHT node receives OnionRequest packet it decrypts inner packet and sends it to the next node.

+--------+                       +--------+                       +--------+                       +--------+   +------------------+   +------------+
|        |   +---------------+   |        |   +---------------+   |        |   +---------------+   |        |   | AnnounceRequest  |   |            |
| Sender |---| OnionRequest0 |-->| Node 1 |---| OnionRequest1 |-->| Node 2 |---| OnionRequest2 |-->| Node 3 |---+------------------+-->| Onion node |
|        |   +---------------+   |        |   +---------------+   |        |   +---------------+   |        |   | OnionDataRequest |   |            |
+--------+                       +--------+                       +--------+                       +--------+   +------------------+   +------------+

Similarly to requests there are responses AnnounceResponse and OnionDataResponse that enclosed to three kind of OnionRespose packets: OnionResponse3, OnionResponse2 and OnionResponse1. OnionResponse packets are processed in the same way but with reverse ordering.

+------------+                        +--------+                        +--------+                        +--------+   +-------------------+   +----------+
|            |   +----------------+   |        |   +----------------+   |        |   +----------------+   |        |   | AnnounceResponse  |   |          |
| Onion node |---| OnionResponse3 |-->| Node 3 |---| OnionResponse2 |-->| Node 2 |---| OnionResponse1 |-->| Node 1 |---+-------------------+-->| Receiver |
|            |   +----------------+   |        |   +----------------+   |        |   +----------------+   |        |   | OnionDataResponse |   |          |
+------------+                        +--------+                        +--------+                        +--------+   +-------------------+   +----------+

When onion node handles AnnounceRequest packet it sends answer to original sender using the same onion path with the help of received onion return addresses. But when it handles OnionDataRequest packet it should send response packet to another destination node by its long term public key. That means that when onion node should store long term public keys of announced node along with onion return addresses.

OnionRequest0

  • Decrypt payload using own onion sk, temporary_pk and nonce
  • Take IpPort of next node from decrypted payload
  • Create OnionRequest1 packet using next encrypted payload
  • Encrypt IpPort of sender using OnionReturn format and append it to OnionRequest1
  • Send OnionRequest1 to the next node

OnionRequest1

  • Decrypt payload using own onion sk, temporary_pk and nonce
  • Take IpPort of next node from decrypted payload
  • Create OnionRequest2 packet using next encrypted payload
  • Encrypt IpPort of sender and previous OnionReturn using OnionReturn format and append it to OnionRequest2
  • Send OnionRequest2 to the next node

OnionRequest2

  • Decrypt payload using own onion sk, temporary_pk and nonce
  • Take IpPort of next node from decrypted payload
  • Create AnnounceRequest or OnionDataRequest packet depending on what decprypted payload contains
  • Encrypt IpPort of sender and previous OnionReturn using OnionReturn format and append it to sending packet
  • Send packet to the next node

AnnounceRequest

It's used for announcing ourselves to onion node and for looking for other announced nodes. If we want to announce ourselves we should send one AnnounceRequest packet with PingId set to 0 to acquire correct PingId of onion node. Then using this PingId we can send another AnnounceRequest to be added to onion nodes list. If AnnounceRequest succeed we will get AnnounceResponse with is_stored set to 2. Otherwise is_stored will be set to 0. If we are looking for another node we should send AnnounceRequest packet with PingId set to 0 and with PublicKey of this node. If node is found we will get AnnounceResponse with is_stored set to 1. Otherwise is_stored will be set to 0.

  • Decrypt payload using own sk, real_or_temporary_pk and nonce
  • If PingId from decrypted payload is valid:
    • Try to add node to onion nodes list.
    • If succeed set is_stored variable to 2 or to 0 otherwise.
    • Set PingId to the next variable that stores PingId or PublicKey
  • If PingId from decrypted payload is invalid:
    • Try to find node in onion nodes list using PublicKey for search
    • If found:
      • Set is_stored variable to 1
      • Set PublicKey for data encryption of found node to the next variable that stores PingId or PublicKey
    • If not found:
      • Set is_stored variable to 0
      • Set PingId to the next variable that stores PingId or PublicKey
  • Add up to 4 closest to PublicKey we are searching for DHT nodes from KBucket
  • Create AnnounceResponse packet using real_or_temporary_pk

PingId is a sha256 hash of random secret bytes, current time divided by a 20 second timeout, PublicKey and IpPort of sender.

AnnounceResponse

It's used to respond to AnnounceRequest packet. is_stored variable contains the result of sent request. It might have values:

  • 0: failed to announce ourselves of find requested node
  • 1: requested node is found by it's real PublicKey
  • 2: we successfully announces ourselves

OnionDataRequest

It's used to send data requests to dht node using onion paths. When DHT node receives OnionDataRequest it sends OnionDataResponse to destination node for which data request is intended. Thus, data request will go through 7 intermediate nodes until destination node gets it.

  • Find node by destination_pk in onion nodes list.
  • Drop packit if it's not found.
  • Create OnionDataResponse packet using received OnionDataRequest packet and OnionReturn of found node.
  • Send it to node through which destination node announced itself.

OnionDataResponse

When onion node receives OnionDataRequest packet it converts it to OnionDataResponse and sends to destination node if it announced itself and is contained in onion nodes list.

OnionResponse3

  • Decrypt onion return 3 using own onion symmetric key getting onion return 2 and address of next node
  • Create OnionResponse2 packet using decrypted onion return 2 and payload
  • Send OnionResponse2 packet to decrypted address

OnionResponse2

  • Decrypt onion return 2 using own onion symmetric key getting onion return 1 and address of next node
  • Create OnionResponse1 packet using decrypted onion return 1 and payload
  • Send OnionResponse1 packet to decrypted address

OnionResponse1

  • Decrypt onion return 1 using own onion symmetric key getting address of destination node
  • Create AnnounceResponse or OnionDataResponse packet using payload
  • Send AnnounceResponse or OnionDataResponse packet to decrypted address

Notes

  • Onion symmectic key used for onion return encryption should be updated every 2 hours to force onion paths expiration.

kurnevsky avatar Mar 01 '18 20:03 kurnevsky

Move this chapter to https://github.com/tox-rs/book

kpp avatar May 11 '19 13:05 kpp

Why is the request numbered 0,1,2 but the response is numbered 1,2,3?

algor1th avatar May 15 '19 10:05 algor1th

@algor1th

suhr avatar May 15 '19 11:05 suhr

But what is the reason they are indexed in such a way? Wouldn't it be clearer to number them the same way?

algor1th avatar May 15 '19 11:05 algor1th

OnionRequest[i] is a package from i'th node in the chain, OnionResponse[i] is a package to i'th node in the chain.

I believe it's rather reasonable. Also, this name scheme is symmetric (OnionRequest1 — OnionResponse1) on the sequence diagram.

suhr avatar May 15 '19 12:05 suhr