1use 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 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}