github.com/vipernet-xyz/tm@v0.34.24/spec/p2p/peer.md (about)

     1  # Peers
     2  
     3  This document explains how Tendermint Peers are identified and how they connect to one another.
     4  
     5  For details on peer discovery, see the [peer exchange (PEX) reactor doc](https://github.com/vipernet-xyz/tm/blob/v0.34.x/spec/reactors/pex/pex.md).
     6  
     7  ## Peer Identity
     8  
     9  Tendermint peers are expected to maintain long-term persistent identities in the form of a public key.
    10  Each peer has an ID defined as `peer.ID == peer.PubKey.Address()`, where `Address` uses the scheme defined in `crypto` package.
    11  
    12  A single peer ID can have multiple IP addresses associated with it, but a node
    13  will only ever connect to one at a time.
    14  
    15  When attempting to connect to a peer, we use the PeerURL: `<ID>@<IP>:<PORT>`.
    16  We will attempt to connect to the peer at IP:PORT, and verify,
    17  via authenticated encryption, that it is in possession of the private key
    18  corresponding to `<ID>`. This prevents man-in-the-middle attacks on the peer layer.
    19  
    20  ## Connections
    21  
    22  All p2p connections use TCP.
    23  Upon establishing a successful TCP connection with a peer,
    24  two handshakes are performed: one for authenticated encryption, and one for Tendermint versioning.
    25  Both handshakes have configurable timeouts (they should complete quickly).
    26  
    27  ### Authenticated Encryption Handshake
    28  
    29  Tendermint implements the Station-to-Station protocol
    30  using X25519 keys for Diffie-Helman key-exchange and chacha20poly1305 for encryption.
    31  
    32  Previous versions of this protocol (0.32 and below) suffered from malleability attacks whereas an active man
    33  in the middle attacker could compromise confidentiality as described in [Prime, Order Please!
    34  Revisiting Small Subgroup and Invalid Curve Attacks on
    35  Protocols using Diffie-Hellman](https://eprint.iacr.org/2019/526.pdf).
    36  
    37  We have added dependency on the Merlin a keccak based transcript hashing protocol to ensure non-malleability.
    38  
    39  It goes as follows:
    40  
    41  - generate an ephemeral X25519 keypair
    42  - send the ephemeral public key to the peer
    43  - wait to receive the peer's ephemeral public key
    44  - create a new Merlin Transcript with the string "TENDERMINT_SECRET_CONNECTION_TRANSCRIPT_HASH"
    45  - Sort the ephemeral keys and add the high labeled "EPHEMERAL_UPPER_PUBLIC_KEY" and the low keys labeled "EPHEMERAL_LOWER_PUBLIC_KEY" to the Merlin transcript.
    46  - compute the Diffie-Hellman shared secret using the peers ephemeral public key and our ephemeral private key
    47  - add the DH secret to the transcript labeled DH_SECRET.
    48  - generate two keys to use for encryption (sending and receiving) and a challenge for authentication as follows:
    49      - create a hkdf-sha256 instance with the key being the diffie hellman shared secret, and info parameter as
    50      `TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN`
    51      - get 64 bytes of output from hkdf-sha256
    52      - 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.
    53  - use a separate nonce for receiving and sending. Both nonces start at 0, and should support the full 96 bit nonce range
    54  - all communications from now on are encrypted in 1400 byte frames (plus encoding overhead),
    55    using the respective secret and nonce. Each nonce is incremented by one after each use.
    56  - we now have an encrypted channel, but still need to authenticate
    57  - extract a 32 bytes challenge from merlin transcript with the label "SECRET_CONNECTION_MAC"
    58  - sign the common challenge obtained from the hkdf with our persistent private key
    59  - send the amino encoded persistent pubkey and signature to the peer
    60  - wait to receive the persistent public key and signature from the peer
    61  - verify the signature on the challenge using the peer's persistent public key
    62  
    63  If this is an outgoing connection (we dialed the peer) and we used a peer ID,
    64  then finally verify that the peer's persistent public key corresponds to the peer ID we dialed,
    65  ie. `peer.PubKey.Address() == <ID>`.
    66  
    67  The connection has now been authenticated. All traffic is encrypted.
    68  
    69  Note: only the dialer can authenticate the identity of the peer,
    70  but this is what we care about since when we join the network we wish to
    71  ensure we have reached the intended peer (and are not being MITMd).
    72  
    73  ### Peer Filter
    74  
    75  Before continuing, we check if the new peer has the same ID as ourselves or
    76  an existing peer. If so, we disconnect.
    77  
    78  We also check the peer's address and public key against
    79  an optional whitelist which can be managed through the ABCI app -
    80  if the whitelist is enabled and the peer does not qualify, the connection is
    81  terminated.
    82  
    83  ### Tendermint Version Handshake
    84  
    85  The Tendermint Version Handshake allows the peers to exchange their NodeInfo:
    86  
    87  ```golang
    88  type NodeInfo struct {
    89    Version    p2p.Version
    90    ID         p2p.ID
    91    ListenAddr string
    92  
    93    Network    string
    94    SoftwareVersion    string
    95    Channels   []int8
    96  
    97    Moniker    string
    98    Other      NodeInfoOther
    99  }
   100  
   101  type Version struct {
   102   P2P uint64
   103   Block uint64
   104   App uint64
   105  }
   106  
   107  type NodeInfoOther struct {
   108   TxIndex          string
   109   RPCAddress       string
   110  }
   111  ```
   112  
   113  The connection is disconnected if:
   114  
   115  - `peer.NodeInfo.ID` is not equal `peerConn.ID`
   116  - `peer.NodeInfo.Version.Block` does not match ours
   117  - `peer.NodeInfo.Network` is not the same as ours
   118  - `peer.Channels` does not intersect with our known Channels.
   119  - `peer.NodeInfo.ListenAddr` is malformed or is a DNS host that cannot be
   120    resolved
   121  
   122  At this point, if we have not disconnected, the peer is valid.
   123  It is added to the switch and hence all reactors via the `AddPeer` method.
   124  Note that each reactor may handle multiple channels.
   125  
   126  ## Connection Activity
   127  
   128  Once a peer is added, incoming messages for a given reactor are handled through
   129  that reactor's `Receive` method, and output messages are sent directly by the Reactors
   130  on each peer. A typical reactor maintains per-peer go-routine(s) that handle this.