Plaintext Connection Protocol
An insecure connection handshake for non-production environments.
| Lifecycle Stage | Maturity | Status | Latest Revision |
|---|---|---|---|
| 1A | Working Draft | Active | r0, 2019-05-27 |
Authors: @yusefnapora
Interest Group: TBD
See the lifecycle document for context about maturity level and spec status.
Overview
Secure communications are a key feature of libp2p, and encrypted transport is configured by default in libp2p implementations to encourage security for all production traffic. However, there are some use cases such as testing in which encryption is unnecessary. For such cases, the plaintext "security" protocol can be used. By conforming to the same interface as real security adapters like SECIO and TLS, the plaintext module can be used as a drop-in replacement when encryption is not needed.
As the name suggests, the plaintext security module does no encryption, and all data is transmitted in plain text. However, peer identity in libp2p is derived from public keys, even when peers are communicating over an insecure channel. For this reason, peers using the plaintext protocol still exchange public keys and peer ids when connecting to each other.
This document describes the exchange of peer ids and keys that occurs when initiating a plaintext connection. This exchange happens after the plaintext protocol has been negotiated as part of the connection upgrade process.
Protocol Id and Version History
The plaintext protocol described in this document has the protocol id of
/plaintext/1.1.0.
An earlier version, /plaintext/1.0.0, was implemented in several languages,
but did not include any exchange of public keys or peer ids. This led to
undefined behavior in parts of libp2p that assumed the presence of a peer id.
As version 1.0.0 had no associated wire protocol, it was never specified.
Messages
Peers exchange their peer id and public key encoded in a [protobuf][protobuf-spec] message using the protobuf version 2 syntax.
syntax = "proto2";
message Exchange {
optional bytes id = 1;
optional PublicKey pubkey = 2;
}
The id field contains the peer's id encoded as a multihash,
using the binary multihash encoding.
The PublicKey message uses the same definition specified in the peer id
spec. For reference, it is defined as follows:
enum KeyType {
RSA = 0;
Ed25519 = 1;
Secp256k1 = 2;
ECDSA = 3;
}
message PublicKey {
required KeyType Type = 1;
required bytes Data = 2;
}
The encoding of the Data field in the PublicKey message is specified in the
key encoding section of the peer id spec.
Protocol
Prerequisites
Prior to undertaking the exchange described below, it is assumed that we have already established a dedicated bidirectional channel between both parties, and that they have negotiated the plaintext protocol id as described in the protocol negotiation section of the connection establishment spec.
Message Framing
All messages sent over the wire are prefixed with the message length in bytes, encoded as an unsigned variable length integer as defined by the multiformats unsigned-varint spec.
Exchange
Once the plaintext protocol has been negotiated, both peers send an Exchange
message containing their peer id and public key.
Upon receiving an Exchange message from the remote peer, each side will
validate that the given peer id is consistent with the given public key by
deriving a peer id from the key and asserting that it's a match with the id
field in the Exchange message.
Dialing a peer in libp2p requires knowledge of the listening peer's peer id. As a result, the dialing peer also verifies that the peer id presented by the listening peer matches the peer id that they attempted to dial. As the listening peer has no prior knowledge of the dialer's id, only one peer is able to perform this additional check.
Once each side has received the Exchange message, they may store the public
key and peer id for the remote peer in their local peer metadata storage (e.g.
go-libp2p's peerstore, or js-libp2p's
peer-book).
Following delivery of Exchange message, the plaintext protocol is complete. No
response is required, as it is assumed that peers will close the connection if
peer id validation fails.
Once the exchange is complete, the remainder of the connection upgrade process takes place, and a stream multiplexer is negotiated if the underlying transport requires one.