diff --git a/Cargo.toml b/Cargo.toml index 117ab4de4..b4b2c585f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,5 @@ members = [ ] resolver = "2" +[patch.crates-io] +rustls-pemfile = { git = "https://github.com/rustls/pemfile", rev = "c72614681cd0335463fb6c49ee10cd1c25912e5a" } diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index afd2081c9..66c00235a 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -27,7 +27,7 @@ codegen = ["dep:async-trait"] gzip = ["dep:flate2"] default = ["transport", "codegen", "prost"] prost = ["dep:prost"] -tls = ["dep:rustls-pemfile", "transport", "dep:tokio-rustls", "dep:rustls", "tokio/rt", "tokio/macros"] +tls = ["dep:rustls-pki-types", "dep:rustls-pemfile", "transport", "dep:tokio-rustls", "tokio/rt", "tokio/macros"] tls-roots = ["tls-roots-common", "dep:rustls-native-certs"] tls-roots-common = ["tls"] tls-webpki-roots = ["tls-roots-common", "dep:webpki-roots"] @@ -77,11 +77,11 @@ axum = {version = "0.6.9", default_features = false, optional = true} # rustls async-stream = { version = "0.3", optional = true } -rustls-pemfile = { version = "1.0", optional = true } -rustls-native-certs = { version = "0.6.3", optional = true } -tokio-rustls = { version = "0.24.1", optional = true } -rustls = { version = "0.21.7", optional = true } -webpki-roots = { version = "0.25.0", optional = true } +rustls-pki-types = { version = "0.2", optional = true } +rustls-pemfile = { version = "2.0.0-alpha.1", optional = true } +rustls-native-certs = { version = "0.7.0-alpha.1", optional = true } +tokio-rustls = { version = "0.25.0-alpha.1", optional = true } +webpki-roots = { version = "0.26.0-alpha.1", optional = true } # compression flate2 = {version = "1.0", optional = true} diff --git a/tonic/src/transport/service/tls.rs b/tonic/src/transport/service/tls.rs index f956132fb..e73df7d2b 100644 --- a/tonic/src/transport/service/tls.rs +++ b/tonic/src/transport/service/tls.rs @@ -1,17 +1,23 @@ -use super::io::BoxedIo; -use crate::transport::{ - server::{Connected, TlsStream}, - Certificate, Identity, +use std::{ + io::Cursor, + {fmt, sync::Arc}, }; -use std::{fmt, sync::Arc}; + +use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::{ - rustls::{ClientConfig, RootCertStore, ServerConfig, ServerName}, + rustls::{server::WebPkiClientVerifier, ClientConfig, RootCertStore, ServerConfig, ServerName}, TlsAcceptor as RustlsAcceptor, TlsConnector as RustlsConnector, }; +use super::io::BoxedIo; +use crate::transport::{ + server::{Connected, TlsStream}, + Certificate, Identity, +}; + /// h2 alpn in plain format for rustls. -const ALPN_H2: &str = "h2"; +const ALPN_H2: &[u8] = b"h2"; #[derive(Debug)] enum TlsError { @@ -36,31 +42,25 @@ impl TlsConnector { let mut roots = RootCertStore::empty(); #[cfg(feature = "tls-roots")] - roots.add_parsable_certificates(&rustls_native_certs::load_native_certs()?); + roots.add_parsable_certificates(rustls_native_certs::load_native_certs()?); #[cfg(feature = "tls-webpki-roots")] - roots.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - tokio_rustls::rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); + roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); if let Some(cert) = ca_cert { - rustls_keys::add_certs_from_pem(std::io::Cursor::new(cert.as_ref()), &mut roots)?; + add_certs_from_pem(&mut Cursor::new(cert), &mut roots)?; } let builder = builder.with_root_certificates(roots); let mut config = match identity { Some(identity) => { - let (client_cert, client_key) = rustls_keys::load_identity(identity)?; + let (client_cert, client_key) = load_identity(identity)?; builder.with_client_auth_cert(client_cert, client_key)? } None => builder.with_no_client_auth(), }; - config.alpn_protocols.push(ALPN_H2.as_bytes().to_vec()); + config.alpn_protocols.push(ALPN_H2.into()); Ok(Self { config: Arc::new(config), domain: Arc::new(domain.as_str().try_into()?), @@ -71,22 +71,16 @@ impl TlsConnector { where I: AsyncRead + AsyncWrite + Send + Unpin + 'static, { - let tls_io = { - let io = RustlsConnector::from(self.config.clone()) - .connect(self.domain.as_ref().to_owned(), io) - .await?; + let io = RustlsConnector::from(self.config.clone()) + .connect(self.domain.as_ref().to_owned(), io) + .await?; - let (_, session) = io.get_ref(); - - match session.alpn_protocol() { - Some(b) if b == b"h2" => (), - _ => return Err(TlsError::H2NotNegotiated.into()), - }; - - BoxedIo::new(io) - }; + let (_, session) = io.get_ref(); + if session.alpn_protocol() != Some(ALPN_H2) { + return Err(TlsError::H2NotNegotiated)?; + } - Ok(tls_io) + Ok(BoxedIo::new(io)) } } @@ -109,28 +103,25 @@ impl TlsAcceptor { ) -> Result { let builder = ServerConfig::builder().with_safe_defaults(); - let builder = match (client_ca_root, client_auth_optional) { - (None, _) => builder.with_no_client_auth(), - (Some(cert), true) => { - use tokio_rustls::rustls::server::AllowAnyAnonymousOrAuthenticatedClient; - let mut roots = RootCertStore::empty(); - rustls_keys::add_certs_from_pem(std::io::Cursor::new(cert.as_ref()), &mut roots)?; - builder.with_client_cert_verifier( - AllowAnyAnonymousOrAuthenticatedClient::new(roots).boxed(), - ) - } - (Some(cert), false) => { - use tokio_rustls::rustls::server::AllowAnyAuthenticatedClient; + let builder = match client_ca_root { + None => builder.with_no_client_auth(), + Some(cert) => { let mut roots = RootCertStore::empty(); - rustls_keys::add_certs_from_pem(std::io::Cursor::new(cert.as_ref()), &mut roots)?; - builder.with_client_cert_verifier(AllowAnyAuthenticatedClient::new(roots).boxed()) + add_certs_from_pem(&mut Cursor::new(cert), &mut roots)?; + let verifier = if client_auth_optional { + WebPkiClientVerifier::builder(roots.into()).allow_unauthenticated() + } else { + WebPkiClientVerifier::builder(roots.into()) + } + .build()?; + builder.with_client_cert_verifier(verifier) } }; - let (cert, key) = rustls_keys::load_identity(identity)?; + let (cert, key) = load_identity(identity)?; let mut config = builder.with_single_cert(cert, key)?; - config.alpn_protocols.push(ALPN_H2.as_bytes().to_vec()); + config.alpn_protocols.push(ALPN_H2.into()); Ok(Self { inner: Arc::new(config), }) @@ -166,137 +157,30 @@ impl fmt::Display for TlsError { impl std::error::Error for TlsError {} -mod rustls_keys { - use std::io::Cursor; - - use tokio_rustls::rustls::{Certificate, PrivateKey, RootCertStore}; - - use crate::transport::service::tls::TlsError; - use crate::transport::Identity; - - pub(super) fn load_rustls_private_key( - mut cursor: std::io::Cursor<&[u8]>, - ) -> Result { - while let Ok(Some(item)) = rustls_pemfile::read_one(&mut cursor) { - match item { - rustls_pemfile::Item::RSAKey(key) - | rustls_pemfile::Item::PKCS8Key(key) - | rustls_pemfile::Item::ECKey(key) => return Ok(PrivateKey(key)), - _ => continue, - } - } - - // Otherwise we have a Private Key parsing problem - Err(Box::new(TlsError::PrivateKeyParseError)) - } - - pub(crate) fn load_identity( - identity: Identity, - ) -> Result<(Vec, PrivateKey), crate::Error> { - let cert = { - let mut cert = std::io::Cursor::new(identity.cert.as_ref()); - match rustls_pemfile::certs(&mut cert) { - Ok(certs) => certs.into_iter().map(Certificate).collect(), - Err(_) => return Err(Box::new(TlsError::CertificateParseError)), - } - }; - - let key = { - let key = std::io::Cursor::new(identity.key.as_ref()); - match load_rustls_private_key(key) { - Ok(key) => key, - Err(e) => { - return Err(e); - } - } - }; +fn load_identity( + identity: Identity, +) -> Result<(Vec>, PrivateKeyDer<'static>), crate::Error> { + let cert = rustls_pemfile::certs(&mut Cursor::new(identity.cert)) + .collect::, _>>() + .map_err(|_| TlsError::CertificateParseError)?; - Ok((cert, key)) - } + let key = match rustls_pemfile::private_key(&mut Cursor::new(identity.key)) { + Ok(Some(key)) => key, + _ => return Err(TlsError::PrivateKeyParseError)?, + }; - pub(crate) fn add_certs_from_pem( - mut certs: Cursor<&[u8]>, - roots: &mut RootCertStore, - ) -> Result<(), crate::Error> { - let (_, ignored) = roots.add_parsable_certificates(&rustls_pemfile::certs(&mut certs)?); - match ignored == 0 { - true => Ok(()), - false => Err(Box::new(TlsError::CertificateParseError)), - } - } + Ok((cert, key)) } -#[cfg(test)] -mod tests { - use std::io::Cursor; - - // generated by: openssl ecparam -keygen -name 'prime256v1' - const SIMPLE_EC_KEY: &str = r#"-----BEGIN EC PARAMETERS----- -BggqhkjOPQMBBw== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MHcCAQEEICIDyh40kMVWGDAYr1gXnMfeMeO3zXYigOaWrg5SNB+zoAoGCCqGSM49 -AwEHoUQDQgAEacJyVg299dkPTzUaMbOmACUfF67yp+ZrDhXVjn/5WxBAgjcmFBHg -Tw8dfwpMzaJPXX5lWYzP276fcmbRO25CXw== ------END EC PRIVATE KEY-----"#; - - // generated by: openssl genpkey -algorithm rsa - const SIMPLE_PKCS8_KEY: &str = r#"-----BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKHkX1YIvqOIAllD -5fKcIxu2kYjIxxAAQrOBRTloGZUKdPFQY1RANB4t/LEaI5/NJ6NK4915pTn35QAQ -zHJl+X4rNFMgVt+o/nY40PgrQxyyv5A0/URp+iS8Yn3GKt3q6p4zguiO9uNXhiiD -b+VKIFRDm4bHR2yM7pNJ0kMdoattAgMBAAECgYAMpw6UaMaNfVnBpD7agT11MwWY -zShRpdOQt++zFuG49kJBgejhcssf+LQhG0vhd2U7q+S3MISrTSaGpMl1v2aKR/nV -G7X4Bb6X8vrVSMrfze2loT0aNri9jKDZkD/muy6+9JkhRa03NOdhDdseokmcqF3L -xsU4BUOOFYb23ycoeQJBANOGxbZu/3BqsPJMQmXWo1CXuaviZ83lTczPtrz9mJVl -Zs/KmGnJ8I2Azu/dlYXsHRvbIbqA93l1M3GnsWl5IxsCQQDD7hKvOY6qzUNyj+R4 -vul/3xaqjiTj59f3jN7Fh6+9AY+WfvEkWfyUUAXY74z43wBgtORfMXnZnjFO96tJ -sswXAkBDYDtb19E/cox4MTg5DfwpMJrwmAYufCqi4Uq4uiI++/SanVKc57jaqbvA -hZkZ9lJzTAJbULcDFgTT3/FPwkkfAkEAqbSDMIzdGuox2n/x9/f8jcpweogmQdUl -xgCZUGSnfkFk2ojXW5Ip6Viqx+0toL6fOCRWjnFvRmPz958kGPCqPwJBAID4y7XV -peOO6Yadu0YbSmFNluRebia6410p5jR21LhG1ty2h22xVhlBWjOC+TyDuKwhmiYT -ed50S3LR1PWt4zE= ------END PRIVATE KEY-----"#; - - // generated by: openssl genrsa - const SIMPLE_RSA_KEY: &str = r#"-----BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAoEILGds1/RGBHT7jM4R+EL24sQ6Bsn14GgTHc7WoZ7lainEH -H/n+DtHCYUXYyJnN5AMIi3pkigCP1hdXXBQga3zs3lXoi/mAMkT6vjuqQ7Xg5/95 -ABx5Ztyy25mZNaXm77glyAzSscKHxWYooXVJYG4C3SGuBJJ1zVjxen6Rkzse5Lpr -yZOUUeqeV3M6KbJ/dkR37HFQVwmlctQukFnb4kozFBQDDnkXi9jT/PH00g6JpW3z -YMzdMq2RMadJ0dzYv62OtdtqmQpVz0dRu/yODV4DkhrWwgPRj2uY4DnYthzILESB -x41gxHj+jqo6NW+C+0fr6uh2CXtD0p+ZVANtBQIDAQABAoIBAE7IaOCrLV1dr5WL -BvKancbpHdSdBxGSMOrJkqvFkCZ9ro8EhbYolcb/Q4nCZpInWlpPS3IWFzroj811 -6BJyKoXtAh1DKnE1lNohowrGFiv3S7uBkiCF3wC8Wokud20yQ9dxNdGkzCdrNIfM -cwj8ubfYHTxMhFnnDlaG9R98/V/dFy0FLxL37eMP/heMbcwKKm9P/G2FqvuCn8a4 -FoPbAfvaR64IGCybjoiTjUD7xMHIV4Gr5K07br2TzG2zVlFTacoqXyGBbVVy+ibt -QMh0sn+rMkAy+cFse+yCYZeAFa4FzwGz43sdFviU7uvLG7yXpvZ+uDACFzxlxUVg -v57r1cECgYEA1MMJEe6IunDUyuzRaFNTfQX16QcAv/xLN/1TtVB3HUX5p2bIZKDr -XEl0NCVOrCoz5RsYqbtGmp8B4Yxl3DeX+WeWeD9/f2ZTVGWyBx1N6dZ5hRsyfzG/ -xVBUqYxkChjXQ20cNtf8u7JKdnVjOJen9M92nXhFRTwgH83Id4gPp70CgYEAwNN8 -lvVJnd05ekyf1qIKOSyKiSGnGa5288PpqsjYMZisXy12y4n8cK2pX5Z5PICHeJVu -K99WdTtO7Q4ghCXRB1jR5pTd4/3/3089SQyDnWz9jlA3pGWcSLDTB1dBJXpMQ6yG -cR2dX5hPDNIdKsc+9Bl/OF5PScvGVUYv4SLF6ukCgYAVhh2WyNDgO6XrWYXdzgA2 -N7Im/uReh8F8So57W0aRmZCmFMnVFEp7LZsp41RQKnzRgqo+EYoU/l0MWk27t4wS -WR5pz9KwKsPnV9poydgl/eKRSq0THQ9PgM7v0BoWw2iTk6g1DCivPFw4G6wL/5uo -MozHZXFsjaaaUREktokO6QKBgC3Dg7RILtqaoIOYH+9OseJz4cU+CWyc7XpZKuHv -nO/YbkCAh8syyojrjmEzUz66umwx+t3KubhFBSxZx/nVB9EYkWiKOEdeBxY2tjLa -F3qLXXojK7GGtBrEbLE3UizU47jD/3xlLO59NXWzgFygwR4p1vnH2EWJaV7fs4lZ -OWPRAoGAL0nX0vZ0N9qPETiQan1uHjKYuuFiSP+cwRXVSUYIQM9qDRlKG9zjugwO -az+B6uiR4TrgbwG+faCQwcGk9B8QbcoIb8IigwrWe3XpVaEtcsqFORX0r+tJNDoY -I0O2DOQVPKSK2N5AZzXY4IkybWTV4Yxc7rdXEO3dOOpHGKbpwFQ= ------END RSA PRIVATE KEY-----"#; - - #[test] - fn test_parse_ec_key() { - for (n, key) in [SIMPLE_EC_KEY, SIMPLE_PKCS8_KEY, SIMPLE_RSA_KEY] - .iter() - .enumerate() - { - let c = Cursor::new(key.as_bytes()); - let key = super::rustls_keys::load_rustls_private_key(c); - - assert!(key.is_ok(), "at the {}-th case", n); - } +fn add_certs_from_pem( + mut certs: &mut dyn std::io::BufRead, + roots: &mut RootCertStore, +) -> Result<(), crate::Error> { + for cert in rustls_pemfile::certs(&mut certs).collect::, _>>()? { + roots + .add(cert) + .map_err(|_| TlsError::CertificateParseError)?; } + + Ok(()) }