// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //! Definitions to build a [`Client`]. //! //! See [`ClientBuilder`]. use crate::{ cipher_suite::CipherSuite, client::Client, client_config::ClientConfig, extension::{ExtensionType, MlsExtension}, group::{ mls_rules::{DefaultMlsRules, MlsRules}, proposal::ProposalType, }, identity::CredentialType, identity::SigningIdentity, protocol_version::ProtocolVersion, psk::{ExternalPskId, PreSharedKey}, storage_provider::in_memory::{ InMemoryGroupStateStorage, InMemoryKeyPackageStorage, InMemoryPreSharedKeyStorage, }, tree_kem::{Capabilities, Lifetime}, Sealed, }; #[cfg(feature = "std")] use crate::time::MlsTime; use alloc::vec::Vec; #[cfg(feature = "sqlite")] use mls_rs_provider_sqlite::{ SqLiteDataStorageEngine, SqLiteDataStorageError, { connection_strategy::ConnectionStrategy, storage::{SqLiteGroupStateStorage, SqLiteKeyPackageStorage, SqLitePreSharedKeyStorage}, }, }; #[cfg(feature = "private_message")] pub use crate::group::padding::PaddingMode; /// Base client configuration type when instantiating `ClientBuilder` pub type BaseConfig = Config< InMemoryKeyPackageStorage, InMemoryPreSharedKeyStorage, InMemoryGroupStateStorage, Missing, DefaultMlsRules, Missing, >; /// Base client configuration type when instantiating `ClientBuilder` pub type BaseInMemoryConfig = Config< InMemoryKeyPackageStorage, InMemoryPreSharedKeyStorage, InMemoryGroupStateStorage, Missing, Missing, Missing, >; pub type EmptyConfig = Config; /// Base client configuration that is backed by SQLite storage. #[cfg(feature = "sqlite")] pub type BaseSqlConfig = Config< SqLiteKeyPackageStorage, SqLitePreSharedKeyStorage, SqLiteGroupStateStorage, Missing, DefaultMlsRules, Missing, >; /// Builder for [`Client`] /// /// This is returned by [`Client::builder`] and allows to tweak settings the `Client` will use. At a /// minimum, the builder must be told the [`CryptoProvider`] and [`IdentityProvider`] to use. Other /// settings have default values. This means that the following /// methods must be called before [`ClientBuilder::build`]: /// /// - To specify the [`CryptoProvider`]: [`ClientBuilder::crypto_provider`] /// - To specify the [`IdentityProvider`]: [`ClientBuilder::identity_provider`] /// /// # Example /// /// ``` /// use mls_rs::{ /// Client, /// identity::{SigningIdentity, basic::{BasicIdentityProvider, BasicCredential}}, /// CipherSuite, /// }; /// /// use mls_rs_crypto_openssl::OpensslCryptoProvider; /// /// // Replace by code to load the certificate and secret key /// let secret_key = b"never hard-code secrets".to_vec().into(); /// let public_key = b"test invalid public key".to_vec().into(); /// let basic_identity = BasicCredential::new(b"name".to_vec()); /// let signing_identity = SigningIdentity::new(basic_identity.into_credential(), public_key); /// /// /// let _client = Client::builder() /// .crypto_provider(OpensslCryptoProvider::default()) /// .identity_provider(BasicIdentityProvider::new()) /// .signing_identity(signing_identity, secret_key, CipherSuite::CURVE25519_AES128) /// .build(); /// ``` /// /// # Spelling out a `Client` type /// /// There are two main ways to spell out a `Client` type if needed (e.g. function return type). /// /// The first option uses `impl MlsConfig`: /// ``` /// use mls_rs::{ /// Client, /// client_builder::MlsConfig, /// identity::{SigningIdentity, basic::{BasicIdentityProvider, BasicCredential}}, /// CipherSuite, /// }; /// /// use mls_rs_crypto_openssl::OpensslCryptoProvider; /// /// fn make_client() -> Client { /// // Replace by code to load the certificate and secret key /// let secret_key = b"never hard-code secrets".to_vec().into(); /// let public_key = b"test invalid public key".to_vec().into(); /// let basic_identity = BasicCredential::new(b"name".to_vec()); /// let signing_identity = SigningIdentity::new(basic_identity.into_credential(), public_key); /// /// Client::builder() /// .crypto_provider(OpensslCryptoProvider::default()) /// .identity_provider(BasicIdentityProvider::new()) /// .signing_identity(signing_identity, secret_key, CipherSuite::CURVE25519_AES128) /// .build() /// } ///``` /// /// The second option is more verbose and consists in writing the full `Client` type: /// ``` /// use mls_rs::{ /// Client, /// client_builder::{BaseConfig, WithIdentityProvider, WithCryptoProvider}, /// identity::{SigningIdentity, basic::{BasicIdentityProvider, BasicCredential}}, /// CipherSuite, /// }; /// /// use mls_rs_crypto_openssl::OpensslCryptoProvider; /// /// type MlsClient = Client< /// WithIdentityProvider< /// BasicIdentityProvider, /// WithCryptoProvider, /// >, /// >; /// /// fn make_client_2() -> MlsClient { /// // Replace by code to load the certificate and secret key /// let secret_key = b"never hard-code secrets".to_vec().into(); /// let public_key = b"test invalid public key".to_vec().into(); /// let basic_identity = BasicCredential::new(b"name".to_vec()); /// let signing_identity = SigningIdentity::new(basic_identity.into_credential(), public_key); /// /// Client::builder() /// .crypto_provider(OpensslCryptoProvider::default()) /// .identity_provider(BasicIdentityProvider::new()) /// .signing_identity(signing_identity, secret_key, CipherSuite::CURVE25519_AES128) /// .build() /// } /// /// ``` #[derive(Debug)] pub struct ClientBuilder(C); impl Default for ClientBuilder { fn default() -> Self { Self::new() } } impl ClientBuilder { pub(crate) fn from_config(c: C) -> Self { Self(c) } } impl ClientBuilder { /// Create a new client builder with default in-memory providers pub fn new() -> Self { Self(Config(ConfigInner { settings: Default::default(), key_package_repo: Default::default(), psk_store: Default::default(), group_state_storage: Default::default(), identity_provider: Missing, mls_rules: DefaultMlsRules::new(), crypto_provider: Missing, signer: Default::default(), signing_identity: Default::default(), version: ProtocolVersion::MLS_10, })) } } impl ClientBuilder { pub fn new_empty() -> Self { Self(Config(ConfigInner { settings: Default::default(), key_package_repo: Missing, psk_store: Missing, group_state_storage: Missing, identity_provider: Missing, mls_rules: Missing, crypto_provider: Missing, signer: Default::default(), signing_identity: Default::default(), version: ProtocolVersion::MLS_10, })) } } #[cfg(feature = "sqlite")] impl ClientBuilder { /// Create a new client builder with SQLite storage providers. pub fn new_sqlite( storage: SqLiteDataStorageEngine, ) -> Result { Ok(Self(Config(ConfigInner { settings: Default::default(), key_package_repo: storage.key_package_storage()?, psk_store: storage.pre_shared_key_storage()?, group_state_storage: storage.group_state_storage()?, identity_provider: Missing, mls_rules: DefaultMlsRules::new(), crypto_provider: Missing, signer: Default::default(), signing_identity: Default::default(), version: ProtocolVersion::MLS_10, }))) } } impl ClientBuilder { /// Add an extension type to the list of extension types supported by the client. pub fn extension_type(self, type_: ExtensionType) -> ClientBuilder> { self.extension_types(Some(type_)) } /// Add multiple extension types to the list of extension types supported by the client. pub fn extension_types(self, types: I) -> ClientBuilder> where I: IntoIterator, { let mut c = self.0.into_config(); c.0.settings.extension_types.extend(types); ClientBuilder(c) } /// Add a custom proposal type to the list of proposals types supported by the client. pub fn custom_proposal_type(self, type_: ProposalType) -> ClientBuilder> { self.custom_proposal_types(Some(type_)) } /// Add multiple custom proposal types to the list of proposal types supported by the client. pub fn custom_proposal_types(self, types: I) -> ClientBuilder> where I: IntoIterator, { let mut c = self.0.into_config(); c.0.settings.custom_proposal_types.extend(types); ClientBuilder(c) } /// Add a protocol version to the list of protocol versions supported by the client. /// /// If no protocol version is explicitly added, the client will support all protocol versions /// supported by this crate. pub fn protocol_version(self, version: ProtocolVersion) -> ClientBuilder> { self.protocol_versions(Some(version)) } /// Add multiple protocol versions to the list of protocol versions supported by the client. /// /// If no protocol version is explicitly added, the client will support all protocol versions /// supported by this crate. pub fn protocol_versions(self, versions: I) -> ClientBuilder> where I: IntoIterator, { let mut c = self.0.into_config(); c.0.settings.protocol_versions.extend(versions); ClientBuilder(c) } /// Add a key package extension to the list of key package extensions supported by the client. pub fn key_package_extension( self, extension: T, ) -> Result>, ExtensionError> where T: MlsExtension, Self: Sized, { let mut c = self.0.into_config(); c.0.settings.key_package_extensions.set_from(extension)?; Ok(ClientBuilder(c)) } /// Add multiple key package extensions to the list of key package extensions supported by the /// client. pub fn key_package_extensions( self, extensions: ExtensionList, ) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.settings.key_package_extensions.append(extensions); ClientBuilder(c) } /// Add a leaf node extension to the list of leaf node extensions supported by the client. pub fn leaf_node_extension( self, extension: T, ) -> Result>, ExtensionError> where T: MlsExtension, Self: Sized, { let mut c = self.0.into_config(); c.0.settings.leaf_node_extensions.set_from(extension)?; Ok(ClientBuilder(c)) } /// Add multiple leaf node extensions to the list of leaf node extensions supported by the /// client. pub fn leaf_node_extensions( self, extensions: ExtensionList, ) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.settings.leaf_node_extensions.append(extensions); ClientBuilder(c) } /// Set the lifetime duration in seconds of key packages generated by the client. pub fn key_package_lifetime(self, duration_in_s: u64) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.settings.lifetime_in_s = duration_in_s; ClientBuilder(c) } /// Set the key package repository to be used by the client. /// /// By default, an in-memory repository is used. pub fn key_package_repo(self, key_package_repo: K) -> ClientBuilder> where K: KeyPackageStorage, { let Config(c) = self.0.into_config(); ClientBuilder(Config(ConfigInner { settings: c.settings, key_package_repo, psk_store: c.psk_store, group_state_storage: c.group_state_storage, identity_provider: c.identity_provider, mls_rules: c.mls_rules, crypto_provider: c.crypto_provider, signer: c.signer, signing_identity: c.signing_identity, version: c.version, })) } /// Set the PSK store to be used by the client. /// /// By default, an in-memory store is used. pub fn psk_store

