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