Skip to content

Latest commit

 

History

History
164 lines (123 loc) · 6.59 KB

README.md

File metadata and controls

164 lines (123 loc) · 6.59 KB

Plaintext Secure Channel

An insecure connection handshake for non-production environments.

⚠️ Intended only for debugging and interoperability testing purposes. ⚠️

Lifecycle Stage Maturity Status Latest Revision
1A Working Draft Active r0, 2019-05-27

Authors: @yusefnapora

Interest Group: @raulk, @Warchant, @Stebalien, @mhchia

See the lifecycle document for context about the 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.

It bears repeating that the plaintext protocol was designed for development and testing ONLY, and MUST NOT be used in production environments. No encryption or authentication of any kind is provided. Also note that enabling the plaintext module will effectively nullify the security guarantees of any other security modules that may be enabled, as an attacker will be able to negotiate a plaintext connection at any time.

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/2.0.0.

An earlier version, /plaintext/1.0.0, was implemented in several languages, but it 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 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:

syntax = "proto2";

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 handshake 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. Actual payloads exchanged once the plaintext handshake has completed are NOT prefixed with their lengths, but sent as-is.

Exchange

Once the plaintext protocol has been negotiated, both peers immediately 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 and verification of Exchange messages, the plaintext protocol is complete. Should a verification or timeout error occur, the connection MUST be terminated abruptly.

The connection is now ready for insecure and unauthenticated data exchange. While we do exchange public keys upfront, replay attacks and forgery are trivial, and we perform no authentication of messages. Therefore, we reiterate the unsuitability of /plaintext/2.0.0 for production usage.