use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
use async_trait::async_trait;
use futures::{AsyncRead, AsyncWrite};
use rustls::{Certificate, Error as TLSError, ServerName};
use rustls_crate as rustls;
use std::{
io::{self, Error as IoError, Result as IoResult},
sync::Arc,
};
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "rustls", any(feature = "tokio", feature = "async-std"))))
)]
#[derive(Clone)]
#[non_exhaustive]
pub struct RustlsProvider {
config: Arc<async_rustls::rustls::ClientConfig>,
}
impl<S> CertifiedConn for async_rustls::client::TlsStream<S> {
fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
let (_, session) = self.get_ref();
Ok(session
.peer_certificates()
.and_then(|certs| certs.get(0).map(|c| Vec::from(c.as_ref()))))
}
}
pub struct RustlsConnector<S> {
connector: async_rustls::TlsConnector,
_phantom: std::marker::PhantomData<fn(S) -> S>,
}
#[async_trait]
impl<S> TlsConnector<S> for RustlsConnector<S>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
type Conn = async_rustls::client::TlsStream<S>;
async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
let name = sni_hostname
.try_into()
.map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
self.connector.connect(name, stream).await
}
}
impl<S> TlsProvider<S> for RustlsProvider
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
type Connector = RustlsConnector<S>;
type TlsStream = async_rustls::client::TlsStream<S>;
fn tls_connector(&self) -> Self::Connector {
let connector = async_rustls::TlsConnector::from(Arc::clone(&self.config));
RustlsConnector {
connector,
_phantom: std::marker::PhantomData,
}
}
}
impl RustlsProvider {
pub(crate) fn new() -> Self {
let config = async_rustls::rustls::ClientConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(std::sync::Arc::new(Verifier {}))
.with_no_client_auth();
RustlsProvider {
config: Arc::new(config),
}
}
}
impl Default for RustlsProvider {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
struct Verifier {}
impl rustls_crate::client::ServerCertVerifier for Verifier {
fn verify_server_cert(
&self,
end_entity: &Certificate,
_intermediates: &[Certificate],
_server_name: &ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, TLSError> {
let _cert = get_cert(end_entity)?;
Ok(rustls::client::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::Certificate,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::HandshakeSignatureValid, TLSError> {
let cert = get_cert(cert)?;
let scheme = convert_scheme(dss.scheme)?;
cert.check_signature(scheme, message, dss.signature())
.map(|_| rustls::client::HandshakeSignatureValid::assertion())
.map_err(|_| TLSError::InvalidCertificateSignature)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::Certificate,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::HandshakeSignatureValid, TLSError> {
let cert = get_cert(cert)?;
let scheme = convert_scheme(dss.scheme)?;
cert.check_tls13_signature(scheme, message, dss.signature())
.map(|_| rustls::client::HandshakeSignatureValid::assertion())
.map_err(|_| TLSError::InvalidCertificateSignature)
}
}
fn get_cert(c: &rustls::Certificate) -> Result<x509_signature::X509Certificate, TLSError> {
x509_signature::parse_certificate(c.as_ref()).map_err(|_| TLSError::InvalidCertificateSignature)
}
fn convert_scheme(
scheme: rustls::SignatureScheme,
) -> Result<x509_signature::SignatureScheme, TLSError> {
use rustls::SignatureScheme as R;
use x509_signature::SignatureScheme as X;
Ok(match scheme {
R::RSA_PKCS1_SHA256 => X::RSA_PKCS1_SHA256,
R::ECDSA_NISTP256_SHA256 => X::ECDSA_NISTP256_SHA256,
R::RSA_PKCS1_SHA384 => X::RSA_PKCS1_SHA384,
R::ECDSA_NISTP384_SHA384 => X::ECDSA_NISTP384_SHA384,
R::RSA_PKCS1_SHA512 => X::RSA_PKCS1_SHA512,
R::RSA_PSS_SHA256 => X::RSA_PSS_SHA256,
R::RSA_PSS_SHA384 => X::RSA_PSS_SHA384,
R::RSA_PSS_SHA512 => X::RSA_PSS_SHA512,
R::ED25519 => X::ED25519,
R::ED448 => X::ED448,
R::RSA_PKCS1_SHA1 | R::ECDSA_SHA1_Legacy | R::ECDSA_NISTP521_SHA512 => {
return Err(TLSError::PeerIncompatibleError(format!(
"Unsupported signature scheme {:?}",
scheme
)));
}
R::Unknown(_) => {
return Err(TLSError::PeerIncompatibleError(format!(
"Unrecognized signature scheme {:?}",
scheme
)))
}
})
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_cvt_scheme() {
use rustls::SignatureScheme as R;
use x509_signature::SignatureScheme as X;
macro_rules! check_cvt {
{ $id:ident } =>
{ assert_eq!(convert_scheme(R::$id).unwrap(), X::$id); }
}
check_cvt!(RSA_PKCS1_SHA256);
check_cvt!(RSA_PKCS1_SHA384);
check_cvt!(RSA_PKCS1_SHA512);
check_cvt!(ECDSA_NISTP256_SHA256);
check_cvt!(ECDSA_NISTP384_SHA384);
check_cvt!(RSA_PSS_SHA256);
check_cvt!(RSA_PSS_SHA384);
check_cvt!(RSA_PSS_SHA512);
check_cvt!(ED25519);
check_cvt!(ED448);
assert!(convert_scheme(R::RSA_PKCS1_SHA1).is_err());
assert!(convert_scheme(R::Unknown(0x1337)).is_err());
}
}