From 06a77c29f8a8f8ac29cc556d716c17896839ed11 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 11:16:51 +1000 Subject: [PATCH 01/48] Add skeleton for libp2p-tls This code is extracted from https://github.com/libp2p/rust-libp2p/pull/2289. Co-authored-by: David Craven Co-authored-by: Roman Proskuryakov Co-authored-by: Max Inden --- Cargo.toml | 4 + transports/tls/Cargo.toml | 16 + transports/tls/src/certificate.rs | 462 ++++++++++++++++++ transports/tls/src/lib.rs | 104 ++++ transports/tls/src/test_assets/ed25519.der | Bin 0 -> 324 bytes transports/tls/src/test_assets/ed448.der | Bin 0 -> 400 bytes transports/tls/src/test_assets/gen.sh | 63 +++ .../tls/src/test_assets/nistp256_sha256.der | Bin 0 -> 388 bytes .../tls/src/test_assets/nistp384_sha256.der | Bin 0 -> 450 bytes .../tls/src/test_assets/nistp384_sha384.der | Bin 0 -> 450 bytes .../tls/src/test_assets/nistp521_sha512.der | Bin 0 -> 525 bytes transports/tls/src/test_assets/openssl.cfg | 6 + .../tls/src/test_assets/pkcs1_sha256.der | Bin 0 -> 324 bytes .../tls/src/test_assets/rsa_pkcs1_sha256.der | Bin 0 -> 785 bytes .../tls/src/test_assets/rsa_pkcs1_sha384.der | Bin 0 -> 785 bytes .../tls/src/test_assets/rsa_pkcs1_sha512.der | Bin 0 -> 785 bytes .../tls/src/test_assets/rsa_pss_sha384.der | Bin 0 -> 878 bytes transports/tls/src/verifier.rs | 213 ++++++++ 18 files changed, 868 insertions(+) create mode 100644 transports/tls/Cargo.toml create mode 100644 transports/tls/src/certificate.rs create mode 100644 transports/tls/src/lib.rs create mode 100644 transports/tls/src/test_assets/ed25519.der create mode 100644 transports/tls/src/test_assets/ed448.der create mode 100644 transports/tls/src/test_assets/gen.sh create mode 100644 transports/tls/src/test_assets/nistp256_sha256.der create mode 100644 transports/tls/src/test_assets/nistp384_sha256.der create mode 100644 transports/tls/src/test_assets/nistp384_sha384.der create mode 100644 transports/tls/src/test_assets/nistp521_sha512.der create mode 100644 transports/tls/src/test_assets/openssl.cfg create mode 100644 transports/tls/src/test_assets/pkcs1_sha256.der create mode 100644 transports/tls/src/test_assets/rsa_pkcs1_sha256.der create mode 100644 transports/tls/src/test_assets/rsa_pkcs1_sha384.der create mode 100644 transports/tls/src/test_assets/rsa_pkcs1_sha512.der create mode 100644 transports/tls/src/test_assets/rsa_pss_sha384.der create mode 100644 transports/tls/src/verifier.rs diff --git a/Cargo.toml b/Cargo.toml index e3e2283b2f6..22348e656fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ default = [ "identify", "kad", "gossipsub", + "tls", "mdns-async-io", "mplex", "noise", @@ -46,6 +47,7 @@ floodsub = ["dep:libp2p-floodsub"] identify = ["dep:libp2p-identify", "libp2p-metrics?/identify"] kad = ["dep:libp2p-kad", "libp2p-metrics?/kad"] gossipsub = ["dep:libp2p-gossipsub", "libp2p-metrics?/gossipsub"] +tls = ["dep:libp2p-tls"] metrics = ["dep:libp2p-metrics"] mdns-async-io = ["dep:libp2p-mdns", "libp2p-mdns?/async-io"] mdns-tokio = ["dep:libp2p-mdns", "libp2p-mdns?/tokio"] @@ -86,6 +88,7 @@ libp2p-dcutr = { version = "0.6.0", path = "protocols/dcutr", optional = true } libp2p-floodsub = { version = "0.39.1", path = "protocols/floodsub", optional = true } libp2p-identify = { version = "0.39.0", path = "protocols/identify", optional = true } libp2p-kad = { version = "0.41.0", path = "protocols/kad", optional = true } +libp2p-tls = { version = "0.1.0", path = "transports/tls", optional = true } libp2p-metrics = { version = "0.10.0", path = "misc/metrics", optional = true } libp2p-mplex = { version = "0.36.1", path = "muxers/mplex", optional = true } libp2p-noise = { version = "0.39.1", path = "transports/noise", optional = true } @@ -149,6 +152,7 @@ members = [ "transports/deflate", "transports/dns", "transports/noise", + "transports/tls", "transports/plaintext", "transports/pnet", "transports/tcp", diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml new file mode 100644 index 00000000000..867a4ec81f3 --- /dev/null +++ b/transports/tls/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "libp2p-tls" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } +x509-parser = "0.13.0" +libp2p-core = { version = "0.36.0", path = "../../core" } +webpki = "0.22.0" +rcgen = "0.9.2" +yasna = "0.5.0" +ring = "0.16.20" +thiserror = "1.0.26" diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs new file mode 100644 index 00000000000..b6d3c8b2c68 --- /dev/null +++ b/transports/tls/src/certificate.rs @@ -0,0 +1,462 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! X.509 certificate handling for libp2p +//! +//! This module handles generation, signing, and verification of certificates. + +use libp2p_core::identity; +use x509_parser::prelude::*; + +/// The libp2p Public Key Extension is a X.509 extension +/// with the Object Identier 1.3.6.1.4.1.53594.1.1, +/// allocated by IANA to the libp2p project at Protocol Labs. +const P2P_EXT_OID: [u64; 9] = [1, 3, 6, 1, 4, 1, 53594, 1, 1]; + +/// The peer signs the concatenation of the string `libp2p-tls-handshake:` +/// and the public key that it used to generate the certificate carrying +/// the libp2p Public Key Extension, using its private host key. +/// This signature provides cryptographic proof that the peer was +/// in possession of the private host key at the time the certificate was signed. +const P2P_SIGNING_PREFIX: [u8; 21] = *b"libp2p-tls-handshake:"; + +// Certificates MUST use the NamedCurve encoding for elliptic curve parameters. +// 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; + +/// Generates a self-signed TLS certificate that includes a libp2p-specific +/// certificate extension containing the public key of the given keypair. +pub fn make_certificate( + keypair: &identity::Keypair, +) -> Result { + // Keypair used to sign the certificate. + // SHOULD NOT be related to the host's key. + // Endpoints MAY generate a new key and certificate + // for every connection attempt, or they MAY reuse the same key + // and certificate for multiple connections. + let certif_keypair = rcgen::KeyPair::generate(P2P_SIGNATURE_ALGORITHM)?; + + // Generate the libp2p-specific extension. + // The certificate MUST contain the libp2p Public Key Extension. + let libp2p_extension: rcgen::CustomExtension = { + // The peer signs the concatenation of the string `libp2p-tls-handshake:` + // and the public key that it used to generate the certificate carrying + // the libp2p Public Key Extension, using its private host key. + let signature = { + let mut msg = vec![]; + msg.extend(P2P_SIGNING_PREFIX); + msg.extend(certif_keypair.public_key_der()); + + keypair + .sign(&msg) + .map_err(|_| rcgen::RcgenError::RingUnspecified)? + }; + + // The public host key and the signature are ANS.1-encoded + // into the SignedKey data structure, which is carried + // in the libp2p Public Key Extension. + // SignedKey ::= SEQUENCE { + // publicKey OCTET STRING, + // signature OCTET STRING + // } + let extension_content = { + let serialized_pubkey = keypair.public().to_protobuf_encoding(); + yasna::encode_der(&(serialized_pubkey, signature)) + }; + + // This extension MAY be marked critical. + let mut ext = rcgen::CustomExtension::from_oid_content(&P2P_EXT_OID, extension_content); + ext.set_criticality(true); + ext + }; + + let certificate = { + let mut params = rcgen::CertificateParams::new(vec![]); + params.distinguished_name = rcgen::DistinguishedName::new(); + params.custom_extensions.push(libp2p_extension); + params.alg = P2P_SIGNATURE_ALGORITHM; + params.key_pair = Some(certif_keypair); + rcgen::Certificate::from_params(params)? + }; + + Ok(certificate) +} + +/// The contents of the specific libp2p extension, containing the public host key +/// and a signature performed using the private host key. +pub struct P2pExtension { + pub(crate) public_key: identity::PublicKey, + /// This signature provides cryptographic proof that the peer was + /// in possession of the private host key at the time the certificate was signed. + signature: Vec, +} + +/// An X.509 certificate with a libp2p-specific extension +/// is used to secure libp2p connections. +pub struct P2pCertificate<'a> { + certificate: X509Certificate<'a>, + /// This is a specific libp2p Public Key Extension with two values: + /// * the public host key + /// * a signature performed using the private host key + pub(crate) extension: P2pExtension, +} + +/// Parse TLS certificate from DER input that includes a libp2p-specific +/// certificate extension containing a public key of a peer. +pub fn parse_certificate(der_input: &[u8]) -> Result { + use webpki::Error; + let x509 = X509Certificate::from_der(der_input) + .map(|(_rest_input, x509)| x509) + .map_err(|_| Error::BadDer)?; + + let p2p_ext_oid = der_parser::oid::Oid::from(&P2P_EXT_OID) + .expect("This is a valid OID of p2p extension; qed"); + + let mut libp2p_extension = None; + + for ext in x509.extensions() { + let oid = &ext.oid; + if oid == &p2p_ext_oid && libp2p_extension.is_some() { + // The extension was already parsed + return Err(Error::BadDer); + } + + if oid == &p2p_ext_oid { + // The public host key and the signature are ANS.1-encoded + // into the SignedKey data structure, which is carried + // in the libp2p Public Key Extension. + // SignedKey ::= SEQUENCE { + // publicKey OCTET STRING, + // signature OCTET STRING + // } + let (public_key, signature): (Vec, Vec) = + yasna::decode_der(ext.value).map_err(|_| Error::ExtensionValueInvalid)?; + // The publicKey field of SignedKey contains the public host key + // of the endpoint, encoded using the following protobuf: + // enum KeyType { + // RSA = 0; + // Ed25519 = 1; + // Secp256k1 = 2; + // ECDSA = 3; + // } + // message PublicKey { + // required KeyType Type = 1; + // required bytes Data = 2; + // } + let public_key = identity::PublicKey::from_protobuf_encoding(&public_key) + .map_err(|_| Error::UnknownIssuer)?; + let ext = P2pExtension { + public_key, + signature, + }; + libp2p_extension = Some(ext); + continue; + } + + if ext.critical { + // Endpoints MUST abort the connection attempt if the certificate + // contains critical extensions that the endpoint does not understand. + return Err(Error::UnsupportedCriticalExtension); + } + + // Implementations MUST ignore non-critical extensions with unknown OIDs. + } + + // The certificate MUST contain the libp2p Public Key Extension. + // If this extension is missing, endpoints MUST abort the connection attempt. + let extension = libp2p_extension.ok_or(Error::BadDer)?; + + Ok(P2pCertificate { + certificate: x509, + extension, + }) +} + +impl P2pCertificate<'_> { + /// This method validates the certificate according to libp2p TLS 1.3 specs. + /// The certificate MUST: + /// 1. be valid at the time it is received by the peer; + /// 2. use the NamedCurve encoding; + /// 3. use hash functions with an output length not less than 256 bits; + /// 4. be self signed; + /// 5. contain a valid signature in the specific libp2p extension. + pub fn verify(&self) -> Result<(), webpki::Error> { + use webpki::Error; + // The certificate MUST have NotBefore and NotAfter fields set + // such that the certificate is valid at the time it is received by the peer. + if !self.certificate.validity().is_valid() { + return Err(Error::InvalidCertValidity); + } + + // Certificates MUST use the NamedCurve encoding for elliptic curve parameters. + // Similarly, hash functions with an output length less than 256 bits + // MUST NOT be used, due to the possibility of collision attacks. + // In particular, MD5 and SHA1 MUST NOT be used. + // Endpoints MUST abort the connection attempt if it is not used. + let signature_scheme = self.signature_scheme()?; + // Endpoints MUST abort the connection attempt if the certificate’s + // self-signature is not valid. + let raw_certificate = self.certificate.tbs_certificate.as_ref(); + let signature = self.certificate.signature_value.data; + // check if self signed + self.verify_signature(signature_scheme, raw_certificate, signature) + .map_err(|_| Error::SignatureAlgorithmMismatch)?; + + let subject_pki = self.certificate.public_key().raw; + + // The peer signs the concatenation of the string `libp2p-tls-handshake:` + // and the public key that it used to generate the certificate carrying + // the libp2p Public Key Extension, using its private host key. + let mut msg = vec![]; + msg.extend(P2P_SIGNING_PREFIX); + msg.extend(subject_pki); + + // This signature provides cryptographic proof that the peer was in possession + // of the private host key at the time the certificate was signed. + // Peers MUST verify the signature, and abort the connection attempt + // if signature verification fails. + let user_owns_sk = self + .extension + .public_key + .verify(&msg, &self.extension.signature); + if !user_owns_sk { + return Err(Error::UnknownIssuer); + } + Ok(()) + } + + /// Return the signature scheme corresponding to [`AlgorithmIdentifier`]s + /// of `subject_pki` and `signature_algorithm` + /// according to ``. + pub fn signature_scheme(&self) -> Result { + // Certificates MUST use the NamedCurve encoding for elliptic curve parameters. + // Endpoints MUST abort the connection attempt if it is not used. + use oid_registry::*; + use rustls::SignatureScheme::*; + use webpki::Error; + let signature_algorithm = &self.certificate.signature_algorithm; + let pki_algorithm = &self.certificate.tbs_certificate.subject_pki.algorithm; + + if pki_algorithm.algorithm == OID_PKCS1_RSAENCRYPTION { + if signature_algorithm.algorithm == OID_PKCS1_SHA256WITHRSA { + return Ok(RSA_PKCS1_SHA256); + } + if signature_algorithm.algorithm == OID_PKCS1_SHA384WITHRSA { + return Ok(RSA_PKCS1_SHA384); + } + if signature_algorithm.algorithm == OID_PKCS1_SHA512WITHRSA { + return Ok(RSA_PKCS1_SHA512); + } + if signature_algorithm.algorithm == OID_PKCS1_RSASSAPSS { + // According to https://datatracker.ietf.org/doc/html/rfc4055#section-3.1: + // Inside of params there shuld be a sequence of: + // - Hash Algorithm + // - Mask Algorithm + // - Salt Length + // - Trailer Field + + // We are interested in Hash Algorithm only, however the der parser parses + // params into a mess, so here is a workaround to fix it: + fn get_hash_oid<'a>( + signature_algorithm: &'a AlgorithmIdentifier, + ) -> Option> { + let params = signature_algorithm.parameters.as_ref()?; + let params = params.as_sequence().ok()?; + let first_param = params.get(0)?; + let hash_oid_der = first_param.as_slice().ok()?; + let (_, obj) = der_parser::parse_der(hash_oid_der).ok()?; + let hash_oid = obj.as_sequence().ok()?.get(0)?.as_oid_val().ok()?; + Some(hash_oid) + } + + let hash_oid = get_hash_oid(signature_algorithm).ok_or(webpki::Error::BadDer)?; + + if hash_oid == OID_NIST_HASH_SHA256 { + return Ok(RSA_PSS_SHA256); + } + if hash_oid == OID_NIST_HASH_SHA384 { + return Ok(RSA_PSS_SHA384); + } + if hash_oid == OID_NIST_HASH_SHA512 { + return Ok(RSA_PSS_SHA512); + } + + // Default hash algo is SHA-1, however: + // In particular, MD5 and SHA1 MUST NOT be used. + return Err(Error::UnsupportedSignatureAlgorithm); + } + } + + if pki_algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY { + let signature_param = pki_algorithm + .parameters + .as_ref() + .ok_or(webpki::Error::BadDer)? + .as_oid_val() + .map_err(|_| webpki::Error::BadDer)?; + if signature_param == OID_EC_P256 + && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA256 + { + return Ok(ECDSA_NISTP256_SHA256); + } + if signature_param == OID_NIST_EC_P384 + && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA384 + { + return Ok(ECDSA_NISTP384_SHA384); + } + if signature_param == OID_NIST_EC_P521 + && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA512 + { + return Ok(ECDSA_NISTP521_SHA512); + } + return Err(webpki::Error::UnsupportedSignatureAlgorithm); + } + + if signature_algorithm.algorithm == OID_SIG_ED25519 { + return Ok(ED25519); + } + if signature_algorithm.algorithm == OID_SIG_ED448 { + return Ok(ED448); + } + + Err(webpki::Error::UnsupportedSignatureAlgorithm) + } + + /// Get a [`ring::signature::UnparsedPublicKey`] for this `signature_scheme`. + /// Return `Error` if the `signature_scheme` does not match the public key signature + /// and hashing algorithm or if the `signature_scheme` is not supported. + pub fn public_key( + &self, + signature_scheme: rustls::SignatureScheme, + ) -> Result, webpki::Error> { + use ring::signature; + use rustls::SignatureScheme::*; + + let current_signature_scheme = self.signature_scheme()?; + if signature_scheme != current_signature_scheme { + // This certificate was signed with a different signature scheme + return Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey); + } + + let verification_algorithm: &dyn signature::VerificationAlgorithm = match signature_scheme { + RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_2048_8192_SHA256, + RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_2048_8192_SHA384, + RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_2048_8192_SHA512, + ECDSA_NISTP256_SHA256 => &signature::ECDSA_P256_SHA256_ASN1, + ECDSA_NISTP384_SHA384 => &signature::ECDSA_P384_SHA384_ASN1, + ECDSA_NISTP521_SHA512 => { + // See https://github.com/briansmith/ring/issues/824 + return Err(webpki::Error::UnsupportedSignatureAlgorithm); + } + RSA_PSS_SHA256 => &signature::RSA_PSS_2048_8192_SHA256, + RSA_PSS_SHA384 => &signature::RSA_PSS_2048_8192_SHA384, + RSA_PSS_SHA512 => &signature::RSA_PSS_2048_8192_SHA512, + ED25519 => &signature::ED25519, + ED448 => { + // See https://github.com/briansmith/ring/issues/463 + return Err(webpki::Error::UnsupportedSignatureAlgorithm); + } + // Similarly, hash functions with an output length less than 256 bits + // MUST NOT be used, due to the possibility of collision attacks. + // In particular, MD5 and SHA1 MUST NOT be used. + RSA_PKCS1_SHA1 => return Err(webpki::Error::UnsupportedSignatureAlgorithm), + ECDSA_SHA1_Legacy => return Err(webpki::Error::UnsupportedSignatureAlgorithm), + Unknown(_) => return Err(webpki::Error::UnsupportedSignatureAlgorithm), + }; + let spki = &self.certificate.tbs_certificate.subject_pki; + let key = + signature::UnparsedPublicKey::new(verification_algorithm, spki.subject_public_key.data); + Ok(key) + } + /// Verify the `signature` of the `message` signed by the private key corresponding to the public key stored + /// in the certificate. + pub fn verify_signature( + &self, + signature_scheme: rustls::SignatureScheme, + message: &[u8], + signature: &[u8], + ) -> Result<(), webpki::Error> { + let pk = self.public_key(signature_scheme)?; + pk.verify(message, signature) + .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sanity_check() { + let keypair = libp2p_core::identity::Keypair::generate_ed25519(); + let cert = make_certificate(&keypair).unwrap(); + let cert_der = cert.serialize_der().unwrap(); + let parsed_cert = parse_certificate(&cert_der).unwrap(); + assert!(parsed_cert.verify().is_ok()); + assert_eq!(keypair.public(), parsed_cert.extension.public_key); + } + + macro_rules! check_cert { + ($name:ident, $path:literal, $scheme:path) => { + #[test] + fn $name() { + let cert: &[u8] = include_bytes!($path); + + let cert = parse_certificate(cert).unwrap(); + assert!(cert.verify().is_err()); // Because p2p extension + // was not signed with the private key + // of the certificate. + assert_eq!(cert.signature_scheme(), Ok($scheme)); + } + }; + } + + check_cert! {ed448, "./test_assets/ed448.der", rustls::SignatureScheme::ED448} + check_cert! {ed25519, "./test_assets/ed25519.der", rustls::SignatureScheme::ED25519} + check_cert! {rsa_pkcs1_sha256, "./test_assets/rsa_pkcs1_sha256.der", rustls::SignatureScheme::RSA_PKCS1_SHA256} + check_cert! {rsa_pkcs1_sha384, "./test_assets/rsa_pkcs1_sha384.der", rustls::SignatureScheme::RSA_PKCS1_SHA384} + check_cert! {rsa_pkcs1_sha512, "./test_assets/rsa_pkcs1_sha512.der", rustls::SignatureScheme::RSA_PKCS1_SHA512} + check_cert! {nistp256_sha256, "./test_assets/nistp256_sha256.der", rustls::SignatureScheme::ECDSA_NISTP256_SHA256} + check_cert! {nistp384_sha384, "./test_assets/nistp384_sha384.der", rustls::SignatureScheme::ECDSA_NISTP384_SHA384} + check_cert! {nistp521_sha512, "./test_assets/nistp521_sha512.der", rustls::SignatureScheme::ECDSA_NISTP521_SHA512} + + #[test] + fn rsa_pss_sha384() { + let cert: &[u8] = include_bytes!("./test_assets/rsa_pss_sha384.der"); + + let cert = parse_certificate(cert).unwrap(); + cert.verify().unwrap(); // that was a fairly generated certificate. + + assert_eq!( + cert.signature_scheme(), + Ok(rustls::SignatureScheme::RSA_PSS_SHA384) + ); + } + + #[test] + fn nistp384_sha256() { + let cert: &[u8] = include_bytes!("./test_assets/nistp384_sha256.der"); + + let cert = parse_certificate(cert).unwrap(); + + assert!(cert.signature_scheme().is_err()); + } +} diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs new file mode 100644 index 00000000000..78b31a7d4e9 --- /dev/null +++ b/transports/tls/src/lib.rs @@ -0,0 +1,104 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! TLS configuration for QUIC based on libp2p TLS specs. + +pub(crate) mod certificate; +mod verifier; + +use std::sync::Arc; +use thiserror::Error; + +use rustls::{ + cipher_suite::{ + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, + }, + SupportedCipherSuite, +}; + +/// A list of the TLS 1.3 cipher suites supported by rustls. +// By default rustls creates client/server configs with both +// TLS 1.3 __and__ 1.2 cipher suites. But we don't need 1.2. +static TLS13_CIPHERSUITES: &[SupportedCipherSuite] = &[ + // TLS1.3 suites + TLS13_CHACHA20_POLY1305_SHA256, + TLS13_AES_256_GCM_SHA384, + TLS13_AES_128_GCM_SHA256, +]; + +const P2P_ALPN: [u8; 6] = *b"libp2p"; + +/// Error creating a configuration +#[derive(Debug, Error)] +pub enum ConfigError { + /// TLS private key or certificate rejected + #[error("TLS private or certificate key rejected: {0}")] + TLSError(#[from] rustls::Error), + /// Certificate generation error + #[error("Certificate generation error: {0}")] + RcgenError(#[from] rcgen::RcgenError), +} + +/// Create a TLS client configuration for libp2p. +pub fn make_client_config( + keypair: &libp2p_core::identity::Keypair, +) -> Result { + let (certificate, key) = make_cert_key(keypair)?; + let verifier = Arc::new(verifier::Libp2pCertificateVerifier); + let mut crypto = rustls::ClientConfig::builder() + .with_cipher_suites(TLS13_CIPHERSUITES) + .with_safe_default_kx_groups() + .with_protocol_versions(&[&rustls::version::TLS13]) + .expect("Cipher suites and kx groups are configured; qed") + .with_custom_certificate_verifier(verifier) + .with_single_cert(vec![certificate], key) + .expect("Client cert key DER is valid; qed"); + crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; + Ok(crypto) +} + +/// Create a TLS server configuration for libp2p. +pub fn make_server_config( + keypair: &libp2p_core::identity::Keypair, +) -> Result { + let (certificate, key) = make_cert_key(keypair)?; + let verifier = Arc::new(verifier::Libp2pCertificateVerifier); + let mut crypto = rustls::ServerConfig::builder() + .with_cipher_suites(TLS13_CIPHERSUITES) + .with_safe_default_kx_groups() + .with_protocol_versions(&[&rustls::version::TLS13]) + .expect("Cipher suites and kx groups are configured; qed") + .with_client_cert_verifier(verifier) + .with_single_cert(vec![certificate], key) + .expect("Server cert key DER is valid; qed"); + crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; + Ok(crypto) +} + +/// Create a random private key and certificate signed with this key for rustls. +fn make_cert_key( + keypair: &libp2p_core::identity::Keypair, +) -> Result<(rustls::Certificate, rustls::PrivateKey), ConfigError> { + let cert = certificate::make_certificate(keypair)?; + let private_key = cert.serialize_private_key_der(); + let cert = rustls::Certificate(cert.serialize_der()?); + let key = rustls::PrivateKey(private_key); + Ok((cert, key)) +} diff --git a/transports/tls/src/test_assets/ed25519.der b/transports/tls/src/test_assets/ed25519.der new file mode 100644 index 0000000000000000000000000000000000000000..494a199561a67047c63aa847ebd5a734d664a974 GIT binary patch literal 324 zcmXqLVstQQ{JemfiIIs(gsZ!KuJPrk8h>J!?Gd%_Vy}DSeZYW~jafUjz<|L(PMp`s z(9p=x)WFctz{E5P$Tb2oO`u$$3N5H&W<`by-3L?mpZR?`CQ59!Z)V-Wt`7G%!P(Va z`JM^-Zl^sCEv`4HHK=Ce(q?01VQgL$#RvrdS+Wc=SX4L|g%s|mOgtj`mczUK#Rs0- zUXDkYWBx7udC6Aw|C|-mpZ~qX&*Cs;#k`<1Dt|S%Y?=E^^l+_ObJC0J#}7>VU2{SB z&J?C_tuGxZ4gZd^A3k8ap-S(B*rsiTF6wtQK36_yos(}Zx|Omf`T3iC8RwaGO^j7t(-$c*dCA@9-p7CM LTr=(RtS{UEyA_6f literal 0 HcmV?d00001 diff --git a/transports/tls/src/test_assets/ed448.der b/transports/tls/src/test_assets/ed448.der new file mode 100644 index 0000000000000000000000000000000000000000..c74123868473acbc8b680c478d80aabc7371d6b7 GIT binary patch literal 400 zcmXqLV(c+!V&qxC%*4pVB*MQwaM@J6(&HYkoYmTlks*D;u+RYM}vxft)z6 zk)ffHp{aqPp@E5M6p(8KWST&^Ko!nV#mrU=*VyfM-|{N)?>iS8a&+&3Y3u()K5aR+ zIm*WxUn14%uUb0pFKWD}C=YQ|;vp7sy zF)!$h%3sYbTjo9!JzT5Sob=-Q@dML-*IW?3GleN!>q|#U!@r~KhY#3psM0$jwrN|T zi~1dn&y^2a=j2<9?q-Ggp_rlWg<_CUmqNG0l_FrDK;;PW5BCaUp$h2rkL mO`5a*>{jW{X%6E$_KeR(Eb8+q&&WykHGDhu;xlitFaQ9G!K6U| literal 0 HcmV?d00001 diff --git a/transports/tls/src/test_assets/gen.sh b/transports/tls/src/test_assets/gen.sh new file mode 100644 index 00000000000..4b7718874dd --- /dev/null +++ b/transports/tls/src/test_assets/gen.sh @@ -0,0 +1,63 @@ +#ED25519 (works): +openssl genpkey -algorithm ed25519 -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out ed25519.der + +#ED448 (works): +openssl genpkey -algorithm ed448 -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out ed448.der + +#RSA_PKCS1_SHA256 (works): +openssl genpkey -algorithm rsa -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha256 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out rsa_pkcs1_sha256.der + +#RSA_PKCS1_SHA384 (works): +# reuse privateKey.key and req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha384 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out rsa_pkcs1_sha384.der + +#RSA_PKCS1_SHA512 (works): +# reuse privateKey.key and req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha512 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out rsa_pkcs1_sha512.der + +#RSA-PSS TODO +# openssl genpkey -algorithm rsa-pss -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:3 -out privateKey.key +# # -sigopt rsa_pss_saltlen:20 +# # -sigopt rsa_padding_mode:pss +# # -sigopt rsa_mgf1_md:sha256 +# openssl req -x509 -nodes -days 365 -subj="/" -key privateKey.key -sha256 -sigopt rsa_pss_saltlen:20 -sigopt rsa_padding_mode:pss -sigopt rsa_mgf1_md:sha256 -out certificate.crt + +#ECDSA_NISTP256_SHA256 (works): +openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha256 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out nistp256_sha256.der + +#ECDSA_NISTP384_SHA384 (works): +openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha384 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out nistp384_sha384.der + +#ECDSA_NISTP521_SHA512 (works): +openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-521 -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha512 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out nistp521_sha512.der + +#ECDSA_NISTP384_SHA256 (must fail): +openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out privateKey.key +openssl req -new -subj="/" -key privateKey.key -out req.pem +openssl x509 -req -in req.pem -signkey privateKey.key -sha256 -out certificate.crt -extensions p2p_ext -extfile ./openssl.cfg +openssl x509 -outform der -in certificate.crt -out nistp384_sha256.der + + +# Remove tmp files + +rm req.pem certificate.crt privateKey.key diff --git a/transports/tls/src/test_assets/nistp256_sha256.der b/transports/tls/src/test_assets/nistp256_sha256.der new file mode 100644 index 0000000000000000000000000000000000000000..8023645e9b07e58ab410f71564699cc8433aebe8 GIT binary patch literal 388 zcmXqLVr(#IVpLzi%*4pVBoeuh$A9gw`qI<8*#%$TwHM>@k+^8U#m1r4=5fxJg_+5K z!9Y%&*T~S&$k5cl(9podGz!Qy0y0gYT%d|b17S9Huns0hs8(i1c4j9A7AM7ZKl|>U z^|{d|0l!Z7IyXK-&0$Y zm%ozhEISpuY;nCotwA*#mo^(C3uE)5C`KUo&yr=3!J@*!D5P*dW#SRhw;bN>FFx?x z_HsPJ9P@AK&r7zd|L3fj{`~J9einx*E9M2AQTeO6Wy{=WqK9kMnv-5!KYn1^@0tt3 zccw6fYklcRY4~@P{qOzsUR(cP@bVZrRqVBo@}pf&g3 zq6+cO<~!|rv8ifqPo<1>?AvUWbS{K0Vf^U+O^8XswRaWE=@pM=?bs@wcDHSHh2YAC STQ@y5C|BTrHNV5|x;FscJeIfs literal 0 HcmV?d00001 diff --git a/transports/tls/src/test_assets/nistp384_sha256.der b/transports/tls/src/test_assets/nistp384_sha256.der new file mode 100644 index 0000000000000000000000000000000000000000..5d76fa8f4a90ca3bba0a22150e4805d2ca9380a7 GIT binary patch literal 450 zcmXqLV%%rY#OShsnTe5!Nkl#(dWKqA?wTq_--bescs|x?tDhTiv2kd%d7QIlVP-O5 zFpv}HH8M0bGBhV>ja9pmg+YlqiGjuNWmseH z9Le0PpTAFUy=}kTa^Zx4Gk;#~J0!ko+G1y4v%mg-uB*G(GR7A_TK-f+tgawWX#0Bc zy0kNYFF)Xu$jO|1jxBMnk=@6YGcR`iHDl^K=hTvt*{`|GCcHx6NoM+k#q|cY2Gwj_ z+H8z0jLnOp7=hqFOO`cq%ekGextUSCuds7>a(4+aRfv`!C~?lwexjwC0r@ jpXU1e{N8I>`jY#B^pUBD$~Q(%uW#7c(2*{x@@WzPu{5%@ literal 0 HcmV?d00001 diff --git a/transports/tls/src/test_assets/nistp384_sha384.der b/transports/tls/src/test_assets/nistp384_sha384.der new file mode 100644 index 0000000000000000000000000000000000000000..a81a5ce1ab748be7714c385ae4f3525bd9024fd2 GIT binary patch literal 450 zcmXqLV%%rY#OShsnTe5!Nu=gc^!`1Y^>UiEDTD_NenC=ldlAS z-lX}{j&IKH@XKv(|9?$h-Fdi~adXzn9MP9gnKspYlUZqhJm<*YwpSbzECPgz*Js`? z7y56bbH40i%;WC(w-ZZ!x9{fPQ!RKkN9lLsuMDs24O1RX+Q-5+r|-@alg0H0wFcE} zT-t1mER4;Iq8NeTKTDQD28#*@qmaV=l!-?~-*R}jzxcp&+sp9?bIiY`KQGy;{-3jA z`t!ec_*opLte6*cM&+;OmMwFii5{+1YfgG`{rG`tziTcC-}PyRYgN zC(DVX8|v-Lrrsz%C=+1Fx=}fyNxddUMXYWeTi^!~CIio?eBN}+>C6G^l>dC5Y#Z=n lNz%U>Th1%0D~@{HF0c6_Joi(}?Ch7f&pvow&e6Qe1OS~`yc+-j literal 0 HcmV?d00001 diff --git a/transports/tls/src/test_assets/nistp521_sha512.der b/transports/tls/src/test_assets/nistp521_sha512.der new file mode 100644 index 0000000000000000000000000000000000000000..2846361f278e37f4338e35848304af02af4721e5 GIT binary patch literal 525 zcmXqLV&XJtV$52=%*4pVBqDR5TjaPvpWN2z&XlP(!8U^GU0huOHE>K0|Yy$x{cCZ#EMmARMMivHT=EgP#7Dka- zL6Wn6m|iqy516L5b>s6TtB%xP{c$aQ?$V%ZPgi$sZ+4p}wtwyY;FcE`)-f*XWvR1avZ&&|oGI`uZV*mRCW#>&gT-yYb~l)AT5b#c8xtwA*#mo^(C3uE)5C`KUo z&yr=3!J@*!D5P*dW#SRhw;bN>FFx?x_HsPJ9P@AK&r7zd|L3fj{`~J9einx*E9M2A zQTeO6Wy{=WqK9kMnv-5!KYn1^@0tt3ccw6fYklcRY4~@P{qOzsUR(cP@b!O7g%0}QSXCMSj|%2T3ly_8jatJb(T)^6+0-`4B(ZMk|iKikOg zE7wV^x6;2mb-V1e$mx?#HTE(@+^))3nl5JWdGW>YsO9R{W@R)mIWa!J;`~DKE5koE z<)Zly_Dr-$&9`PRS9U2HPnZ=8L8z9UOtY2y{g=1#{X%U7@a7CkZ0 LW8!JtW5NgkxUAVe literal 0 HcmV?d00001 diff --git a/transports/tls/src/test_assets/openssl.cfg b/transports/tls/src/test_assets/openssl.cfg new file mode 100644 index 00000000000..62f02baee8b --- /dev/null +++ b/transports/tls/src/test_assets/openssl.cfg @@ -0,0 +1,6 @@ +[ p2p_ext ] +1.3.6.1.4.1.53594.1.1 = critical,ASN1:SEQUENCE:ExtBody + +[ ExtBody ] +pubkey = FORMAT:HEX,OCTETSTRING:08011220DF6491C415ED084B87E8F00CDB4A41C4035CFEA5F9D23D25FF9CA897E7FDDC0F +signature = FORMAT:HEX,OCTETSTRING:94A89E52CC24FD29B4B49DE615C37D268362E8D7C7C096FB7CD013DC9402572AF4886480FEC507C3C03DB07A2EC816B2B6714427DC28F379E0859C6F3B15BB05 diff --git a/transports/tls/src/test_assets/pkcs1_sha256.der b/transports/tls/src/test_assets/pkcs1_sha256.der new file mode 100644 index 0000000000000000000000000000000000000000..0449728ee28cbf651c604319dde98adccf09a972 GIT binary patch literal 324 zcmXqLVstQQ{JemfiIIs(M8EgZMO6_-S-n@gy3W5hD&ec=e{I0Z#;l!MV8CD?C(dhR zXlP_;Vq|D!YG4=z2DJv&Y+TxGj4X`Ji=r5T;6F>2K?aKo2cwX}{gjDEMBj3Fx4-zn zbKA@D2y@K8r9Us(s{Ws|V*2yHclcQxrmUD3bVlW`=9VpUpNSr>RclUqasBv#X}@bO z2;Z5)6t4B9BchN82d#7RtwndU!kp^JaLP?jrthO} zWJs;p?nk8;%NP1Nmzb63-iWqi3$a_aT_`a)YN6m+m89z*Z?;?i{eM(y=|t71f4*23 M%(xjA&LNQU!^hIZ7oGD#4|0^Herbh^>%92}FkhHa7utKm zX+DXLrckXxX&9^4=l>jM(E6~^c2D%J0 z@2gk9aBCcbJH4MnIN0VkvFc0+_}*^0VU|*QD&PtUG8S1y_DSehdfJ@7z~@G674Iu1 zLUEn2Bwz&)^fF31+LwsAx!+$i&T;DwxTI~BVHFbxI? zDuzgg_YDC73k3iJf&l>ls{O>va6j?)%|O$*2S_6zx_OsT=e})EL9`9%VdYzD?;oNlFtjR)LJf9x2dSf3xgE<8j*6V5KhZ z1kw>ELCebYN!v&&LNQU!^hIZ7oGD#4|0^Herbh^>%92}FkhHa7utKm zX+DXLrckXxX&9^4=l>jM(E6~^c2D%J0 z@2gk9aBCcbJH4MnIN0VkvFc0+_}*^0VU|*QD&PtUG8S1y_DSehdfJ@7z~@G674Iu1 zLUEn2Bwz&)^fF31+LwsAx!+$i&T;DwxTI~BVHFbxI? zDuzgg_YDC73lAYYhX=D?z&@=>dr?(eP@!eG}fqOFo%}ZW#*l76S6i> zZ1KzN`906wsd>zF`I^0zaN}~+b}pHlqZglMunBk($nm+p9it)W3g5lR!##vLawn|v z60*daTN04wlT8`+hs_S$UNc8*1%WZ6snSs?x0f=yQ&?r!4u{s?A=+ML3A1pxkgy^_ zm}b+S!ZuVb7#oKWf@~z4H2W^Luvc;>FKAMJ1*`5u=xBKDF+pTLQUYDmcW8DIPJ@W; PfHNE258&LNQU!^hIZ7oGD#4|0^Herbh^>%92}FkhHa7utKm zX+DXLrckXxX&9^4=l>jM(E6~^c2D%J0 z@2gk9aBCcbJH4MnIN0VkvFc0+_}*^0VU|*QD&PtUG8S1y_DSehdfJ@7z~@G674Iu1 zLUEn2Bwz&)^fF31+LwsAx!+$i&T;DwxTI~BVHFbxI? zDuzgg_YDC74Fv!Lf&l>lBDp_*l%n5u2o|HCz*VVC^{6CJm2jtSz-!c}4ao5QWO?hx z)1V(q2CcODGd4Jo^_){yo%|YxwKz4L#8LM{&w(PNI%VZ=vg(V|u1hz|-?aC|(Su}p z4BWTyQ$#^356{ToI-QLQfcf~WrZMAt^HL|tHAA#Iei|H-TlJDia@lNbKdt6QXt^mm z=@DU`6NPW5I%k5fQnftOKT<9-z+E=NI|#!P8V&W%HogIa>E)jUk61O_DT@suIFc18 zR0rsQrH7CX$>dO9665jGrO!rm%p2%zn_ZjXoLHSakH}9!39870w0HHFz{|#&(B{FI z%FM#V#LBQx#y|?8f&)!<5zsIL0|o;n34Q|u14A$bG7Jo&B>0UCjSLOUjDVD>fuT_p zP*oG75=cMHI!0Co<|amdkT@4p6C)$TwQ$SX3P%e9pUOXD@ikd{+^y;P@x5yvdjIj8 z--{%t>mA@2B}B&UzG|A?kMT%+b986Wu0Bay^@I z^H1}>O$k;EOP5c0bh)fa$Ys&vq<$ys`@1YS?kZ+Zzs+1{m*e6x=k)@HAFpnPPTj5k zxq8aqPb_=P!$iz}Iu}=eepQukJmF8fzwKwfO$mI@`2tJ5&ITB0c|K#iuahsnb4|an zkH3n4%6>MDEY>X_7 z&5NQK8CkLnGFVhN7=;w}XFX-n+bWZ?{g!*pF2|ruM!%Eq{ie){6`j>#CBN4*hsEKX z(e?!u4wLP6G}+0!|K53&eWU-mP332giU+;EsS;LmxqjioGx=Nxt~Z~tZg=~8Bv^3I zt&J58-^#z#zWc;JnSqrG$pILrj0_AJ{kQu*S3hp{nrfoiTfHWJVdLk#=;iZ%hsDJ9 zZU}t%C__JM$H9_26YD*Av^q~Ribe^ybJg$7P7%9!?kH#f=Z(?V54E_?+iW#kNn-9} znPlJVnph{l~n?D^Fb!O|zdKy;*vejm#bg zYh8Oy_kw*#gsg6#-uXeK>1+75vu}O=rcc_-?dX4W#;3{lb2t1Ky{qmTvHg8d!nUr0 zO@D$fp8L^ZAkF_=RAApphvPFqd%_=&@}5{08G6=uNA#6FW$(YK lNgdWa{IqpPkGqoH+}o-4D^nglx@N$}K5+wIlp(ixFaY)iXKMfe literal 0 HcmV?d00001 diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs new file mode 100644 index 00000000000..bf91dd33124 --- /dev/null +++ b/transports/tls/src/verifier.rs @@ -0,0 +1,213 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! TLS 1.3 certificates and handshakes handling for libp2p +//! +//! This module handles a verification of a client/server certificate chain +//! and signatures allegedly by the given certificates. + +use crate::certificate::parse_certificate; +use rustls::{ + client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, + internal::msgs::handshake::DigitallySignedStruct, + server::{ClientCertVerified, ClientCertVerifier}, + Certificate, DistinguishedNames, SignatureScheme, +}; + +/// 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 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 Libp2pCertificateVerifier { + /// Return the list of SignatureSchemes that this verifier will handle, + /// in `verify_tls12_signature` and `verify_tls13_signature` calls. + /// + /// This should be in priority order, with the most preferred first. + pub fn verification_schemes() -> Vec { + vec![ + // TODO SignatureScheme::ECDSA_NISTP521_SHA512 is not supported by `ring` yet + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP256_SHA256, + // TODO SignatureScheme::ED448 is not supported by `ring` yet + SignatureScheme::ED25519, + // In particular, RSA SHOULD NOT be used unless + // no elliptic curve algorithms are supported. + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA256, + ] + } +} + +impl ServerCertVerifier for Libp2pCertificateVerifier { + fn verify_server_cert( + &self, + end_entity: &Certificate, + intermediates: &[Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> Result { + verify_presented_certs(end_entity, intermediates)?; + + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &Certificate, + _dss: &DigitallySignedStruct, + ) -> Result { + // The libp2p handshake uses TLS 1.3 (and higher). + // Endpoints MUST NOT negotiate lower TLS versions. + Err(rustls::Error::PeerIncompatibleError( + "Only TLS 1.3 certificates are supported".to_string(), + )) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &Certificate, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature(cert, dss.scheme, message, dss.sig.0.as_ref()) + } + + fn supported_verify_schemes(&self) -> Vec { + Self::verification_schemes() + } +} + +/// 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 ClientCertVerifier for Libp2pCertificateVerifier { + fn offer_client_auth(&self) -> bool { + true + } + + fn client_auth_root_subjects(&self) -> Option { + Some(vec![]) + } + + fn verify_client_cert( + &self, + end_entity: &Certificate, + intermediates: &[Certificate], + _now: std::time::SystemTime, + ) -> Result { + verify_presented_certs(end_entity, intermediates)?; + + Ok(ClientCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &Certificate, + _dss: &DigitallySignedStruct, + ) -> Result { + // The libp2p handshake uses TLS 1.3 (and higher). + // Endpoints MUST NOT negotiate lower TLS versions. + Err(rustls::Error::PeerIncompatibleError( + "Only TLS 1.3 certificates are supported".to_string(), + )) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &Certificate, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature(cert, dss.scheme, message, dss.sig.0.as_ref()) + } + + fn supported_verify_schemes(&self) -> Vec { + Self::verification_schemes() + } +} + +/// When receiving the certificate chain, an endpoint +/// MUST check these conditions and abort the connection attempt if +/// (a) the presented certificate is not yet valid, OR +/// (b) if it is expired. +/// Endpoints MUST abort the connection attempt if more than one certificate is received, +/// or if the certificate’s self-signature is not valid. +fn verify_presented_certs( + end_entity: &Certificate, + intermediates: &[Certificate], +) -> Result<(), rustls::Error> { + if !intermediates.is_empty() { + return Err(rustls::Error::General( + "libp2p-tls requires exactly one certificate".into(), + )); + } + + parse_certificate(end_entity.as_ref()) + .map_err(pki_error)? + .verify() + .map_err(pki_error)?; + + Ok(()) +} + +fn verify_tls13_signature( + cert: &Certificate, + signature_scheme: SignatureScheme, + message: &[u8], + signature: &[u8], +) -> Result { + parse_certificate(cert.as_ref()) + .map_err(pki_error)? + .verify_signature(signature_scheme, message, signature) + .map_err(pki_error)?; + + Ok(HandshakeSignatureValid::assertion()) +} + +fn pki_error(error: webpki::Error) -> rustls::Error { + use webpki::Error::*; + match error { + BadDer | BadDerTime => rustls::Error::InvalidCertificateEncoding, + InvalidSignatureForPublicKey => rustls::Error::InvalidCertificateSignature, + UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => { + rustls::Error::InvalidCertificateSignatureType + } + e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {}", e)), + } +} From 5364b4310d761a07e7d6fbf965e6a697e4781f56 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 11:29:04 +1000 Subject: [PATCH 02/48] Minor reformatting --- transports/tls/src/certificate.rs | 12 ++++++++---- transports/tls/src/lib.rs | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index b6d3c8b2c68..25a7834c666 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -121,10 +121,9 @@ pub struct P2pCertificate<'a> { /// Parse TLS certificate from DER input that includes a libp2p-specific /// certificate extension containing a public key of a peer. pub fn parse_certificate(der_input: &[u8]) -> Result { - use webpki::Error; let x509 = X509Certificate::from_der(der_input) .map(|(_rest_input, x509)| x509) - .map_err(|_| Error::BadDer)?; + .map_err(|_| webpki::Error::BadDer)?; let p2p_ext_oid = der_parser::oid::Oid::from(&P2P_EXT_OID) .expect("This is a valid OID of p2p extension; qed"); @@ -239,6 +238,7 @@ impl P2pCertificate<'_> { if !user_owns_sk { return Err(Error::UnknownIssuer); } + Ok(()) } @@ -250,7 +250,7 @@ impl P2pCertificate<'_> { // Endpoints MUST abort the connection attempt if it is not used. use oid_registry::*; use rustls::SignatureScheme::*; - use webpki::Error; + let signature_algorithm = &self.certificate.signature_algorithm; let pki_algorithm = &self.certificate.tbs_certificate.subject_pki.algorithm; @@ -383,6 +383,7 @@ impl P2pCertificate<'_> { let spki = &self.certificate.tbs_certificate.subject_pki; let key = signature::UnparsedPublicKey::new(verification_algorithm, spki.subject_public_key.data); + Ok(key) } /// Verify the `signature` of the `message` signed by the private key corresponding to the public key stored @@ -396,6 +397,7 @@ impl P2pCertificate<'_> { let pk = self.public_key(signature_scheme)?; pk.verify(message, signature) .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?; + Ok(()) } } @@ -406,10 +408,12 @@ mod tests { #[test] fn sanity_check() { - let keypair = libp2p_core::identity::Keypair::generate_ed25519(); + let keypair = identity::Keypair::generate_ed25519(); let cert = make_certificate(&keypair).unwrap(); + let cert_der = cert.serialize_der().unwrap(); let parsed_cert = parse_certificate(&cert_der).unwrap(); + assert!(parsed_cert.verify().is_ok()); assert_eq!(keypair.public(), parsed_cert.extension.public_key); } diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 78b31a7d4e9..20aab012913 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -61,16 +61,17 @@ pub fn make_client_config( keypair: &libp2p_core::identity::Keypair, ) -> Result { let (certificate, key) = make_cert_key(keypair)?; - let verifier = Arc::new(verifier::Libp2pCertificateVerifier); + let mut crypto = rustls::ClientConfig::builder() .with_cipher_suites(TLS13_CIPHERSUITES) .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS13]) .expect("Cipher suites and kx groups are configured; qed") - .with_custom_certificate_verifier(verifier) + .with_custom_certificate_verifier(Arc::new(verifier::Libp2pCertificateVerifier)) .with_single_cert(vec![certificate], key) .expect("Client cert key DER is valid; qed"); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; + Ok(crypto) } @@ -79,16 +80,17 @@ pub fn make_server_config( keypair: &libp2p_core::identity::Keypair, ) -> Result { let (certificate, key) = make_cert_key(keypair)?; - let verifier = Arc::new(verifier::Libp2pCertificateVerifier); + let mut crypto = rustls::ServerConfig::builder() .with_cipher_suites(TLS13_CIPHERSUITES) .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS13]) .expect("Cipher suites and kx groups are configured; qed") - .with_client_cert_verifier(verifier) + .with_client_cert_verifier(Arc::new(verifier::Libp2pCertificateVerifier)) .with_single_cert(vec![certificate], key) .expect("Server cert key DER is valid; qed"); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; + Ok(crypto) } @@ -98,7 +100,9 @@ fn make_cert_key( ) -> Result<(rustls::Certificate, rustls::PrivateKey), ConfigError> { let cert = certificate::make_certificate(keypair)?; let private_key = cert.serialize_private_key_der(); + let cert = rustls::Certificate(cert.serialize_der()?); let key = rustls::PrivateKey(private_key); + Ok((cert, key)) } From 71b2fe8703eab3dd8546fef55d25696b241241ce Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 11:31:05 +1000 Subject: [PATCH 03/48] Fix compile errors and remove `Error` type --- transports/tls/Cargo.toml | 1 - transports/tls/src/certificate.rs | 12 ++++++------ transports/tls/src/lib.rs | 18 +++--------------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 867a4ec81f3..c28e4e105b2 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -13,4 +13,3 @@ webpki = "0.22.0" rcgen = "0.9.2" yasna = "0.5.0" ring = "0.16.20" -thiserror = "1.0.26" diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 25a7834c666..3af4bf34a33 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -134,7 +134,7 @@ pub fn parse_certificate(der_input: &[u8]) -> Result Result, Vec) = - yasna::decode_der(ext.value).map_err(|_| Error::ExtensionValueInvalid)?; + yasna::decode_der(ext.value).map_err(|_| webpki::Error::ExtensionValueInvalid)?; // The publicKey field of SignedKey contains the public host key // of the endpoint, encoded using the following protobuf: // enum KeyType { @@ -160,7 +160,7 @@ pub fn parse_certificate(der_input: &[u8]) -> Result Result Result { // Default hash algo is SHA-1, however: // In particular, MD5 and SHA1 MUST NOT be used. - return Err(Error::UnsupportedSignatureAlgorithm); + return Err(webpki::Error::UnsupportedSignatureAlgorithm); } } diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 20aab012913..7cee8071db1 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -24,7 +24,6 @@ pub(crate) mod certificate; mod verifier; use std::sync::Arc; -use thiserror::Error; use rustls::{ cipher_suite::{ @@ -45,21 +44,10 @@ static TLS13_CIPHERSUITES: &[SupportedCipherSuite] = &[ const P2P_ALPN: [u8; 6] = *b"libp2p"; -/// Error creating a configuration -#[derive(Debug, Error)] -pub enum ConfigError { - /// TLS private key or certificate rejected - #[error("TLS private or certificate key rejected: {0}")] - TLSError(#[from] rustls::Error), - /// Certificate generation error - #[error("Certificate generation error: {0}")] - RcgenError(#[from] rcgen::RcgenError), -} - /// Create a TLS client configuration for libp2p. pub fn make_client_config( keypair: &libp2p_core::identity::Keypair, -) -> Result { +) -> Result { let (certificate, key) = make_cert_key(keypair)?; let mut crypto = rustls::ClientConfig::builder() @@ -78,7 +66,7 @@ pub fn make_client_config( /// Create a TLS server configuration for libp2p. pub fn make_server_config( keypair: &libp2p_core::identity::Keypair, -) -> Result { +) -> Result { let (certificate, key) = make_cert_key(keypair)?; let mut crypto = rustls::ServerConfig::builder() @@ -97,7 +85,7 @@ pub fn make_server_config( /// Create a random private key and certificate signed with this key for rustls. fn make_cert_key( keypair: &libp2p_core::identity::Keypair, -) -> Result<(rustls::Certificate, rustls::PrivateKey), ConfigError> { +) -> Result<(rustls::Certificate, rustls::PrivateKey), rcgen::RcgenError> { let cert = certificate::make_certificate(keypair)?; let private_key = cert.serialize_private_key_der(); From 80c0d1c6eac3b1f60df432a65f31c07c03b81e3e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 11:32:28 +1000 Subject: [PATCH 04/48] Remove unnecessary `pub(crate)` --- transports/tls/src/certificate.rs | 4 ++-- transports/tls/src/lib.rs | 2 +- transports/tls/src/verifier.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 3af4bf34a33..313d231f451 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -102,7 +102,7 @@ pub fn make_certificate( /// The contents of the specific libp2p extension, containing the public host key /// and a signature performed using the private host key. pub struct P2pExtension { - pub(crate) public_key: identity::PublicKey, + public_key: identity::PublicKey, /// This signature provides cryptographic proof that the peer was /// in possession of the private host key at the time the certificate was signed. signature: Vec, @@ -115,7 +115,7 @@ pub struct P2pCertificate<'a> { /// This is a specific libp2p Public Key Extension with two values: /// * the public host key /// * a signature performed using the private host key - pub(crate) extension: P2pExtension, + extension: P2pExtension, } /// Parse TLS certificate from DER input that includes a libp2p-specific diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 7cee8071db1..911b7c46764 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -20,7 +20,7 @@ //! TLS configuration for QUIC based on libp2p TLS specs. -pub(crate) mod certificate; +mod certificate; mod verifier; use std::sync::Arc; diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index bf91dd33124..41765f44bc2 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -34,7 +34,7 @@ use rustls::{ /// 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 Libp2pCertificateVerifier; +pub struct Libp2pCertificateVerifier; /// libp2p requires the following of X.509 server certificate chains: /// From 03a2aed8a880d36580322c410e0e2e0d376c9a2f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 12:32:31 +1000 Subject: [PATCH 05/48] Implement `Upgrade` traits to allow use of TLS as authentication --- transports/tls/Cargo.toml | 8 ++ transports/tls/src/certificate.rs | 9 +- transports/tls/src/lib.rs | 3 + transports/tls/src/upgrade.rs | 174 ++++++++++++++++++++++++++++++ transports/tls/tests/smoke.rs | 73 +++++++++++++ 5 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 transports/tls/src/upgrade.rs create mode 100644 transports/tls/tests/smoke.rs diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index c28e4e105b2..791fe959c3f 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -13,3 +13,11 @@ webpki = "0.22.0" rcgen = "0.9.2" yasna = "0.5.0" ring = "0.16.20" +futures = { version = "0.3.24", default-features = false } +thiserror = "1.0.36" +futures-rustls = "0.22.2" +either = "1.8.0" + +[dev-dependencies] +libp2p = { path = "../..", features = ["yamux"], default-features = false } +tokio = { version = "1.21.1", features = ["full"] } diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 313d231f451..92bb06c9eb0 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -22,7 +22,7 @@ //! //! This module handles generation, signing, and verification of certificates. -use libp2p_core::identity; +use libp2p_core::{identity, PeerId}; use x509_parser::prelude::*; /// The libp2p Public Key Extension is a X.509 extension @@ -118,6 +118,13 @@ pub struct P2pCertificate<'a> { extension: P2pExtension, } +impl<'a> P2pCertificate<'a> { + /// The [`PeerId`] of the remote peer. + pub fn peer_id(&self) -> PeerId { + self.extension.public_key.to_peer_id() + } +} + /// Parse TLS certificate from DER input that includes a libp2p-specific /// certificate extension containing a public key of a peer. pub fn parse_certificate(der_input: &[u8]) -> Result { diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 911b7c46764..881c3198960 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -21,6 +21,7 @@ //! TLS configuration for QUIC based on libp2p TLS specs. mod certificate; +mod upgrade; mod verifier; use std::sync::Arc; @@ -32,6 +33,8 @@ use rustls::{ SupportedCipherSuite, }; +pub use upgrade::{Config, Error, Stream}; + /// A list of the TLS 1.3 cipher suites supported by rustls. // By default rustls creates client/server configs with both // TLS 1.3 __and__ 1.2 cipher suites. But we don't need 1.2. diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs new file mode 100644 index 00000000000..73c2997b2c4 --- /dev/null +++ b/transports/tls/src/upgrade.rs @@ -0,0 +1,174 @@ +use crate::certificate::{parse_certificate, P2pCertificate}; +use futures::future::BoxFuture; +use futures::AsyncWrite; +use futures::{AsyncRead, FutureExt}; +use futures_rustls::TlsStream; +use libp2p_core::{identity, InboundUpgrade, OutboundUpgrade, PeerId, UpgradeInfo}; +use rustls::{CommonState, ServerName}; +use std::io::{IoSlice, IoSliceMut}; +use std::net::{IpAddr, Ipv4Addr}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to generate certificate")] + CertificateGeneration(#[from] rcgen::RcgenError), // TODO: This leaks `rcgen` dependency. + #[error("Failed to upgrade server connection")] + ServerUpgrade(std::io::Error), + #[error("Failed to upgrade client connection")] + ClientUpgrade(std::io::Error), + #[error("Failed to parse certificate")] + BadCertificate(#[from] webpki::Error), // TODO: This leaks `webpki` dependency. +} + +#[derive(Clone)] +pub struct Config { + server: rustls::ServerConfig, + client: rustls::ClientConfig, +} + +impl Config { + pub fn new(identity: &identity::Keypair) -> Result { + Ok(Self { + server: crate::make_server_config(&identity)?, + client: crate::make_client_config(&identity)?, + }) + } +} + +impl UpgradeInfo for Config { + type Info = &'static [u8]; + type InfoIter = std::iter::Once; + + fn protocol_info(&self) -> Self::InfoIter { + std::iter::once(b"/tls/1.0.0") + } +} + +pub struct Stream { + inner: TlsStream, +} + +impl InboundUpgrade for Config +where + C: AsyncRead + AsyncWrite + Send + Unpin + 'static, +{ + type Output = (PeerId, Stream); + type Error = Error; + type Future = BoxFuture<'static, Result>; + + fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future { + async move { + let stream = futures_rustls::TlsAcceptor::from(Arc::new(self.server)) + .accept(socket) + .await + .map_err(Error::ServerUpgrade)?; + + let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id(); + + Ok(( + peer_id, + Stream { + inner: stream.into(), + }, + )) + } + .boxed() + } +} + +impl OutboundUpgrade for Config +where + C: AsyncRead + AsyncWrite + Send + Unpin + 'static, +{ + type Output = (PeerId, Stream); + type Error = Error; + type Future = BoxFuture<'static, Result>; + + fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future { + async move { + // Spec: In order to keep this flexibility for future versions, clients that only support the version of the handshake defined in this document MUST NOT send any value in the Server Name Indication. + // Setting `ServerName` to unspecified will disable the use of the SNI extension. + let name = ServerName::IpAddress(IpAddr::V4(Ipv4Addr::UNSPECIFIED)); + + let stream = futures_rustls::TlsConnector::from(Arc::new(self.client)) + .connect(name, socket) + .await + .map_err(Error::ClientUpgrade)?; + + let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id(); + + Ok(( + peer_id, + Stream { + inner: stream.into(), + }, + )) + } + .boxed() + } +} + +impl AsyncRead for Stream +where + C: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.inner).poll_read(cx, buf) + } + + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + Pin::new(&mut self.inner).poll_read_vectored(cx, bufs) + } +} + +impl AsyncWrite for Stream +where + C: AsyncRead + AsyncWrite + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.inner).poll_write(cx, buf) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut self.inner).poll_write_vectored(cx, bufs) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } +} + +fn extract_single_certificate(state: &CommonState) -> Result, webpki::Error> { + let cert = match state + .peer_certificates() + .expect("config enforces presence of certificates") + { + [single] => single, + _ => panic!("config enforces exactly one certificate"), + }; + + parse_certificate(cert.as_ref()) +} diff --git a/transports/tls/tests/smoke.rs b/transports/tls/tests/smoke.rs new file mode 100644 index 00000000000..0e4b3441b0d --- /dev/null +++ b/transports/tls/tests/smoke.rs @@ -0,0 +1,73 @@ +use futures::{future, StreamExt}; +use libp2p::multiaddr::Protocol; +use libp2p::swarm::{DummyBehaviour, KeepAlive, SwarmEvent}; +use libp2p::Swarm; +use libp2p_core::transport::MemoryTransport; +use libp2p_core::upgrade::Version; +use libp2p_core::Transport; + +#[tokio::test] +async fn can_establish_connection() { + let mut swarm1 = make_swarm(); + let mut swarm2 = make_swarm(); + + let listen_address = { + let expected_listener_id = swarm1.listen_on(Protocol::Memory(0).into()).unwrap(); + + loop { + match swarm1.next().await.unwrap() { + SwarmEvent::NewListenAddr { + address, + listener_id, + } if listener_id == expected_listener_id => break address, + _ => continue, + }; + } + }; + swarm2.dial(listen_address).unwrap(); + + let await_inbound_connection = async { + loop { + match swarm1.next().await.unwrap() { + SwarmEvent::ConnectionEstablished { peer_id, .. } => break peer_id, + SwarmEvent::IncomingConnectionError { error, .. } => { + panic!("Incoming connection failed: {}", error) + } + _ => continue, + }; + } + }; + let await_outbound_connection = async { + loop { + match swarm2.next().await.unwrap() { + SwarmEvent::ConnectionEstablished { peer_id, .. } => break peer_id, + SwarmEvent::OutgoingConnectionError { error, .. } => { + panic!("Failed to dial: {}", error) + } + _ => continue, + }; + } + }; + + let (inbound_peer_id, outbound_peer_id) = + future::join(await_inbound_connection, await_outbound_connection).await; + + assert_eq!(&inbound_peer_id, swarm2.local_peer_id()); + assert_eq!(&outbound_peer_id, swarm1.local_peer_id()); +} + +fn make_swarm() -> Swarm { + let identity = libp2p::identity::Keypair::generate_ed25519(); + + let transport = MemoryTransport::default() + .upgrade(Version::V1) + .authenticate(libp2p_tls::Config::new(&identity).unwrap()) + .multiplex(libp2p::yamux::YamuxConfig::default()) + .boxed(); + + Swarm::new( + transport, + DummyBehaviour::with_keep_alive(KeepAlive::Yes), + identity.public().to_peer_id(), + ) +} From c5b1558e729ca341f6b377835c4ca85d4d820088 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 14:56:42 +1000 Subject: [PATCH 06/48] Add changelog files --- CHANGELOG.md | 3 +++ transports/tls/CHANGELOG.md | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 transports/tls/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f77aa78e1a..57125dc3769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - [`libp2p-uds` CHANGELOG](transports/uds/CHANGELOG.md) - [`libp2p-wasm-ext` CHANGELOG](transports/wasm-ext/CHANGELOG.md) - [`libp2p-websocket` CHANGELOG](transports/websocket/CHANGELOG.md) +- [`libp2p-tls` CHANGELOG](transports/tls/CHANGELOG.md) ## Multiplexers @@ -69,6 +70,8 @@ - Update to [`libp2p-swarm` `v0.39.1`](swarm/CHANGELOG.md#0391). +- Introduce [`libp2p-tls` `v0.1.0`](transports/tls/CHANGELOG.md#010). + # 0.48.0 - Update to [`libp2p-core` `v0.36.0`](core/CHANGELOG.md#0360). diff --git a/transports/tls/CHANGELOG.md b/transports/tls/CHANGELOG.md new file mode 100644 index 00000000000..9a05756f769 --- /dev/null +++ b/transports/tls/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 [unreleased] + +Initial release. From 2316505af0aabaf437101aa3cf5b5d0b33ca3b41 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 14:58:55 +1000 Subject: [PATCH 07/48] Fix docs --- transports/tls/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 881c3198960..bb68d3587ae 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -18,7 +18,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//! TLS configuration for QUIC based on libp2p TLS specs. +//! TLS configuration based on libp2p TLS specs. +//! +//! See . mod certificate; mod upgrade; From 334544d754adac29722218be15df0e7e65404f19 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:02:26 +1000 Subject: [PATCH 08/48] Minor doc and name adjustments --- transports/tls/src/certificate.rs | 72 +++++++++++++++---------------- transports/tls/src/lib.rs | 2 +- transports/tls/src/upgrade.rs | 5 ++- transports/tls/src/verifier.rs | 6 +-- 4 files changed, 43 insertions(+), 42 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 92bb06c9eb0..a39910f0538 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -43,9 +43,7 @@ static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_ /// Generates a self-signed TLS certificate that includes a libp2p-specific /// certificate extension containing the public key of the given keypair. -pub fn make_certificate( - keypair: &identity::Keypair, -) -> Result { +pub fn generate(keypair: &identity::Keypair) -> Result { // Keypair used to sign the certificate. // SHOULD NOT be related to the host's key. // Endpoints MAY generate a new key and certificate @@ -99,35 +97,11 @@ pub fn make_certificate( Ok(certificate) } -/// The contents of the specific libp2p extension, containing the public host key -/// and a signature performed using the private host key. -pub struct P2pExtension { - public_key: identity::PublicKey, - /// This signature provides cryptographic proof that the peer was - /// in possession of the private host key at the time the certificate was signed. - signature: Vec, -} - -/// An X.509 certificate with a libp2p-specific extension -/// is used to secure libp2p connections. -pub struct P2pCertificate<'a> { - certificate: X509Certificate<'a>, - /// This is a specific libp2p Public Key Extension with two values: - /// * the public host key - /// * a signature performed using the private host key - extension: P2pExtension, -} - -impl<'a> P2pCertificate<'a> { - /// The [`PeerId`] of the remote peer. - pub fn peer_id(&self) -> PeerId { - self.extension.public_key.to_peer_id() - } -} - -/// Parse TLS certificate from DER input that includes a libp2p-specific -/// certificate extension containing a public key of a peer. -pub fn parse_certificate(der_input: &[u8]) -> Result { +/// Attempts to parse the provided bytes as a [`P2pCertificate`]. +/// +/// For this to succeed, the certificate must contain the specified extension and the signature must +/// match the embedded public key. +pub fn parse(der_input: &[u8]) -> Result { let x509 = X509Certificate::from_der(der_input) .map(|(_rest_input, x509)| x509) .map_err(|_| webpki::Error::BadDer)?; @@ -195,6 +169,32 @@ pub fn parse_certificate(der_input: &[u8]) -> Result, +} + +/// An X.509 certificate with a libp2p-specific extension +/// is used to secure libp2p connections. +pub struct P2pCertificate<'a> { + certificate: X509Certificate<'a>, + /// This is a specific libp2p Public Key Extension with two values: + /// * the public host key + /// * a signature performed using the private host key + extension: P2pExtension, +} + +impl<'a> P2pCertificate<'a> { + /// The [`PeerId`] of the remote peer. + pub fn peer_id(&self) -> PeerId { + self.extension.public_key.to_peer_id() + } +} + impl P2pCertificate<'_> { /// This method validates the certificate according to libp2p TLS 1.3 specs. /// The certificate MUST: @@ -416,10 +416,10 @@ mod tests { #[test] fn sanity_check() { let keypair = identity::Keypair::generate_ed25519(); - let cert = make_certificate(&keypair).unwrap(); + let cert = generate(&keypair).unwrap(); let cert_der = cert.serialize_der().unwrap(); - let parsed_cert = parse_certificate(&cert_der).unwrap(); + let parsed_cert = parse(&cert_der).unwrap(); assert!(parsed_cert.verify().is_ok()); assert_eq!(keypair.public(), parsed_cert.extension.public_key); @@ -453,7 +453,7 @@ mod tests { fn rsa_pss_sha384() { let cert: &[u8] = include_bytes!("./test_assets/rsa_pss_sha384.der"); - let cert = parse_certificate(cert).unwrap(); + let cert = parse(cert).unwrap(); cert.verify().unwrap(); // that was a fairly generated certificate. assert_eq!( @@ -466,7 +466,7 @@ mod tests { fn nistp384_sha256() { let cert: &[u8] = include_bytes!("./test_assets/nistp384_sha256.der"); - let cert = parse_certificate(cert).unwrap(); + let cert = parse(cert).unwrap(); assert!(cert.signature_scheme().is_err()); } diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index bb68d3587ae..2cb52b75711 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -91,7 +91,7 @@ pub fn make_server_config( fn make_cert_key( keypair: &libp2p_core::identity::Keypair, ) -> Result<(rustls::Certificate, rustls::PrivateKey), rcgen::RcgenError> { - let cert = certificate::make_certificate(keypair)?; + let cert = certificate::generate(keypair)?; let private_key = cert.serialize_private_key_der(); let cert = rustls::Certificate(cert.serialize_der()?); diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index 73c2997b2c4..f2b6023ba09 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -1,4 +1,4 @@ -use crate::certificate::{parse_certificate, P2pCertificate}; +use crate::certificate; use futures::future::BoxFuture; use futures::AsyncWrite; use futures::{AsyncRead, FutureExt}; @@ -10,6 +10,7 @@ use std::net::{IpAddr, Ipv4Addr}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; +use crate::certificate::P2pCertificate; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -170,5 +171,5 @@ fn extract_single_certificate(state: &CommonState) -> Result, _ => panic!("config enforces exactly one certificate"), }; - parse_certificate(cert.as_ref()) + certificate::parse(cert.as_ref()) } diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 41765f44bc2..c42723c1ee4 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -23,7 +23,7 @@ //! This module handles a verification of a client/server certificate chain //! and signatures allegedly by the given certificates. -use crate::certificate::parse_certificate; +use crate::certificate::parse; use rustls::{ client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, internal::msgs::handshake::DigitallySignedStruct, @@ -178,7 +178,7 @@ fn verify_presented_certs( )); } - parse_certificate(end_entity.as_ref()) + parse(end_entity.as_ref()) .map_err(pki_error)? .verify() .map_err(pki_error)?; @@ -192,7 +192,7 @@ fn verify_tls13_signature( message: &[u8], signature: &[u8], ) -> Result { - parse_certificate(cert.as_ref()) + parse(cert.as_ref()) .map_err(pki_error)? .verify_signature(signature_scheme, message, signature) .map_err(pki_error)?; From 4d6db8236f6b8de53d9dbf4b4cd2f53407c3226d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:06:17 +1000 Subject: [PATCH 09/48] Verify p2p extension as part of parsing certificate We shouldn't rely on our callers to call `verify` here. --- transports/tls/src/certificate.rs | 11 ++++++++--- transports/tls/src/verifier.rs | 9 +++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index a39910f0538..fbef8884d1f 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -163,10 +163,14 @@ pub fn parse(der_input: &[u8]) -> Result { // If this extension is missing, endpoints MUST abort the connection attempt. let extension = libp2p_extension.ok_or(webpki::Error::BadDer)?; - Ok(P2pCertificate { + let certificate = P2pCertificate { certificate: x509, extension, - }) + }; + + certificate.verify()?; + + Ok(certificate) } /// The contents of the specific libp2p extension, containing the public host key @@ -203,7 +207,7 @@ impl P2pCertificate<'_> { /// 3. use hash functions with an output length not less than 256 bits; /// 4. be self signed; /// 5. contain a valid signature in the specific libp2p extension. - pub fn verify(&self) -> Result<(), webpki::Error> { + fn verify(&self) -> Result<(), webpki::Error> { use webpki::Error; // The certificate MUST have NotBefore and NotAfter fields set // such that the certificate is valid at the time it is received by the peer. @@ -393,6 +397,7 @@ impl P2pCertificate<'_> { Ok(key) } + /// Verify the `signature` of the `message` signed by the private key corresponding to the public key stored /// in the certificate. pub fn verify_signature( diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index c42723c1ee4..523e22b3c03 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -23,7 +23,7 @@ //! This module handles a verification of a client/server certificate chain //! and signatures allegedly by the given certificates. -use crate::certificate::parse; +use crate::certificate; use rustls::{ client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, internal::msgs::handshake::DigitallySignedStruct, @@ -178,10 +178,7 @@ fn verify_presented_certs( )); } - parse(end_entity.as_ref()) - .map_err(pki_error)? - .verify() - .map_err(pki_error)?; + certificate::parse(end_entity.as_ref()).map_err(pki_error)?; Ok(()) } @@ -192,7 +189,7 @@ fn verify_tls13_signature( message: &[u8], signature: &[u8], ) -> Result { - parse(cert.as_ref()) + certificate::parse(cert.as_ref()) .map_err(pki_error)? .verify_signature(signature_scheme, message, signature) .map_err(pki_error)?; From e4547a5442c5ae12e970af48916b6af50c675a68 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:09:27 +1000 Subject: [PATCH 10/48] Fix compile error --- transports/tls/src/certificate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index fbef8884d1f..8b49e0b8639 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -436,7 +436,7 @@ mod tests { fn $name() { let cert: &[u8] = include_bytes!($path); - let cert = parse_certificate(cert).unwrap(); + let cert = parse(cert).unwrap(); assert!(cert.verify().is_err()); // Because p2p extension // was not signed with the private key // of the certificate. From 3267ccda9f279a0375bf28ff6816b9cab3fa9373 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:10:01 +1000 Subject: [PATCH 11/48] Extract private fn that allows to control the cert keypair This will be useful for testing. --- transports/tls/src/certificate.rs | 21 +++++++++++++++------ transports/tls/src/upgrade.rs | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 8b49e0b8639..0da2d7ffe0f 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -43,14 +43,23 @@ static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_ /// Generates a self-signed TLS certificate that includes a libp2p-specific /// certificate extension containing the public key of the given keypair. -pub fn generate(keypair: &identity::Keypair) -> Result { +pub fn generate( + identity_keypair: &identity::Keypair, +) -> Result { // Keypair used to sign the certificate. // SHOULD NOT be related to the host's key. // Endpoints MAY generate a new key and certificate // for every connection attempt, or they MAY reuse the same key // and certificate for multiple connections. - let certif_keypair = rcgen::KeyPair::generate(P2P_SIGNATURE_ALGORITHM)?; + let certificate_keypair = rcgen::KeyPair::generate(P2P_SIGNATURE_ALGORITHM)?; + _generate(identity_keypair, certificate_keypair) +} + +fn _generate( + identity_keypair: &identity::Keypair, + certificate_keypair: rcgen::KeyPair, +) -> Result { // Generate the libp2p-specific extension. // The certificate MUST contain the libp2p Public Key Extension. let libp2p_extension: rcgen::CustomExtension = { @@ -60,9 +69,9 @@ pub fn generate(keypair: &identity::Keypair) -> Result Result Result Date: Mon, 26 Sep 2022 15:48:49 +1000 Subject: [PATCH 12/48] fixup! Verify p2p extension as part of parsing certificate --- transports/tls/src/certificate.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 0da2d7ffe0f..24fecad2070 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -111,6 +111,17 @@ fn _generate( /// For this to succeed, the certificate must contain the specified extension and the signature must /// match the embedded public key. pub fn parse(der_input: &[u8]) -> Result { + let certificate = parse_unverified(der_input)?; + + certificate.verify()?; + + Ok(certificate) +} + +/// Internal function that only parses but does not verify the certificate. +/// +/// Useful for testing but unsuitable for production. +fn parse_unverified(der_input: &[u8]) -> Result { let x509 = X509Certificate::from_der(der_input) .map(|(_rest_input, x509)| x509) .map_err(|_| webpki::Error::BadDer)?; @@ -177,8 +188,6 @@ pub fn parse(der_input: &[u8]) -> Result { extension, }; - certificate.verify()?; - Ok(certificate) } @@ -445,7 +454,7 @@ mod tests { fn $name() { let cert: &[u8] = include_bytes!($path); - let cert = parse(cert).unwrap(); + let cert = parse_unverified(cert).unwrap(); assert!(cert.verify().is_err()); // Because p2p extension // was not signed with the private key // of the certificate. @@ -468,7 +477,6 @@ mod tests { let cert: &[u8] = include_bytes!("./test_assets/rsa_pss_sha384.der"); let cert = parse(cert).unwrap(); - cert.verify().unwrap(); // that was a fairly generated certificate. assert_eq!( cert.signature_scheme(), @@ -480,7 +488,7 @@ mod tests { fn nistp384_sha256() { let cert: &[u8] = include_bytes!("./test_assets/nistp384_sha256.der"); - let cert = parse(cert).unwrap(); + let cert = parse_unverified(cert).unwrap(); assert!(cert.signature_scheme().is_err()); } From b810bcc0edbaef449caa68d9bc5549bb5312569c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:49:15 +1000 Subject: [PATCH 13/48] Add static test vectors --- transports/tls/Cargo.toml | 2 ++ transports/tls/src/certificate.rs | 51 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 791fe959c3f..8320579168c 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -21,3 +21,5 @@ either = "1.8.0" [dev-dependencies] libp2p = { path = "../..", features = ["yamux"], default-features = false } tokio = { version = "1.21.1", features = ["full"] } +hex = "0.4.3" +hex-literal = "0.3.4" diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 24fecad2070..decbc21b786 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -193,6 +193,7 @@ fn parse_unverified(der_input: &[u8]) -> Result { /// The contents of the specific libp2p extension, containing the public host key /// and a signature performed using the private host key. +#[derive(Debug)] pub struct P2pExtension { public_key: identity::PublicKey, /// This signature provides cryptographic proof that the peer was @@ -202,6 +203,7 @@ pub struct P2pExtension { /// An X.509 certificate with a libp2p-specific extension /// is used to secure libp2p connections. +#[derive(Debug)] pub struct P2pCertificate<'a> { certificate: X509Certificate<'a>, /// This is a specific libp2p Public Key Extension with two values: @@ -435,6 +437,7 @@ impl P2pCertificate<'_> { #[cfg(test)] mod tests { use super::*; + use hex_literal::hex; #[test] fn sanity_check() { @@ -492,4 +495,52 @@ mod tests { assert!(cert.signature_scheme().is_err()); } + + #[test] + fn can_parse_certificate_with_ed25519_keypair() { + let certificate = parse(&hex!("308201773082011ea003020102020900f5bd0debaa597f52300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200046bf9871220d71dcb3483ecdfcbfcc7c103f8509d0974b3c18ab1f1be1302d643103a08f7a7722c1b247ba3876fe2c59e26526f479d7718a85202ddbe47562358a37f307d307b060a2b0601040183a25a01010101ff046a30680424080112207fda21856709c5ae12fd6e8450623f15f11955d384212b89f56e7e136d2e17280440aaa6bffabe91b6f30c35e3aa4f94b1188fed96b0ffdd393f4c58c1c047854120e674ce64c788406d1c2c4b116581fd7411b309881c3c7f20b46e54c7e6fe7f0f300a06082a8648ce3d040302034700304402207d1a1dbd2bda235ff2ec87daf006f9b04ba076a5a5530180cd9c2e8f6399e09d0220458527178c7e77024601dbb1b256593e9b96d961b96349d1f560114f61a87595")).unwrap(); + let peer_id = certificate.peer_id(); + + assert_eq!( + "12D3KooWJRSrypvnpHgc6ZAgyCni4KcSmbV7uGRaMw5LgMKT18fq" + .parse::() + .unwrap(), + peer_id + ); + } + + #[test] + fn fails_to_parse_bad_certificate_with_ed25519_keypair() { + let result = parse(&hex!("308201773082011da003020102020830a73c5d896a1109300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004bbe62df9a7c1c46b7f1f21d556deec5382a36df146fb29c7f1240e60d7d5328570e3b71d99602b77a65c9b3655f62837f8d66b59f1763b8c9beba3be07778043a37f307d307b060a2b0601040183a25a01010101ff046a3068042408011220ec8094573afb9728088860864f7bcea2d4fd412fef09a8e2d24d482377c20db60440ecabae8354afa2f0af4b8d2ad871e865cb5a7c0c8d3dbdbf42de577f92461a0ebb0a28703e33581af7d2a4f2270fc37aec6261fcc95f8af08f3f4806581c730a300a06082a8648ce3d040302034800304502202dfb17a6fa0f94ee0e2e6a3b9fb6e986f311dee27392058016464bd130930a61022100ba4b937a11c8d3172b81e7cd04aedb79b978c4379c2b5b24d565dd5d67d3cb3c")); + + let error = result.unwrap_err(); + + assert_eq!(format!("{}", error), "UnknownIssuer"); + } + + #[test] + fn can_parse_certificate_with_ecdsa_keypair() { + let certificate = parse(&hex!("308201c030820166a003020102020900eaf419a6e3edb4a6300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200048dbf1116c7c608d6d5292bd826c3feb53483a89fce434bf64538a359c8e07538ff71f6766239be6a146dcc1a5f3bb934bcd4ae2ae1d4da28ac68b4a20593f06ba381c63081c33081c0060a2b0601040183a25a01010101ff0481ae3081ab045f0803125b3059301306072a8648ce3d020106082a8648ce3d0301070342000484b93fa456a74bd0153919f036db7bc63c802f055bc7023395d0203de718ee0fc7b570b767cdd858aca6c7c4113ff002e78bd2138ac1a3b26dde3519e06979ad04483046022100bc84014cea5a41feabdf4c161096564b9ccf4b62fbef4fe1cd382c84e11101780221009204f086a84cb8ed8a9ddd7868dc90c792ee434adf62c66f99a08a5eba11615b300a06082a8648ce3d0403020348003045022054b437be9a2edf591312d68ff24bf91367ad4143f76cf80b5658f232ade820da022100e23b48de9df9c25d4c83ddddf75d2676f0b9318ee2a6c88a736d85eab94a912f")).unwrap(); + let peer_id = certificate.peer_id(); + + assert_eq!( + "QmZcrvr3r4S3QvwFdae3c2EWTfo792Y14UpzCZurhmiWeX" + .parse::() + .unwrap(), + peer_id + ); + } + + #[test] + fn can_parse_certificate_with_secp256k1_keypair() { + let certificate = parse(&hex!("3082018230820128a003020102020900f3b305f55622cfdf300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000458f7e9581748ff9bdd933b655cc0e5552a1248f840658cc221dec2186b5a2fe4641b86ab7590a3422cdbb1000cf97662f27e5910d7569f22feed8829c8b52e0fa38188308185308182060a2b0601040183a25a01010101ff0471306f042508021221026b053094d1112bce799dc8026040ae6d4eb574157929f1598172061f753d9b1b04463044022040712707e97794c478d93989aaa28ae1f71c03af524a8a4bd2d98424948a782302207b61b7f074b696a25fb9e0059141a811cccc4cc28042d9301b9b2a4015e87470300a06082a8648ce3d04030203480030450220143ae4d86fdc8675d2480bb6912eca5e39165df7f572d836aa2f2d6acfab13f8022100831d1979a98f0c4a6fb5069ca374de92f1a1205c962a6d90ad3d7554cb7d9df4")).unwrap(); + let peer_id = certificate.peer_id(); + + assert_eq!( + "16Uiu2HAm2dSCBFxuge46aEt7U1oejtYuBUZXxASHqmcfVmk4gsbx" + .parse::() + .unwrap(), + peer_id + ); + } } From 7c6e6cfd907e5730f960eac95e763d89e62c833c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:50:14 +1000 Subject: [PATCH 14/48] Re-order items by importance --- transports/tls/src/certificate.rs | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index decbc21b786..e282595e95f 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -56,6 +56,39 @@ pub fn generate( _generate(identity_keypair, certificate_keypair) } +/// Attempts to parse the provided bytes as a [`P2pCertificate`]. +/// +/// For this to succeed, the certificate must contain the specified extension and the signature must +/// match the embedded public key. +pub fn parse(der_input: &[u8]) -> Result { + let certificate = parse_unverified(der_input)?; + + certificate.verify()?; + + Ok(certificate) +} + +/// An X.509 certificate with a libp2p-specific extension +/// is used to secure libp2p connections. +#[derive(Debug)] +pub struct P2pCertificate<'a> { + certificate: X509Certificate<'a>, + /// This is a specific libp2p Public Key Extension with two values: + /// * the public host key + /// * a signature performed using the private host key + extension: P2pExtension, +} + +/// The contents of the specific libp2p extension, containing the public host key +/// and a signature performed using the private host key. +#[derive(Debug)] +pub struct P2pExtension { + public_key: identity::PublicKey, + /// This signature provides cryptographic proof that the peer was + /// in possession of the private host key at the time the certificate was signed. + signature: Vec, +} + fn _generate( identity_keypair: &identity::Keypair, certificate_keypair: rcgen::KeyPair, @@ -106,18 +139,6 @@ fn _generate( Ok(certificate) } -/// Attempts to parse the provided bytes as a [`P2pCertificate`]. -/// -/// For this to succeed, the certificate must contain the specified extension and the signature must -/// match the embedded public key. -pub fn parse(der_input: &[u8]) -> Result { - let certificate = parse_unverified(der_input)?; - - certificate.verify()?; - - Ok(certificate) -} - /// Internal function that only parses but does not verify the certificate. /// /// Useful for testing but unsuitable for production. @@ -191,27 +212,6 @@ fn parse_unverified(der_input: &[u8]) -> Result { Ok(certificate) } -/// The contents of the specific libp2p extension, containing the public host key -/// and a signature performed using the private host key. -#[derive(Debug)] -pub struct P2pExtension { - public_key: identity::PublicKey, - /// This signature provides cryptographic proof that the peer was - /// in possession of the private host key at the time the certificate was signed. - signature: Vec, -} - -/// An X.509 certificate with a libp2p-specific extension -/// is used to secure libp2p connections. -#[derive(Debug)] -pub struct P2pCertificate<'a> { - certificate: X509Certificate<'a>, - /// This is a specific libp2p Public Key Extension with two values: - /// * the public host key - /// * a signature performed using the private host key - extension: P2pExtension, -} - impl<'a> P2pCertificate<'a> { /// The [`PeerId`] of the remote peer. pub fn peer_id(&self) -> PeerId { From a8d965fff0502521af8f19c1b21eaaa5fe51503d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:51:57 +1000 Subject: [PATCH 15/48] Re-order functions in impl block and make private where possible --- transports/tls/src/certificate.rs | 132 +++++++++++++++--------------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index e282595e95f..2923a8d99bf 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -212,14 +212,75 @@ fn parse_unverified(der_input: &[u8]) -> Result { Ok(certificate) } -impl<'a> P2pCertificate<'a> { +impl P2pCertificate<'_> { /// The [`PeerId`] of the remote peer. pub fn peer_id(&self) -> PeerId { self.extension.public_key.to_peer_id() } -} -impl P2pCertificate<'_> { + /// Verify the `signature` of the `message` signed by the private key corresponding to the public key stored + /// in the certificate. + pub fn verify_signature( + &self, + signature_scheme: rustls::SignatureScheme, + message: &[u8], + signature: &[u8], + ) -> Result<(), webpki::Error> { + let pk = self.public_key(signature_scheme)?; + pk.verify(message, signature) + .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?; + + Ok(()) + } + + /// Get a [`ring::signature::UnparsedPublicKey`] for this `signature_scheme`. + /// Return `Error` if the `signature_scheme` does not match the public key signature + /// and hashing algorithm or if the `signature_scheme` is not supported. + fn public_key( + &self, + signature_scheme: rustls::SignatureScheme, + ) -> Result, webpki::Error> { + use ring::signature; + use rustls::SignatureScheme::*; + + let current_signature_scheme = self.signature_scheme()?; + if signature_scheme != current_signature_scheme { + // This certificate was signed with a different signature scheme + return Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey); + } + + let verification_algorithm: &dyn signature::VerificationAlgorithm = match signature_scheme { + RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_2048_8192_SHA256, + RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_2048_8192_SHA384, + RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_2048_8192_SHA512, + ECDSA_NISTP256_SHA256 => &signature::ECDSA_P256_SHA256_ASN1, + ECDSA_NISTP384_SHA384 => &signature::ECDSA_P384_SHA384_ASN1, + ECDSA_NISTP521_SHA512 => { + // See https://github.com/briansmith/ring/issues/824 + return Err(webpki::Error::UnsupportedSignatureAlgorithm); + } + RSA_PSS_SHA256 => &signature::RSA_PSS_2048_8192_SHA256, + RSA_PSS_SHA384 => &signature::RSA_PSS_2048_8192_SHA384, + RSA_PSS_SHA512 => &signature::RSA_PSS_2048_8192_SHA512, + ED25519 => &signature::ED25519, + ED448 => { + // See https://github.com/briansmith/ring/issues/463 + return Err(webpki::Error::UnsupportedSignatureAlgorithm); + } + // Similarly, hash functions with an output length less than 256 bits + // MUST NOT be used, due to the possibility of collision attacks. + // In particular, MD5 and SHA1 MUST NOT be used. + RSA_PKCS1_SHA1 => return Err(webpki::Error::UnsupportedSignatureAlgorithm), + ECDSA_SHA1_Legacy => return Err(webpki::Error::UnsupportedSignatureAlgorithm), + Unknown(_) => return Err(webpki::Error::UnsupportedSignatureAlgorithm), + }; + let spki = &self.certificate.tbs_certificate.subject_pki; + let key = + signature::UnparsedPublicKey::new(verification_algorithm, spki.subject_public_key.data); + + Ok(key) + } + /// This method validates the certificate according to libp2p TLS 1.3 specs. /// The certificate MUST: /// 1. be valid at the time it is received by the peer; @@ -276,7 +337,7 @@ impl P2pCertificate<'_> { /// Return the signature scheme corresponding to [`AlgorithmIdentifier`]s /// of `subject_pki` and `signature_algorithm` /// according to ``. - pub fn signature_scheme(&self) -> Result { + fn signature_scheme(&self) -> Result { // Certificates MUST use the NamedCurve encoding for elliptic curve parameters. // Endpoints MUST abort the connection attempt if it is not used. use oid_registry::*; @@ -369,69 +430,6 @@ impl P2pCertificate<'_> { Err(webpki::Error::UnsupportedSignatureAlgorithm) } - - /// Get a [`ring::signature::UnparsedPublicKey`] for this `signature_scheme`. - /// Return `Error` if the `signature_scheme` does not match the public key signature - /// and hashing algorithm or if the `signature_scheme` is not supported. - pub fn public_key( - &self, - signature_scheme: rustls::SignatureScheme, - ) -> Result, webpki::Error> { - use ring::signature; - use rustls::SignatureScheme::*; - - let current_signature_scheme = self.signature_scheme()?; - if signature_scheme != current_signature_scheme { - // This certificate was signed with a different signature scheme - return Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey); - } - - let verification_algorithm: &dyn signature::VerificationAlgorithm = match signature_scheme { - RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_2048_8192_SHA256, - RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_2048_8192_SHA384, - RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_2048_8192_SHA512, - ECDSA_NISTP256_SHA256 => &signature::ECDSA_P256_SHA256_ASN1, - ECDSA_NISTP384_SHA384 => &signature::ECDSA_P384_SHA384_ASN1, - ECDSA_NISTP521_SHA512 => { - // See https://github.com/briansmith/ring/issues/824 - return Err(webpki::Error::UnsupportedSignatureAlgorithm); - } - RSA_PSS_SHA256 => &signature::RSA_PSS_2048_8192_SHA256, - RSA_PSS_SHA384 => &signature::RSA_PSS_2048_8192_SHA384, - RSA_PSS_SHA512 => &signature::RSA_PSS_2048_8192_SHA512, - ED25519 => &signature::ED25519, - ED448 => { - // See https://github.com/briansmith/ring/issues/463 - return Err(webpki::Error::UnsupportedSignatureAlgorithm); - } - // Similarly, hash functions with an output length less than 256 bits - // MUST NOT be used, due to the possibility of collision attacks. - // In particular, MD5 and SHA1 MUST NOT be used. - RSA_PKCS1_SHA1 => return Err(webpki::Error::UnsupportedSignatureAlgorithm), - ECDSA_SHA1_Legacy => return Err(webpki::Error::UnsupportedSignatureAlgorithm), - Unknown(_) => return Err(webpki::Error::UnsupportedSignatureAlgorithm), - }; - let spki = &self.certificate.tbs_certificate.subject_pki; - let key = - signature::UnparsedPublicKey::new(verification_algorithm, spki.subject_public_key.data); - - Ok(key) - } - - /// Verify the `signature` of the `message` signed by the private key corresponding to the public key stored - /// in the certificate. - pub fn verify_signature( - &self, - signature_scheme: rustls::SignatureScheme, - message: &[u8], - signature: &[u8], - ) -> Result<(), webpki::Error> { - let pk = self.public_key(signature_scheme)?; - pk.verify(message, signature) - .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?; - - Ok(()) - } } #[cfg(test)] From 6d68ffa7c0fc3a8adbc1f299e67d46b7b7e65176 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:58:47 +1000 Subject: [PATCH 16/48] Tidy up manifest --- transports/tls/Cargo.toml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 8320579168c..f03461a1abd 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -6,17 +6,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } -x509-parser = "0.13.0" +futures = { version = "0.3.24", default-features = false } +futures-rustls = "0.22.2" libp2p-core = { version = "0.36.0", path = "../../core" } -webpki = "0.22.0" rcgen = "0.9.2" -yasna = "0.5.0" ring = "0.16.20" -futures = { version = "0.3.24", default-features = false } +rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } thiserror = "1.0.36" -futures-rustls = "0.22.2" -either = "1.8.0" +webpki = "0.22.0" +x509-parser = "0.13.0" +yasna = "0.5.0" [dev-dependencies] libp2p = { path = "../..", features = ["yamux"], default-features = false } From 21b43194b2b4b6eae4e0f2ac37c03463d05d79d2 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:58:57 +1000 Subject: [PATCH 17/48] Sort hex dependencies --- transports/tls/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index f03461a1abd..a104b899b2a 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -18,7 +18,7 @@ x509-parser = "0.13.0" yasna = "0.5.0" [dev-dependencies] -libp2p = { path = "../..", features = ["yamux"], default-features = false } -tokio = { version = "1.21.1", features = ["full"] } hex = "0.4.3" hex-literal = "0.3.4" +libp2p = { path = "../..", features = ["yamux"], default-features = false } +tokio = { version = "1.21.1", features = ["full"] } From f0cac36c9147ee4d7d1f4072eb39e4ff1ac2f23e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 15:59:43 +1000 Subject: [PATCH 18/48] Fix clippy --- transports/tls/src/upgrade.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index e7cf6f78618..d5b18fc15b8 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -33,8 +33,8 @@ pub struct Config { impl Config { pub fn new(identity: &identity::Keypair) -> Result { Ok(Self { - server: crate::make_server_config(&identity)?, - client: crate::make_client_config(&identity)?, + server: crate::make_server_config(identity)?, + client: crate::make_client_config(identity)?, }) } } From 39fdf410d7a6a2b0bb212050e8b6303dc2d8ca6b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:01:58 +1000 Subject: [PATCH 19/48] Remove `Stream` type We are already leaking the `rustls` dependency to our clients through the `make_{server,client}_config` functions which have to be public for QUIC. Might as well reduce boilerplate and not wrap `TlsStream`. --- transports/tls/src/lib.rs | 3 +- transports/tls/src/upgrade.rs | 69 ++--------------------------------- 2 files changed, 6 insertions(+), 66 deletions(-) diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 2cb52b75711..c29bcc97d13 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -35,7 +35,8 @@ use rustls::{ SupportedCipherSuite, }; -pub use upgrade::{Config, Error, Stream}; +pub use futures_rustls::TlsStream; +pub use upgrade::{Config, Error}; /// A list of the TLS 1.3 cipher suites supported by rustls. // By default rustls creates client/server configs with both diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index d5b18fc15b8..713ded00007 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -6,11 +6,8 @@ use futures::{AsyncRead, FutureExt}; use futures_rustls::TlsStream; use libp2p_core::{identity, InboundUpgrade, OutboundUpgrade, PeerId, UpgradeInfo}; use rustls::{CommonState, ServerName}; -use std::io::{IoSlice, IoSliceMut}; use std::net::{IpAddr, Ipv4Addr}; -use std::pin::Pin; use std::sync::Arc; -use std::task::{Context, Poll}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -48,15 +45,11 @@ impl UpgradeInfo for Config { } } -pub struct Stream { - inner: TlsStream, -} - impl InboundUpgrade for Config where C: AsyncRead + AsyncWrite + Send + Unpin + 'static, { - type Output = (PeerId, Stream); + type Output = (PeerId, TlsStream); type Error = Error; type Future = BoxFuture<'static, Result>; @@ -71,9 +64,7 @@ where Ok(( peer_id, - Stream { - inner: stream.into(), - }, + stream.into(), )) } .boxed() @@ -84,7 +75,7 @@ impl OutboundUpgrade for Config where C: AsyncRead + AsyncWrite + Send + Unpin + 'static, { - type Output = (PeerId, Stream); + type Output = (PeerId, TlsStream); type Error = Error; type Future = BoxFuture<'static, Result>; @@ -103,65 +94,13 @@ where Ok(( peer_id, - Stream { - inner: stream.into(), - }, + stream.into(), )) } .boxed() } } -impl AsyncRead for Stream -where - C: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - Pin::new(&mut self.inner).poll_read(cx, buf) - } - - fn poll_read_vectored( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - Pin::new(&mut self.inner).poll_read_vectored(cx, bufs) - } -} - -impl AsyncWrite for Stream -where - C: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut self.inner).poll_write(cx, buf) - } - - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - Pin::new(&mut self.inner).poll_write_vectored(cx, bufs) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_close(cx) - } -} - fn extract_single_certificate(state: &CommonState) -> Result, webpki::Error> { let cert = match state .peer_certificates() From 3b76e39c68192318111fd9142df5a79f281c3bf6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:03:44 +1000 Subject: [PATCH 20/48] Fmt --- transports/tls/src/upgrade.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index 713ded00007..a7cff474a05 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -62,10 +62,7 @@ where let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id(); - Ok(( - peer_id, - stream.into(), - )) + Ok((peer_id, stream.into())) } .boxed() } @@ -92,10 +89,7 @@ where let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id(); - Ok(( - peer_id, - stream.into(), - )) + Ok((peer_id, stream.into())) } .boxed() } From 3ddf442ebcd67648e877ce06d32246aba10f7a2a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:08:26 +1000 Subject: [PATCH 21/48] Exclude `libp2p-tls` from wasm build --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 22348e656fc..53c849db15c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,6 @@ libp2p-dcutr = { version = "0.6.0", path = "protocols/dcutr", optional = true } libp2p-floodsub = { version = "0.39.1", path = "protocols/floodsub", optional = true } libp2p-identify = { version = "0.39.0", path = "protocols/identify", optional = true } libp2p-kad = { version = "0.41.0", path = "protocols/kad", optional = true } -libp2p-tls = { version = "0.1.0", path = "transports/tls", optional = true } libp2p-metrics = { version = "0.10.0", path = "misc/metrics", optional = true } libp2p-mplex = { version = "0.36.1", path = "muxers/mplex", optional = true } libp2p-noise = { version = "0.39.1", path = "transports/noise", optional = true } @@ -114,6 +113,7 @@ libp2p-dns = { version = "0.36.0", path = "transports/dns", optional = true, def libp2p-mdns = { version = "0.40.0", path = "protocols/mdns", optional = true, default-features = false } libp2p-tcp = { version = "0.37.0", path = "transports/tcp", default-features = false, optional = true } libp2p-websocket = { version = "0.38.0", path = "transports/websocket", optional = true } +libp2p-tls = { version = "0.1.0", path = "transports/tls", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] libp2p-gossipsub = { version = "0.41.1", path = "protocols/gossipsub", optional = true } From d0bb3faa8f5976f34e98c1b4e6b51d8915d56c90 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:11:01 +1000 Subject: [PATCH 22/48] Add license to libp2p-tls --- transports/tls/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index a104b899b2a..85e36045e32 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -2,6 +2,7 @@ name = "libp2p-tls" version = "0.1.0" edition = "2021" +license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 01ecdbc304f3affc6f2b460184767ca742b4fc44 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:17:09 +1000 Subject: [PATCH 23/48] Don't leak `rcgen` dependency in public API --- transports/tls/src/certificate.rs | 10 ++++++---- transports/tls/src/lib.rs | 6 +++--- transports/tls/src/upgrade.rs | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 2923a8d99bf..8a908dbeaf0 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -43,9 +43,7 @@ static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_ /// Generates a self-signed TLS certificate that includes a libp2p-specific /// certificate extension containing the public key of the given keypair. -pub fn generate( - identity_keypair: &identity::Keypair, -) -> Result { +pub fn generate(identity_keypair: &identity::Keypair) -> Result { // Keypair used to sign the certificate. // SHOULD NOT be related to the host's key. // Endpoints MAY generate a new key and certificate @@ -56,6 +54,10 @@ pub fn generate( _generate(identity_keypair, certificate_keypair) } +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct GenError(#[from] rcgen::RcgenError); + /// Attempts to parse the provided bytes as a [`P2pCertificate`]. /// /// For this to succeed, the certificate must contain the specified extension and the signature must @@ -92,7 +94,7 @@ pub struct P2pExtension { fn _generate( identity_keypair: &identity::Keypair, certificate_keypair: rcgen::KeyPair, -) -> Result { +) -> Result { // Generate the libp2p-specific extension. // The certificate MUST contain the libp2p Public Key Extension. let libp2p_extension: rcgen::CustomExtension = { diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index c29bcc97d13..bca099150c1 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -53,7 +53,7 @@ const P2P_ALPN: [u8; 6] = *b"libp2p"; /// Create a TLS client configuration for libp2p. pub fn make_client_config( keypair: &libp2p_core::identity::Keypair, -) -> Result { +) -> Result { let (certificate, key) = make_cert_key(keypair)?; let mut crypto = rustls::ClientConfig::builder() @@ -72,7 +72,7 @@ pub fn make_client_config( /// Create a TLS server configuration for libp2p. pub fn make_server_config( keypair: &libp2p_core::identity::Keypair, -) -> Result { +) -> Result { let (certificate, key) = make_cert_key(keypair)?; let mut crypto = rustls::ServerConfig::builder() @@ -91,7 +91,7 @@ pub fn make_server_config( /// Create a random private key and certificate signed with this key for rustls. fn make_cert_key( keypair: &libp2p_core::identity::Keypair, -) -> Result<(rustls::Certificate, rustls::PrivateKey), rcgen::RcgenError> { +) -> Result<(rustls::Certificate, rustls::PrivateKey), certificate::GenError> { let cert = certificate::generate(keypair)?; let private_key = cert.serialize_private_key_der(); diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index a7cff474a05..3c6aa1ffcb8 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -12,7 +12,7 @@ use std::sync::Arc; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Failed to generate certificate")] - CertificateGeneration(#[from] rcgen::RcgenError), // TODO: This leaks `rcgen` dependency. + CertificateGeneration(#[from] certificate::GenError), #[error("Failed to upgrade server connection")] ServerUpgrade(std::io::Error), #[error("Failed to upgrade client connection")] @@ -28,7 +28,7 @@ pub struct Config { } impl Config { - pub fn new(identity: &identity::Keypair) -> Result { + pub fn new(identity: &identity::Keypair) -> Result { Ok(Self { server: crate::make_server_config(identity)?, client: crate::make_client_config(identity)?, From 67eda949c451253f5dd6411b8dd98931b969ea14 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:27:46 +1000 Subject: [PATCH 24/48] Don't leak `webpki` dependency --- transports/tls/src/certificate.rs | 12 +++++++++-- transports/tls/src/lib.rs | 5 ++--- transports/tls/src/upgrade.rs | 6 ++++-- transports/tls/src/verifier.rs | 33 +++++++++++++++++++------------ transports/tls/tests/smoke.rs | 2 +- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 8a908dbeaf0..e84520efc07 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -58,11 +58,19 @@ pub fn generate(identity_keypair: &identity::Keypair) -> Result Result { +pub fn parse(der_input: &[u8]) -> Result { let certificate = parse_unverified(der_input)?; certificate.verify()?; @@ -227,7 +235,7 @@ impl P2pCertificate<'_> { signature_scheme: rustls::SignatureScheme, message: &[u8], signature: &[u8], - ) -> Result<(), webpki::Error> { + ) -> Result<(), VerificationError> { let pk = self.public_key(signature_scheme)?; pk.verify(message, signature) .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?; diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index bca099150c1..f715581f6d2 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -22,8 +22,8 @@ //! //! See . -mod certificate; -mod upgrade; +pub mod certificate; +pub mod upgrade; mod verifier; use std::sync::Arc; @@ -36,7 +36,6 @@ use rustls::{ }; pub use futures_rustls::TlsStream; -pub use upgrade::{Config, Error}; /// A list of the TLS 1.3 cipher suites supported by rustls. // By default rustls creates client/server configs with both diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index 3c6aa1ffcb8..f43359130d5 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -18,7 +18,7 @@ pub enum Error { #[error("Failed to upgrade client connection")] ClientUpgrade(std::io::Error), #[error("Failed to parse certificate")] - BadCertificate(#[from] webpki::Error), // TODO: This leaks `webpki` dependency. + BadCertificate(#[from] certificate::ParseError), } #[derive(Clone)] @@ -95,7 +95,9 @@ where } } -fn extract_single_certificate(state: &CommonState) -> Result, webpki::Error> { +fn extract_single_certificate( + state: &CommonState, +) -> Result, certificate::ParseError> { let cert = match state .peer_certificates() .expect("config enforces presence of certificates") diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 523e22b3c03..9996ff71606 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -178,7 +178,7 @@ fn verify_presented_certs( )); } - certificate::parse(end_entity.as_ref()).map_err(pki_error)?; + certificate::parse(end_entity.as_ref())?; Ok(()) } @@ -189,22 +189,29 @@ fn verify_tls13_signature( message: &[u8], signature: &[u8], ) -> Result { - certificate::parse(cert.as_ref()) - .map_err(pki_error)? - .verify_signature(signature_scheme, message, signature) - .map_err(pki_error)?; + certificate::parse(cert.as_ref())?.verify_signature(signature_scheme, message, signature)?; Ok(HandshakeSignatureValid::assertion()) } -fn pki_error(error: webpki::Error) -> rustls::Error { - use webpki::Error::*; - match error { - BadDer | BadDerTime => rustls::Error::InvalidCertificateEncoding, - InvalidSignatureForPublicKey => rustls::Error::InvalidCertificateSignature, - UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => { - rustls::Error::InvalidCertificateSignatureType +impl From for rustls::Error { + fn from(certificate::ParseError(e): certificate::ParseError) -> Self { + use webpki::Error::*; + match e { + BadDer => rustls::Error::InvalidCertificateEncoding, + e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {}", e)), + } + } +} +impl From for rustls::Error { + fn from(certificate::VerificationError(e): certificate::VerificationError) -> Self { + use webpki::Error::*; + match e { + InvalidSignatureForPublicKey => rustls::Error::InvalidCertificateSignature, + UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => { + rustls::Error::InvalidCertificateSignatureType + } + e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {}", e)), } - e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {}", e)), } } diff --git a/transports/tls/tests/smoke.rs b/transports/tls/tests/smoke.rs index 0e4b3441b0d..8ac9696c5f8 100644 --- a/transports/tls/tests/smoke.rs +++ b/transports/tls/tests/smoke.rs @@ -61,7 +61,7 @@ fn make_swarm() -> Swarm { let transport = MemoryTransport::default() .upgrade(Version::V1) - .authenticate(libp2p_tls::Config::new(&identity).unwrap()) + .authenticate(libp2p_tls::upgrade::Config::new(&identity).unwrap()) .multiplex(libp2p::yamux::YamuxConfig::default()) .boxed(); From eedbefcb3e82ca0ccdf60c87b7c5fdf736f98332 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 16:28:42 +1000 Subject: [PATCH 25/48] Add note about exposed dependency to manifest --- transports/tls/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 85e36045e32..776e2076aa2 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -12,7 +12,7 @@ futures-rustls = "0.22.2" libp2p-core = { version = "0.36.0", path = "../../core" } rcgen = "0.9.2" ring = "0.16.20" -rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } +rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } # Exposed dependency! Bump major version on any change. thiserror = "1.0.36" webpki = "0.22.0" x509-parser = "0.13.0" From d9f1036dbd8f404b56180dfbdf671008e83e2ae6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 20:17:01 +1000 Subject: [PATCH 26/48] Upgraxe x509-parser to 0.14 Co-authored-by: Roman Proskuryakov --- transports/tls/Cargo.toml | 2 +- transports/tls/src/certificate.rs | 51 +++++++++++++------------------ 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 776e2076aa2..f10816fdd95 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -15,7 +15,7 @@ ring = "0.16.20" rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } # Exposed dependency! Bump major version on any change. thiserror = "1.0.36" webpki = "0.22.0" -x509-parser = "0.13.0" +x509-parser = "0.14.0" yasna = "0.5.0" [dev-dependencies] diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index e84520efc07..7a28f06b411 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -23,7 +23,7 @@ //! This module handles generation, signing, and verification of certificates. use libp2p_core::{identity, PeerId}; -use x509_parser::prelude::*; +use x509_parser::{prelude::*, signature_algorithm::SignatureAlgorithm}; /// The libp2p Public Key Extension is a X.509 extension /// with the Object Identier 1.3.6.1.4.1.53594.1.1, @@ -285,8 +285,10 @@ impl P2pCertificate<'_> { Unknown(_) => return Err(webpki::Error::UnsupportedSignatureAlgorithm), }; let spki = &self.certificate.tbs_certificate.subject_pki; - let key = - signature::UnparsedPublicKey::new(verification_algorithm, spki.subject_public_key.data); + let key = signature::UnparsedPublicKey::new( + verification_algorithm, + spki.subject_public_key.as_ref(), + ); Ok(key) } @@ -315,7 +317,7 @@ impl P2pCertificate<'_> { // Endpoints MUST abort the connection attempt if the certificate’s // self-signature is not valid. let raw_certificate = self.certificate.tbs_certificate.as_ref(); - let signature = self.certificate.signature_value.data; + let signature = self.certificate.signature_value.as_ref(); // check if self signed self.verify_signature(signature_scheme, raw_certificate, signature) .map_err(|_| Error::SignatureAlgorithmMismatch)?; @@ -374,30 +376,21 @@ impl P2pCertificate<'_> { // - Salt Length // - Trailer Field - // We are interested in Hash Algorithm only, however the der parser parses - // params into a mess, so here is a workaround to fix it: - fn get_hash_oid<'a>( - signature_algorithm: &'a AlgorithmIdentifier, - ) -> Option> { - let params = signature_algorithm.parameters.as_ref()?; - let params = params.as_sequence().ok()?; - let first_param = params.get(0)?; - let hash_oid_der = first_param.as_slice().ok()?; - let (_, obj) = der_parser::parse_der(hash_oid_der).ok()?; - let hash_oid = obj.as_sequence().ok()?.get(0)?.as_oid_val().ok()?; - Some(hash_oid) - } - - let hash_oid = get_hash_oid(signature_algorithm).ok_or(webpki::Error::BadDer)?; - - if hash_oid == OID_NIST_HASH_SHA256 { - return Ok(RSA_PSS_SHA256); - } - if hash_oid == OID_NIST_HASH_SHA384 { - return Ok(RSA_PSS_SHA384); - } - if hash_oid == OID_NIST_HASH_SHA512 { - return Ok(RSA_PSS_SHA512); + // We are interested in Hash Algorithm only + + if let Ok(SignatureAlgorithm::RSASSA_PSS(params)) = + SignatureAlgorithm::try_from(signature_algorithm) + { + let hash_oid = params.hash_algorithm_oid(); + if hash_oid == &OID_NIST_HASH_SHA256 { + return Ok(RSA_PSS_SHA256); + } + if hash_oid == &OID_NIST_HASH_SHA384 { + return Ok(RSA_PSS_SHA384); + } + if hash_oid == &OID_NIST_HASH_SHA512 { + return Ok(RSA_PSS_SHA512); + } } // Default hash algo is SHA-1, however: @@ -411,7 +404,7 @@ impl P2pCertificate<'_> { .parameters .as_ref() .ok_or(webpki::Error::BadDer)? - .as_oid_val() + .as_oid() .map_err(|_| webpki::Error::BadDer)?; if signature_param == OID_EC_P256 && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA256 From d74fe573342c01026c2cdf2c1d3139ce68329607 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 26 Sep 2022 20:19:10 +1000 Subject: [PATCH 27/48] Remove comment --- transports/tls/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index f10816fdd95..6fd7db61427 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" edition = "2021" license = "MIT" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] futures = { version = "0.3.24", default-features = false } futures-rustls = "0.22.2" From eaa27f42c04265e3d3402caec6baab91378996c1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 27 Sep 2022 10:29:54 +1000 Subject: [PATCH 28/48] Exclude test assets from publishing --- transports/tls/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 6fd7db61427..2f2fa79d1aa 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -3,6 +3,7 @@ name = "libp2p-tls" version = "0.1.0" edition = "2021" license = "MIT" +exclude = ["src/test_assets"] [dependencies] futures = { version = "0.3.24", default-features = false } From f95e9d96e86cf7f91178261d2a985a141ee4b00f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 27 Sep 2022 10:32:35 +1000 Subject: [PATCH 29/48] Move errors further down These are not interesting enough to be as prominent. --- transports/tls/src/certificate.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 7a28f06b411..5505e8bfe47 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -54,18 +54,6 @@ pub fn generate(identity_keypair: &identity::Keypair) -> Result, } +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct GenError(#[from] rcgen::RcgenError); + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct ParseError(#[from] pub(crate) webpki::Error); + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct VerificationError(#[from] pub(crate) webpki::Error); + fn _generate( identity_keypair: &identity::Keypair, certificate_keypair: rcgen::KeyPair, From 7acf98b2304f777976ce5af3bfcc7fde881f6ee8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 27 Sep 2022 10:35:49 +1000 Subject: [PATCH 30/48] Inline `_generate` function This is a left-over from an earlier iteration where we thought that we need to control the keypair for deterministic tests. --- transports/tls/src/certificate.rs | 92 ++++++++++++++----------------- 1 file changed, 41 insertions(+), 51 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 5505e8bfe47..b7a04ff216d 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -51,7 +51,47 @@ pub fn generate(identity_keypair: &identity::Keypair) -> Result Result { - // Generate the libp2p-specific extension. - // The certificate MUST contain the libp2p Public Key Extension. - let libp2p_extension: rcgen::CustomExtension = { - // The peer signs the concatenation of the string `libp2p-tls-handshake:` - // and the public key that it used to generate the certificate carrying - // the libp2p Public Key Extension, using its private host key. - let signature = { - let mut msg = vec![]; - msg.extend(P2P_SIGNING_PREFIX); - msg.extend(certificate_keypair.public_key_der()); - - identity_keypair - .sign(&msg) - .map_err(|_| rcgen::RcgenError::RingUnspecified)? - }; - - // The public host key and the signature are ANS.1-encoded - // into the SignedKey data structure, which is carried - // in the libp2p Public Key Extension. - // SignedKey ::= SEQUENCE { - // publicKey OCTET STRING, - // signature OCTET STRING - // } - let extension_content = { - let serialized_pubkey = identity_keypair.public().to_protobuf_encoding(); - yasna::encode_der(&(serialized_pubkey, signature)) - }; - - // This extension MAY be marked critical. - let mut ext = rcgen::CustomExtension::from_oid_content(&P2P_EXT_OID, extension_content); - ext.set_criticality(true); - ext - }; - - let certificate = { - let mut params = rcgen::CertificateParams::new(vec![]); - params.distinguished_name = rcgen::DistinguishedName::new(); - params.custom_extensions.push(libp2p_extension); - params.alg = P2P_SIGNATURE_ALGORITHM; - params.key_pair = Some(certificate_keypair); - rcgen::Certificate::from_params(params)? - }; - - Ok(certificate) -} - /// Internal function that only parses but does not verify the certificate. /// /// Useful for testing but unsuitable for production. From 4f15b3b2ab9e507986383e61d3c3f4a3fe252020 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 27 Sep 2022 11:12:01 +1000 Subject: [PATCH 31/48] Extract `make_libp2p_extension` function --- transports/tls/src/certificate.rs | 72 +++++++++++++++++-------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index b7a04ff216d..5c8a3f7c5d0 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -51,41 +51,13 @@ pub fn generate(identity_keypair: &identity::Keypair) -> Result Result { Ok(certificate) } +fn make_libp2p_extension( + identity_keypair: &identity::Keypair, + certificate_keypair: &rcgen::KeyPair, +) -> Result { + // The peer signs the concatenation of the string `libp2p-tls-handshake:` + // and the public key that it used to generate the certificate carrying + // the libp2p Public Key Extension, using its private host key. + let signature = { + let mut msg = vec![]; + msg.extend(P2P_SIGNING_PREFIX); + msg.extend(certificate_keypair.public_key_der()); + + identity_keypair + .sign(&msg) + .map_err(|_| rcgen::RcgenError::RingUnspecified)? + }; + + // The public host key and the signature are ANS.1-encoded + // into the SignedKey data structure, which is carried + // in the libp2p Public Key Extension. + // SignedKey ::= SEQUENCE { + // publicKey OCTET STRING, + // signature OCTET STRING + // } + let extension_content = { + let serialized_pubkey = identity_keypair.public().to_protobuf_encoding(); + yasna::encode_der(&(serialized_pubkey, signature)) + }; + + // This extension MAY be marked critical. + let mut ext = rcgen::CustomExtension::from_oid_content(&P2P_EXT_OID, extension_content); + ext.set_criticality(true); + + Ok(ext) +} + impl P2pCertificate<'_> { /// The [`PeerId`] of the remote peer. pub fn peer_id(&self) -> PeerId { From e1f34aeafb9a543465e52d6bfffdfeb620cd9843 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 27 Sep 2022 11:25:31 +1000 Subject: [PATCH 32/48] Use `rustls` types in signatures This actually hides the `rcgen` dependency from our users. We already leak `rustls` which is basically unavoidable for as long as we want to expose the `make_{server,client}_config` functions so it seems adequate to just use those types in the `certificate` module too. --- transports/tls/src/certificate.rs | 41 ++++++++++++++++++------------- transports/tls/src/lib.rs | 21 +++------------- transports/tls/src/upgrade.rs | 2 +- transports/tls/src/verifier.rs | 4 +-- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 5c8a3f7c5d0..6ab31a349d1 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -43,13 +43,16 @@ static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_ /// Generates a self-signed TLS certificate that includes a libp2p-specific /// certificate extension containing the public key of the given keypair. -pub fn generate(identity_keypair: &identity::Keypair) -> Result { +pub fn generate( + identity_keypair: &identity::Keypair, +) -> Result<(rustls::Certificate, rustls::PrivateKey), GenError> { // Keypair used to sign the certificate. // SHOULD NOT be related to the host's key. // Endpoints MAY generate a new key and certificate // for every connection attempt, or they MAY reuse the same key // and certificate for multiple connections. let certificate_keypair = rcgen::KeyPair::generate(P2P_SIGNATURE_ALGORITHM)?; + let rustls_key = rustls::PrivateKey(certificate_keypair.serialize_der()); let certificate = { let mut params = rcgen::CertificateParams::new(vec![]); @@ -63,15 +66,17 @@ pub fn generate(identity_keypair: &identity::Keypair) -> Result Result { - let certificate = parse_unverified(der_input)?; +pub fn parse(certificate: &rustls::Certificate) -> Result, ParseError> { + let certificate = parse_unverified(certificate.as_ref())?; certificate.verify()?; @@ -441,10 +446,9 @@ mod tests { #[test] fn sanity_check() { let keypair = identity::Keypair::generate_ed25519(); - let cert = generate(&keypair).unwrap(); - let cert_der = cert.serialize_der().unwrap(); - let parsed_cert = parse(&cert_der).unwrap(); + let (cert, _) = generate(&keypair).unwrap(); + let parsed_cert = parse(&cert).unwrap(); assert!(parsed_cert.verify().is_ok()); assert_eq!(keypair.public(), parsed_cert.extension.public_key); @@ -476,9 +480,9 @@ mod tests { #[test] fn rsa_pss_sha384() { - let cert: &[u8] = include_bytes!("./test_assets/rsa_pss_sha384.der"); + let cert = rustls::Certificate(include_bytes!("./test_assets/rsa_pss_sha384.der").to_vec()); - let cert = parse(cert).unwrap(); + let cert = parse(&cert).unwrap(); assert_eq!( cert.signature_scheme(), @@ -497,8 +501,9 @@ mod tests { #[test] fn can_parse_certificate_with_ed25519_keypair() { - let certificate = parse(&hex!("308201773082011ea003020102020900f5bd0debaa597f52300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200046bf9871220d71dcb3483ecdfcbfcc7c103f8509d0974b3c18ab1f1be1302d643103a08f7a7722c1b247ba3876fe2c59e26526f479d7718a85202ddbe47562358a37f307d307b060a2b0601040183a25a01010101ff046a30680424080112207fda21856709c5ae12fd6e8450623f15f11955d384212b89f56e7e136d2e17280440aaa6bffabe91b6f30c35e3aa4f94b1188fed96b0ffdd393f4c58c1c047854120e674ce64c788406d1c2c4b116581fd7411b309881c3c7f20b46e54c7e6fe7f0f300a06082a8648ce3d040302034700304402207d1a1dbd2bda235ff2ec87daf006f9b04ba076a5a5530180cd9c2e8f6399e09d0220458527178c7e77024601dbb1b256593e9b96d961b96349d1f560114f61a87595")).unwrap(); - let peer_id = certificate.peer_id(); + let certificate = rustls::Certificate(hex!("308201773082011ea003020102020900f5bd0debaa597f52300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200046bf9871220d71dcb3483ecdfcbfcc7c103f8509d0974b3c18ab1f1be1302d643103a08f7a7722c1b247ba3876fe2c59e26526f479d7718a85202ddbe47562358a37f307d307b060a2b0601040183a25a01010101ff046a30680424080112207fda21856709c5ae12fd6e8450623f15f11955d384212b89f56e7e136d2e17280440aaa6bffabe91b6f30c35e3aa4f94b1188fed96b0ffdd393f4c58c1c047854120e674ce64c788406d1c2c4b116581fd7411b309881c3c7f20b46e54c7e6fe7f0f300a06082a8648ce3d040302034700304402207d1a1dbd2bda235ff2ec87daf006f9b04ba076a5a5530180cd9c2e8f6399e09d0220458527178c7e77024601dbb1b256593e9b96d961b96349d1f560114f61a87595").to_vec()); + + let peer_id = parse(&certificate).unwrap().peer_id(); assert_eq!( "12D3KooWJRSrypvnpHgc6ZAgyCni4KcSmbV7uGRaMw5LgMKT18fq" @@ -510,17 +515,18 @@ mod tests { #[test] fn fails_to_parse_bad_certificate_with_ed25519_keypair() { - let result = parse(&hex!("308201773082011da003020102020830a73c5d896a1109300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004bbe62df9a7c1c46b7f1f21d556deec5382a36df146fb29c7f1240e60d7d5328570e3b71d99602b77a65c9b3655f62837f8d66b59f1763b8c9beba3be07778043a37f307d307b060a2b0601040183a25a01010101ff046a3068042408011220ec8094573afb9728088860864f7bcea2d4fd412fef09a8e2d24d482377c20db60440ecabae8354afa2f0af4b8d2ad871e865cb5a7c0c8d3dbdbf42de577f92461a0ebb0a28703e33581af7d2a4f2270fc37aec6261fcc95f8af08f3f4806581c730a300a06082a8648ce3d040302034800304502202dfb17a6fa0f94ee0e2e6a3b9fb6e986f311dee27392058016464bd130930a61022100ba4b937a11c8d3172b81e7cd04aedb79b978c4379c2b5b24d565dd5d67d3cb3c")); + let certificate = rustls::Certificate(hex!("308201773082011da003020102020830a73c5d896a1109300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004bbe62df9a7c1c46b7f1f21d556deec5382a36df146fb29c7f1240e60d7d5328570e3b71d99602b77a65c9b3655f62837f8d66b59f1763b8c9beba3be07778043a37f307d307b060a2b0601040183a25a01010101ff046a3068042408011220ec8094573afb9728088860864f7bcea2d4fd412fef09a8e2d24d482377c20db60440ecabae8354afa2f0af4b8d2ad871e865cb5a7c0c8d3dbdbf42de577f92461a0ebb0a28703e33581af7d2a4f2270fc37aec6261fcc95f8af08f3f4806581c730a300a06082a8648ce3d040302034800304502202dfb17a6fa0f94ee0e2e6a3b9fb6e986f311dee27392058016464bd130930a61022100ba4b937a11c8d3172b81e7cd04aedb79b978c4379c2b5b24d565dd5d67d3cb3c").to_vec()); - let error = result.unwrap_err(); + let error = parse(&certificate).unwrap_err(); assert_eq!(format!("{}", error), "UnknownIssuer"); } #[test] fn can_parse_certificate_with_ecdsa_keypair() { - let certificate = parse(&hex!("308201c030820166a003020102020900eaf419a6e3edb4a6300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200048dbf1116c7c608d6d5292bd826c3feb53483a89fce434bf64538a359c8e07538ff71f6766239be6a146dcc1a5f3bb934bcd4ae2ae1d4da28ac68b4a20593f06ba381c63081c33081c0060a2b0601040183a25a01010101ff0481ae3081ab045f0803125b3059301306072a8648ce3d020106082a8648ce3d0301070342000484b93fa456a74bd0153919f036db7bc63c802f055bc7023395d0203de718ee0fc7b570b767cdd858aca6c7c4113ff002e78bd2138ac1a3b26dde3519e06979ad04483046022100bc84014cea5a41feabdf4c161096564b9ccf4b62fbef4fe1cd382c84e11101780221009204f086a84cb8ed8a9ddd7868dc90c792ee434adf62c66f99a08a5eba11615b300a06082a8648ce3d0403020348003045022054b437be9a2edf591312d68ff24bf91367ad4143f76cf80b5658f232ade820da022100e23b48de9df9c25d4c83ddddf75d2676f0b9318ee2a6c88a736d85eab94a912f")).unwrap(); - let peer_id = certificate.peer_id(); + let certificate = rustls::Certificate(hex!("308201c030820166a003020102020900eaf419a6e3edb4a6300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200048dbf1116c7c608d6d5292bd826c3feb53483a89fce434bf64538a359c8e07538ff71f6766239be6a146dcc1a5f3bb934bcd4ae2ae1d4da28ac68b4a20593f06ba381c63081c33081c0060a2b0601040183a25a01010101ff0481ae3081ab045f0803125b3059301306072a8648ce3d020106082a8648ce3d0301070342000484b93fa456a74bd0153919f036db7bc63c802f055bc7023395d0203de718ee0fc7b570b767cdd858aca6c7c4113ff002e78bd2138ac1a3b26dde3519e06979ad04483046022100bc84014cea5a41feabdf4c161096564b9ccf4b62fbef4fe1cd382c84e11101780221009204f086a84cb8ed8a9ddd7868dc90c792ee434adf62c66f99a08a5eba11615b300a06082a8648ce3d0403020348003045022054b437be9a2edf591312d68ff24bf91367ad4143f76cf80b5658f232ade820da022100e23b48de9df9c25d4c83ddddf75d2676f0b9318ee2a6c88a736d85eab94a912f").to_vec()); + + let peer_id = parse(&certificate).unwrap().peer_id(); assert_eq!( "QmZcrvr3r4S3QvwFdae3c2EWTfo792Y14UpzCZurhmiWeX" @@ -532,8 +538,9 @@ mod tests { #[test] fn can_parse_certificate_with_secp256k1_keypair() { - let certificate = parse(&hex!("3082018230820128a003020102020900f3b305f55622cfdf300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000458f7e9581748ff9bdd933b655cc0e5552a1248f840658cc221dec2186b5a2fe4641b86ab7590a3422cdbb1000cf97662f27e5910d7569f22feed8829c8b52e0fa38188308185308182060a2b0601040183a25a01010101ff0471306f042508021221026b053094d1112bce799dc8026040ae6d4eb574157929f1598172061f753d9b1b04463044022040712707e97794c478d93989aaa28ae1f71c03af524a8a4bd2d98424948a782302207b61b7f074b696a25fb9e0059141a811cccc4cc28042d9301b9b2a4015e87470300a06082a8648ce3d04030203480030450220143ae4d86fdc8675d2480bb6912eca5e39165df7f572d836aa2f2d6acfab13f8022100831d1979a98f0c4a6fb5069ca374de92f1a1205c962a6d90ad3d7554cb7d9df4")).unwrap(); - let peer_id = certificate.peer_id(); + let certificate = rustls::Certificate(hex!("3082018230820128a003020102020900f3b305f55622cfdf300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000458f7e9581748ff9bdd933b655cc0e5552a1248f840658cc221dec2186b5a2fe4641b86ab7590a3422cdbb1000cf97662f27e5910d7569f22feed8829c8b52e0fa38188308185308182060a2b0601040183a25a01010101ff0471306f042508021221026b053094d1112bce799dc8026040ae6d4eb574157929f1598172061f753d9b1b04463044022040712707e97794c478d93989aaa28ae1f71c03af524a8a4bd2d98424948a782302207b61b7f074b696a25fb9e0059141a811cccc4cc28042d9301b9b2a4015e87470300a06082a8648ce3d04030203480030450220143ae4d86fdc8675d2480bb6912eca5e39165df7f572d836aa2f2d6acfab13f8022100831d1979a98f0c4a6fb5069ca374de92f1a1205c962a6d90ad3d7554cb7d9df4").to_vec()); + + let peer_id = parse(&certificate).unwrap().peer_id(); assert_eq!( "16Uiu2HAm2dSCBFxuge46aEt7U1oejtYuBUZXxASHqmcfVmk4gsbx" diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index f715581f6d2..59ddc069e97 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -53,7 +53,7 @@ const P2P_ALPN: [u8; 6] = *b"libp2p"; pub fn make_client_config( keypair: &libp2p_core::identity::Keypair, ) -> Result { - let (certificate, key) = make_cert_key(keypair)?; + let (certificate, private_key) = certificate::generate(keypair)?; let mut crypto = rustls::ClientConfig::builder() .with_cipher_suites(TLS13_CIPHERSUITES) @@ -61,7 +61,7 @@ pub fn make_client_config( .with_protocol_versions(&[&rustls::version::TLS13]) .expect("Cipher suites and kx groups are configured; qed") .with_custom_certificate_verifier(Arc::new(verifier::Libp2pCertificateVerifier)) - .with_single_cert(vec![certificate], key) + .with_single_cert(vec![certificate], private_key) .expect("Client cert key DER is valid; qed"); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; @@ -72,7 +72,7 @@ pub fn make_client_config( pub fn make_server_config( keypair: &libp2p_core::identity::Keypair, ) -> Result { - let (certificate, key) = make_cert_key(keypair)?; + let (certificate, private_key) = certificate::generate(keypair)?; let mut crypto = rustls::ServerConfig::builder() .with_cipher_suites(TLS13_CIPHERSUITES) @@ -80,22 +80,9 @@ pub fn make_server_config( .with_protocol_versions(&[&rustls::version::TLS13]) .expect("Cipher suites and kx groups are configured; qed") .with_client_cert_verifier(Arc::new(verifier::Libp2pCertificateVerifier)) - .with_single_cert(vec![certificate], key) + .with_single_cert(vec![certificate], private_key) .expect("Server cert key DER is valid; qed"); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; Ok(crypto) } - -/// Create a random private key and certificate signed with this key for rustls. -fn make_cert_key( - keypair: &libp2p_core::identity::Keypair, -) -> Result<(rustls::Certificate, rustls::PrivateKey), certificate::GenError> { - let cert = certificate::generate(keypair)?; - let private_key = cert.serialize_private_key_der(); - - let cert = rustls::Certificate(cert.serialize_der()?); - let key = rustls::PrivateKey(private_key); - - Ok((cert, key)) -} diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index f43359130d5..a7ed538d3f2 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -106,5 +106,5 @@ fn extract_single_certificate( _ => panic!("config enforces exactly one certificate"), }; - certificate::parse(cert.as_ref()) + certificate::parse(cert) } diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 9996ff71606..9af02e95298 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -178,7 +178,7 @@ fn verify_presented_certs( )); } - certificate::parse(end_entity.as_ref())?; + certificate::parse(end_entity)?; Ok(()) } @@ -189,7 +189,7 @@ fn verify_tls13_signature( message: &[u8], signature: &[u8], ) -> Result { - certificate::parse(cert.as_ref())?.verify_signature(signature_scheme, message, signature)?; + certificate::parse(cert)?.verify_signature(signature_scheme, message, signature)?; Ok(HandshakeSignatureValid::assertion()) } From 79a464a780e67f04f952e90440cfd6e69a9b184e Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 29 Sep 2022 02:33:44 +0400 Subject: [PATCH 33/48] libp2p-tls: Allow to specify remote peer ID we intend to connect to (#2947) --- transports/tls/src/lib.rs | 12 ++++++++---- transports/tls/src/upgrade.rs | 2 +- transports/tls/src/verifier.rs | 35 +++++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 59ddc069e97..2fceea2762f 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -26,6 +26,7 @@ pub mod certificate; pub mod upgrade; mod verifier; +use libp2p_core::{identity::Keypair, PeerId}; use std::sync::Arc; use rustls::{ @@ -51,7 +52,8 @@ const P2P_ALPN: [u8; 6] = *b"libp2p"; /// Create a TLS client configuration for libp2p. pub fn make_client_config( - keypair: &libp2p_core::identity::Keypair, + keypair: &Keypair, + remote_peer_id: Option, ) -> Result { let (certificate, private_key) = certificate::generate(keypair)?; @@ -60,7 +62,9 @@ pub fn make_client_config( .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS13]) .expect("Cipher suites and kx groups are configured; qed") - .with_custom_certificate_verifier(Arc::new(verifier::Libp2pCertificateVerifier)) + .with_custom_certificate_verifier(Arc::new( + verifier::Libp2pCertificateVerifier::with_remote_peer_id(remote_peer_id), + )) .with_single_cert(vec![certificate], private_key) .expect("Client cert key DER is valid; qed"); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; @@ -70,7 +74,7 @@ pub fn make_client_config( /// Create a TLS server configuration for libp2p. pub fn make_server_config( - keypair: &libp2p_core::identity::Keypair, + keypair: &Keypair, ) -> Result { let (certificate, private_key) = certificate::generate(keypair)?; @@ -79,7 +83,7 @@ pub fn make_server_config( .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS13]) .expect("Cipher suites and kx groups are configured; qed") - .with_client_cert_verifier(Arc::new(verifier::Libp2pCertificateVerifier)) + .with_client_cert_verifier(Arc::new(verifier::Libp2pCertificateVerifier::new())) .with_single_cert(vec![certificate], private_key) .expect("Server cert key DER is valid; qed"); crypto.alpn_protocols = vec![P2P_ALPN.to_vec()]; diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index a7ed538d3f2..6f7dd10dc47 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -31,7 +31,7 @@ impl Config { pub fn new(identity: &identity::Keypair) -> Result { Ok(Self { server: crate::make_server_config(identity)?, - client: crate::make_client_config(identity)?, + client: crate::make_client_config(identity, None)?, }) } } diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 9af02e95298..2815b92c9a3 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -24,6 +24,7 @@ //! and signatures allegedly by the given certificates. use crate::certificate; +use libp2p_core::PeerId; use rustls::{ client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, internal::msgs::handshake::DigitallySignedStruct, @@ -34,7 +35,10 @@ use rustls::{ /// 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 struct Libp2pCertificateVerifier; +pub struct Libp2pCertificateVerifier { + /// The peer ID we intend to connect to + remote_peer_id: Option, +} /// libp2p requires the following of X.509 server certificate chains: /// @@ -43,6 +47,15 @@ pub struct Libp2pCertificateVerifier; /// - The certificate must have a valid libp2p extension that includes a /// signature of its public key. impl Libp2pCertificateVerifier { + pub fn new() -> Self { + Self { + remote_peer_id: None, + } + } + pub fn with_remote_peer_id(remote_peer_id: Option) -> Self { + Self { remote_peer_id } + } + /// Return the list of SignatureSchemes that this verifier will handle, /// in `verify_tls12_signature` and `verify_tls13_signature` calls. /// @@ -76,7 +89,19 @@ impl ServerCertVerifier for Libp2pCertificateVerifier { _ocsp_response: &[u8], _now: std::time::SystemTime, ) -> Result { - verify_presented_certs(end_entity, intermediates)?; + let peer_id = verify_presented_certs(end_entity, intermediates)?; + + if let Some(remote_peer_id) = self.remote_peer_id { + // The public host key allows the peer to calculate the peer ID of the peer + // it is connecting to. Clients MUST verify that the peer ID derived from + // the certificate matches the peer ID they intended to connect to, + // and MUST abort the connection if there is a mismatch. + if remote_peer_id != peer_id { + return Err(rustls::Error::PeerMisbehavedError( + "Wrong peer ID in p2p extension".to_string(), + )); + } + } Ok(ServerCertVerified::assertion()) } @@ -171,16 +196,16 @@ impl ClientCertVerifier for Libp2pCertificateVerifier { fn verify_presented_certs( end_entity: &Certificate, intermediates: &[Certificate], -) -> Result<(), rustls::Error> { +) -> Result { if !intermediates.is_empty() { return Err(rustls::Error::General( "libp2p-tls requires exactly one certificate".into(), )); } - certificate::parse(end_entity)?; + let cert = certificate::parse(end_entity)?; - Ok(()) + Ok(cert.peer_id()) } fn verify_tls13_signature( From a1546be810dd2969e742909fe06c83215be8ee90 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 30 Sep 2022 15:33:47 +1000 Subject: [PATCH 34/48] Point to correct version of `libp2p-tls` --- transports/tls/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 2f2fa79d1aa..e6d73800862 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -8,7 +8,7 @@ exclude = ["src/test_assets"] [dependencies] futures = { version = "0.3.24", default-features = false } futures-rustls = "0.22.2" -libp2p-core = { version = "0.36.0", path = "../../core" } +libp2p-core = { version = "0.37.0", path = "../../core" } rcgen = "0.9.2" ring = "0.16.20" rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } # Exposed dependency! Bump major version on any change. From f6ba989f6fb27e58485dc67ccccff814da977a63 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 30 Sep 2022 15:40:27 +1000 Subject: [PATCH 35/48] Polish public API and expose via root crate --- src/lib.rs | 4 ++++ transports/tls/src/lib.rs | 7 ++++--- transports/tls/src/upgrade.rs | 10 +++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 96a197cf516..84e53ff5111 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,6 +130,10 @@ pub use libp2p_swarm as swarm; #[cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))] #[doc(inline)] pub use libp2p_tcp as tcp; +#[cfg(feature = "tls")] +#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] +#[doc(inline)] +pub use libp2p_tls as tls; #[cfg(feature = "uds")] #[cfg_attr(docsrs, doc(cfg(feature = "uds")))] #[doc(inline)] diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 2fceea2762f..e06da2ea556 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -23,20 +23,21 @@ //! See . pub mod certificate; -pub mod upgrade; +mod upgrade; mod verifier; use libp2p_core::{identity::Keypair, PeerId}; -use std::sync::Arc; - use rustls::{ cipher_suite::{ TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, }, SupportedCipherSuite, }; +use std::sync::Arc; pub use futures_rustls::TlsStream; +pub use upgrade::Config; +pub use upgrade::UpgradeError; /// A list of the TLS 1.3 cipher suites supported by rustls. // By default rustls creates client/server configs with both diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index 6f7dd10dc47..335222e560d 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -10,7 +10,7 @@ use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; #[derive(thiserror::Error, Debug)] -pub enum Error { +pub enum UpgradeError { #[error("Failed to generate certificate")] CertificateGeneration(#[from] certificate::GenError), #[error("Failed to upgrade server connection")] @@ -50,7 +50,7 @@ where C: AsyncRead + AsyncWrite + Send + Unpin + 'static, { type Output = (PeerId, TlsStream); - type Error = Error; + type Error = UpgradeError; type Future = BoxFuture<'static, Result>; fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future { @@ -58,7 +58,7 @@ where let stream = futures_rustls::TlsAcceptor::from(Arc::new(self.server)) .accept(socket) .await - .map_err(Error::ServerUpgrade)?; + .map_err(UpgradeError::ServerUpgrade)?; let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id(); @@ -73,7 +73,7 @@ where C: AsyncRead + AsyncWrite + Send + Unpin + 'static, { type Output = (PeerId, TlsStream); - type Error = Error; + type Error = UpgradeError; type Future = BoxFuture<'static, Result>; fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future { @@ -85,7 +85,7 @@ where let stream = futures_rustls::TlsConnector::from(Arc::new(self.client)) .connect(name, socket) .await - .map_err(Error::ClientUpgrade)?; + .map_err(UpgradeError::ClientUpgrade)?; let peer_id = extract_single_certificate(stream.get_ref().1)?.peer_id(); From 4a5c4da9e145e8919b4ba30a9688d6a467eb67c1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 30 Sep 2022 15:45:29 +1000 Subject: [PATCH 36/48] Add license headers --- transports/tls/src/certificate.rs | 2 +- transports/tls/src/lib.rs | 2 +- transports/tls/src/upgrade.rs | 20 ++++++++++++++++++++ transports/tls/src/verifier.rs | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 6ab31a349d1..f08caf3e30c 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright 2022 Protocol Labs. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index e06da2ea556..545e831694f 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright 2022 Protocol Labs. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), diff --git a/transports/tls/src/upgrade.rs b/transports/tls/src/upgrade.rs index 335222e560d..4efb3471d7f 100644 --- a/transports/tls/src/upgrade.rs +++ b/transports/tls/src/upgrade.rs @@ -1,3 +1,23 @@ +// Copyright 2022 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + use crate::certificate; use crate::certificate::P2pCertificate; use futures::future::BoxFuture; diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 2815b92c9a3..d84df712947 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright 2022 Protocol Labs. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), From d078a5e8efe1ab7fa4d21bee0b87a32376d92b90 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 30 Sep 2022 16:01:18 +1000 Subject: [PATCH 37/48] Fix compile error in test --- transports/tls/tests/smoke.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/tls/tests/smoke.rs b/transports/tls/tests/smoke.rs index 8ac9696c5f8..0e4b3441b0d 100644 --- a/transports/tls/tests/smoke.rs +++ b/transports/tls/tests/smoke.rs @@ -61,7 +61,7 @@ fn make_swarm() -> Swarm { let transport = MemoryTransport::default() .upgrade(Version::V1) - .authenticate(libp2p_tls::upgrade::Config::new(&identity).unwrap()) + .authenticate(libp2p_tls::Config::new(&identity).unwrap()) .multiplex(libp2p::yamux::YamuxConfig::default()) .boxed(); From d362aefa748a6cd91123a9eb5cbc463d2c9c4039 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 09:48:24 +1100 Subject: [PATCH 38/48] Make it more explicit which protocol versions we support --- transports/tls/src/lib.rs | 4 ++-- transports/tls/src/verifier.rs | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 545e831694f..32bd86c50a9 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -61,7 +61,7 @@ pub fn make_client_config( let mut crypto = rustls::ClientConfig::builder() .with_cipher_suites(TLS13_CIPHERSUITES) .with_safe_default_kx_groups() - .with_protocol_versions(&[&rustls::version::TLS13]) + .with_protocol_versions(verifier::PROTOCOL_VERSIONS) .expect("Cipher suites and kx groups are configured; qed") .with_custom_certificate_verifier(Arc::new( verifier::Libp2pCertificateVerifier::with_remote_peer_id(remote_peer_id), @@ -82,7 +82,7 @@ pub fn make_server_config( let mut crypto = rustls::ServerConfig::builder() .with_cipher_suites(TLS13_CIPHERSUITES) .with_safe_default_kx_groups() - .with_protocol_versions(&[&rustls::version::TLS13]) + .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) diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index d84df712947..4b089b9e873 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -29,9 +29,17 @@ use rustls::{ client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, internal::msgs::handshake::DigitallySignedStruct, server::{ClientCertVerified, ClientCertVerifier}, - Certificate, DistinguishedNames, SignatureScheme, + Certificate, DistinguishedNames, SignatureScheme, SupportedProtocolVersion, }; +/// The protocol versions supported by this verifier. +/// +/// The spec says: +/// +/// > The libp2p handshake uses TLS 1.3 (and higher). +/// > Endpoints MUST NOT negotiate lower TLS versions. +pub static PROTOCOL_VERSIONS: &'static [&SupportedProtocolVersion] = &[&rustls::version::TLS13]; + /// 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`. @@ -112,11 +120,7 @@ impl ServerCertVerifier for Libp2pCertificateVerifier { _cert: &Certificate, _dss: &DigitallySignedStruct, ) -> Result { - // The libp2p handshake uses TLS 1.3 (and higher). - // Endpoints MUST NOT negotiate lower TLS versions. - Err(rustls::Error::PeerIncompatibleError( - "Only TLS 1.3 certificates are supported".to_string(), - )) + unreachable!("`PROTOCOL_VERSIONS` only allows TLS 1.3") } fn verify_tls13_signature( @@ -166,11 +170,7 @@ impl ClientCertVerifier for Libp2pCertificateVerifier { _cert: &Certificate, _dss: &DigitallySignedStruct, ) -> Result { - // The libp2p handshake uses TLS 1.3 (and higher). - // Endpoints MUST NOT negotiate lower TLS versions. - Err(rustls::Error::PeerIncompatibleError( - "Only TLS 1.3 certificates are supported".to_string(), - )) + unreachable!("`PROTOCOL_VERSIONS` only allows TLS 1.3") } fn verify_tls13_signature( From 10b4c828efd1a83ff2d5a2a387dc569a461d5b48 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 09:51:26 +1100 Subject: [PATCH 39/48] Move config of supported cipher suits into `verifier` module --- transports/tls/src/lib.rs | 14 ++------------ transports/tls/src/verifier.rs | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 32bd86c50a9..a46168e2af6 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -39,16 +39,6 @@ pub use futures_rustls::TlsStream; pub use upgrade::Config; pub use upgrade::UpgradeError; -/// A list of the TLS 1.3 cipher suites supported by rustls. -// By default rustls creates client/server configs with both -// TLS 1.3 __and__ 1.2 cipher suites. But we don't need 1.2. -static TLS13_CIPHERSUITES: &[SupportedCipherSuite] = &[ - // TLS1.3 suites - TLS13_CHACHA20_POLY1305_SHA256, - TLS13_AES_256_GCM_SHA384, - TLS13_AES_128_GCM_SHA256, -]; - const P2P_ALPN: [u8; 6] = *b"libp2p"; /// Create a TLS client configuration for libp2p. @@ -59,7 +49,7 @@ pub fn make_client_config( let (certificate, private_key) = certificate::generate(keypair)?; let mut crypto = rustls::ClientConfig::builder() - .with_cipher_suites(TLS13_CIPHERSUITES) + .with_cipher_suites(verifier::CIPHERSUITES) .with_safe_default_kx_groups() .with_protocol_versions(verifier::PROTOCOL_VERSIONS) .expect("Cipher suites and kx groups are configured; qed") @@ -80,7 +70,7 @@ pub fn make_server_config( let (certificate, private_key) = certificate::generate(keypair)?; let mut crypto = rustls::ServerConfig::builder() - .with_cipher_suites(TLS13_CIPHERSUITES) + .with_cipher_suites(verifier::CIPHERSUITES) .with_safe_default_kx_groups() .with_protocol_versions(verifier::PROTOCOL_VERSIONS) .expect("Cipher suites and kx groups are configured; qed") diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 4b089b9e873..6c8058d300a 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -26,10 +26,14 @@ use crate::certificate; use libp2p_core::PeerId; use rustls::{ + cipher_suite::{ + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, + }, client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, internal::msgs::handshake::DigitallySignedStruct, server::{ClientCertVerified, ClientCertVerifier}, - Certificate, DistinguishedNames, SignatureScheme, SupportedProtocolVersion, + Certificate, DistinguishedNames, SignatureScheme, SupportedCipherSuite, + SupportedProtocolVersion, }; /// The protocol versions supported by this verifier. @@ -40,6 +44,16 @@ use rustls::{ /// > Endpoints MUST NOT negotiate lower TLS versions. pub static PROTOCOL_VERSIONS: &'static [&SupportedProtocolVersion] = &[&rustls::version::TLS13]; +/// A list of the TLS 1.3 cipher suites supported by rustls. +// By default rustls creates client/server configs with both +// TLS 1.3 __and__ 1.2 cipher suites. But we don't need 1.2. +pub static CIPHERSUITES: &[SupportedCipherSuite] = &[ + // TLS1.3 suites + TLS13_CHACHA20_POLY1305_SHA256, + TLS13_AES_256_GCM_SHA384, + TLS13_AES_128_GCM_SHA256, +]; + /// 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`. From e4e7bfda7e9f11979566ad86e0b5e9c8241ba1bb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 09:52:23 +1100 Subject: [PATCH 40/48] Fix clippy lints --- transports/tls/src/lib.rs | 6 ------ transports/tls/src/verifier.rs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index a46168e2af6..951b45fa630 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -27,12 +27,6 @@ mod upgrade; mod verifier; use libp2p_core::{identity::Keypair, PeerId}; -use rustls::{ - cipher_suite::{ - TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, - }, - SupportedCipherSuite, -}; use std::sync::Arc; pub use futures_rustls::TlsStream; diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 6c8058d300a..2eb701c580c 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -42,7 +42,7 @@ use rustls::{ /// /// > The libp2p handshake uses TLS 1.3 (and higher). /// > Endpoints MUST NOT negotiate lower TLS versions. -pub static PROTOCOL_VERSIONS: &'static [&SupportedProtocolVersion] = &[&rustls::version::TLS13]; +pub static PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&rustls::version::TLS13]; /// A list of the TLS 1.3 cipher suites supported by rustls. // By default rustls creates client/server configs with both From 2ac833e8edadfd574ac4d2580e0e8eb6f78e0c8d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 09:54:39 +1100 Subject: [PATCH 41/48] Remove unnecessary `pub` --- transports/tls/src/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 2eb701c580c..4dab1b3912f 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -82,7 +82,7 @@ impl Libp2pCertificateVerifier { /// in `verify_tls12_signature` and `verify_tls13_signature` calls. /// /// This should be in priority order, with the most preferred first. - pub fn verification_schemes() -> Vec { + fn verification_schemes() -> Vec { vec![ // TODO SignatureScheme::ECDSA_NISTP521_SHA512 is not supported by `ring` yet SignatureScheme::ECDSA_NISTP384_SHA384, From 6e0312601a07e40516d7d036a4144dfea0cd52d3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 09:57:02 +1100 Subject: [PATCH 42/48] Clarify why we need `dangerous_configuration` --- transports/tls/Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index e6d73800862..71fee37d93e 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -11,12 +11,17 @@ futures-rustls = "0.22.2" libp2p-core = { version = "0.37.0", path = "../../core" } rcgen = "0.9.2" ring = "0.16.20" -rustls = { version = "0.20.2", default-features = false, features = ["dangerous_configuration"] } # Exposed dependency! Bump major version on any change. thiserror = "1.0.36" webpki = "0.22.0" x509-parser = "0.14.0" yasna = "0.5.0" +# Exposed dependencies. Breaking changes to these are breaking changes to us. +[dependencies.rustls] +version = "0.20.2" +default-features = false +features = ["dangerous_configuration"] # Must enable this to allow for custom verification code. + [dev-dependencies] hex = "0.4.3" hex-literal = "0.3.4" From 1edc6f310e888e9e24ad30d3a82373109c904d5e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 09:58:20 +1100 Subject: [PATCH 43/48] Fix link to signature scheme --- transports/tls/src/certificate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index f08caf3e30c..7fcfc39842a 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -351,7 +351,7 @@ impl P2pCertificate<'_> { /// Return the signature scheme corresponding to [`AlgorithmIdentifier`]s /// of `subject_pki` and `signature_algorithm` - /// according to ``. + /// according to . fn signature_scheme(&self) -> Result { // Certificates MUST use the NamedCurve encoding for elliptic curve parameters. // Endpoints MUST abort the connection attempt if it is not used. From a3af0173c4c894175341469f12b467fd6a28e8e6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 6 Oct 2022 10:18:32 +1100 Subject: [PATCH 44/48] Adapt to new `swarm` interface --- transports/tls/tests/smoke.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transports/tls/tests/smoke.rs b/transports/tls/tests/smoke.rs index 0e4b3441b0d..1def8717e01 100644 --- a/transports/tls/tests/smoke.rs +++ b/transports/tls/tests/smoke.rs @@ -1,6 +1,6 @@ use futures::{future, StreamExt}; use libp2p::multiaddr::Protocol; -use libp2p::swarm::{DummyBehaviour, KeepAlive, SwarmEvent}; +use libp2p::swarm::{keep_alive, SwarmEvent}; use libp2p::Swarm; use libp2p_core::transport::MemoryTransport; use libp2p_core::upgrade::Version; @@ -56,7 +56,7 @@ async fn can_establish_connection() { assert_eq!(&outbound_peer_id, swarm1.local_peer_id()); } -fn make_swarm() -> Swarm { +fn make_swarm() -> Swarm { let identity = libp2p::identity::Keypair::generate_ed25519(); let transport = MemoryTransport::default() @@ -67,7 +67,7 @@ fn make_swarm() -> Swarm { Swarm::new( transport, - DummyBehaviour::with_keep_alive(KeepAlive::Yes), + keep_alive::Behaviour, identity.public().to_peer_id(), ) } From f11c54e7396c2d53895c7be47752f7e5c2d23e56 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 7 Oct 2022 17:40:53 +1100 Subject: [PATCH 45/48] Restore original copy-right --- transports/tls/src/certificate.rs | 2 +- transports/tls/src/lib.rs | 1 + transports/tls/src/verifier.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 7fcfc39842a..43d9c8468ca 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Protocol Labs. +// Copyright 2021 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 951b45fa630..3dca24c801a 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -1,3 +1,4 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. // Copyright 2022 Protocol Labs. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index 4dab1b3912f..c1bbc283e5c 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Protocol Labs. +// Copyright 2021 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), From 3fe63862ab70733503c88bdcc37ab74451010fd5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 19 Oct 2022 09:43:16 +1100 Subject: [PATCH 46/48] Set `libp2p-tls` to `0.1.0-alpha` --- Cargo.toml | 2 +- transports/tls/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3349ecb0e68..152585b4970 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,7 +132,7 @@ libp2p-dns = { version = "0.37.0", path = "transports/dns", optional = true } libp2p-mdns = { version = "0.41.0", path = "protocols/mdns", optional = true } libp2p-tcp = { version = "0.37.0", path = "transports/tcp", optional = true } libp2p-websocket = { version = "0.39.0", path = "transports/websocket", optional = true } -libp2p-tls = { version = "0.1.0", path = "transports/tls", optional = true } +libp2p-tls = { version = "0.1.0-alpha", path = "transports/tls", optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] libp2p-gossipsub = { version = "0.42.1", path = "protocols/gossipsub", optional = true } diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 71fee37d93e..edd2a90ccee 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-tls" -version = "0.1.0" +version = "0.1.0-alpha" edition = "2021" license = "MIT" exclude = ["src/test_assets"] From ed988101e5a171345bd6d431dc1b7e8b491b9e31 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 19 Oct 2022 10:21:04 +1100 Subject: [PATCH 47/48] Fix clippy error in latest rustls release --- transports/tls/Cargo.toml | 2 +- transports/tls/src/verifier.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index edd2a90ccee..1edcef01a52 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -18,7 +18,7 @@ yasna = "0.5.0" # Exposed dependencies. Breaking changes to these are breaking changes to us. [dependencies.rustls] -version = "0.20.2" +version = "0.20.7" default-features = false features = ["dangerous_configuration"] # Must enable this to allow for custom verification code. diff --git a/transports/tls/src/verifier.rs b/transports/tls/src/verifier.rs index c1bbc283e5c..05d38068414 100644 --- a/transports/tls/src/verifier.rs +++ b/transports/tls/src/verifier.rs @@ -143,7 +143,7 @@ impl ServerCertVerifier for Libp2pCertificateVerifier { cert: &Certificate, dss: &DigitallySignedStruct, ) -> Result { - verify_tls13_signature(cert, dss.scheme, message, dss.sig.0.as_ref()) + verify_tls13_signature(cert, dss.scheme, message, dss.signature()) } fn supported_verify_schemes(&self) -> Vec { @@ -193,7 +193,7 @@ impl ClientCertVerifier for Libp2pCertificateVerifier { cert: &Certificate, dss: &DigitallySignedStruct, ) -> Result { - verify_tls13_signature(cert, dss.scheme, message, dss.sig.0.as_ref()) + verify_tls13_signature(cert, dss.scheme, message, dss.signature()) } fn supported_verify_schemes(&self) -> Vec { From 27b58f7e3cd8a6dc898bf8839eb5d3dae0cb2b9b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 24 Oct 2022 10:34:42 +1100 Subject: [PATCH 48/48] Use `doc_auto_cfg` for libp2p-tls --- transports/tls/Cargo.toml | 7 +++++++ transports/tls/src/lib.rs | 2 ++ 2 files changed, 9 insertions(+) diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 1edcef01a52..ac8244aecc5 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -27,3 +27,10 @@ hex = "0.4.3" hex-literal = "0.3.4" libp2p = { path = "../..", features = ["yamux"], default-features = false } tokio = { version = "1.21.1", features = ["full"] } + +# Passing arguments to the docsrs builder in order to properly document cfg's. +# More information: https://docs.rs/about/builds#cross-compiling +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] +rustc-args = ["--cfg", "docsrs"] diff --git a/transports/tls/src/lib.rs b/transports/tls/src/lib.rs index 3dca24c801a..6bf22eea3f3 100644 --- a/transports/tls/src/lib.rs +++ b/transports/tls/src/lib.rs @@ -23,6 +23,8 @@ //! //! See . +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + pub mod certificate; mod upgrade; mod verifier;