From 1617abb3bfcf3e8663568e90a8fc4009aef04ba9 Mon Sep 17 00:00:00 2001 From: Arpan Kapoor Date: Thu, 18 Jul 2024 20:30:33 +0530 Subject: [PATCH] fix(tls): fix rustls panic due to critical libp2p extension Resolves #5487. Pull-Request: #5498. --- Cargo.lock | 24 +++++++++--------- Cargo.toml | 2 +- transports/tls/CHANGELOG.md | 5 ++++ transports/tls/Cargo.toml | 2 +- transports/tls/src/certificate.rs | 41 +++++++++++++++++++++++++++++++ transports/tls/src/lib.rs | 17 ++++++++++--- 6 files changed, 73 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a9dc56e987..80a186f0026 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1727,7 +1727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.9", + "rustls 0.23.11", "rustls-pki-types", ] @@ -3159,7 +3159,7 @@ dependencies = [ "quinn", "rand 0.8.5", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.11", "socket2 0.5.7", "thiserror", "tokio", @@ -3373,7 +3373,7 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.4.0" +version = "0.4.1" dependencies = [ "futures", "futures-rustls", @@ -3385,7 +3385,7 @@ dependencies = [ "libp2p-yamux", "rcgen", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.11", "rustls-webpki 0.101.7", "thiserror", "tokio", @@ -4607,7 +4607,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.9", + "rustls 0.23.11", "thiserror", "tokio", "tracing", @@ -4623,7 +4623,7 @@ dependencies = [ "rand 0.8.5", "ring 0.17.8", "rustc-hash", - "rustls 0.23.9", + "rustls 0.23.11", "slab", "thiserror", "tinyvec", @@ -5156,21 +5156,21 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.5", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" dependencies = [ "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.5", "subtle", "zeroize", ] @@ -5203,9 +5203,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" dependencies = [ "ring 0.17.8", "rustls-pki-types", diff --git a/Cargo.toml b/Cargo.toml index 84767573b9e..55fc43d5b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,7 +106,7 @@ libp2p-swarm = { version = "0.45.0", path = "swarm" } libp2p-swarm-derive = { version = "=0.34.2", path = "swarm-derive" } # `libp2p-swarm-derive` may not be compatible with different `libp2p-swarm` non-breaking releases. E.g. `libp2p-swarm` might introduce a new enum variant `FromSwarm` (which is `#[non-exhaustive]`) in a non-breaking release. Older versions of `libp2p-swarm-derive` would not forward this enum variant within the `NetworkBehaviour` hierarchy. Thus the version pinning is required. libp2p-swarm-test = { version = "0.3.0", path = "swarm-test" } libp2p-tcp = { version = "0.42.0", path = "transports/tcp" } -libp2p-tls = { version = "0.4.0", path = "transports/tls" } +libp2p-tls = { version = "0.4.1", path = "transports/tls" } libp2p-uds = { version = "0.40.0", path = "transports/uds" } libp2p-upnp = { version = "0.2.2", path = "protocols/upnp" } libp2p-webrtc = { version = "0.7.1-alpha", path = "transports/webrtc" } diff --git a/transports/tls/CHANGELOG.md b/transports/tls/CHANGELOG.md index 3c6d0a2a1f5..0343c690834 100644 --- a/transports/tls/CHANGELOG.md +++ b/transports/tls/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.1 + +- Fix a panic caused by `rustls` parsing the libp2p TLS extension. + See [PR 5498](https://github.com/libp2p/rust-libp2p/pull/5498). + ## 0.4.0 - Upgrade `rustls` to `0.23`. See [PR 5385](https://github.com/libp2p/rust-libp2p/pull/5385) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index a4817f20336..c4b30951e66 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-tls" -version = "0.4.0" +version = "0.4.1" edition = "2021" rust-version = { workspace = true } description = "TLS configuration based on libp2p TLS specs." diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index bbd353e32bd..65b373bcf9b 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -26,6 +26,8 @@ use libp2p_identity as identity; use libp2p_identity::PeerId; use x509_parser::{prelude::*, signature_algorithm::SignatureAlgorithm}; +use std::sync::Arc; + /// The libp2p Public Key Extension is a X.509 extension /// with the Object Identifier 1.3.6.1.4.1.53594.1.1, /// allocated by IANA to the libp2p project at Protocol Labs. @@ -42,6 +44,45 @@ const P2P_SIGNING_PREFIX: [u8; 21] = *b"libp2p-tls-handshake:"; // Similarly, hash functions with an output length less than 256 bits MUST NOT be used. static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_P256_SHA256; +#[derive(Debug)] +pub(crate) struct AlwaysResolvesCert(Arc); + +impl AlwaysResolvesCert { + pub(crate) fn new( + cert: rustls::pki_types::CertificateDer<'static>, + key: &rustls::pki_types::PrivateKeyDer<'_>, + ) -> Result { + let certified_key = rustls::sign::CertifiedKey::new( + vec![cert], + rustls::crypto::ring::sign::any_ecdsa_type(key)?, + ); + Ok(Self(Arc::new(certified_key))) + } +} + +impl rustls::client::ResolvesClientCert for AlwaysResolvesCert { + fn resolve( + &self, + _root_hint_subjects: &[&[u8]], + _sigschemes: &[rustls::SignatureScheme], + ) -> Option> { + Some(Arc::clone(&self.0)) + } + + fn has_certs(&self) -> bool { + true + } +} + +impl rustls::server::ResolvesServerCert for AlwaysResolvesCert { + fn resolve( + &self, + _client_hello: rustls::server::ClientHello<'_>, + ) -> Option> { + Some(Arc::clone(&self.0)) + } +} + /// Generates a self-signed TLS certificate that includes a libp2p-specific /// certificate extension containing the public key of the given keypair. pub fn generate( diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 741a4b48077..3aa66db12b3 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -29,6 +29,7 @@ pub mod certificate; mod upgrade; mod verifier; +use certificate::AlwaysResolvesCert; use libp2p_identity::Keypair; use libp2p_identity::PeerId; use std::sync::Arc; @@ -49,6 +50,11 @@ pub fn make_client_config( let mut provider = rustls::crypto::ring::default_provider(); provider.cipher_suites = verifier::CIPHERSUITES.to_vec(); + let cert_resolver = Arc::new( + AlwaysResolvesCert::new(certificate, &private_key) + .expect("Client cert key DER is valid; qed"), + ); + let mut crypto = rustls::ClientConfig::builder_with_provider(provider.into()) .with_protocol_versions(verifier::PROTOCOL_VERSIONS) .expect("Cipher suites and kx groups are configured; qed") @@ -56,8 +62,7 @@ pub fn make_client_config( .with_custom_certificate_verifier(Arc::new( verifier::Libp2pCertificateVerifier::with_remote_peer_id(remote_peer_id), )) - .with_client_auth_cert(vec![certificate], private_key) - .expect("Client cert key DER is valid; qed"); + .with_client_cert_resolver(cert_resolver); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; Ok(crypto) @@ -72,12 +77,16 @@ pub fn make_server_config( let mut provider = rustls::crypto::ring::default_provider(); provider.cipher_suites = verifier::CIPHERSUITES.to_vec(); + let cert_resolver = Arc::new( + AlwaysResolvesCert::new(certificate, &private_key) + .expect("Server cert key DER is valid; qed"), + ); + let mut crypto = rustls::ServerConfig::builder_with_provider(provider.into()) .with_protocol_versions(verifier::PROTOCOL_VERSIONS) .expect("Cipher suites and kx groups are configured; qed") .with_client_cert_verifier(Arc::new(verifier::Libp2pCertificateVerifier::new())) - .with_single_cert(vec![certificate], private_key) - .expect("Server cert key DER is valid; qed"); + .with_cert_resolver(cert_resolver); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; Ok(crypto)