(self, psk_store: P) -> ClientBuilder> where P: PreSharedKeyStorage, { let Config(c) = self.0.into_config(); ClientBuilder(Config(ConfigInner { settings: c.settings, key_package_repo: c.key_package_repo, psk_store, group_state_storage: c.group_state_storage, identity_provider: c.identity_provider, mls_rules: c.mls_rules, crypto_provider: c.crypto_provider, signer: c.signer, signing_identity: c.signing_identity, version: c.version, })) } /// Set the group state storage to be used by the client. /// /// By default, an in-memory storage is used. pub fn group_state_storage( self, group_state_storage: G, ) -> ClientBuilder> where G: GroupStateStorage, { let Config(c) = self.0.into_config(); ClientBuilder(Config(ConfigInner { settings: c.settings, key_package_repo: c.key_package_repo, psk_store: c.psk_store, group_state_storage, identity_provider: c.identity_provider, crypto_provider: c.crypto_provider, mls_rules: c.mls_rules, signer: c.signer, signing_identity: c.signing_identity, version: c.version, })) } /// Set the identity validator to be used by the client. pub fn identity_provider( self, identity_provider: I, ) -> ClientBuilder> where I: IdentityProvider, { let Config(c) = self.0.into_config(); ClientBuilder(Config(ConfigInner { settings: c.settings, key_package_repo: c.key_package_repo, psk_store: c.psk_store, group_state_storage: c.group_state_storage, identity_provider, mls_rules: c.mls_rules, crypto_provider: c.crypto_provider, signer: c.signer, signing_identity: c.signing_identity, version: c.version, })) } /// Set the crypto provider to be used by the client. pub fn crypto_provider( self, crypto_provider: Cp, ) -> ClientBuilder> where Cp: CryptoProvider, { let Config(c) = self.0.into_config(); ClientBuilder(Config(ConfigInner { settings: c.settings, key_package_repo: c.key_package_repo, psk_store: c.psk_store, group_state_storage: c.group_state_storage, identity_provider: c.identity_provider, mls_rules: c.mls_rules, crypto_provider, signer: c.signer, signing_identity: c.signing_identity, version: c.version, })) } /// Set the user-defined proposal rules to be used by the client. /// /// User-defined rules are used when sending and receiving commits before /// enforcing general MLS protocol rules. If the rule set returns an error when /// receiving a commit, the entire commit is considered invalid. If the /// rule set would return an error when sending a commit, individual proposals /// may be filtered out to compensate. pub fn mls_rules(self, mls_rules: Pr) -> ClientBuilder> where Pr: MlsRules, { let Config(c) = self.0.into_config(); ClientBuilder(Config(ConfigInner { settings: c.settings, key_package_repo: c.key_package_repo, psk_store: c.psk_store, group_state_storage: c.group_state_storage, identity_provider: c.identity_provider, mls_rules, crypto_provider: c.crypto_provider, signer: c.signer, signing_identity: c.signing_identity, version: c.version, })) } /// Set the protocol version used by the client. By default, the client uses version MLS 1.0 pub fn used_protocol_version( self, version: ProtocolVersion, ) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.version = version; ClientBuilder(c) } /// Set the signing identity used by the client as well as the matching signer and cipher suite. /// This must be called in order to create groups and key packages. pub fn signing_identity( self, signing_identity: SigningIdentity, signer: SignatureSecretKey, cipher_suite: CipherSuite, ) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.signer = Some(signer); c.0.signing_identity = Some((signing_identity, cipher_suite)); ClientBuilder(c) } /// Set the signer used by the client. This must be called in order to join groups. pub fn signer(self, signer: SignatureSecretKey) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.signer = Some(signer); ClientBuilder(c) } #[cfg(any(test, feature = "test_util"))] pub(crate) fn key_package_not_before( self, key_package_not_before: u64, ) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.settings.key_package_not_before = Some(key_package_not_before); ClientBuilder(c) } } impl ClientBuilder where C::KeyPackageRepository: KeyPackageStorage + Clone, C::PskStore: PreSharedKeyStorage + Clone, C::GroupStateStorage: GroupStateStorage + Clone, C::IdentityProvider: IdentityProvider + Clone, C::MlsRules: MlsRules + Clone, C::CryptoProvider: CryptoProvider + Clone, { pub(crate) fn build_config(self) -> IntoConfigOutput { let mut c = self.0.into_config(); if c.0.settings.protocol_versions.is_empty() { c.0.settings.protocol_versions = ProtocolVersion::all().collect(); } c } /// Build a client. /// /// See [`ClientBuilder`] documentation if the return type of this function needs to be spelled /// out. pub fn build(self) -> Client> { let mut c = self.build_config(); let version = c.0.version; let signer = c.0.signer.take(); let signing_identity = c.0.signing_identity.take(); Client::new(c, signer, signing_identity, version) } } impl> ClientBuilder { /// Add a PSK to the in-memory PSK store. pub fn psk( self, psk_id: ExternalPskId, psk: PreSharedKey, ) -> ClientBuilder> { let mut c = self.0.into_config(); c.0.psk_store.insert(psk_id, psk); ClientBuilder(c) } } /// Marker type for required `ClientBuilder` services that have not been specified yet. #[derive(Debug)] pub struct Missing; /// Change the key package repository used by a client configuration. /// /// See [`ClientBuilder::key_package_repo`]. pub type WithKeyPackageRepo = Config< K, ::PskStore, ::GroupStateStorage, ::IdentityProvider, ::MlsRules, ::CryptoProvider, >; /// Change the PSK store used by a client configuration. /// /// See [`ClientBuilder::psk_store`]. pub type WithPskStore = Config< ::KeyPackageRepository, P, ::GroupStateStorage, ::IdentityProvider, ::MlsRules, ::CryptoProvider, >; /// Change the group state storage used by a client configuration. /// /// See [`ClientBuilder::group_state_storage`]. pub type WithGroupStateStorage = Config< ::KeyPackageRepository, ::PskStore, G, ::IdentityProvider, ::MlsRules, ::CryptoProvider, >; /// Change the identity validator used by a client configuration. /// /// See [`ClientBuilder::identity_provider`]. pub type WithIdentityProvider = Config< ::KeyPackageRepository, ::PskStore, ::GroupStateStorage, I, ::MlsRules, ::CryptoProvider, >; /// Change the proposal rules used by a client configuration. /// /// See [`ClientBuilder::mls_rules`]. pub type WithMlsRules = Config< ::KeyPackageRepository, ::PskStore, ::GroupStateStorage, ::IdentityProvider, Pr, ::CryptoProvider, >; /// Change the crypto provider used by a client configuration. /// /// See [`ClientBuilder::crypto_provider`]. pub type WithCryptoProvider = Config< ::KeyPackageRepository, ::PskStore, ::GroupStateStorage, ::IdentityProvider, ::MlsRules, Cp, >; /// Helper alias for `Config`. pub type IntoConfigOutput = Config< ::KeyPackageRepository, ::PskStore, ::GroupStateStorage, ::IdentityProvider, ::MlsRules, ::CryptoProvider, >; /// Helper alias to make a `Config` from a `ClientConfig` pub type MakeConfig = Config< ::KeyPackageRepository, ::PskStore, ::GroupStateStorage, ::IdentityProvider, ::MlsRules, ::CryptoProvider, >; impl ClientConfig for ConfigInner where Kpr: KeyPackageStorage + Clone, Ps: PreSharedKeyStorage + Clone, Gss: GroupStateStorage + Clone, Ip: IdentityProvider + Clone, Pr: MlsRules + Clone, Cp: CryptoProvider + Clone, { type KeyPackageRepository = Kpr; type PskStore = Ps; type GroupStateStorage = Gss; type IdentityProvider = Ip; type MlsRules = Pr; type CryptoProvider = Cp; fn supported_extensions(&self) -> Vec { self.settings.extension_types.clone() } fn supported_protocol_versions(&self) -> Vec { self.settings.protocol_versions.clone() } fn key_package_repo(&self) -> Self::KeyPackageRepository { self.key_package_repo.clone() } fn mls_rules(&self) -> Self::MlsRules { self.mls_rules.clone() } fn secret_store(&self) -> Self::PskStore { self.psk_store.clone() } fn group_state_storage(&self) -> Self::GroupStateStorage { self.group_state_storage.clone() } fn identity_provider(&self) -> Self::IdentityProvider { self.identity_provider.clone() } fn crypto_provider(&self) -> Self::CryptoProvider { self.crypto_provider.clone() } fn key_package_extensions(&self) -> ExtensionList { self.settings.key_package_extensions.clone() } fn leaf_node_extensions(&self) -> ExtensionList { self.settings.leaf_node_extensions.clone() } fn lifetime(&self) -> Lifetime { #[cfg(feature = "std")] let now_timestamp = MlsTime::now().seconds_since_epoch(); #[cfg(not(feature = "std"))] let now_timestamp = 0; #[cfg(test)] let now_timestamp = self .settings .key_package_not_before .unwrap_or(now_timestamp); Lifetime { not_before: now_timestamp, not_after: now_timestamp + self.settings.lifetime_in_s, } } fn supported_custom_proposals(&self) -> Vec { self.settings.custom_proposal_types.clone() } } impl Sealed for Config {} impl MlsConfig for Config where Kpr: KeyPackageStorage + Clone, Ps: PreSharedKeyStorage + Clone, Gss: GroupStateStorage + Clone, Ip: IdentityProvider + Clone, Pr: MlsRules + Clone, Cp: CryptoProvider + Clone, { type Output = ConfigInner; fn get(&self) -> &Self::Output { &self.0 } } /// Helper trait to allow consuming crates to easily write a client type as `Client` /// /// It is not meant to be implemented by consuming crates. `T: MlsConfig` implies `T: ClientConfig`. pub trait MlsConfig: Clone + Send + Sync + Sealed { #[doc(hidden)] type Output: ClientConfig; #[doc(hidden)] fn get(&self) -> &Self::Output; } /// Blanket implementation so that `T: MlsConfig` implies `T: ClientConfig` impl ClientConfig for T { type KeyPackageRepository = ::KeyPackageRepository; type PskStore = ::PskStore; type GroupStateStorage = ::GroupStateStorage; type IdentityProvider = ::IdentityProvider; type MlsRules = ::MlsRules; type CryptoProvider = ::CryptoProvider; fn supported_extensions(&self) -> Vec { self.get().supported_extensions() } fn supported_custom_proposals(&self) -> Vec { self.get().supported_custom_proposals() } fn supported_protocol_versions(&self) -> Vec { self.get().supported_protocol_versions() } fn key_package_repo(&self) -> Self::KeyPackageRepository { self.get().key_package_repo() } fn mls_rules(&self) -> Self::MlsRules { self.get().mls_rules() } fn secret_store(&self) -> Self::PskStore { self.get().secret_store() } fn group_state_storage(&self) -> Self::GroupStateStorage { self.get().group_state_storage() } fn identity_provider(&self) -> Self::IdentityProvider { self.get().identity_provider() } fn crypto_provider(&self) -> Self::CryptoProvider { self.get().crypto_provider() } fn key_package_extensions(&self) -> ExtensionList { self.get().key_package_extensions() } fn leaf_node_extensions(&self) -> ExtensionList { self.get().leaf_node_extensions() } fn lifetime(&self) -> Lifetime { self.get().lifetime() } fn capabilities(&self) -> Capabilities { self.get().capabilities() } fn version_supported(&self, version: ProtocolVersion) -> bool { self.get().version_supported(version) } fn supported_credential_types(&self) -> Vec { self.get().supported_credential_types() } } #[derive(Clone, Debug)] pub(crate) struct Settings { pub(crate) extension_types: Vec, pub(crate) protocol_versions: Vec, pub(crate) custom_proposal_types: Vec, pub(crate) key_package_extensions: ExtensionList, pub(crate) leaf_node_extensions: ExtensionList, pub(crate) lifetime_in_s: u64, #[cfg(any(test, feature = "test_util"))] pub(crate) key_package_not_before: Option, } impl Default for Settings { fn default() -> Self { Self { extension_types: Default::default(), protocol_versions: Default::default(), key_package_extensions: Default::default(), leaf_node_extensions: Default::default(), lifetime_in_s: 365 * 24 * 3600, custom_proposal_types: Default::default(), #[cfg(any(test, feature = "test_util"))] key_package_not_before: None, } } } pub(crate) fn recreate_config( c: T, signer: Option, signing_identity: Option<(SigningIdentity, CipherSuite)>, version: ProtocolVersion, ) -> MakeConfig { Config(ConfigInner { settings: Settings { extension_types: c.supported_extensions(), protocol_versions: c.supported_protocol_versions(), custom_proposal_types: c.supported_custom_proposals(), key_package_extensions: c.key_package_extensions(), leaf_node_extensions: c.leaf_node_extensions(), lifetime_in_s: { let l = c.lifetime(); l.not_after - l.not_before }, #[cfg(any(test, feature = "test_util"))] key_package_not_before: None, }, key_package_repo: c.key_package_repo(), psk_store: c.secret_store(), group_state_storage: c.group_state_storage(), identity_provider: c.identity_provider(), mls_rules: c.mls_rules(), crypto_provider: c.crypto_provider(), signer, signing_identity, version, }) } /// Definitions meant to be private that are inaccessible outside this crate. They need to be marked /// `pub` because they appear in public definitions. mod private { use mls_rs_core::{ crypto::{CipherSuite, SignatureSecretKey}, identity::SigningIdentity, protocol_version::ProtocolVersion, }; use crate::client_builder::{IntoConfigOutput, Settings}; #[derive(Clone, Debug)] pub struct Config(pub(crate) ConfigInner); #[derive(Clone, Debug)] pub struct ConfigInner { pub(crate) settings: Settings, pub(crate) key_package_repo: Kpr, pub(crate) psk_store: Ps, pub(crate) group_state_storage: Gss, pub(crate) identity_provider: Ip, pub(crate) mls_rules: Pr, pub(crate) crypto_provider: Cp, pub(crate) signer: Option, pub(crate) signing_identity: Option<(SigningIdentity, CipherSuite)>, pub(crate) version: ProtocolVersion, } pub trait IntoConfig { type KeyPackageRepository; type PskStore; type GroupStateStorage; type IdentityProvider; type MlsRules; type CryptoProvider; fn into_config(self) -> IntoConfigOutput; } impl IntoConfig for Config { type KeyPackageRepository = Kpr; type PskStore = Ps; type GroupStateStorage = Gss; type IdentityProvider = Ip; type MlsRules = Pr; type CryptoProvider = Cp; fn into_config(self) -> Self { self } } } use mls_rs_core::{ crypto::{CryptoProvider, SignatureSecretKey}, extension::{ExtensionError, ExtensionList}, group::GroupStateStorage, identity::IdentityProvider, key_package::KeyPackageStorage, psk::PreSharedKeyStorage, }; use private::{Config, ConfigInner, IntoConfig}; #[cfg(test)] pub(crate) mod test_utils { use crate::{ client_builder::{BaseConfig, ClientBuilder, WithIdentityProvider}, crypto::test_utils::TestCryptoProvider, identity::{ basic::BasicIdentityProvider, test_utils::{get_test_signing_identity, BasicWithCustomProvider}, }, CipherSuite, }; use super::WithCryptoProvider; pub type TestClientConfig = WithIdentityProvider< BasicWithCustomProvider, WithCryptoProvider, >; pub type TestClientBuilder = ClientBuilder; impl TestClientBuilder { pub fn new_for_test() -> Self { ClientBuilder::new() .crypto_provider(TestCryptoProvider::new()) .identity_provider(BasicWithCustomProvider::new(BasicIdentityProvider::new())) } #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] pub async fn with_random_signing_identity( self, identity: &str, cipher_suite: CipherSuite, ) -> Self { let (signing_identity, signer) = get_test_signing_identity(cipher_suite, identity.as_bytes()).await; self.signing_identity(signing_identity, signer, cipher_suite) } } }