mwc_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 `mwc_libp2p_core::InboundUpgrade` and `mwc_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 mwc_libp2p_core::{identity, Transport, upgrade};
43//! use mwc_libp2p_tcp::TcpConfig;
44//! use mwc_libp2p_noise::{Keypair, X25519Spec, NoiseConfig};
45//!
46//! # fn main() {
47//! let id_keys = identity::Keypair::generate_ed25519();
48//! let dh_keys = Keypair::<X25519Spec>::new().into_authentic(&id_keys).unwrap();
49//! let noise = NoiseConfig::xx(dh_keys).into_authenticated();
50//! let builder = TcpConfig::new().upgrade(upgrade::Version::V1).authenticate(noise);
51//! // let transport = builder.multiplex(...);
52//! # }
53//! ```
54//!
55//! [noise]: http://noiseprotocol.org/
56
57mod error;
58mod io;
59mod protocol;
60
61pub use error::NoiseError;
62pub use io::NoiseOutput;
63pub use io::handshake;
64pub use io::handshake::{Handshake, RemoteIdentity, IdentityExchange};
65pub use protocol::{Keypair, AuthenticKeypair, KeypairIdentity, PublicKey, SecretKey};
66pub use protocol::{Protocol, ProtocolParams, IX, IK, XX};
67pub use protocol::{x25519::X25519, x25519_spec::X25519Spec};
68
69use futures::prelude::*;
70use mwc_libp2p_core::{identity, PeerId, UpgradeInfo, InboundUpgrade, OutboundUpgrade};
71use std::pin::Pin;
72use zeroize::Zeroize;
73
74/// The protocol upgrade configuration.
75#[derive(Clone)]
76pub struct NoiseConfig<P, C: Zeroize, R = ()> {
77    dh_keys: AuthenticKeypair<C>,
78    params: ProtocolParams,
79    legacy: LegacyConfig,
80    remote: R,
81    _marker: std::marker::PhantomData<P>
82}
83
84impl<H, C: Zeroize, R> NoiseConfig<H, C, R> {
85    /// Turn the `NoiseConfig` into an authenticated upgrade for use
86    /// with a [`Network`](mwc_libp2p_core::Network).
87    pub fn into_authenticated(self) -> NoiseAuthenticated<H, C, R> {
88        NoiseAuthenticated { config: self }
89    }
90
91    /// Sets the legacy configuration options to use, if any.
92    pub fn set_legacy_config(&mut self, cfg: LegacyConfig) -> &mut Self {
93        self.legacy = cfg;
94        self
95    }
96}
97
98impl<C> NoiseConfig<IX, C>
99where
100    C: Protocol<C> + Zeroize
101{
102    /// Create a new `NoiseConfig` for the `IX` handshake pattern.
103    pub fn ix(dh_keys: AuthenticKeypair<C>) -> Self {
104        NoiseConfig {
105            dh_keys,
106            params: C::params_ix(),
107            legacy: LegacyConfig::default(),
108            remote: (),
109            _marker: std::marker::PhantomData
110        }
111    }
112}
113
114impl<C> NoiseConfig<XX, C>
115where
116    C: Protocol<C> + Zeroize
117{
118    /// Create a new `NoiseConfig` for the `XX` handshake pattern.
119    pub fn xx(dh_keys: AuthenticKeypair<C>) -> Self {
120        NoiseConfig {
121            dh_keys,
122            params: C::params_xx(),
123            legacy: LegacyConfig::default(),
124            remote: (),
125            _marker: std::marker::PhantomData
126        }
127    }
128}
129
130impl<C> NoiseConfig<IK, C>
131where
132    C: Protocol<C> + Zeroize
133{
134    /// Create a new `NoiseConfig` for the `IK` handshake pattern (recipient side).
135    ///
136    /// Since the identity of the local node is known to the remote, this configuration
137    /// does not transmit a static DH public key or public identity key to the remote.
138    pub fn ik_listener(dh_keys: AuthenticKeypair<C>) -> Self {
139        NoiseConfig {
140            dh_keys,
141            params: C::params_ik(),
142            legacy: LegacyConfig::default(),
143            remote: (),
144            _marker: std::marker::PhantomData
145        }
146    }
147}
148
149impl<C> NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
150where
151    C: Protocol<C> + Zeroize
152{
153    /// Create a new `NoiseConfig` for the `IK` handshake pattern (initiator side).
154    ///
155    /// In this configuration, the remote identity is known to the local node,
156    /// but the local node still needs to transmit its own public identity.
157    pub fn ik_dialer(
158        dh_keys: AuthenticKeypair<C>,
159        remote_id: identity::PublicKey,
160        remote_dh: PublicKey<C>
161    ) -> Self {
162        NoiseConfig {
163            dh_keys,
164            params: C::params_ik(),
165            legacy: LegacyConfig::default(),
166            remote: (remote_dh, remote_id),
167            _marker: std::marker::PhantomData
168        }
169    }
170}
171
172// Handshake pattern IX /////////////////////////////////////////////////////
173
174impl<T, C> InboundUpgrade<T> for NoiseConfig<IX, C>
175where
176    NoiseConfig<IX, C>: UpgradeInfo,
177    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
178    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
179{
180    type Output = (RemoteIdentity<C>, NoiseOutput<T>);
181    type Error = NoiseError;
182    type Future = Handshake<T, C>;
183
184    fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
185        let session = self.params.into_builder()
186            .local_private_key(self.dh_keys.secret().as_ref())
187            .build_responder()
188            .map_err(NoiseError::from);
189        handshake::rt1_responder(socket, session,
190            self.dh_keys.into_identity(),
191            IdentityExchange::Mutual,
192            self.legacy)
193    }
194}
195
196impl<T, C> OutboundUpgrade<T> for NoiseConfig<IX, C>
197where
198    NoiseConfig<IX, C>: UpgradeInfo,
199    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
200    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
201{
202    type Output = (RemoteIdentity<C>, NoiseOutput<T>);
203    type Error = NoiseError;
204    type Future = Handshake<T, C>;
205
206    fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
207        let session = self.params.into_builder()
208            .local_private_key(self.dh_keys.secret().as_ref())
209            .build_initiator()
210            .map_err(NoiseError::from);
211        handshake::rt1_initiator(socket, session,
212                                 self.dh_keys.into_identity(),
213                                 IdentityExchange::Mutual,
214                                 self.legacy)
215    }
216}
217
218// Handshake pattern XX /////////////////////////////////////////////////////
219
220impl<T, C> InboundUpgrade<T> for NoiseConfig<XX, C>
221where
222    NoiseConfig<XX, C>: UpgradeInfo,
223    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
224    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
225{
226    type Output = (RemoteIdentity<C>, NoiseOutput<T>);
227    type Error = NoiseError;
228    type Future = Handshake<T, C>;
229
230    fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
231        let session = self.params.into_builder()
232            .local_private_key(self.dh_keys.secret().as_ref())
233            .build_responder()
234            .map_err(NoiseError::from);
235        handshake::rt15_responder(socket, session,
236            self.dh_keys.into_identity(),
237            IdentityExchange::Mutual,
238            self.legacy)
239    }
240}
241
242impl<T, C> OutboundUpgrade<T> for NoiseConfig<XX, C>
243where
244    NoiseConfig<XX, C>: UpgradeInfo,
245    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
246    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
247{
248    type Output = (RemoteIdentity<C>, NoiseOutput<T>);
249    type Error = NoiseError;
250    type Future = Handshake<T, C>;
251
252    fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
253        let session = self.params.into_builder()
254            .local_private_key(self.dh_keys.secret().as_ref())
255            .build_initiator()
256            .map_err(NoiseError::from);
257        handshake::rt15_initiator(socket, session,
258            self.dh_keys.into_identity(),
259            IdentityExchange::Mutual,
260            self.legacy)
261    }
262}
263
264// Handshake pattern IK /////////////////////////////////////////////////////
265
266impl<T, C, R> InboundUpgrade<T> for NoiseConfig<IK, C, R>
267where
268    NoiseConfig<IK, C, R>: UpgradeInfo,
269    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
270    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
271{
272    type Output = (RemoteIdentity<C>, NoiseOutput<T>);
273    type Error = NoiseError;
274    type Future = Handshake<T, C>;
275
276    fn upgrade_inbound(self, socket: T, _: Self::Info) -> Self::Future {
277        let session = self.params.into_builder()
278            .local_private_key(self.dh_keys.secret().as_ref())
279            .build_responder()
280            .map_err(NoiseError::from);
281        handshake::rt1_responder(socket, session,
282            self.dh_keys.into_identity(),
283            IdentityExchange::Receive,
284            self.legacy)
285    }
286}
287
288impl<T, C> OutboundUpgrade<T> for NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>
289where
290    NoiseConfig<IK, C, (PublicKey<C>, identity::PublicKey)>: UpgradeInfo,
291    T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
292    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
293{
294    type Output = (RemoteIdentity<C>, NoiseOutput<T>);
295    type Error = NoiseError;
296    type Future = Handshake<T, C>;
297
298    fn upgrade_outbound(self, socket: T, _: Self::Info) -> Self::Future {
299        let session = self.params.into_builder()
300            .local_private_key(self.dh_keys.secret().as_ref())
301            .remote_public_key(self.remote.0.as_ref())
302            .build_initiator()
303            .map_err(NoiseError::from);
304        handshake::rt1_initiator(socket, session,
305            self.dh_keys.into_identity(),
306            IdentityExchange::Send { remote: self.remote.1 },
307            self.legacy)
308    }
309}
310
311// Authenticated Upgrades /////////////////////////////////////////////////////
312
313/// A `NoiseAuthenticated` transport upgrade that wraps around any
314/// `NoiseConfig` handshake and verifies that the remote identified with a
315/// [`RemoteIdentity::IdentityKey`], aborting otherwise.
316///
317/// See [`NoiseConfig::into_authenticated`].
318///
319/// On success, the upgrade yields the [`PeerId`] obtained from the
320/// `RemoteIdentity`. The output of this upgrade is thus directly suitable
321/// for creating an [`authenticated`](mwc_libp2p_core::transport::upgrade::Authenticate)
322/// transport for use with a [`Network`](mwc_libp2p_core::Network).
323#[derive(Clone)]
324pub struct NoiseAuthenticated<P, C: Zeroize, R> {
325    config: NoiseConfig<P, C, R>
326}
327
328impl<P, C: Zeroize, R> UpgradeInfo for NoiseAuthenticated<P, C, R>
329where
330    NoiseConfig<P, C, R>: UpgradeInfo
331{
332    type Info = <NoiseConfig<P, C, R> as UpgradeInfo>::Info;
333    type InfoIter = <NoiseConfig<P, C, R> as UpgradeInfo>::InfoIter;
334
335    fn protocol_info(&self) -> Self::InfoIter {
336        self.config.protocol_info()
337    }
338}
339
340impl<T, P, C, R> InboundUpgrade<T> for NoiseAuthenticated<P, C, R>
341where
342    NoiseConfig<P, C, R>: UpgradeInfo + InboundUpgrade<T,
343        Output = (RemoteIdentity<C>, NoiseOutput<T>),
344        Error = NoiseError
345    > + 'static,
346    <NoiseConfig<P, C, R> as InboundUpgrade<T>>::Future: Send,
347    T: AsyncRead + AsyncWrite + Send + 'static,
348    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
349{
350    type Output = (PeerId, NoiseOutput<T>);
351    type Error = NoiseError;
352    type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
353
354    fn upgrade_inbound(self, socket: T, info: Self::Info) -> Self::Future {
355        Box::pin(self.config.upgrade_inbound(socket, info)
356            .and_then(|(remote, io)| match remote {
357                RemoteIdentity::IdentityKey(pk) => future::ok((pk.into_peer_id(), io)),
358                _ => future::err(NoiseError::AuthenticationFailed)
359            }))
360    }
361}
362
363impl<T, P, C, R> OutboundUpgrade<T> for NoiseAuthenticated<P, C, R>
364where
365    NoiseConfig<P, C, R>: UpgradeInfo + OutboundUpgrade<T,
366        Output = (RemoteIdentity<C>, NoiseOutput<T>),
367        Error = NoiseError
368    > + 'static,
369    <NoiseConfig<P, C, R> as OutboundUpgrade<T>>::Future: Send,
370    T: AsyncRead + AsyncWrite + Send + 'static,
371    C: Protocol<C> + AsRef<[u8]> + Zeroize + Send + 'static,
372{
373    type Output = (PeerId, NoiseOutput<T>);
374    type Error = NoiseError;
375    type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
376
377    fn upgrade_outbound(self, socket: T, info: Self::Info) -> Self::Future {
378        Box::pin(self.config.upgrade_outbound(socket, info)
379            .and_then(|(remote, io)| match remote {
380                RemoteIdentity::IdentityKey(pk) => future::ok((pk.into_peer_id(), io)),
381                _ => future::err(NoiseError::AuthenticationFailed)
382            }))
383    }
384}
385
386/// Legacy configuration options.
387#[derive(Clone)]
388pub struct LegacyConfig {
389    /// Whether to continue sending legacy handshake payloads,
390    /// i.e. length-prefixed protobuf payloads inside a length-prefixed
391    /// noise frame. These payloads are not interoperable with other
392    /// libp2p implementations.
393    pub send_legacy_handshake: bool,
394    /// Whether to support receiving legacy handshake payloads,
395    /// i.e. length-prefixed protobuf payloads inside a length-prefixed
396    /// noise frame. These payloads are not interoperable with other
397    /// libp2p implementations.
398    pub recv_legacy_handshake: bool,
399}
400
401impl Default for LegacyConfig {
402    fn default() -> Self {
403        Self {
404            send_legacy_handshake: false,
405            recv_legacy_handshake: false,
406        }
407    }
408}