Skip to content
Closed
Changes from all 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
150 changes: 102 additions & 48 deletions noise/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Lifecycle Stage | Maturity | Status | Latest Revision |
|-----------------|---------------|--------|-----------------|
| 1A | Working Draft | Active | r0, 2019-08-05 |
| 1A | Working Draft | Active | r1, 2019-09-09 |

Authors: [@yusefnapora]

Expand Down Expand Up @@ -33,7 +33,7 @@ and spec status.
- [The Noise Handshake](#the-noise-handshake)
- [Static Key Authentication](#static-key-authentication)
- [libp2p Data in Handshake Messages](#libp2p-data-in-handshake-messages)
- [The libp2p Signed Handshake Payload](#the-libp2p-signed-handshake-payload)
- [The libp2p Handshake Payload](#the-libp2p-handshake-payload)
- [Supported Handshake Patterns](#supported-handshake-patterns)
- [XX](#xx)
- [Optimistic 0-RTT with Noise Pipes](#optimistic-0-rtt-with-noise-pipes)
Expand All @@ -43,7 +43,6 @@ and spec status.
- [Cryptographic Primitives](#cryptographic-primitives)
- [Valid Noise Protocol Names](#valid-noise-protocol-names)
- [Wire Format](#wire-format)
- [Encrypted Payloads](#encrypted-payloads)
- [Encryption and I/O](#encryption-and-io)
- [libp2p Interfaces and API](#libp2p-interfaces-and-api)
- [Initialization](#initialization)
Expand All @@ -56,6 +55,11 @@ and spec status.
- [Why ChaChaPoly?](#why-chachapoly)
- [Distinct Noise and Identity Keys](#distinct-noise-and-identity-keys)
- [Why Not Noise Signatures?](#why-not-noise-signatures)
- [Future Work](#future-work)
- [QUIC Support](#quic-support)
- [Pre-shared Keys / Private Networking](#pre-shared-keys--private-networking)
- [Changelog](#changelog)
- [r1 - 2019-09-09](#r1---2019-09-09)

## Overview

Expand Down Expand Up @@ -93,9 +97,17 @@ traffic. The [Noise Handshake section](#the-noise-handshake) describes the
libp2p-specific data is exchanged during the
handshake](#libp2p-data-in-handshake-messages).

During the handshake, the static
DH key used for Noise is authenticated using the libp2p identity keypair, as
described in the [Static Key Authentication section](#static-key-authentication).
By default, noise-libp2p uses the [`XX` handshake pattern](#xx), which provides
strong privacy and security guarantees and requires 1.5 round trip message
exchanges to be sound. Implementations may optionally support a compound
protocol called "Noise Pipes." Noise Pipes allows peers to attempt the more
efficient [`IK` handshake pattern](#ik) using a cached static key, falling back
to the `XX` pattern if the `IK` attempt fails. For details, see [Optimistic
0-RTT With Noise Pipes](#optimistic-0-rtt-with-noise-pipes).

During the handshake, the static DH key used for Noise is authenticated using
the libp2p identity keypair, as described in the [Static Key Authentication
section](#static-key-authentication).

Following a successful handshake, peers use the resulting encryption keys to
send ciphertexts back and forth. The format for transport messages and the wire
Expand Down Expand Up @@ -172,9 +184,9 @@ exposure.

To authenticate the static Noise key used in a handshake, noise-libp2p includes
a signature of the static Noise public key in a [handshake
payload](#the-libp2p-signed-handshake-payload). This signature is produced with
the private libp2p identity key, which proves that the sender was in possession
of the private identity key at the time the payload was generated.
payload](#the-libp2p--handshake-payload). This signature is produced with the
private libp2p identity key, which proves that the sender was in possession of
the private identity key at the time the payload was generated.

### libp2p Data in Handshake Messages

Expand All @@ -198,11 +210,11 @@ been validated. If the handshake fails for any reason, the early data payload
MUST be discarded immediately.

Any early data provided to noise-libp2p MUST be included in the [signed
handshake payload](#the-libp2p-signed-handshake-payload) as a byte string
without alteration by the noise-libp2p implementation, and a valid signature of
the early data MUST be included as described below.
handshake payload](#the-libp2p-handshake-payload) as a byte string without
alteration by the noise-libp2p implementation, and a valid signature of the
early data MUST be included as described below.

#### The libp2p Signed Handshake Payload
#### The libp2p Handshake Payload

libp2p-specific data, including the signature used for static key
authentication, is transmitted in Noise handshake message payloads. When
Expand Down Expand Up @@ -275,7 +287,9 @@ handshake pattern.
The [IK handshake pattern](#ik) is used in the context of [Optimistic 0-RTT with
Noise Pipes](#optimistic-0-rtt-with-noise-pipes) and is described in that
section along with the [`XXfallback`](#xxfallback) variation on the `XX`
pattern.
pattern. Support for `IK` and `XXfallback` is optional, and they are only
supported in the context of the Noise Pipes pattern. Implementations that do not
support Noise Pipes should not support the `IK` handshake.

#### XX

Expand All @@ -292,8 +306,8 @@ to the other party.
The first handshake message contains the initiator's ephemeral public key, which
allows subsequent key exchanges and message payloads to be encrypted.

The second and third handshake messages include a [signed handshake
payload](#the-libp2p-signed-handshake-payload), which contains a signature
The second and third handshake messages include a [Noise handshake
payload](#the-libp2p-handshake-payload), which contains a signature
authenticating the sender's static Noise key as described in the [Static Key
Authentication section](#static-key-authentication) and may include other
internal libp2p data.
Expand Down Expand Up @@ -335,6 +349,16 @@ implementations that do support it SHOULD offer a single configuration option to
enable Noise Pipes, rather than separate options for enabling `IK` and
`XXfallback`.

Although Noise Pipes can be more efficient than the default `XX` pattern, it's
worth noting that in many real-world use cases the difference is not as large as
it would appear at a glance. While `XX` requires 1.5 round trips, the final
handshake message (sent by the initiator) can be immediately followed by a
transport message, both of which will almost certainly fit in the same network
packet or datagram. In cases where the initiator of the handshake is also the
party expected to send the first transport message (as in the common request /
reply scenario), the cost of the final handshake message is effectively
eliminated.

#### IK


Expand All @@ -357,8 +381,8 @@ key has changed, they may initiate an [`XXfallback`](#xxfallback) handshake,
using the ephemeral public key from the failed `IK` handshake message as
pre-message knowledge.

Each handshake message will include a [libp2p signed handshake
payload](#the-libp2p-signed-handshake-payload) that identifies the sender and
Each handshake message will include a [libp2p handshake
payload](#the-libp2p-handshake-payload) that identifies the sender and
authenticates the static Noise key.

#### XXfallback
Expand Down Expand Up @@ -388,8 +412,8 @@ key is obtained from her initial `IK` message, moving it to the pre-message
section of the handshake pattern. Essentially, the failed `IK` message serves
the same role as the first handshake message in the standard `XX` pattern.

Each handshake message will include a [libp2p signed handshake
payload](#the-libp2p-signed-handshake-payload) that identifies the sender and
Each handshake message will include a [libp2p handshake
payload](#the-libp2p-handshake-payload) that identifies the sender and
authenticates the static Noise key.

#### Noise Pipes Message Flow
Expand Down Expand Up @@ -469,7 +493,9 @@ and [SHA256 hash function][npf-hash-sha256] as defined in the Noise spec.
## Valid Noise Protocol Names

This section lists the [Noise protocol names][npf-protocol-names] that are valid
according to the definitions in this spec.
according to the definitions in this spec. While these names are useful
internally when working with Noise, they are never sent "on the wire" or used
for any kind of handshake negotiation and are provided for reference.

Because only a single set of cryptographic primitives is supported, the Noise
protocol name depends on the handshake pattern in use.
Expand Down Expand Up @@ -501,34 +527,18 @@ The `noise_message` field contains a [Noise Message as defined in the Noise
spec][npf-message-format], which has a maximum length of 65535 bytes.

During the handshake phase, `noise_message` will be a Noise handshake message.
Noise handshake messages may contain encrypted payloads. If so, they will have
the structure described in the [Encrypted Payloads
section](#encrypted-payloads).
Noise handshake messages may contain encrypted payloads. If so, the decrypted
handshake payload will have the format described in [The libp2p Hanshake
Payload](#the-libp2p-handshake-payload).

After the handshake completes, `noise_message` will be a Noise transport
message, which is defined as an AEAD ciphertext consisting of an encrypted
payload plus 16 bytes of authentication data. The decrypted plaintext of the
encrypted payload will have the structure described in the [Encrypted Payloads
section](#encrypted-payloads).

### Encrypted Payloads

All Noise transport messages have a single encrypted payload. Noise handshake
messages may or may not have an encrypted payload.

Once decrypted, the plaintext of an encrypted payload will have this structure:
encrypted payload will contain up to 65535 bytes of "application layer" data.
It is the responsibilty of the noise-libp2p implementation to segment
application data into chunks that will fit into a Noise transport message when
sending, and to buffer and recombine chunks when receiving.

| `body_len` | `body` | `padding` |
|------------|-----------------|-----------------|
| 2 bytes | variable length | variable length |

The `body_len` field stores the length in bytes of the `body` field as an
unsigned 16-bit big-endian integer.

All data following the `body` field consists of padding bytes, which must be
ignored by the recipient. Senders SHOULD use a source of random data to populate
the padding field and may use any length of padding that does not cause the
total length of the Noise message to exceed 65535 bytes.

## Encryption and I/O

Expand Down Expand Up @@ -557,10 +567,12 @@ transport message, which is an AEAD ciphertext consisting of an encrypted
payload plus 16 bytes of authentication data, as [defined in the Noise
spec][npf-message-format].

When decrypted, the payload of a Noise transport message will have the structure
described in [Encrypted Payloads](#encrypted-payloads). Receivers MUST decode
the `body_len` field from the decrypted payload, and MUST ignore any additional
padding following the `body` field.
When decrypted, the payload of a Noise transport message will contain up to
65535 bytes of plaintext "application layer" data. This should be buffered by
the reciever and exposed as a continuous readable stream of binary data.
Likewise, when sending data, the noise-libp2p module should expose a writable
streaming interface. The segmentation of data into Noise transport messages
should be "invisible" outside of the noise-libp2p module.

In the unlikely event that peers exchange more than `2^64 - 1` messages, they
MUST terminate the connection to avoid reusing nonces, in accordance with the
Expand Down Expand Up @@ -726,6 +738,47 @@ to complete a Noise Signatures handshake. Also, only Ed25519 signatures are
currently supported by the spec, while libp2p identity keys may be of other
unsupported types like RSA.

## Future Work

This section will outline some things we'd like to accomplish in the future but
were ommitted from this draft because the path forward isn't yet clear.

### QUIC Support

go-libp2p has recently added support for [QUIC][ietf-quic-spec], a UDP-based
protocol with many excellent properties, including native support for
multiplexing and encryption. The current QUIC draft spec uses TLS 1.3 to secure
connections. However, it is possible to extend QUIC to use Noise, as
demonstrated by [nQUIC][nquic-paper], an experimental variant of the QUIC spec.

We would like to explore the possiblity of integrating the Noise handshake into
libp2p's QUIC transport. This would likely build upon the nQUIC work, and may
involve extending nQUIC to support more than the `IK` handshake specified in the
nQUIC paper.

### Pre-shared Keys / Private Networking

Noise supports a "psk" variant of all the fundamental handshake patterns, which
uses an additional pre-shared key to secure traffic. This could potentially
provide an alternative to the current [private networking][libp2p-psk-spec]
functionality in libp2p, in which a pre-shared key is used to encrypt all data
within a given libp2p network.

The current libp2p private network feature operates independently of the secure
handshake protocol, encrypting all traffic even before the secure channel
protocol has been negotiated. As a result, using Noise to implement private
networks may require changes to internal libp2p APIs, the scope of which aren't
yet clear.


## Changelog

### r1 - 2019-09-09

- removed the padding from the plaintext of encrypted payloads for simpler I/O
- added Future Work section
- a bit more context on Noise Pipes, including why you might not need it

[peer-id-spec]: ../peer-ids/peer-ids.md
[peer-id-spec-signing-rules]: ../peer-ids/peer-ids.md#how-keys-are-encoded-and-messages-signed

Expand Down Expand Up @@ -755,3 +808,4 @@ unsupported types like RSA.
[protobuf]: https://developers.google.com/protocol-buffers/
[noise-socket-spec]: https://github.com/noisesocket/spec
[noise-signatures-spec]: https://github.com/noiseprotocol/noise_sig_spec/blob/master/output/noise_sig.pdf
[ietf-quic-spec]: https://datatracker.ietf.org/doc/draft-ietf-quic-transport/