@@ -24,7 +24,7 @@ pub struct CaRootsConfig {
2424 extra_roots : Vec < CertificateDer < ' static > > ,
2525}
2626
27- #[ derive( Debug , Clone ) ]
27+ #[ derive( derive_more :: Debug , Clone ) ]
2828enum Mode {
2929 /// Use a compiled-in copy of the root certificates trusted by Mozilla.
3030 ///
@@ -42,6 +42,12 @@ enum Mode {
4242 /// May only be used in tests or local development setups.
4343 #[ cfg( any( test, feature = "test-utils" ) ) ]
4444 InsecureSkipVerify ,
45+ /// Use a callback to create a [`ClientConfig`] used in all TLS requests.
46+ Custom {
47+ #[ debug( "Arc<dyn Fn>" ) ]
48+ make_client_config :
49+ Arc < dyn ' static + Send + Sync + Fn ( Arc < CryptoProvider > ) -> io:: Result < ClientConfig > > ,
50+ } ,
4551}
4652
4753impl Default for CaRootsConfig {
@@ -90,13 +96,52 @@ impl CaRootsConfig {
9096 }
9197
9298 /// Only trust the explicitly set root certificates.
93- pub fn custom ( roots : impl IntoIterator < Item = CertificateDer < ' static > > ) -> Self {
99+ pub fn custom_roots ( roots : impl IntoIterator < Item = CertificateDer < ' static > > ) -> Self {
94100 Self {
95101 mode : Mode :: ExtraRootsOnly ,
96102 extra_roots : roots. into_iter ( ) . collect ( ) ,
97103 }
98104 }
99105
106+ /// Creates a [`CaRootsConfig`] that uses a callback function to create a [`ClientConfig`].
107+ ///
108+ /// This is an advanced feature and you should only use this if none of the other constructor
109+ /// functions cover your needs. Wrongly implementing the callback may lead to insecure connections
110+ /// being accepted.
111+ ///
112+ /// The [`CryptoProvider`] passed to the callback should be used for all cryptographic operations.
113+ ///
114+ /// ## Example
115+ ///
116+ /// This example implements the behavior of [`Self::embedded`] via [`Self::custom`].
117+ ///
118+ /// ```rust
119+ /// # use std::sync::Arc;
120+ /// # use iroh_relay::tls::CaRootsConfig;
121+ /// let root_store = Arc::new(rustls::RootCertStore {
122+ /// roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
123+ /// });
124+ /// let ca_roots_config =
125+ /// CaRootsConfig::custom_client_config_builder(Arc::new(move |crypto_provider| {
126+ /// let client_config = rustls::ClientConfig::builder_with_provider(crypto_provider)
127+ /// .with_safe_default_protocol_versions()
128+ /// .expect("protocols supported by crypto provider")
129+ /// .with_root_certificates(root_store.clone())
130+ /// .with_no_client_auth();
131+ /// Ok(client_config)
132+ /// }));
133+ /// ```
134+ pub fn custom_client_config_builder (
135+ make_client_config : Arc <
136+ dyn ' static + Send + Sync + Fn ( Arc < CryptoProvider > ) -> io:: Result < ClientConfig > ,
137+ > ,
138+ ) -> Self {
139+ Self {
140+ mode : Mode :: Custom { make_client_config } ,
141+ extra_roots : Vec :: new ( ) ,
142+ }
143+ }
144+
100145 /// Add additional root certificates to the list of trusted certificates.
101146 pub fn with_extra_roots (
102147 mut self ,
@@ -106,49 +151,50 @@ impl CaRootsConfig {
106151 self
107152 }
108153
109- /// Builds a [`ServerCertVerifier`] from this config.
110- pub fn server_cert_verifier (
111- & self ,
112- crypto_provider : Arc < CryptoProvider > ,
113- ) -> io:: Result < Arc < dyn ServerCertVerifier > > {
114- Ok ( match self . mode {
154+ /// Build a [`ClientConfig`] from this config.
155+ pub fn client_config ( & self , crypto_provider : Arc < CryptoProvider > ) -> io:: Result < ClientConfig > {
156+ let verifier: Arc < dyn ServerCertVerifier > = match self . mode {
115157 #[ cfg( feature = "platform-verifier" ) ]
116158 Mode :: System => {
117159 #[ cfg( not( target_os = "android" ) ) ]
118160 let verifier = rustls_platform_verifier:: Verifier :: new_with_extra_roots (
119161 self . extra_roots . clone ( ) ,
120- crypto_provider,
162+ crypto_provider. clone ( ) ,
121163 ) ;
122164 #[ cfg( target_os = "android" ) ]
123- let verifier = rustls_platform_verifier:: Verifier :: new ( crypto_provider) ;
165+ let verifier = rustls_platform_verifier:: Verifier :: new ( crypto_provider. clone ( ) ) ;
124166 Arc :: new ( verifier. map_err ( io:: Error :: other) ?)
125167 }
126168 Mode :: EmbeddedWebPki => {
127169 let mut root_store = rustls:: RootCertStore {
128170 roots : webpki_roots:: TLS_SERVER_ROOTS . to_vec ( ) ,
129171 } ;
130172 root_store. add_parsable_certificates ( self . extra_roots . clone ( ) ) ;
131- WebPkiServerVerifier :: builder_with_provider ( Arc :: new ( root_store) , crypto_provider)
132- . build ( )
133- . map_err ( io:: Error :: other) ?
173+ WebPkiServerVerifier :: builder_with_provider (
174+ Arc :: new ( root_store) ,
175+ crypto_provider. clone ( ) ,
176+ )
177+ . build ( )
178+ . map_err ( io:: Error :: other) ?
134179 }
135180 Mode :: ExtraRootsOnly => {
136181 let mut root_store = rustls:: RootCertStore { roots : vec ! [ ] } ;
137182 root_store. add_parsable_certificates ( self . extra_roots . clone ( ) ) ;
138- WebPkiServerVerifier :: builder_with_provider ( Arc :: new ( root_store) , crypto_provider)
139- . build ( )
140- . map_err ( io:: Error :: other) ?
183+ WebPkiServerVerifier :: builder_with_provider (
184+ Arc :: new ( root_store) ,
185+ crypto_provider. clone ( ) ,
186+ )
187+ . build ( )
188+ . map_err ( io:: Error :: other) ?
141189 }
142190 #[ cfg( any( test, feature = "test-utils" ) ) ]
143- Mode :: InsecureSkipVerify => {
144- Arc :: new ( no_cert_verifier:: NoCertVerifier { crypto_provider } )
145- }
146- } )
147- }
148-
149- /// Build a [`ClientConfig`] from this config.
150- pub fn client_config ( & self , crypto_provider : Arc < CryptoProvider > ) -> io:: Result < ClientConfig > {
151- let verifier = self . server_cert_verifier ( crypto_provider. clone ( ) ) ?;
191+ Mode :: InsecureSkipVerify => Arc :: new ( no_cert_verifier:: NoCertVerifier {
192+ crypto_provider : crypto_provider. clone ( ) ,
193+ } ) ,
194+ Mode :: Custom {
195+ ref make_client_config,
196+ } => return make_client_config ( crypto_provider) ,
197+ } ;
152198 let config = ClientConfig :: builder_with_provider ( crypto_provider)
153199 . with_safe_default_protocol_versions ( )
154200 . expect ( "protocols supported by ring" )
0 commit comments