1 // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // Copyright by contributors to this project.
3 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
4 
5 //! Definitions to build an [`ExternalClient`].
6 //!
7 //! See [`ExternalClientBuilder`].
8 
9 use crate::{
10     crypto::SignaturePublicKey,
11     extension::ExtensionType,
12     external_client::{ExternalClient, ExternalClientConfig},
13     group::{
14         mls_rules::{DefaultMlsRules, MlsRules},
15         proposal::ProposalType,
16     },
17     identity::CredentialType,
18     protocol_version::ProtocolVersion,
19     tree_kem::Capabilities,
20     CryptoProvider, Sealed,
21 };
22 use std::{
23     collections::HashMap,
24     fmt::{self, Debug},
25 };
26 
27 /// Base client configuration type when instantiating `ExternalClientBuilder`
28 pub type ExternalBaseConfig = Config<Missing, DefaultMlsRules, Missing>;
29 
30 /// Builder for [`ExternalClient`]
31 ///
32 /// This is returned by [`ExternalClient::builder`] and allows to tweak settings the
33 /// `ExternalClient` will use. At a minimum, the builder must be told the [`CryptoProvider`]
34 /// and [`IdentityProvider`] to use. Other settings have default values. This
35 /// means that the following methods must be called before [`ExternalClientBuilder::build`]:
36 ///
37 /// - To specify the [`CryptoProvider`]: [`ExternalClientBuilder::crypto_provider`]
38 /// - To specify the [`IdentityProvider`]: [`ExternalClientBuilder::identity_provider`]
39 ///
40 /// # Example
41 ///
42 /// ```
43 /// use mls_rs::{
44 ///     external_client::ExternalClient,
45 ///     identity::basic::BasicIdentityProvider,
46 /// };
47 ///
48 /// use mls_rs_crypto_openssl::OpensslCryptoProvider;
49 ///
50 /// let _client = ExternalClient::builder()
51 ///     .crypto_provider(OpensslCryptoProvider::default())
52 ///     .identity_provider(BasicIdentityProvider::new())
53 ///     .build();
54 /// ```
55 ///
56 /// # Spelling out an `ExternalClient` type
57 ///
58 /// There are two main ways to spell out an `ExternalClient` type if needed (e.g. function return type).
59 ///
60 /// The first option uses `impl MlsConfig`:
61 /// ```
62 /// use mls_rs::{
63 ///     external_client::{ExternalClient, builder::MlsConfig},
64 ///     identity::basic::BasicIdentityProvider,
65 /// };
66 ///
67 /// use mls_rs_crypto_openssl::OpensslCryptoProvider;
68 ///
69 /// fn make_client() -> ExternalClient<impl MlsConfig> {
70 ///     ExternalClient::builder()
71 ///         .crypto_provider(OpensslCryptoProvider::default())
72 ///         .identity_provider(BasicIdentityProvider::new())
73 ///         .build()
74 /// }
75 ///```
76 ///
77 /// The second option is more verbose and consists in writing the full `ExternalClient` type:
78 /// ```
79 /// use mls_rs::{
80 ///     external_client::{ExternalClient, builder::{ExternalBaseConfig, WithIdentityProvider, WithCryptoProvider}},
81 ///     identity::basic::BasicIdentityProvider,
82 /// };
83 ///
84 /// use mls_rs_crypto_openssl::OpensslCryptoProvider;
85 ///
86 /// type MlsClient = ExternalClient<WithIdentityProvider<
87 ///     BasicIdentityProvider,
88 ///     WithCryptoProvider<OpensslCryptoProvider, ExternalBaseConfig>,
89 /// >>;
90 ///
91 /// fn make_client_2() -> MlsClient {
92 ///     ExternalClient::builder()
93 ///         .crypto_provider(OpensslCryptoProvider::new())
94 ///         .identity_provider(BasicIdentityProvider::new())
95 ///         .build()
96 /// }
97 ///
98 /// ```
99 #[derive(Debug)]
100 pub struct ExternalClientBuilder<C>(C);
101 
102 impl Default for ExternalClientBuilder<ExternalBaseConfig> {
default() -> Self103     fn default() -> Self {
104         Self::new()
105     }
106 }
107 
108 impl ExternalClientBuilder<ExternalBaseConfig> {
new() -> Self109     pub fn new() -> Self {
110         Self(Config(ConfigInner {
111             settings: Default::default(),
112             identity_provider: Missing,
113             mls_rules: DefaultMlsRules::new(),
114             crypto_provider: Missing,
115             signing_data: None,
116         }))
117     }
118 }
119 
120 impl<C: IntoConfig> ExternalClientBuilder<C> {
121     /// Add an extension type to the list of extension types supported by the client.
extension_type( self, type_: ExtensionType, ) -> ExternalClientBuilder<IntoConfigOutput<C>>122     pub fn extension_type(
123         self,
124         type_: ExtensionType,
125     ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
126         self.extension_types(Some(type_))
127     }
128 
129     /// Add multiple extension types to the list of extension types supported by the client.
extension_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>> where I: IntoIterator<Item = ExtensionType>,130     pub fn extension_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
131     where
132         I: IntoIterator<Item = ExtensionType>,
133     {
134         let mut c = self.0.into_config();
135         c.0.settings.extension_types.extend(types);
136         ExternalClientBuilder(c)
137     }
138 
139     /// Add a custom proposal type to the list of proposals types supported by the client.
custom_proposal_type( self, type_: ProposalType, ) -> ExternalClientBuilder<IntoConfigOutput<C>>140     pub fn custom_proposal_type(
141         self,
142         type_: ProposalType,
143     ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
144         self.custom_proposal_types(Some(type_))
145     }
146 
147     /// Add multiple custom proposal types to the list of proposal types supported by the client.
custom_proposal_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>> where I: IntoIterator<Item = ProposalType>,148     pub fn custom_proposal_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
149     where
150         I: IntoIterator<Item = ProposalType>,
151     {
152         let mut c = self.0.into_config();
153         c.0.settings.custom_proposal_types.extend(types);
154         ExternalClientBuilder(c)
155     }
156 
157     /// Add a protocol version to the list of protocol versions supported by the client.
158     ///
159     /// If no protocol version is explicitly added, the client will support all protocol versions
160     /// supported by this crate.
protocol_version( self, version: ProtocolVersion, ) -> ExternalClientBuilder<IntoConfigOutput<C>>161     pub fn protocol_version(
162         self,
163         version: ProtocolVersion,
164     ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
165         self.protocol_versions(Some(version))
166     }
167 
168     /// Add multiple protocol versions to the list of protocol versions supported by the client.
169     ///
170     /// If no protocol version is explicitly added, the client will support all protocol versions
171     /// supported by this crate.
protocol_versions<I>(self, versions: I) -> ExternalClientBuilder<IntoConfigOutput<C>> where I: IntoIterator<Item = ProtocolVersion>,172     pub fn protocol_versions<I>(self, versions: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
173     where
174         I: IntoIterator<Item = ProtocolVersion>,
175     {
176         let mut c = self.0.into_config();
177         c.0.settings.protocol_versions.extend(versions);
178         ExternalClientBuilder(c)
179     }
180 
181     /// Add an external signing key to be used by the client.
external_signing_key( self, id: Vec<u8>, key: SignaturePublicKey, ) -> ExternalClientBuilder<IntoConfigOutput<C>>182     pub fn external_signing_key(
183         self,
184         id: Vec<u8>,
185         key: SignaturePublicKey,
186     ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
187         let mut c = self.0.into_config();
188         c.0.settings.external_signing_keys.insert(id, key);
189         ExternalClientBuilder(c)
190     }
191 
192     /// Specify the number of epochs before the current one to keep.
193     ///
194     /// By default, all epochs are kept.
max_epoch_jitter(self, max_jitter: u64) -> ExternalClientBuilder<IntoConfigOutput<C>>195     pub fn max_epoch_jitter(self, max_jitter: u64) -> ExternalClientBuilder<IntoConfigOutput<C>> {
196         let mut c = self.0.into_config();
197         c.0.settings.max_epoch_jitter = Some(max_jitter);
198         ExternalClientBuilder(c)
199     }
200 
201     /// Specify whether processed proposals should be cached by the external group. In case they
202     /// are not cached by the group, they should be cached externally and inserted using
203     /// `ExternalGroup::insert_proposal` before processing the next commit.
cache_proposals( self, cache_proposals: bool, ) -> ExternalClientBuilder<IntoConfigOutput<C>>204     pub fn cache_proposals(
205         self,
206         cache_proposals: bool,
207     ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
208         let mut c = self.0.into_config();
209         c.0.settings.cache_proposals = cache_proposals;
210         ExternalClientBuilder(c)
211     }
212 
213     /// Set the identity validator to be used by the client.
identity_provider<I>( self, identity_provider: I, ) -> ExternalClientBuilder<WithIdentityProvider<I, C>> where I: IdentityProvider,214     pub fn identity_provider<I>(
215         self,
216         identity_provider: I,
217     ) -> ExternalClientBuilder<WithIdentityProvider<I, C>>
218     where
219         I: IdentityProvider,
220     {
221         let Config(c) = self.0.into_config();
222         ExternalClientBuilder(Config(ConfigInner {
223             settings: c.settings,
224             identity_provider,
225             mls_rules: c.mls_rules,
226             crypto_provider: c.crypto_provider,
227             signing_data: c.signing_data,
228         }))
229     }
230 
231     /// Set the crypto provider to be used by the client.
232     ///
233     // TODO add a comment once we have a default provider
crypto_provider<Cp>( self, crypto_provider: Cp, ) -> ExternalClientBuilder<WithCryptoProvider<Cp, C>> where Cp: CryptoProvider,234     pub fn crypto_provider<Cp>(
235         self,
236         crypto_provider: Cp,
237     ) -> ExternalClientBuilder<WithCryptoProvider<Cp, C>>
238     where
239         Cp: CryptoProvider,
240     {
241         let Config(c) = self.0.into_config();
242         ExternalClientBuilder(Config(ConfigInner {
243             settings: c.settings,
244             identity_provider: c.identity_provider,
245             mls_rules: c.mls_rules,
246             crypto_provider,
247             signing_data: c.signing_data,
248         }))
249     }
250 
251     /// Set the user-defined proposal rules to be used by the client.
252     ///
253     /// User-defined rules are used when sending and receiving commits before
254     /// enforcing general MLS protocol rules. If the rule set returns an error when
255     /// receiving a commit, the entire commit is considered invalid. If the
256     /// rule set would return an error when sending a commit, individual proposals
257     /// may be filtered out to compensate.
mls_rules<Pr>(self, mls_rules: Pr) -> ExternalClientBuilder<WithMlsRules<Pr, C>> where Pr: MlsRules,258     pub fn mls_rules<Pr>(self, mls_rules: Pr) -> ExternalClientBuilder<WithMlsRules<Pr, C>>
259     where
260         Pr: MlsRules,
261     {
262         let Config(c) = self.0.into_config();
263         ExternalClientBuilder(Config(ConfigInner {
264             settings: c.settings,
265             identity_provider: c.identity_provider,
266             mls_rules,
267             crypto_provider: c.crypto_provider,
268             signing_data: c.signing_data,
269         }))
270     }
271 
272     /// Set the signature secret key used by the client to send external proposals.
signer( self, signer: SignatureSecretKey, signing_identity: SigningIdentity, ) -> ExternalClientBuilder<IntoConfigOutput<C>>273     pub fn signer(
274         self,
275         signer: SignatureSecretKey,
276         signing_identity: SigningIdentity,
277     ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
278         let mut c = self.0.into_config();
279         c.0.signing_data = Some((signer, signing_identity));
280         ExternalClientBuilder(c)
281     }
282 }
283 
284 impl<C: IntoConfig> ExternalClientBuilder<C>
285 where
286     C::IdentityProvider: IdentityProvider + Clone,
287     C::MlsRules: MlsRules + Clone,
288     C::CryptoProvider: CryptoProvider + Clone,
289 {
build_config(self) -> IntoConfigOutput<C>290     pub(crate) fn build_config(self) -> IntoConfigOutput<C> {
291         let mut c = self.0.into_config();
292 
293         if c.0.settings.protocol_versions.is_empty() {
294             c.0.settings.protocol_versions = ProtocolVersion::all().collect();
295         }
296 
297         c
298     }
299 
300     /// Build an external client.
301     ///
302     /// See [`ExternalClientBuilder`] documentation if the return type of this function needs to be
303     /// spelled out.
build(self) -> ExternalClient<IntoConfigOutput<C>>304     pub fn build(self) -> ExternalClient<IntoConfigOutput<C>> {
305         let mut c = self.build_config();
306         let signing_data = c.0.signing_data.take();
307         ExternalClient::new(c, signing_data)
308     }
309 }
310 
311 /// Marker type for required `ExternalClientBuilder` services that have not been specified yet.
312 #[derive(Debug)]
313 pub struct Missing;
314 
315 /// Change the identity validator used by a client configuration.
316 ///
317 /// See [`ExternalClientBuilder::identity_provider`].
318 pub type WithIdentityProvider<I, C> =
319     Config<I, <C as IntoConfig>::MlsRules, <C as IntoConfig>::CryptoProvider>;
320 
321 /// Change the proposal filter used by a client configuration.
322 ///
323 /// See [`ExternalClientBuilder::mls_rules`].
324 pub type WithMlsRules<Pr, C> =
325     Config<<C as IntoConfig>::IdentityProvider, Pr, <C as IntoConfig>::CryptoProvider>;
326 
327 /// Change the crypto provider used by a client configuration.
328 ///
329 /// See [`ExternalClientBuilder::crypto_provider`].
330 pub type WithCryptoProvider<Cp, C> =
331     Config<<C as IntoConfig>::IdentityProvider, <C as IntoConfig>::MlsRules, Cp>;
332 
333 /// Helper alias for `Config`.
334 pub type IntoConfigOutput<C> = Config<
335     <C as IntoConfig>::IdentityProvider,
336     <C as IntoConfig>::MlsRules,
337     <C as IntoConfig>::CryptoProvider,
338 >;
339 
340 impl<Ip, Pr, Cp> ExternalClientConfig for ConfigInner<Ip, Pr, Cp>
341 where
342     Ip: IdentityProvider + Clone,
343     Pr: MlsRules + Clone,
344     Cp: CryptoProvider + Clone,
345 {
346     type IdentityProvider = Ip;
347     type MlsRules = Pr;
348     type CryptoProvider = Cp;
349 
supported_extensions(&self) -> Vec<ExtensionType>350     fn supported_extensions(&self) -> Vec<ExtensionType> {
351         self.settings.extension_types.clone()
352     }
353 
supported_protocol_versions(&self) -> Vec<ProtocolVersion>354     fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
355         self.settings.protocol_versions.clone()
356     }
357 
identity_provider(&self) -> Self::IdentityProvider358     fn identity_provider(&self) -> Self::IdentityProvider {
359         self.identity_provider.clone()
360     }
361 
crypto_provider(&self) -> Self::CryptoProvider362     fn crypto_provider(&self) -> Self::CryptoProvider {
363         self.crypto_provider.clone()
364     }
365 
external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey>366     fn external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey> {
367         self.settings
368             .external_signing_keys
369             .get(external_key_id)
370             .cloned()
371     }
372 
mls_rules(&self) -> Self::MlsRules373     fn mls_rules(&self) -> Self::MlsRules {
374         self.mls_rules.clone()
375     }
376 
max_epoch_jitter(&self) -> Option<u64>377     fn max_epoch_jitter(&self) -> Option<u64> {
378         self.settings.max_epoch_jitter
379     }
380 
cache_proposals(&self) -> bool381     fn cache_proposals(&self) -> bool {
382         self.settings.cache_proposals
383     }
384 
supported_custom_proposals(&self) -> Vec<ProposalType>385     fn supported_custom_proposals(&self) -> Vec<ProposalType> {
386         self.settings.custom_proposal_types.clone()
387     }
388 }
389 
390 impl<Ip, Mpf, Cp> Sealed for Config<Ip, Mpf, Cp> {}
391 
392 impl<Ip, Pr, Cp> MlsConfig for Config<Ip, Pr, Cp>
393 where
394     Ip: IdentityProvider + Clone,
395     Pr: MlsRules + Clone,
396     Cp: CryptoProvider + Clone,
397 {
398     type Output = ConfigInner<Ip, Pr, Cp>;
399 
get(&self) -> &Self::Output400     fn get(&self) -> &Self::Output {
401         &self.0
402     }
403 }
404 
405 /// Helper trait to allow consuming crates to easily write an external client type as
406 /// `ExternalClient<impl MlsConfig>`
407 ///
408 /// It is not meant to be implemented by consuming crates. `T: MlsConfig` implies
409 /// `T: ExternalClientConfig`.
410 pub trait MlsConfig: Send + Sync + Clone + Sealed {
411     #[doc(hidden)]
412     type Output: ExternalClientConfig;
413 
414     #[doc(hidden)]
get(&self) -> &Self::Output415     fn get(&self) -> &Self::Output;
416 }
417 
418 /// Blanket implementation so that `T: MlsConfig` implies `T: ExternalClientConfig`
419 impl<T: MlsConfig> ExternalClientConfig for T {
420     type IdentityProvider = <T::Output as ExternalClientConfig>::IdentityProvider;
421     type MlsRules = <T::Output as ExternalClientConfig>::MlsRules;
422     type CryptoProvider = <T::Output as ExternalClientConfig>::CryptoProvider;
423 
supported_extensions(&self) -> Vec<ExtensionType>424     fn supported_extensions(&self) -> Vec<ExtensionType> {
425         self.get().supported_extensions()
426     }
427 
supported_protocol_versions(&self) -> Vec<ProtocolVersion>428     fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
429         self.get().supported_protocol_versions()
430     }
431 
supported_custom_proposals(&self) -> Vec<ProposalType>432     fn supported_custom_proposals(&self) -> Vec<ProposalType> {
433         self.get().supported_custom_proposals()
434     }
435 
identity_provider(&self) -> Self::IdentityProvider436     fn identity_provider(&self) -> Self::IdentityProvider {
437         self.get().identity_provider()
438     }
439 
crypto_provider(&self) -> Self::CryptoProvider440     fn crypto_provider(&self) -> Self::CryptoProvider {
441         self.get().crypto_provider()
442     }
443 
external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey>444     fn external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey> {
445         self.get().external_signing_key(external_key_id)
446     }
447 
mls_rules(&self) -> Self::MlsRules448     fn mls_rules(&self) -> Self::MlsRules {
449         self.get().mls_rules()
450     }
451 
cache_proposals(&self) -> bool452     fn cache_proposals(&self) -> bool {
453         self.get().cache_proposals()
454     }
455 
max_epoch_jitter(&self) -> Option<u64>456     fn max_epoch_jitter(&self) -> Option<u64> {
457         self.get().max_epoch_jitter()
458     }
459 
capabilities(&self) -> Capabilities460     fn capabilities(&self) -> Capabilities {
461         self.get().capabilities()
462     }
463 
version_supported(&self, version: ProtocolVersion) -> bool464     fn version_supported(&self, version: ProtocolVersion) -> bool {
465         self.get().version_supported(version)
466     }
467 
supported_credentials(&self) -> Vec<CredentialType>468     fn supported_credentials(&self) -> Vec<CredentialType> {
469         self.get().supported_credentials()
470     }
471 }
472 
473 #[derive(Clone)]
474 pub(crate) struct Settings {
475     pub(crate) extension_types: Vec<ExtensionType>,
476     pub(crate) custom_proposal_types: Vec<ProposalType>,
477     pub(crate) protocol_versions: Vec<ProtocolVersion>,
478     pub(crate) external_signing_keys: HashMap<Vec<u8>, SignaturePublicKey>,
479     pub(crate) max_epoch_jitter: Option<u64>,
480     pub(crate) cache_proposals: bool,
481 }
482 
483 impl Debug for Settings {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result484     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485         f.debug_struct("Settings")
486             .field("extension_types", &self.extension_types)
487             .field("custom_proposal_types", &self.custom_proposal_types)
488             .field("protocol_versions", &self.protocol_versions)
489             .field(
490                 "external_signing_keys",
491                 &mls_rs_core::debug::pretty_with(|f| {
492                     f.debug_map()
493                         .entries(
494                             self.external_signing_keys
495                                 .iter()
496                                 .map(|(k, v)| (mls_rs_core::debug::pretty_bytes(k), v)),
497                         )
498                         .finish()
499                 }),
500             )
501             .field("max_epoch_jitter", &self.max_epoch_jitter)
502             .field("cache_proposals", &self.cache_proposals)
503             .finish()
504     }
505 }
506 
507 impl Default for Settings {
default() -> Self508     fn default() -> Self {
509         Self {
510             cache_proposals: true,
511             extension_types: vec![],
512             protocol_versions: vec![],
513             external_signing_keys: Default::default(),
514             max_epoch_jitter: None,
515             custom_proposal_types: vec![],
516         }
517     }
518 }
519 
520 /// Definitions meant to be private that are inaccessible outside this crate. They need to be marked
521 /// `pub` because they appear in public definitions.
522 mod private {
523     use mls_rs_core::{crypto::SignatureSecretKey, identity::SigningIdentity};
524 
525     use super::{IntoConfigOutput, Settings};
526 
527     #[derive(Clone, Debug)]
528     pub struct Config<Ip, Pr, Cp>(pub(crate) ConfigInner<Ip, Pr, Cp>);
529 
530     #[derive(Clone, Debug)]
531     pub struct ConfigInner<Ip, Mpf, Cp> {
532         pub(crate) settings: Settings,
533         pub(crate) identity_provider: Ip,
534         pub(crate) mls_rules: Mpf,
535         pub(crate) crypto_provider: Cp,
536         pub(crate) signing_data: Option<(SignatureSecretKey, SigningIdentity)>,
537     }
538 
539     pub trait IntoConfig {
540         type IdentityProvider;
541         type MlsRules;
542         type CryptoProvider;
543 
into_config(self) -> IntoConfigOutput<Self>544         fn into_config(self) -> IntoConfigOutput<Self>;
545     }
546 
547     impl<Ip, Pr, Cp> IntoConfig for Config<Ip, Pr, Cp> {
548         type IdentityProvider = Ip;
549         type MlsRules = Pr;
550         type CryptoProvider = Cp;
551 
into_config(self) -> Self552         fn into_config(self) -> Self {
553             self
554         }
555     }
556 }
557 
558 use mls_rs_core::{
559     crypto::SignatureSecretKey,
560     identity::{IdentityProvider, SigningIdentity},
561 };
562 use private::{Config, ConfigInner, IntoConfig};
563 
564 #[cfg(test)]
565 pub(crate) mod test_utils {
566     use crate::{
567         cipher_suite::CipherSuite, crypto::test_utils::TestCryptoProvider,
568         identity::basic::BasicIdentityProvider,
569     };
570 
571     use super::{
572         ExternalBaseConfig, ExternalClientBuilder, WithCryptoProvider, WithIdentityProvider,
573     };
574 
575     pub type TestExternalClientConfig = WithIdentityProvider<
576         BasicIdentityProvider,
577         WithCryptoProvider<TestCryptoProvider, ExternalBaseConfig>,
578     >;
579 
580     pub type TestExternalClientBuilder = ExternalClientBuilder<TestExternalClientConfig>;
581 
582     impl TestExternalClientBuilder {
new_for_test() -> Self583         pub fn new_for_test() -> Self {
584             ExternalClientBuilder::new()
585                 .crypto_provider(TestCryptoProvider::default())
586                 .identity_provider(BasicIdentityProvider::new())
587         }
588 
new_for_test_disabling_cipher_suite(cipher_suite: CipherSuite) -> Self589         pub fn new_for_test_disabling_cipher_suite(cipher_suite: CipherSuite) -> Self {
590             let crypto_provider = TestCryptoProvider::with_enabled_cipher_suites(
591                 TestCryptoProvider::all_supported_cipher_suites()
592                     .into_iter()
593                     .filter(|cs| cs != &cipher_suite)
594                     .collect(),
595             );
596 
597             ExternalClientBuilder::new()
598                 .crypto_provider(crypto_provider)
599                 .identity_provider(BasicIdentityProvider::new())
600         }
601     }
602 }
603