1use std::sync::Arc;
2
3use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
4use rustls::crypto::{CryptoProvider, WebPkiSupportedAlgorithms};
5use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
6use rustls::server::ParsedCertificate;
7use rustls::{ClientConfig, DigitallySignedStruct, Error as TlsError, RootCertStore, SignatureScheme};
8
9use crate::client::Result;
10use crate::Error;
11
12#[derive(Debug)]
14pub struct TlsOptions {
15 identity: Option<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>,
16 ca_certs: RootCertStore,
17 hostname_verification: bool,
18}
19
20impl Clone for TlsOptions {
21 fn clone(&self) -> Self {
22 Self {
23 identity: self.identity.as_ref().map(|id| (id.0.clone(), id.1.clone_key())),
24 ca_certs: self.ca_certs.clone(),
25 hostname_verification: self.hostname_verification,
26 }
27 }
28}
29
30impl Default for TlsOptions {
31 fn default() -> Self {
33 let mut options = Self::no_ca();
34 options.ca_certs.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
35 options
36 }
37}
38
39#[derive(Debug)]
45struct TlsServerCertVerifier {
46 roots: RootCertStore,
47 supported: WebPkiSupportedAlgorithms,
48 hostname_verification: bool,
49}
50
51impl TlsServerCertVerifier {
52 fn new(roots: RootCertStore, hostname_verification: bool) -> Self {
53 Self {
54 roots,
55 supported: CryptoProvider::get_default().unwrap().signature_verification_algorithms,
56 hostname_verification,
57 }
58 }
59}
60
61impl ServerCertVerifier for TlsServerCertVerifier {
62 fn verify_server_cert(
63 &self,
64 end_entity: &CertificateDer<'_>,
65 intermediates: &[CertificateDer<'_>],
66 server_name: &ServerName<'_>,
67 _ocsp_response: &[u8],
68 now: UnixTime,
69 ) -> Result<ServerCertVerified, TlsError> {
70 let cert = ParsedCertificate::try_from(end_entity)?;
71 rustls::client::verify_server_cert_signed_by_trust_anchor(
72 &cert,
73 &self.roots,
74 intermediates,
75 now,
76 self.supported.all,
77 )?;
78
79 if self.hostname_verification {
80 rustls::client::verify_server_name(&cert, server_name)?;
81 }
82 Ok(ServerCertVerified::assertion())
83 }
84
85 fn verify_tls12_signature(
86 &self,
87 message: &[u8],
88 cert: &CertificateDer<'_>,
89 dss: &DigitallySignedStruct,
90 ) -> Result<HandshakeSignatureValid, TlsError> {
91 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.supported)
92 }
93
94 fn verify_tls13_signature(
95 &self,
96 message: &[u8],
97 cert: &CertificateDer<'_>,
98 dss: &DigitallySignedStruct,
99 ) -> Result<HandshakeSignatureValid, TlsError> {
100 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.supported)
101 }
102
103 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
104 self.supported.supported_schemes()
105 }
106}
107
108impl TlsOptions {
109 pub fn no_ca() -> Self {
112 Self { ca_certs: RootCertStore::empty(), identity: None, hostname_verification: true }
113 }
114
115 pub unsafe fn with_no_hostname_verification(mut self) -> Self {
120 self.hostname_verification = false;
121 self
122 }
123
124 pub fn with_pem_ca_certs(mut self, certs: &str) -> Result<Self> {
126 for r in rustls_pemfile::certs(&mut certs.as_bytes()) {
127 let cert = match r {
128 Ok(cert) => cert,
129 Err(err) => return Err(Error::with_other("fail to read cert", err)),
130 };
131 if let Err(err) = self.ca_certs.add(cert) {
132 return Err(Error::with_other("fail to add cert", err));
133 }
134 }
135 Ok(self)
136 }
137
138 pub fn with_pem_identity(mut self, cert: &str, key: &str) -> Result<Self> {
140 let r: std::result::Result<Vec<_>, _> = rustls_pemfile::certs(&mut cert.as_bytes()).collect();
141 let certs = match r {
142 Err(err) => return Err(Error::with_other("fail to read cert", err)),
143 Ok(certs) => certs,
144 };
145 let key = match rustls_pemfile::private_key(&mut key.as_bytes()) {
146 Err(err) => return Err(Error::with_other("fail to read client private key", err)),
147 Ok(None) => return Err(Error::BadArguments(&"no client private key")),
148 Ok(Some(key)) => key,
149 };
150 self.identity = Some((certs, key));
151 Ok(self)
152 }
153
154 fn take_roots(&mut self) -> RootCertStore {
155 std::mem::replace(&mut self.ca_certs, RootCertStore::empty())
156 }
157
158 pub(crate) fn into_config(mut self) -> Result<ClientConfig> {
159 let builder = ClientConfig::builder();
161 let verifier = TlsServerCertVerifier::new(self.take_roots(), self.hostname_verification);
162 let builder = builder.dangerous().with_custom_certificate_verifier(Arc::new(verifier));
163 if let Some((client_cert, client_key)) = self.identity.take() {
164 match builder.with_client_auth_cert(client_cert, client_key) {
165 Ok(config) => Ok(config),
166 Err(err) => Err(Error::with_other("invalid client private key", err)),
167 }
168 } else {
169 Ok(builder.with_no_client_auth())
170 }
171 }
172}