libp2p_tls/
upgrade.rs

1// Copyright 2022 Protocol Labs.
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
21use std::{
22    net::{IpAddr, Ipv4Addr},
23    sync::Arc,
24};
25
26use futures::{future::BoxFuture, AsyncRead, AsyncWrite, FutureExt};
27use futures_rustls::TlsStream;
28use libp2p_core::{
29    upgrade::{InboundConnectionUpgrade, OutboundConnectionUpgrade},
30    UpgradeInfo,
31};
32use libp2p_identity as identity;
33use libp2p_identity::PeerId;
34use rustls::{pki_types::ServerName, CommonState};
35
36use crate::{certificate, certificate::P2pCertificate};
37
38#[derive(thiserror::Error, Debug)]
39pub enum UpgradeError {
40    #[error("Failed to generate certificate")]
41    CertificateGeneration(#[from] certificate::GenError),
42    #[error("Failed to upgrade server connection")]
43    ServerUpgrade(std::io::Error),
44    #[error("Failed to upgrade client connection")]
45    ClientUpgrade(std::io::Error),
46    #[error("Failed to parse certificate")]
47    BadCertificate(#[from] certificate::ParseError),
48}
49
50#[derive(Clone)]
51pub struct Config {
52    server: rustls::ServerConfig,
53    client: rustls::ClientConfig,
54}
55
56impl Config {
57    pub fn new(identity: &identity::Keypair) -> Result<Self, certificate::GenError> {
58        Ok(Self {
59            server: crate::make_server_config(identity)?,
60            client: crate::make_client_config(identity, None)?,
61        })
62    }
63}
64
65impl UpgradeInfo for Config {
66    type Info = &'static str;
67    type InfoIter = std::iter::Once<Self::Info>;
68
69    fn protocol_info(&self) -> Self::InfoIter {
70        std::iter::once("/tls/1.0.0")
71    }
72}
73
74impl<C> InboundConnectionUpgrade<C> for Config
75where
76    C: AsyncRead + AsyncWrite + Send + Unpin + 'static,
77{
78    type Output = (PeerId, TlsStream<C>);
79    type Error = UpgradeError;
80    type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>;
81
82    fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future {
83        async move {
84            let stream = futures_rustls::TlsAcceptor::from(Arc::new(self.server))
85                .accept(socket)
86                .await
87                .map_err(UpgradeError::ServerUpgrade)?;
88
89            let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id();
90
91            Ok((peer_id, stream.into()))
92        }
93        .boxed()
94    }
95}
96
97impl<C> OutboundConnectionUpgrade<C> for Config
98where
99    C: AsyncRead + AsyncWrite + Send + Unpin + 'static,
100{
101    type Output = (PeerId, TlsStream<C>);
102    type Error = UpgradeError;
103    type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>;
104
105    fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future {
106        async move {
107            // Spec: In order to keep this flexibility for future versions, clients that only
108            // support the version of the handshake defined in this document MUST NOT send any value
109            // in the Server Name Indication. Setting `ServerName` to unspecified will
110            // disable the use of the SNI extension.
111            let name = ServerName::IpAddress(rustls::pki_types::IpAddr::from(IpAddr::V4(
112                Ipv4Addr::UNSPECIFIED,
113            )));
114
115            let stream = futures_rustls::TlsConnector::from(Arc::new(self.client))
116                .connect(name, socket)
117                .await
118                .map_err(UpgradeError::ClientUpgrade)?;
119
120            let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id();
121
122            Ok((peer_id, stream.into()))
123        }
124        .boxed()
125    }
126}
127
128fn extract_single_certificate(
129    state: &CommonState,
130) -> Result<P2pCertificate<'_>, certificate::ParseError> {
131    let Some([cert]) = state.peer_certificates() else {
132        panic!("config enforces exactly one certificate");
133    };
134
135    certificate::parse(cert)
136}