libp2p_noise/
lib.rs

1// Copyright 2019 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21//! [Noise protocol framework][noise] support for libp2p.
22//!
23//! > **Note**: This crate is still experimental and subject to major breaking changes
24//! >           both on the API and the wire protocol.
25//!
26//! This crate provides `libp2p_core::InboundUpgrade` and `libp2p_core::OutboundUpgrade`
27//! implementations for various noise handshake patterns (currently `IK`, `IX`, and `XX`)
28//! over a particular choice of Diffie–Hellman key agreement (currently only X25519).
29//!
30//! > **Note**: Only the `XX` handshake pattern is currently guaranteed to provide
31//! >           interoperability with other libp2p implementations.
32//!
33//! All upgrades produce as output a pair, consisting of the remote's static public key
34//! and a `NoiseOutput` which represents the established cryptographic session with the
35//! remote, implementing `futures::io::AsyncRead` and `futures::io::AsyncWrite`.
36//!
37//! # Usage
38//!
39//! Example:
40//!
41//! ```
42//! use libp2p_core::{Transport, upgrade, transport::MemoryTransport};
43//! use libp2p_noise as noise;
44//! use libp2p_identity as identity;
45//!
46//! # fn main() {
47//! let id_keys = identity::Keypair::generate_ed25519();
48//! let noise = noise::Config::new(&id_keys).unwrap();
49//! let builder = MemoryTransport::default().upgrade(upgrade::Version::V1).authenticate(noise);
50//! // let transport = builder.multiplex(...);
51//! # }
52//! ```
53//!
54//! [noise]: http://noiseprotocol.org/
55
56#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
57#![allow(deprecated)] // Temporarily until we remove deprecated items.
58
59mod io;
60mod protocol;
61
62pub use io::Output;
63
64use crate::handshake::State;
65use crate::io::handshake;
66use crate::protocol::{noise_params_into_builder, AuthenticKeypair, Keypair, PARAMS_XX};
67use futures::prelude::*;
68use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo};
69use libp2p_identity as identity;
70use libp2p_identity::PeerId;
71use multiaddr::Protocol;
72use multihash::Multihash;
73use snow::params::NoiseParams;
74use std::collections::HashSet;
75use std::fmt::Write;
76use std::pin::Pin;
77
78/// The configuration for the noise handshake.
79#[derive(Clone)]
80pub struct Config {
81    dh_keys: AuthenticKeypair,
82    params: NoiseParams,
83    webtransport_certhashes: Option<HashSet<Multihash<64>>>,
84
85    /// Prologue to use in the noise handshake.
86    ///
87    /// The prologue can contain arbitrary data that will be hashed into the noise handshake.
88    /// For the handshake to succeed, both parties must set the same prologue.
89    ///
90    /// For further information, see <https://noiseprotocol.org/noise.html#prologue>.
91    prologue: Vec<u8>,
92}
93
94impl Config {
95    /// Construct a new configuration for the noise handshake using the XX handshake pattern.
96    pub fn new(identity: &identity::Keypair) -> Result<Self, Error> {
97        let noise_keys = Keypair::new().into_authentic(identity)?;
98
99        Ok(Self {
100            dh_keys: noise_keys,
101            params: PARAMS_XX.clone(),
102            webtransport_certhashes: None,
103            prologue: vec![],
104        })
105    }
106
107    /// Set the noise prologue.
108    pub fn with_prologue(mut self, prologue: Vec<u8>) -> Self {
109        self.prologue = prologue;
110        self
111    }
112
113    /// Set WebTransport certhashes extension.
114    ///
115    /// In case of initiator, these certhashes will be used to validate the ones reported by
116    /// responder.
117    ///
118    /// In case of responder, these certhashes will be reported to initiator.
119    pub fn with_webtransport_certhashes(mut self, certhashes: HashSet<Multihash<64>>) -> Self {
120        self.webtransport_certhashes = Some(certhashes).filter(|h| !h.is_empty());
121        self
122    }
123
124    fn into_responder<S>(self, socket: S) -> Result<State<S>, Error> {
125        let session = noise_params_into_builder(
126            self.params,
127            &self.prologue,
128            self.dh_keys.keypair.secret(),
129            None,
130        )
131        .build_responder()?;
132
133        let state = State::new(
134            socket,
135            session,
136            self.dh_keys.identity,
137            None,
138            self.webtransport_certhashes,
139        );
140
141        Ok(state)
142    }
143
144    fn into_initiator<S>(self, socket: S) -> Result<State<S>, Error> {
145        let session = noise_params_into_builder(
146            self.params,
147            &self.prologue,
148            self.dh_keys.keypair.secret(),
149            None,
150        )
151        .build_initiator()?;
152
153        let state = State::new(
154            socket,
155            session,
156            self.dh_keys.identity,
157            None,
158            self.webtransport_certhashes,
159        );
160
161        Ok(state)
162    }
163}
164
165impl UpgradeInfo for Config {
166    type Info = &'static str;
167    type InfoIter = std::iter::Once<Self::Info>;
168
169    fn protocol_info(&self) -> Self::InfoIter {
170        std::iter::once("/noise")
171    }
172}
173
174impl<T> InboundUpgrade<T> for Config
175where
176    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
177{
178    type Output = (PeerId, Output<T>);
179    type Error = Error;
180    type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
181
182    fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
183        async move {
184            let mut state = self.into_responder(socket)?;
185
186            handshake::recv_empty(&mut state).await?;
187            handshake::send_identity(&mut state).await?;
188            handshake::recv_identity(&mut state).await?;
189
190            let (pk, io) = state.finish()?;
191
192            Ok((pk.to_peer_id(), io))
193        }
194        .boxed()
195    }
196}
197
198impl<T> OutboundUpgrade<T> for Config
199where
200    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
201{
202    type Output = (PeerId, Output<T>);
203    type Error = Error;
204    type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
205
206    fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
207        async move {
208            let mut state = self.into_initiator(socket)?;
209
210            handshake::send_empty(&mut state).await?;
211            handshake::recv_identity(&mut state).await?;
212            handshake::send_identity(&mut state).await?;
213
214            let (pk, io) = state.finish()?;
215
216            Ok((pk.to_peer_id(), io))
217        }
218        .boxed()
219    }
220}
221
222/// libp2p_noise error type.
223#[derive(Debug, thiserror::Error)]
224#[non_exhaustive]
225pub enum Error {
226    #[error(transparent)]
227    Io(#[from] std::io::Error),
228    #[error(transparent)]
229    Noise(#[from] snow::Error),
230    #[error("Invalid public key")]
231    InvalidKey(#[from] libp2p_identity::DecodingError),
232    #[error("Only keys of length 32 bytes are supported")]
233    InvalidLength,
234    #[error("Remote authenticated with an unexpected public key")]
235    UnexpectedKey,
236    #[error("The signature of the remote identity's public key does not verify")]
237    BadSignature,
238    #[error("Authentication failed")]
239    AuthenticationFailed,
240    #[error("failed to decode protobuf ")]
241    InvalidPayload(#[from] DecodeError),
242    #[error(transparent)]
243    SigningError(#[from] libp2p_identity::SigningError),
244    #[error("Expected WebTransport certhashes ({}) are not a subset of received ones ({})", certhashes_to_string(.0), certhashes_to_string(.1))]
245    UnknownWebTransportCerthashes(HashSet<Multihash<64>>, HashSet<Multihash<64>>),
246}
247
248#[derive(Debug, thiserror::Error)]
249#[error(transparent)]
250pub struct DecodeError(quick_protobuf::Error);
251
252fn certhashes_to_string(certhashes: &HashSet<Multihash<64>>) -> String {
253    let mut s = String::new();
254
255    for hash in certhashes {
256        write!(&mut s, "{}", Protocol::Certhash(*hash)).unwrap();
257    }
258
259    s
260}