add draft spec for plaintext key exchange protocol

This commit is contained in:
Yusef Napora
2019-07-08 13:46:01 -04:00
parent 7a3ad9096d
commit 50884872c7

145
plaintext/README.md Normal file
View File

@@ -0,0 +1,145 @@
# 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
[@yusefnapora]: https://github.com/yusefnapora
See the [lifecycle document][lifecycle-spec] for context about maturity level
and spec status.
[lifecycle-spec]: https://github.com/libp2p/specs/blob/master/00-framework-01-spec-lifecycle.md
## 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][secio-spec] and [TLS][tls-spec], 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][peer-id-spec], 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][conn-spec-conn-upgrade].
## 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.
``` protobuf
syntax = "proto2";
message Exchange {
optional bytes id = 1;
optional PublicKey pubkey = 2;
}
```
The `id` field contains the peer's id encoded as a [multihash][multihash],
using the binary multihash encoding.
The `PublicKey` message uses the same definition [specified in the peer id
spec][peer-id-spec-pubkey-message]. For reference, it is defined as follows:
``` protobuf
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][peer-id-spec-key-encoding].
## 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](#protocol-id-and-version-history) as described in the [protocol negotiation
section of the connection establishment spec][conn-spec-protocol-negotiation].
### 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][uvarint-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][go-libp2p-peerstore], or js-libp2p's
[peer-book][js-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][conn-spec-conn-upgrade] takes place, and a stream multiplexer is
negotiated if the underlying transport requires one.
[secio-spec]: ../secio/README.md
[tls-spec]: ../tls/tls.md
[peer-id-spec]: ../peer-ids/peer-ids.md
[peer-id-spec-pubkey-message]: ../peer-ids/peer-ids.md#keys
[peer-id-spec-key-encoding]: ../peer-ids/peer-ids.md#how-keys-are-encoded-and-messages-signed
[uvarint-spec]: https://github.com/multiformats/unsigned-varint
[multihash]: https://github.com/multiformats/multihash
[conn-spec-conn-upgrade]: ../connections/README.md#connection-upgrade
[conn-spec-protocol-negotiation]: ../connnection/README.md#protocol-negotiation
[go-libp2p-peerstore]: https://github.com/libp2p/go-libp2p-peerstore
[js-peer-book]: https://github.com/libp2p/js-peer-book