You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
4.8 KiB

  1. # Peers
  2. This document explains how Tendermint Peers are identified and how they connect to one another.
  3. For details on peer discovery, see the [peer exchange (PEX) reactor doc](https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/pex/pex.md).
  4. ## Peer Identity
  5. Tendermint peers are expected to maintain long-term persistent identities in the form of a public key.
  6. Each peer has an ID defined as `peer.ID == peer.PubKey.Address()`, where `Address` uses the scheme defined in `crypto` package.
  7. A single peer ID can have multiple IP addresses associated with it, but a node
  8. will only ever connect to one at a time.
  9. When attempting to connect to a peer, we use the PeerURL: `<ID>@<IP>:<PORT>`.
  10. We will attempt to connect to the peer at IP:PORT, and verify,
  11. via authenticated encryption, that it is in possession of the private key
  12. corresponding to `<ID>`. This prevents man-in-the-middle attacks on the peer layer.
  13. ## Connections
  14. All p2p connections use TCP.
  15. Upon establishing a successful TCP connection with a peer,
  16. two handhsakes are performed: one for authenticated encryption, and one for Tendermint versioning.
  17. Both handshakes have configurable timeouts (they should complete quickly).
  18. ### Authenticated Encryption Handshake
  19. Tendermint implements the Station-to-Station protocol
  20. using X25519 keys for Diffie-Helman key-exchange and chacha20poly1305 for encryption.
  21. It goes as follows:
  22. - generate an ephemeral X25519 keypair
  23. - send the ephemeral public key to the peer
  24. - wait to receive the peer's ephemeral public key
  25. - compute the Diffie-Hellman shared secret using the peers ephemeral public key and our ephemeral private key
  26. - generate two keys to use for encryption (sending and receiving) and a challenge for authentication as follows:
  27. - create a hkdf-sha256 instance with the key being the diffie hellman shared secret, and info parameter as
  28. `TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN`
  29. - get 96 bytes of output from hkdf-sha256
  30. - if we had the smaller ephemeral pubkey, use the first 32 bytes for the key for receiving, the second 32 bytes for sending; else the opposite
  31. - use the last 32 bytes of output for the challenge
  32. - use a separate nonce for receiving and sending. Both nonces start at 0, and should support the full 96 bit nonce range
  33. - all communications from now on are encrypted in 1024 byte frames,
  34. using the respective secret and nonce. Each nonce is incremented by one after each use.
  35. - we now have an encrypted channel, but still need to authenticate
  36. - sign the common challenge obtained from the hkdf with our persistent private key
  37. - send the amino encoded persistent pubkey and signature to the peer
  38. - wait to receive the persistent public key and signature from the peer
  39. - verify the signature on the challenge using the peer's persistent public key
  40. If this is an outgoing connection (we dialed the peer) and we used a peer ID,
  41. then finally verify that the peer's persistent public key corresponds to the peer ID we dialed,
  42. ie. `peer.PubKey.Address() == <ID>`.
  43. The connection has now been authenticated. All traffic is encrypted.
  44. Note: only the dialer can authenticate the identity of the peer,
  45. but this is what we care about since when we join the network we wish to
  46. ensure we have reached the intended peer (and are not being MITMd).
  47. ### Peer Filter
  48. Before continuing, we check if the new peer has the same ID as ourselves or
  49. an existing peer. If so, we disconnect.
  50. We also check the peer's address and public key against
  51. an optional whitelist which can be managed through the ABCI app -
  52. if the whitelist is enabled and the peer does not qualify, the connection is
  53. terminated.
  54. ### Tendermint Version Handshake
  55. The Tendermint Version Handshake allows the peers to exchange their NodeInfo:
  56. ```golang
  57. type NodeInfo struct {
  58. Version p2p.Version
  59. ID p2p.ID
  60. ListenAddr string
  61. Network string
  62. SoftwareVersion string
  63. Channels []int8
  64. Moniker string
  65. Other NodeInfoOther
  66. }
  67. type Version struct {
  68. P2P uint64
  69. Block uint64
  70. App uint64
  71. }
  72. type NodeInfoOther struct {
  73. TxIndex string
  74. RPCAddress string
  75. }
  76. ```
  77. The connection is disconnected if:
  78. - `peer.NodeInfo.ID` is not equal `peerConn.ID`
  79. - `peer.NodeInfo.Version.Block` does not match ours
  80. - `peer.NodeInfo.Network` is not the same as ours
  81. - `peer.Channels` does not intersect with our known Channels.
  82. - `peer.NodeInfo.ListenAddr` is malformed or is a DNS host that cannot be
  83. resolved
  84. At this point, if we have not disconnected, the peer is valid.
  85. It is added to the switch and hence all reactors via the `AddPeer` method.
  86. Note that each reactor may handle multiple channels.
  87. ## Connection Activity
  88. Once a peer is added, incoming messages for a given reactor are handled through
  89. that reactor's `Receive` method, and output messages are sent directly by the Reactors
  90. on each peer. A typical reactor maintains per-peer go-routine(s) that handle this.