Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

addressing: Specify security protocols in multiaddr #353

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 88 additions & 6 deletions addressing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,40 @@ within it. Using our example above, decapsulating either `/tcp/1234/ws` _or_
unsurprising if you consider the utility of the `/ip4/7.7.7.7/ws` address that
would result from simply removing the `tcp` component.

### The multiaddr security component

Peers MAY advertise their addresses without a security protocol, e.g.
`/ip4/6.6.6.6/tcp/1234/` or `/ip4/6.6.6.6/udp/1234/quic`. The security handshake
protocol is then negotiated using [multistream-select](../connections/README.md#multistream-select). This is
the way the libp2p handshake worked until mid 2021.
This poses a security problem, as the negotiation was not authenticated and
therefore susceptible to man-in-the-middle attacks. A MITM could modify the list
of supported handshake protocols, thereby forcing a downgrade to a (potentially)
less secure handshake protocol. Note that since QUIC is standardized to use
TLS 1.3, no handshake protocol needs to be negotiated when using QUIC.

Peers SHOULD encapsulate the security protocol in the addresses they advertise,
mxinden marked this conversation as resolved.
Show resolved Hide resolved
for example `/ip4/6.6.6.6/tcp/1234/tls` for a TLS 1.3 server listening on TCP
port 1234 and `/ip4/6.6.6.6/tcp/1235/noise` for a Noise server listening on TCP
port 1235. QUIC multiaddrs remain unchanged.
mxinden marked this conversation as resolved.
Show resolved Hide resolved
The nodes jump straight into a cryptographic handshake, thus curtailing the
possibility of packet-inspection-based censorship and dynamic downgrade attacks.
This also applies to circuit addresses: the security protocol is encoded in the
`<destination address>` as defined in [`p2p-circuit` Relay Addresses](#p2p-circuit-relay-addresses).

marten-seemann marked this conversation as resolved.
Show resolved Hide resolved
Advertising the secure channel protocol through the peer's Multiaddr instead of
negotiating the protocol in-band forces users to advertise an updated Multiaddr
when changing the secure channel protocol in use. This is especially cumbersome
when using hardcoded Multiaddresses. Users may leverage the [dnsaddr] Multiaddr
protocol as well as using a new UDP or TCP port for the new protocol to ease the
transition.

Implementations using [Protocol Select](https://github.com/libp2p/specs/pull/349/)
(**TODO**: update link) MUST encapsulate the security protocol in the multiaddr.
Note that it’s not valid to assume that any node that encapsulated the security
mxinden marked this conversation as resolved.
Show resolved Hide resolved
protocol in their multiaddr also supports Protocol Select.


### The p2p multiaddr

libp2p defines the `p2p` multiaddr protocol, whose address component is the
Expand All @@ -168,7 +202,7 @@ within](#encapsulation) another multiaddr.
For example, the above `p2p` address can be combined with the transport address
on which the node is listening:

```
```
/ip4/7.7.7.7/tcp/1234/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
```

Expand Down Expand Up @@ -201,7 +235,7 @@ appreciated.

Most libp2p transports use the IP protocol as a foundational layer, and as a
result, most transport multiaddrs will begin with a component that represents an
IPv4 or IPv6 address.
IPv4 or IPv6 address.

This may be an actual address, such as `/ip4/7.7.7.7` or
`/ip6/fe80::883:a581:fff1:833`, or it could be something that resolves to an IP
Expand All @@ -221,7 +255,7 @@ resolvable or "name-based" protocols:

When the `/dns` protocol is used, the lookup may result in both IPv4 and IPv6
addresses, in which case IPv6 will be preferred. To explicitly resolve to IPv4
or IPv6 addresses, use the `/dns4` or `/dns6` protocols, respectively.
or IPv6 addresses, use the `/dns4` or `/dns6` protocols, respectively.

Note that in some restricted environments, such as inside a web browser, libp2p
may not have access to the resolved IP addresses at all, in which case the
Expand Down Expand Up @@ -271,7 +305,7 @@ wherever TCP/IP sockets are accessible.

Addresses for the TCP transport are of the form `<ip-multiaddr>/tcp/<tcp-port>`,
where `<ip-multiaddr>` is a multiaddr that resolves to an IP address, as
described in the [IP and Name Resolution section](#ip-and-name-resolution).
described in the [IP and Name Resolution section](#ip-and-name-resolution).
The `<tcp-port>` argument must be a 16-bit unsigned integer.

### WebSockets
Expand All @@ -290,7 +324,7 @@ multiaddr format mirrors this arrangement.

A libp2p QUIC multiaddr is of the form `<ip-multiaddr>/udp/<udp-port>/quic`,
where `<ip-multiaddr>` is a multiaddr that resolves to an IP address, as
described in the [IP and Name Resolution section](#ip-and-name-resolution).
described in the [IP and Name Resolution section](#ip-and-name-resolution).
The `<udp-port>` argument must be a 16-bit unsigned integer in network byte order.


Expand Down Expand Up @@ -320,7 +354,7 @@ destination peer.

A full example would be:

```
```
/ip4/127.0.0.1/tcp/5002/p2p/QmdPU7PfRyKehdrP5A3WqmjyD6bhVpU1mLGKppa2FjGDjZ/p2p-circuit/p2p/QmVT6GYwjeeAF5TR485Yc58S3xRF5EFsZ5YAF4VcP3URHt
```

Expand All @@ -329,9 +363,57 @@ Here, the destination peer has the peer id
relay node with peer id `QmdPU7PfRyKehdrP5A3WqmjyD6bhVpU1mLGKppa2FjGDjZ` running
on TCP port 5002 of the IPv4 loopback interface.

#### Relay addresses and multiaddr security component

Instead of negotiating the security protocol in-band, security protocols should
be encapsulated in the multiaddr (see [The multiaddr security component
section](#the-multiaddr-security-component)). Establishing a single relayed
connection involves 3 security protocol upgrades:

1. Upgrading the connection from the source to the relay.

The security protocol is specified in the relay multiaddr (before
`p2p-circuit`).

Example: `/ip4/6.6.6.6/tcp/1234/tls/p2p/QmRelay/p2p-circuit/<destination-multiaddr>`

2. Upgrading the connection from the relay to the destination.

The security protocol is specified in the destination multiaddr (after
`p2p-circuit`).

Note: Specifying this security protocol is only necessary for active
relaying. In the case of passive relaying the connection established by the
destination to the relay will be used to relay the connection.

Example:
- Passive relaying: `<relay-multiaddr>/p2p-circuit/p2p/QmDestination`
- Active relaying: `<relay-multiaddr>/p2p-circuit/ip4/6.6.6.6/tcp/1234/tls/p2p/QmDestination`

3. Upgrading the relayed connection from the source to the destination.

The security protocol is specified by appending
`/p2p-circuit-inner/<relayed-connection-security-protocol>` to the full
address.

<!-- TODO: Is `p2p-circuit-inner` the ideal name? Up for alternative
suggestions. -->

Example: `<relay-mulitaddr>/p2p-circuit/<destination-multiaddr>/p2p-circuit-inner/tls`
mxinden marked this conversation as resolved.
Show resolved Hide resolved

Note: One might be tempted to not specify (3) and simply use the security
protocol in (2). This would break if the security protocol used for (2) can
not be used for (3), e.g. in the case where the relay establishes a QUIC
connection to the destination secured via TLS and the source only supports
Noise.

mxinden marked this conversation as resolved.
Show resolved Hide resolved
See [Security protocol selection for the relayed connection] for details on
how the above integrates with the circuit relayv 2 _Hop_ and _Stop_ protocol.


[peer-id-spec]: ../peer-ids/peer-ids.md
[identify-spec]: ../identify/README.md
[multiaddr-repo]: https://github.com/multiformats/multiaddr
[multiaddr-proto-table]: https://github.com/multiformats/multiaddr/blob/master/protocols.csv
[relay-spec]: ../relay/README.md
[Security protocol selection for the relayed connection]: ../relay/circuit-v2.md#security-protocol-selection-for-the-relayed-connection
49 changes: 23 additions & 26 deletions connections/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ connections, and **listen** to refer to accepting inbound connections.
One of libp2p's core design goals is to be adaptable to many network
environments, including those that don't yet exist. To provide this flexibility,
the connection upgrade process supports multiple protocols for connection
security and stream multiplexing and allows peers to select which to use for
each connection.
security and stream multiplexing. While security protocols are specified
out-of-band through a peer's multiaddr (see [addressing specification]), stream
multiplexing protocols are selected in-band.

The process of selecting protocols is called **protocol negotiation**. In
The process of selecting protocols in-band is called **protocol negotiation**. In
addition to its role in the connection upgrade process, protocol negotiation is
also used whenever [a new stream is opened over an existing
connection](#opening-new-streams-over-a-connection). This allows libp2p
Expand Down Expand Up @@ -133,10 +134,10 @@ details, see [the multistream-select repository][mss].

Before engaging in the multistream-select negotiation process, it is assumed
that the peers have already established a bidirectional communication channel,
which may or may not have the security and multiplexing capabilities of a libp2p
connection. If those capabilities are missing, multistream-select is used in the
[connection upgrade process](#upgrading-connections) to determine how to provide
them.
which may or may not have the multiplexing capability of a libp2p connection. If
that capability is missing, multistream-select is used in the [connection
upgrade process](#upgrading-connections) to determine which multiplexing
protocol to use on a given connection.

Messages are sent encoded as UTF-8 byte strings, and they are always followed by
a `\n` newline character. Each message is also prefixed with its length in bytes
Expand Down Expand Up @@ -190,11 +191,10 @@ that do not natively support the core libp2p capabilities of security and stream
multiplexing. The process of layering capabilities onto "raw" transport
connections is called "upgrading" the connection.

Because there are many valid ways to provide the libp2p capabilities, the
connection upgrade process uses protocol negotiation to decide which specific
protocols to use for each capability. The protocol negotiation process uses
multistream-select as described in the [Protocol
Negotiation](#protocol-negotiation) section.
The security protocol to be used on a connection is determined through the
listeners multiaddr. The multiplexing protocol is determined using protocol
negotiation. The protocol negotiation process uses multistream-select as
described in the [Protocol Negotiation](#protocol-negotiation) section.

When raw connections need both security and multiplexing, security is always
established first, and the negotiation for stream multiplexing takes place over
Expand All @@ -204,23 +204,19 @@ Here's an example of the connection upgrade process:

![see conn-upgrade.plantuml for diagram source](conn-upgrade.svg)

First, the peers both send the multistream protocol id to establish that they'll
use multistream-select to negotiate protocols for the connection upgrade.

Next, the Initiator proposes the [TLS protocol][tls-libp2p] for encryption, but
the Responder rejects the proposal as they don't support TLS.
The security protocol to be used on a connection is determined by the listener's
multiaddr, see [addressing specification], in this case the Noise protocol. The
peers exchange the Noise handshake to establish a secure channel. If the Noise
handshake fails, the connection establishment process aborts. If successful, the
peers will use the secured channel for all future communications, including the
remainder of the connection upgrade process.

The Initiator then proposes the [Noise protocol][noise-spec], which is supported
by the Responder. The Listener echoes back the protocol id for Noise to indicate
agreement.
Once security has been established, the peers both send the multistream protocol
id to establish that they'll use multistream-select to negotiate protocols for
the connection upgrade.

At this point the Noise protocol takes over, and the peers exchange the Noise
handshake to establish a secure channel. If the Noise handshake fails, the
connection establishment process aborts. If successful, the peers will use the
secured channel for all future communications, including the remainder of the
connection upgrade process.

Once security has been established, the peers negotiate which stream multiplexer
Then the peers negotiate which stream multiplexer
to use. The negotiation process works in the same manner as before, with the
dialing peer proposing a multiplexer by sending its protocol id, and the
listening peer responding by either echoing back the supported id or sending
Expand Down Expand Up @@ -409,3 +405,4 @@ updated to incorporate the changes.
[simopen]: ./simopen.md
[resource-manager-issue]: https://github.com/libp2p/go-libp2p/issues/635
[hole-punching]: ./hole-punching.md
[addressing specification]: ../addressing/README.md
26 changes: 6 additions & 20 deletions connections/conn-upgrade.plantuml
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
@startuml

entity Initiator
entity Responder

== Initial multistream handshake ==

Responder -> Initiator: /multistream/1.0.0
Initiator -> Responder: /multistream/1.0.0

== Negotiate security protocol ==

Initiator -> Responder: /tls/1.0.0
note left: Initiator proposes TLS for security
== Upgrade with security protocol from listener's multiaddr ==

Responder -> Initiator: na
note right: Responder does not support TLS yet
... Security handshake ...

Initiator -> Responder: /noise
note left: Initiator falls back to Noise

Responder -> Initiator: /noise
note right: Responder supports Noise, echoes back protocol id
== Initial multistream handshake ==

... Noise handshake ...
Responder -> Initiator: /multistream/1.0.0
Initiator -> Responder: /multistream/1.0.0

== Negotiate stream multiplexer ==

Expand All @@ -31,6 +19,4 @@ note left: Initiator proposes mplex for stream multiplexing

Responder -> Initiator: /mplex/1.0.0
note right: Responder supports mplex, echoes back protocol id


@enduml
@enduml
Loading