Skip to content

Commit

Permalink
Don't require remote PK to dial a peer
Browse files Browse the repository at this point in the history
  • Loading branch information
kpp committed Nov 11, 2021
1 parent 756f2e7 commit 3b1ff34
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 134 deletions.
7 changes: 2 additions & 5 deletions transports/quic/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use libp2p_core::identity::{Keypair, PublicKey};
use libp2p_core::identity::Keypair;
use quinn_proto::crypto::Session;
use quinn_proto::TransportConfig;
use std::sync::Arc;
Expand All @@ -45,11 +45,8 @@ impl TlsCrypto {

pub fn new_client_config(
config: &CryptoConfig,
remote_public: PublicKey,
) -> <quinn_proto::crypto::rustls::TlsSession as Session>::ClientConfig {
let mut client =
crate::tls::make_client_config(&config.keypair, remote_public.to_peer_id())
.expect("invalid config");
let mut client = crate::tls::make_client_config(&config.keypair).expect("invalid config");
if let Some(key_log) = config.keylogger.clone() {
client.key_log = key_log;
}
Expand Down
23 changes: 4 additions & 19 deletions transports/quic/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use crate::muxer::QuicMuxer;
use crate::{QuicConfig, QuicError};
use futures::channel::{mpsc, oneshot};
use futures::prelude::*;
use libp2p_core::identity::PublicKey;
use quinn_proto::generic::ClientConfig as QuinnClientConfig;
use quinn_proto::ServerConfig as QuinnServerConfig;
use quinn_proto::{
Expand All @@ -46,8 +45,6 @@ enum ToEndpoint {
Dial {
/// UDP address to connect to.
addr: SocketAddr,
/// The remotes public key.
public_key: PublicKey,
/// Channel to return the result of the dialing to.
tx: oneshot::Sender<Result<QuicMuxer, QuicError>>,
},
Expand All @@ -70,17 +67,9 @@ pub struct TransportChannel {
}

impl TransportChannel {
pub fn dial(
&mut self,
addr: SocketAddr,
public_key: PublicKey,
) -> oneshot::Receiver<Result<QuicMuxer, QuicError>> {
pub fn dial(&mut self, addr: SocketAddr) -> oneshot::Receiver<Result<QuicMuxer, QuicError>> {
let (tx, rx) = oneshot::channel();
let msg = ToEndpoint::Dial {
addr,
public_key,
tx,
};
let msg = ToEndpoint::Dial { addr, tx };
self.tx.unbounded_send(msg).expect("endpoint has crashed");
rx
}
Expand Down Expand Up @@ -351,12 +340,8 @@ impl Future for Endpoint {
if me.event_slot.is_none() {
while let Poll::Ready(event) = me.channel.poll_next_event(cx) {
match event {
Some(ToEndpoint::Dial {
addr,
public_key,
tx,
}) => {
let crypto = TlsCrypto::new_client_config(&me.crypto_config, public_key);
Some(ToEndpoint::Dial { addr, tx }) => {
let crypto = TlsCrypto::new_client_config(&me.crypto_config);
let client_config = QuinnClientConfig {
transport: me.crypto_config.transport.clone(),
crypto,
Expand Down
6 changes: 2 additions & 4 deletions transports/quic/src/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
mod certificate;
mod verifier;

use libp2p_core::PeerId;
use std::sync::Arc;
use thiserror::Error;

Expand All @@ -50,13 +49,12 @@ pub enum ConfigError {

pub fn make_client_config(
keypair: &libp2p_core::identity::Keypair,
remote_peer_id: PeerId,
) -> Result<rustls::ClientConfig, ConfigError> {
let cert = certificate::make_cert(keypair)?;
let private_key = cert.serialize_private_key_der();
let cert = rustls::Certificate(cert.serialize_der()?);
let key = rustls::PrivateKey(private_key);
let verifier = verifier::Libp2pServerCertificateVerifier(remote_peer_id);
let verifier = verifier::Libp2pCertificateVerifier;

let mut crypto = rustls::ClientConfig::new();
crypto.versions = vec![rustls::ProtocolVersion::TLSv1_3];
Expand All @@ -76,7 +74,7 @@ pub fn make_server_config(
let private_key = cert.serialize_private_key_der();
let cert = rustls::Certificate(cert.serialize_der()?);
let key = rustls::PrivateKey(private_key);
let verifier = verifier::Libp2pClientCertificateVerifier;
let verifier = verifier::Libp2pCertificateVerifier;

let mut crypto = rustls::ServerConfig::new(Arc::new(verifier));
crypto.versions = vec![rustls::ProtocolVersion::TLSv1_3];
Expand Down
23 changes: 6 additions & 17 deletions transports/quic/src/tls/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,23 @@ use webpki::Error;
/// Implementation of the `rustls` certificate verification traits for libp2p.
///
/// Only TLS 1.3 is supported. TLS 1.2 should be disabled in the configuration of `rustls`.
pub(crate) struct Libp2pServerCertificateVerifier(pub(crate) PeerId);
pub(crate) struct Libp2pCertificateVerifier;

/// libp2p requires the following of X.509 server certificate chains:
///
/// - Exactly one certificate must be presented.
/// - The certificate must be self-signed.
/// - The certificate must have a valid libp2p extension that includes a
/// signature of its public key.
impl rustls::ServerCertVerifier for Libp2pServerCertificateVerifier {
impl rustls::ServerCertVerifier for Libp2pCertificateVerifier {
fn verify_server_cert(
&self,
_roots: &rustls::RootCertStore,
presented_certs: &[rustls::Certificate],
_dns_name: webpki::DNSNameRef<'_>,
_ocsp_response: &[u8],
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
let peer_id = verify_presented_certs(presented_certs)?;
if peer_id != self.0 {
return Err(TLSError::PeerIncompatibleError(
"Unexpected peer id".to_string(),
));
}
Ok(ServerCertVerified::assertion())
verify_presented_certs(presented_certs).map(|_| ServerCertVerified::assertion())
}

fn verify_tls12_signature(
Expand All @@ -77,19 +71,14 @@ impl rustls::ServerCertVerifier for Libp2pServerCertificateVerifier {
}
}

/// Implementation of the `rustls` certificate verification traits for libp2p.
///
/// Only TLS 1.3 is supported. TLS 1.2 should be disabled in the configuration of `rustls`.
pub(crate) struct Libp2pClientCertificateVerifier;

/// libp2p requires the following of X.509 client certificate chains:
///
/// - Exactly one certificate must be presented. In particular, client
/// authentication is mandatory in libp2p.
/// - The certificate must be self-signed.
/// - The certificate must have a valid libp2p extension that includes a
/// signature of its public key.
impl rustls::ClientCertVerifier for Libp2pClientCertificateVerifier {
impl rustls::ClientCertVerifier for Libp2pCertificateVerifier {
fn offer_client_auth(&self) -> bool {
true
}
Expand Down Expand Up @@ -186,7 +175,7 @@ fn parse_certificate(
Ok((parsed, libp2p_extension))
}

fn verify_presented_certs(presented_certs: &[Certificate]) -> Result<PeerId, TLSError> {
fn verify_presented_certs(presented_certs: &[Certificate]) -> Result<(), TLSError> {
if presented_certs.len() != 1 {
return Err(TLSError::NoCertificatesPresented);
}
Expand All @@ -198,7 +187,7 @@ fn verify_presented_certs(presented_certs: &[Certificate]) -> Result<PeerId, TLS
.map_err(TLSError::WebPKIError)?;
verify_libp2p_signature(&extension, certificate.subject_public_key_info().spki())
.map_err(TLSError::WebPKIError)?;
Ok(PeerId::from_public_key(&extension.peer_key))
Ok(())
}

struct Libp2pExtension<'a> {
Expand Down
114 changes: 45 additions & 69 deletions transports/quic/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use crate::{QuicConfig, QuicError};
use futures::channel::oneshot;
use futures::prelude::*;
use if_watch::{IfEvent, IfWatcher};
use libp2p_core::identity::PublicKey;
use libp2p_core::multiaddr::{Multiaddr, Protocol};
use libp2p_core::muxing::{StreamMuxer, StreamMuxerBox};
use libp2p_core::transport::{Boxed, ListenerEvent, Transport, TransportError};
Expand All @@ -48,8 +47,7 @@ impl QuicTransport {
addr: Multiaddr,
) -> Result<Self, TransportError<QuicError>> {
let socket_addr = multiaddr_to_socketaddr(&addr)
.map_err(|_| TransportError::MultiaddrNotSupported(addr.clone()))?
.0;
.ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?;
let addresses = if socket_addr.ip().is_unspecified() {
let watcher = IfWatcher::new()
.await
Expand Down Expand Up @@ -100,24 +98,22 @@ impl Transport for QuicTransport {
type Dial = QuicDial;

fn listen_on(self, addr: Multiaddr) -> Result<Self::Listener, TransportError<Self::Error>> {
multiaddr_to_socketaddr(&addr).map_err(|_| TransportError::MultiaddrNotSupported(addr))?;
multiaddr_to_socketaddr(&addr)
.ok_or_else(|| TransportError::MultiaddrNotSupported(addr))?;
Ok(self)
}

fn dial(self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
let (socket_addr, public_key) =
if let Ok((socket_addr, Some(public_key))) = multiaddr_to_socketaddr(&addr) {
(socket_addr, public_key)
} else {
tracing::debug!("invalid multiaddr");
return Err(TransportError::MultiaddrNotSupported(addr.clone()));
};
let socket_addr = multiaddr_to_socketaddr(&addr).ok_or_else(|| {
tracing::debug!("invalid multiaddr");
TransportError::MultiaddrNotSupported(addr.clone())
})?;
if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() {
tracing::debug!("invalid multiaddr");
return Err(TransportError::MultiaddrNotSupported(addr));
}
tracing::debug!("dialing {}", socket_addr);
let rx = self.inner.lock().channel.dial(socket_addr, public_key);
let rx = self.inner.lock().channel.dial(socket_addr);
Ok(QuicDial::Dialing(rx))
}

Expand Down Expand Up @@ -240,38 +236,29 @@ impl Future for QuicUpgrade {
}
}

/// Tries to turn a QUIC multiaddress into a UDP [`SocketAddr`]. Returns an error if the format
/// Tries to turn a QUIC multiaddress into a UDP [`SocketAddr`]. Returns None if the format
/// of the multiaddr is wrong.
fn multiaddr_to_socketaddr(addr: &Multiaddr) -> Result<(SocketAddr, Option<PublicKey>), ()> {
let mut iter = addr.iter().peekable();
let proto1 = iter.next().ok_or(())?;
let proto2 = iter.next().ok_or(())?;
let proto3 = iter.next().ok_or(())?;
fn multiaddr_to_socketaddr(addr: &Multiaddr) -> Option<SocketAddr> {
let mut iter = addr.iter();
let proto1 = iter.next()?;
let proto2 = iter.next()?;
let proto3 = iter.next()?;

let peer_id = if let Some(Protocol::P2p(peer_id)) = iter.peek() {
if peer_id.code() != multihash::Code::Identity.into() {
return Err(());
while let Some(proto) = iter.next() {
match proto {
Protocol::P2p(_) => {} // Ignore a `/p2p/...` prefix of possibly outer protocols, if present.
_ => return None,
}
let public_key =
libp2p_core::PublicKey::from_protobuf_encoding(peer_id.digest()).map_err(|_| ())?;
iter.next();
Some(public_key)
} else {
None
};

if iter.next().is_some() {
return Err(());
}

match (proto1, proto2, proto3) {
(Protocol::Ip4(ip), Protocol::Udp(port), Protocol::Quic) => {
Ok((SocketAddr::new(ip.into(), port), peer_id))
Some(SocketAddr::new(ip.into(), port))
}
(Protocol::Ip6(ip), Protocol::Udp(port), Protocol::Quic) => {
Ok((SocketAddr::new(ip.into(), port), peer_id))
Some(SocketAddr::new(ip.into(), port))
}
_ => Err(()),
_ => None,
}
}

Expand All @@ -288,12 +275,12 @@ mod tests {
use super::*;

#[test]
fn multiaddr_to_udp_conversion() {
fn multiaddr_to_socketaddr_conversion() {
use std::net::{Ipv4Addr, Ipv6Addr};

assert!(
multiaddr_to_socketaddr(&"/ip4/127.0.0.1/udp/1234".parse::<Multiaddr>().unwrap())
.is_err()
.is_none()
);

assert_eq!(
Expand All @@ -302,27 +289,35 @@ mod tests {
.parse::<Multiaddr>()
.unwrap()
),
Ok((
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12345,),
None
Some(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
12345,
))
);

assert!(multiaddr_to_socketaddr(
&"/ip4/127.0.0.1/udp/12345/quic/tcp/12345"
.parse::<Multiaddr>()
.unwrap()
)
.is_none());

assert_eq!(
multiaddr_to_socketaddr(
&"/ip4/255.255.255.255/udp/8080/quic"
.parse::<Multiaddr>()
.unwrap()
),
Ok((
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 8080,),
None
Some(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)),
8080,
))
);
assert_eq!(
multiaddr_to_socketaddr(&"/ip6/::1/udp/12345/quic".parse::<Multiaddr>().unwrap()),
Ok((
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 12345,),
None
Some(SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
12345,
))
);
assert_eq!(
Expand All @@ -331,30 +326,11 @@ mod tests {
.parse::<Multiaddr>()
.unwrap()
),
Ok((
SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
)),
8080,
),
None
))
);
}

#[test]
fn multiaddr_to_pk_conversion() {
use std::net::Ipv4Addr;

let keypair = libp2p_core::identity::Keypair::generate_ed25519();
let peer_id = keypair.public().to_peer_id();
let addr = String::from("/ip4/127.0.0.1/udp/12345/quic/p2p/") + &peer_id.to_base58();
assert_eq!(
multiaddr_to_socketaddr(&addr.parse::<Multiaddr>().unwrap()),
Ok((
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12345,),
Some(keypair.public())
Some(SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
)),
8080,
))
);
}
Expand Down
Loading

0 comments on commit 3b1ff34

Please sign in to comment.