Skip to main content

mls_rs/
client_builder.rs

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 a [`Client`].
6//!
7//! See [`ClientBuilder`].
8
9use crate::{
10    cipher_suite::CipherSuite,
11    client::Client,
12    client_config::ClientConfig,
13    extension::ExtensionType,
14    group::{
15        mls_rules::{DefaultMlsRules, MlsRules},
16        proposal::ProposalType,
17    },
18    identity::CredentialType,
19    identity::SigningIdentity,
20    protocol_version::ProtocolVersion,
21    psk::{ExternalPskId, PreSharedKey},
22    storage_provider::in_memory::{
23        InMemoryGroupStateStorage, InMemoryKeyPackageStorage, InMemoryPreSharedKeyStorage,
24    },
25    time::MlsTime,
26    tree_kem::{Capabilities, Lifetime},
27    Sealed,
28};
29
30use alloc::vec::Vec;
31use core::{fmt::Debug, time::Duration};
32
33#[cfg(feature = "sqlite")]
34use mls_rs_provider_sqlite::{
35    SqLiteDataStorageEngine, SqLiteDataStorageError,
36    {
37        connection_strategy::ConnectionStrategy,
38        storage::{SqLiteGroupStateStorage, SqLiteKeyPackageStorage, SqLitePreSharedKeyStorage},
39    },
40};
41
42#[cfg(feature = "private_message")]
43pub use crate::group::padding::PaddingMode;
44
45/// Base client configuration type when instantiating `ClientBuilder`
46pub type BaseConfig = Config<
47    InMemoryKeyPackageStorage,
48    InMemoryPreSharedKeyStorage,
49    InMemoryGroupStateStorage,
50    Missing,
51    DefaultMlsRules,
52    Missing,
53>;
54
55/// Base client configuration type when instantiating `ClientBuilder`
56pub type BaseInMemoryConfig = Config<
57    InMemoryKeyPackageStorage,
58    InMemoryPreSharedKeyStorage,
59    InMemoryGroupStateStorage,
60    Missing,
61    Missing,
62    Missing,
63>;
64
65pub type EmptyConfig = Config<Missing, Missing, Missing, Missing, Missing, Missing>;
66
67/// Base client configuration that is backed by SQLite storage.
68#[cfg(feature = "sqlite")]
69pub type BaseSqlConfig = Config<
70    SqLiteKeyPackageStorage,
71    SqLitePreSharedKeyStorage,
72    SqLiteGroupStateStorage,
73    Missing,
74    DefaultMlsRules,
75    Missing,
76>;
77
78/// Builder for [`Client`]
79///
80/// This is returned by [`Client::builder`] and allows to tweak settings the `Client` will use. At a
81/// minimum, the builder must be told the [`CryptoProvider`] and [`IdentityProvider`] to use. Other
82/// settings have default values. This means that the following
83/// methods must be called before [`ClientBuilder::build`]:
84///
85/// - To specify the [`CryptoProvider`]: [`ClientBuilder::crypto_provider`]
86/// - To specify the [`IdentityProvider`]: [`ClientBuilder::identity_provider`]
87///
88/// # Example
89///
90/// ```
91/// use mls_rs::{
92///     Client,
93///     identity::{SigningIdentity, basic::{BasicIdentityProvider, BasicCredential}},
94///     CipherSuite,
95/// };
96///
97/// use mls_rs_crypto_openssl::OpensslCryptoProvider;
98///
99/// // Replace by code to load the certificate and secret key
100/// let secret_key = b"never hard-code secrets".to_vec().into();
101/// let public_key = b"test invalid public key".to_vec().into();
102/// let basic_identity = BasicCredential::new(b"name".to_vec());
103/// let signing_identity = SigningIdentity::new(basic_identity.into_credential(), public_key);
104///
105///
106/// let _client = Client::builder()
107///     .crypto_provider(OpensslCryptoProvider::default())
108///     .identity_provider(BasicIdentityProvider::new())
109///     .signing_identity(signing_identity, secret_key, CipherSuite::CURVE25519_AES128)
110///     .build();
111/// ```
112///
113/// # Spelling out a `Client` type
114///
115/// There are two main ways to spell out a `Client` type if needed (e.g. function return type).
116///
117/// The first option uses `impl MlsConfig`:
118/// ```
119/// use mls_rs::{
120///     Client,
121///     client_builder::MlsConfig,
122///     identity::{SigningIdentity, basic::{BasicIdentityProvider, BasicCredential}},
123///     CipherSuite,
124/// };
125///
126/// use mls_rs_crypto_openssl::OpensslCryptoProvider;
127///
128/// fn make_client() -> Client<impl MlsConfig> {
129///     // Replace by code to load the certificate and secret key
130///     let secret_key = b"never hard-code secrets".to_vec().into();
131///     let public_key = b"test invalid public key".to_vec().into();
132///     let basic_identity = BasicCredential::new(b"name".to_vec());
133///     let signing_identity = SigningIdentity::new(basic_identity.into_credential(), public_key);
134///
135///     Client::builder()
136///         .crypto_provider(OpensslCryptoProvider::default())
137///         .identity_provider(BasicIdentityProvider::new())
138///         .signing_identity(signing_identity, secret_key, CipherSuite::CURVE25519_AES128)
139///         .build()
140/// }
141///```
142///
143/// The second option is more verbose and consists in writing the full `Client` type:
144/// ```
145/// use mls_rs::{
146///     Client,
147///     client_builder::{BaseConfig, WithIdentityProvider, WithCryptoProvider},
148///     identity::{SigningIdentity, basic::{BasicIdentityProvider, BasicCredential}},
149///     CipherSuite,
150/// };
151///
152/// use mls_rs_crypto_openssl::OpensslCryptoProvider;
153///
154/// type MlsClient = Client<
155///     WithIdentityProvider<
156///         BasicIdentityProvider,
157///         WithCryptoProvider<OpensslCryptoProvider, BaseConfig>,
158///     >,
159/// >;
160///
161/// fn make_client_2() -> MlsClient {
162///     // Replace by code to load the certificate and secret key
163///     let secret_key = b"never hard-code secrets".to_vec().into();
164///     let public_key = b"test invalid public key".to_vec().into();
165///     let basic_identity = BasicCredential::new(b"name".to_vec());
166///     let signing_identity = SigningIdentity::new(basic_identity.into_credential(), public_key);
167///
168///     Client::builder()
169///         .crypto_provider(OpensslCryptoProvider::default())
170///         .identity_provider(BasicIdentityProvider::new())
171///         .signing_identity(signing_identity, secret_key, CipherSuite::CURVE25519_AES128)
172///         .build()
173/// }
174///
175/// ```
176pub struct ClientBuilder<C>(C);
177
178impl<C> Debug for ClientBuilder<C> {
179    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
180        f.debug_tuple("ClientBuilder").finish()
181    }
182}
183
184impl Default for ClientBuilder<BaseConfig> {
185    fn default() -> Self {
186        Self::new()
187    }
188}
189
190impl<C> ClientBuilder<C> {
191    pub(crate) fn from_config(c: C) -> Self {
192        Self(c)
193    }
194}
195
196impl ClientBuilder<BaseConfig> {
197    /// Create a new client builder with default in-memory providers
198    pub fn new() -> Self {
199        Self(Config(ConfigInner {
200            settings: Default::default(),
201            key_package_repo: Default::default(),
202            psk_store: Default::default(),
203            group_state_storage: Default::default(),
204            identity_provider: Missing,
205            mls_rules: DefaultMlsRules::new(),
206            crypto_provider: Missing,
207            signer: Default::default(),
208            signing_identity: Default::default(),
209            version: ProtocolVersion::MLS_10,
210        }))
211    }
212}
213
214impl ClientBuilder<EmptyConfig> {
215    pub fn new_empty() -> Self {
216        Self(Config(ConfigInner {
217            settings: Default::default(),
218            key_package_repo: Missing,
219            psk_store: Missing,
220            group_state_storage: Missing,
221            identity_provider: Missing,
222            mls_rules: Missing,
223            crypto_provider: Missing,
224            signer: Default::default(),
225            signing_identity: Default::default(),
226            version: ProtocolVersion::MLS_10,
227        }))
228    }
229}
230
231#[cfg(feature = "sqlite")]
232impl ClientBuilder<BaseSqlConfig> {
233    /// Create a new client builder with SQLite storage providers.
234    pub fn new_sqlite<CS: ConnectionStrategy>(
235        storage: SqLiteDataStorageEngine<CS>,
236    ) -> Result<Self, SqLiteDataStorageError> {
237        Ok(Self(Config(ConfigInner {
238            settings: Default::default(),
239            key_package_repo: storage.key_package_storage()?,
240            psk_store: storage.pre_shared_key_storage()?,
241            group_state_storage: storage.group_state_storage()?,
242            identity_provider: Missing,
243            mls_rules: DefaultMlsRules::new(),
244            crypto_provider: Missing,
245            signer: Default::default(),
246            signing_identity: Default::default(),
247            version: ProtocolVersion::MLS_10,
248        })))
249    }
250}
251
252impl<C: IntoConfig> ClientBuilder<C> {
253    /// Add an extension type to the list of extension types supported by the client.
254    pub fn extension_type(self, type_: ExtensionType) -> ClientBuilder<IntoConfigOutput<C>> {
255        self.extension_types(Some(type_))
256    }
257
258    /// Add multiple extension types to the list of extension types supported by the client.
259    pub fn extension_types<I>(self, types: I) -> ClientBuilder<IntoConfigOutput<C>>
260    where
261        I: IntoIterator<Item = ExtensionType>,
262    {
263        let mut c = self.0.into_config();
264        c.0.settings.extension_types.extend(types);
265        ClientBuilder(c)
266    }
267
268    /// Add a custom proposal type to the list of proposals types supported by the client.
269    pub fn custom_proposal_type(self, type_: ProposalType) -> ClientBuilder<IntoConfigOutput<C>> {
270        self.custom_proposal_types(Some(type_))
271    }
272
273    /// Add multiple custom proposal types to the list of proposal types supported by the client.
274    pub fn custom_proposal_types<I>(self, types: I) -> ClientBuilder<IntoConfigOutput<C>>
275    where
276        I: IntoIterator<Item = ProposalType>,
277    {
278        let mut c = self.0.into_config();
279        c.0.settings.custom_proposal_types.extend(types);
280        ClientBuilder(c)
281    }
282
283    /// Add a protocol version to the list of protocol versions supported by the client.
284    ///
285    /// If no protocol version is explicitly added, the client will support all protocol versions
286    /// supported by this crate.
287    pub fn protocol_version(self, version: ProtocolVersion) -> ClientBuilder<IntoConfigOutput<C>> {
288        self.protocol_versions(Some(version))
289    }
290
291    /// Add multiple protocol versions to the list of protocol versions supported by the client.
292    ///
293    /// If no protocol version is explicitly added, the client will support all protocol versions
294    /// supported by this crate.
295    pub fn protocol_versions<I>(self, versions: I) -> ClientBuilder<IntoConfigOutput<C>>
296    where
297        I: IntoIterator<Item = ProtocolVersion>,
298    {
299        let mut c = self.0.into_config();
300        c.0.settings.protocol_versions.extend(versions);
301        ClientBuilder(c)
302    }
303
304    /// Set the lifetime duration of key packages generated by the client.
305    pub fn key_package_lifetime(self, lifetime: Duration) -> ClientBuilder<IntoConfigOutput<C>> {
306        let mut c = self.0.into_config();
307        c.0.settings.lifetime = lifetime;
308        ClientBuilder(c)
309    }
310
311    /// Set the key package repository to be used by the client.
312    ///
313    /// By default, an in-memory repository is used.
314    pub fn key_package_repo<K>(self, key_package_repo: K) -> ClientBuilder<WithKeyPackageRepo<K, C>>
315    where
316        K: KeyPackageStorage,
317    {
318        let Config(c) = self.0.into_config();
319
320        ClientBuilder(Config(ConfigInner {
321            settings: c.settings,
322            key_package_repo,
323            psk_store: c.psk_store,
324            group_state_storage: c.group_state_storage,
325            identity_provider: c.identity_provider,
326            mls_rules: c.mls_rules,
327            crypto_provider: c.crypto_provider,
328            signer: c.signer,
329            signing_identity: c.signing_identity,
330            version: c.version,
331        }))
332    }
333
334    /// Set the PSK store to be used by the client.
335    ///
336    /// By default, an in-memory store is used.
337    pub fn psk_store<P>(self, psk_store: P) -> ClientBuilder<WithPskStore<P, C>>
338    where
339        P: PreSharedKeyStorage,
340    {
341        let Config(c) = self.0.into_config();
342
343        ClientBuilder(Config(ConfigInner {
344            settings: c.settings,
345            key_package_repo: c.key_package_repo,
346            psk_store,
347            group_state_storage: c.group_state_storage,
348            identity_provider: c.identity_provider,
349            mls_rules: c.mls_rules,
350            crypto_provider: c.crypto_provider,
351            signer: c.signer,
352            signing_identity: c.signing_identity,
353            version: c.version,
354        }))
355    }
356
357    /// Set the group state storage to be used by the client.
358    ///
359    /// By default, an in-memory storage is used.
360    pub fn group_state_storage<G>(
361        self,
362        group_state_storage: G,
363    ) -> ClientBuilder<WithGroupStateStorage<G, C>>
364    where
365        G: GroupStateStorage,
366    {
367        let Config(c) = self.0.into_config();
368
369        ClientBuilder(Config(ConfigInner {
370            settings: c.settings,
371            key_package_repo: c.key_package_repo,
372            psk_store: c.psk_store,
373            group_state_storage,
374            identity_provider: c.identity_provider,
375            crypto_provider: c.crypto_provider,
376            mls_rules: c.mls_rules,
377            signer: c.signer,
378            signing_identity: c.signing_identity,
379            version: c.version,
380        }))
381    }
382
383    /// Set the identity validator to be used by the client.
384    pub fn identity_provider<I>(
385        self,
386        identity_provider: I,
387    ) -> ClientBuilder<WithIdentityProvider<I, C>>
388    where
389        I: IdentityProvider,
390    {
391        let Config(c) = self.0.into_config();
392
393        ClientBuilder(Config(ConfigInner {
394            settings: c.settings,
395            key_package_repo: c.key_package_repo,
396            psk_store: c.psk_store,
397            group_state_storage: c.group_state_storage,
398            identity_provider,
399            mls_rules: c.mls_rules,
400            crypto_provider: c.crypto_provider,
401            signer: c.signer,
402            signing_identity: c.signing_identity,
403            version: c.version,
404        }))
405    }
406
407    /// Set the crypto provider to be used by the client.
408    pub fn crypto_provider<Cp>(
409        self,
410        crypto_provider: Cp,
411    ) -> ClientBuilder<WithCryptoProvider<Cp, C>>
412    where
413        Cp: CryptoProvider,
414    {
415        let Config(c) = self.0.into_config();
416
417        ClientBuilder(Config(ConfigInner {
418            settings: c.settings,
419            key_package_repo: c.key_package_repo,
420            psk_store: c.psk_store,
421            group_state_storage: c.group_state_storage,
422            identity_provider: c.identity_provider,
423            mls_rules: c.mls_rules,
424            crypto_provider,
425            signer: c.signer,
426            signing_identity: c.signing_identity,
427            version: c.version,
428        }))
429    }
430
431    /// Set the user-defined proposal rules to be used by the client.
432    ///
433    /// User-defined rules are used when sending and receiving commits before
434    /// enforcing general MLS protocol rules. If the rule set returns an error when
435    /// receiving a commit, the entire commit is considered invalid. If the
436    /// rule set would return an error when sending a commit, individual proposals
437    /// may be filtered out to compensate.
438    pub fn mls_rules<Pr>(self, mls_rules: Pr) -> ClientBuilder<WithMlsRules<Pr, C>>
439    where
440        Pr: MlsRules,
441    {
442        let Config(c) = self.0.into_config();
443
444        ClientBuilder(Config(ConfigInner {
445            settings: c.settings,
446            key_package_repo: c.key_package_repo,
447            psk_store: c.psk_store,
448            group_state_storage: c.group_state_storage,
449            identity_provider: c.identity_provider,
450            mls_rules,
451            crypto_provider: c.crypto_provider,
452            signer: c.signer,
453            signing_identity: c.signing_identity,
454            version: c.version,
455        }))
456    }
457
458    /// Set the protocol version used by the client. By default, the client uses version MLS 1.0
459    pub fn used_protocol_version(
460        self,
461        version: ProtocolVersion,
462    ) -> ClientBuilder<IntoConfigOutput<C>> {
463        let mut c = self.0.into_config();
464        c.0.version = version;
465        ClientBuilder(c)
466    }
467
468    /// Set the signing identity used by the client as well as the matching signer and cipher suite.
469    /// This must be called in order to create groups and key packages.
470    pub fn signing_identity(
471        self,
472        signing_identity: SigningIdentity,
473        signer: SignatureSecretKey,
474        cipher_suite: CipherSuite,
475    ) -> ClientBuilder<IntoConfigOutput<C>> {
476        let mut c = self.0.into_config();
477        c.0.signer = Some(signer);
478        c.0.signing_identity = Some((signing_identity, cipher_suite));
479        ClientBuilder(c)
480    }
481
482    /// Set the signer used by the client. This must be called in order to join groups.
483    pub fn signer(self, signer: SignatureSecretKey) -> ClientBuilder<IntoConfigOutput<C>> {
484        let mut c = self.0.into_config();
485        c.0.signer = Some(signer);
486        ClientBuilder(c)
487    }
488
489    #[cfg(any(test, feature = "test_util"))]
490    pub(crate) fn key_package_not_before(
491        self,
492        key_package_not_before: MlsTime,
493    ) -> ClientBuilder<IntoConfigOutput<C>> {
494        let mut c = self.0.into_config();
495        c.0.settings.key_package_not_before = Some(key_package_not_before);
496        ClientBuilder(c)
497    }
498}
499
500impl<C: IntoConfig> ClientBuilder<C>
501where
502    C::KeyPackageRepository: KeyPackageStorage + Clone,
503    C::PskStore: PreSharedKeyStorage + Clone,
504    C::GroupStateStorage: GroupStateStorage + Clone,
505    C::IdentityProvider: IdentityProvider + Clone,
506    C::MlsRules: MlsRules + Clone,
507    C::CryptoProvider: CryptoProvider + Clone,
508{
509    pub(crate) fn build_config(self) -> IntoConfigOutput<C> {
510        let mut c = self.0.into_config();
511
512        if c.0.settings.protocol_versions.is_empty() {
513            c.0.settings.protocol_versions = ProtocolVersion::all().collect();
514        }
515
516        c
517    }
518
519    /// Build a client.
520    ///
521    /// See [`ClientBuilder`] documentation if the return type of this function needs to be spelled
522    /// out.
523    pub fn build(self) -> Client<IntoConfigOutput<C>> {
524        let mut c = self.build_config();
525        let version = c.0.version;
526        let signer = c.0.signer.take();
527        let signing_identity = c.0.signing_identity.take();
528
529        Client::new(c, signer, signing_identity, version)
530    }
531}
532
533impl<C: IntoConfig<PskStore = InMemoryPreSharedKeyStorage>> ClientBuilder<C> {
534    /// Add a PSK to the in-memory PSK store.
535    pub fn psk(
536        self,
537        psk_id: ExternalPskId,
538        psk: PreSharedKey,
539    ) -> ClientBuilder<IntoConfigOutput<C>> {
540        let mut c = self.0.into_config();
541        c.0.psk_store.insert(psk_id, psk);
542        ClientBuilder(c)
543    }
544}
545
546/// Marker type for required `ClientBuilder` services that have not been specified yet.
547#[derive(Debug)]
548pub struct Missing;
549
550/// Change the key package repository used by a client configuration.
551///
552/// See [`ClientBuilder::key_package_repo`].
553pub type WithKeyPackageRepo<K, C> = Config<
554    K,
555    <C as IntoConfig>::PskStore,
556    <C as IntoConfig>::GroupStateStorage,
557    <C as IntoConfig>::IdentityProvider,
558    <C as IntoConfig>::MlsRules,
559    <C as IntoConfig>::CryptoProvider,
560>;
561
562/// Change the PSK store used by a client configuration.
563///
564/// See [`ClientBuilder::psk_store`].
565pub type WithPskStore<P, C> = Config<
566    <C as IntoConfig>::KeyPackageRepository,
567    P,
568    <C as IntoConfig>::GroupStateStorage,
569    <C as IntoConfig>::IdentityProvider,
570    <C as IntoConfig>::MlsRules,
571    <C as IntoConfig>::CryptoProvider,
572>;
573
574/// Change the group state storage used by a client configuration.
575///
576/// See [`ClientBuilder::group_state_storage`].
577pub type WithGroupStateStorage<G, C> = Config<
578    <C as IntoConfig>::KeyPackageRepository,
579    <C as IntoConfig>::PskStore,
580    G,
581    <C as IntoConfig>::IdentityProvider,
582    <C as IntoConfig>::MlsRules,
583    <C as IntoConfig>::CryptoProvider,
584>;
585
586/// Change the identity validator used by a client configuration.
587///
588/// See [`ClientBuilder::identity_provider`].
589pub type WithIdentityProvider<I, C> = Config<
590    <C as IntoConfig>::KeyPackageRepository,
591    <C as IntoConfig>::PskStore,
592    <C as IntoConfig>::GroupStateStorage,
593    I,
594    <C as IntoConfig>::MlsRules,
595    <C as IntoConfig>::CryptoProvider,
596>;
597
598/// Change the proposal rules used by a client configuration.
599///
600/// See [`ClientBuilder::mls_rules`].
601pub type WithMlsRules<Pr, C> = Config<
602    <C as IntoConfig>::KeyPackageRepository,
603    <C as IntoConfig>::PskStore,
604    <C as IntoConfig>::GroupStateStorage,
605    <C as IntoConfig>::IdentityProvider,
606    Pr,
607    <C as IntoConfig>::CryptoProvider,
608>;
609
610/// Change the crypto provider used by a client configuration.
611///
612/// See [`ClientBuilder::crypto_provider`].
613pub type WithCryptoProvider<Cp, C> = Config<
614    <C as IntoConfig>::KeyPackageRepository,
615    <C as IntoConfig>::PskStore,
616    <C as IntoConfig>::GroupStateStorage,
617    <C as IntoConfig>::IdentityProvider,
618    <C as IntoConfig>::MlsRules,
619    Cp,
620>;
621
622/// Helper alias for `Config`.
623pub type IntoConfigOutput<C> = Config<
624    <C as IntoConfig>::KeyPackageRepository,
625    <C as IntoConfig>::PskStore,
626    <C as IntoConfig>::GroupStateStorage,
627    <C as IntoConfig>::IdentityProvider,
628    <C as IntoConfig>::MlsRules,
629    <C as IntoConfig>::CryptoProvider,
630>;
631
632/// Helper alias to make a `Config` from a `ClientConfig`
633pub type MakeConfig<C> = Config<
634    <C as ClientConfig>::KeyPackageRepository,
635    <C as ClientConfig>::PskStore,
636    <C as ClientConfig>::GroupStateStorage,
637    <C as ClientConfig>::IdentityProvider,
638    <C as ClientConfig>::MlsRules,
639    <C as ClientConfig>::CryptoProvider,
640>;
641
642impl<Kpr, Ps, Gss, Ip, Pr, Cp> ClientConfig for ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp>
643where
644    Kpr: KeyPackageStorage + Clone,
645    Ps: PreSharedKeyStorage + Clone,
646    Gss: GroupStateStorage + Clone,
647    Ip: IdentityProvider + Clone,
648    Pr: MlsRules + Clone,
649    Cp: CryptoProvider + Clone,
650{
651    type KeyPackageRepository = Kpr;
652    type PskStore = Ps;
653    type GroupStateStorage = Gss;
654    type IdentityProvider = Ip;
655    type MlsRules = Pr;
656    type CryptoProvider = Cp;
657
658    fn supported_extensions(&self) -> Vec<ExtensionType> {
659        self.settings.extension_types.clone()
660    }
661
662    fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
663        self.settings.protocol_versions.clone()
664    }
665
666    fn key_package_repo(&self) -> Self::KeyPackageRepository {
667        self.key_package_repo.clone()
668    }
669
670    fn mls_rules(&self) -> Self::MlsRules {
671        self.mls_rules.clone()
672    }
673
674    fn secret_store(&self) -> Self::PskStore {
675        self.psk_store.clone()
676    }
677
678    fn group_state_storage(&self) -> Self::GroupStateStorage {
679        self.group_state_storage.clone()
680    }
681
682    fn identity_provider(&self) -> Self::IdentityProvider {
683        self.identity_provider.clone()
684    }
685
686    fn crypto_provider(&self) -> Self::CryptoProvider {
687        self.crypto_provider.clone()
688    }
689
690    fn lifetime(&self, timestamp: Option<MlsTime>) -> Lifetime {
691        #[cfg(feature = "std")]
692        let now_timestamp = MlsTime::now();
693
694        #[cfg(not(feature = "std"))]
695        let now_timestamp = MlsTime::from(0);
696
697        let now_timestamp = if let Some(now_time) = timestamp {
698            now_time
699        } else {
700            now_timestamp
701        };
702
703        #[cfg(test)]
704        let now_timestamp = self
705            .settings
706            .key_package_not_before
707            .unwrap_or(now_timestamp);
708
709        Lifetime {
710            not_before: now_timestamp,
711            not_after: now_timestamp + self.settings.lifetime,
712        }
713    }
714
715    fn supported_custom_proposals(&self) -> Vec<crate::group::proposal::ProposalType> {
716        self.settings.custom_proposal_types.clone()
717    }
718}
719
720impl<Kpr, Ps, Gss, Ip, Pr, Cp> Sealed for Config<Kpr, Ps, Gss, Ip, Pr, Cp> {}
721
722impl<Kpr, Ps, Gss, Ip, Pr, Cp> MlsConfig for Config<Kpr, Ps, Gss, Ip, Pr, Cp>
723where
724    Kpr: KeyPackageStorage + Clone,
725
726    Ps: PreSharedKeyStorage + Clone,
727    Gss: GroupStateStorage + Clone,
728    Ip: IdentityProvider + Clone,
729    Pr: MlsRules + Clone,
730    Cp: CryptoProvider + Clone,
731{
732    type Output = ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp>;
733
734    fn get(&self) -> &Self::Output {
735        &self.0
736    }
737}
738
739/// Helper trait to allow consuming crates to easily write a client type as `Client<impl MlsConfig>`
740///
741/// It is not meant to be implemented by consuming crates. `T: MlsConfig` implies `T: ClientConfig`.
742pub trait MlsConfig: Clone + Send + Sync + Sealed {
743    #[doc(hidden)]
744    type Output: ClientConfig;
745
746    #[doc(hidden)]
747    fn get(&self) -> &Self::Output;
748}
749
750/// Blanket implementation so that `T: MlsConfig` implies `T: ClientConfig`
751impl<T: MlsConfig> ClientConfig for T {
752    type KeyPackageRepository = <T::Output as ClientConfig>::KeyPackageRepository;
753    type PskStore = <T::Output as ClientConfig>::PskStore;
754    type GroupStateStorage = <T::Output as ClientConfig>::GroupStateStorage;
755    type IdentityProvider = <T::Output as ClientConfig>::IdentityProvider;
756    type MlsRules = <T::Output as ClientConfig>::MlsRules;
757    type CryptoProvider = <T::Output as ClientConfig>::CryptoProvider;
758
759    fn supported_extensions(&self) -> Vec<ExtensionType> {
760        self.get().supported_extensions()
761    }
762
763    fn supported_custom_proposals(&self) -> Vec<ProposalType> {
764        self.get().supported_custom_proposals()
765    }
766
767    fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
768        self.get().supported_protocol_versions()
769    }
770
771    fn key_package_repo(&self) -> Self::KeyPackageRepository {
772        self.get().key_package_repo()
773    }
774
775    fn mls_rules(&self) -> Self::MlsRules {
776        self.get().mls_rules()
777    }
778
779    fn secret_store(&self) -> Self::PskStore {
780        self.get().secret_store()
781    }
782
783    fn group_state_storage(&self) -> Self::GroupStateStorage {
784        self.get().group_state_storage()
785    }
786
787    fn identity_provider(&self) -> Self::IdentityProvider {
788        self.get().identity_provider()
789    }
790
791    fn crypto_provider(&self) -> Self::CryptoProvider {
792        self.get().crypto_provider()
793    }
794
795    fn lifetime(&self, timestamp: Option<MlsTime>) -> Lifetime {
796        self.get().lifetime(timestamp)
797    }
798
799    fn capabilities(&self) -> Capabilities {
800        self.get().capabilities()
801    }
802
803    fn version_supported(&self, version: ProtocolVersion) -> bool {
804        self.get().version_supported(version)
805    }
806
807    fn supported_credential_types(&self) -> Vec<CredentialType> {
808        self.get().supported_credential_types()
809    }
810}
811
812#[derive(Clone, Debug)]
813pub(crate) struct Settings {
814    pub(crate) extension_types: Vec<ExtensionType>,
815    pub(crate) protocol_versions: Vec<ProtocolVersion>,
816    pub(crate) custom_proposal_types: Vec<ProposalType>,
817    pub(crate) lifetime: Duration,
818    #[cfg(any(test, feature = "test_util"))]
819    pub(crate) key_package_not_before: Option<MlsTime>,
820}
821
822impl Default for Settings {
823    fn default() -> Self {
824        Self {
825            extension_types: Default::default(),
826            protocol_versions: Default::default(),
827            lifetime: 365 * 24 * Duration::from_secs(3600),
828            custom_proposal_types: Default::default(),
829            #[cfg(any(test, feature = "test_util"))]
830            key_package_not_before: None,
831        }
832    }
833}
834
835pub(crate) fn recreate_config<T: ClientConfig>(
836    c: T,
837    signer: Option<SignatureSecretKey>,
838    signing_identity: Option<(SigningIdentity, CipherSuite)>,
839    version: ProtocolVersion,
840    timestamp: Option<MlsTime>,
841) -> MakeConfig<T> {
842    Config(ConfigInner {
843        settings: Settings {
844            extension_types: c.supported_extensions(),
845            protocol_versions: c.supported_protocol_versions(),
846            custom_proposal_types: c.supported_custom_proposals(),
847            lifetime: {
848                let l = c.lifetime(timestamp);
849                l.not_after - l.not_before
850            },
851            #[cfg(any(test, feature = "test_util"))]
852            key_package_not_before: None,
853        },
854        key_package_repo: c.key_package_repo(),
855        psk_store: c.secret_store(),
856        group_state_storage: c.group_state_storage(),
857        identity_provider: c.identity_provider(),
858        mls_rules: c.mls_rules(),
859        crypto_provider: c.crypto_provider(),
860        signer,
861        signing_identity,
862        version,
863    })
864}
865
866/// Definitions meant to be private that are inaccessible outside this crate. They need to be marked
867/// `pub` because they appear in public definitions.
868mod private {
869    use mls_rs_core::{
870        crypto::{CipherSuite, SignatureSecretKey},
871        identity::SigningIdentity,
872        protocol_version::ProtocolVersion,
873    };
874
875    use crate::client_builder::{IntoConfigOutput, Settings};
876
877    #[derive(Clone, Debug)]
878    pub struct Config<Kpr, Ps, Gss, Ip, Pr, Cp>(pub(crate) ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp>);
879
880    #[derive(Clone, Debug)]
881    pub struct ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp> {
882        pub(crate) settings: Settings,
883        pub(crate) key_package_repo: Kpr,
884        pub(crate) psk_store: Ps,
885        pub(crate) group_state_storage: Gss,
886        pub(crate) identity_provider: Ip,
887        pub(crate) mls_rules: Pr,
888        pub(crate) crypto_provider: Cp,
889        pub(crate) signer: Option<SignatureSecretKey>,
890        pub(crate) signing_identity: Option<(SigningIdentity, CipherSuite)>,
891        pub(crate) version: ProtocolVersion,
892    }
893
894    pub trait IntoConfig {
895        type KeyPackageRepository;
896        type PskStore;
897        type GroupStateStorage;
898        type IdentityProvider;
899        type MlsRules;
900        type CryptoProvider;
901
902        fn into_config(self) -> IntoConfigOutput<Self>;
903    }
904
905    impl<Kpr, Ps, Gss, Ip, Pr, Cp> IntoConfig for Config<Kpr, Ps, Gss, Ip, Pr, Cp> {
906        type KeyPackageRepository = Kpr;
907        type PskStore = Ps;
908        type GroupStateStorage = Gss;
909        type IdentityProvider = Ip;
910        type MlsRules = Pr;
911        type CryptoProvider = Cp;
912
913        fn into_config(self) -> Self {
914            self
915        }
916    }
917}
918
919use mls_rs_core::{
920    crypto::{CryptoProvider, SignatureSecretKey},
921    group::GroupStateStorage,
922    identity::IdentityProvider,
923    key_package::KeyPackageStorage,
924    psk::PreSharedKeyStorage,
925};
926use private::{Config, ConfigInner, IntoConfig};
927
928#[cfg(test)]
929pub(crate) mod test_utils {
930    use crate::{
931        client_builder::{BaseConfig, ClientBuilder, WithIdentityProvider},
932        crypto::test_utils::TestCryptoProvider,
933        identity::{
934            basic::BasicIdentityProvider,
935            test_utils::{get_test_signing_identity, BasicWithCustomProvider},
936        },
937        CipherSuite,
938    };
939
940    use super::WithCryptoProvider;
941
942    pub type TestClientConfig = WithIdentityProvider<
943        BasicWithCustomProvider,
944        WithCryptoProvider<TestCryptoProvider, BaseConfig>,
945    >;
946
947    pub type TestClientBuilder = ClientBuilder<TestClientConfig>;
948
949    impl TestClientBuilder {
950        pub fn new_for_test() -> Self {
951            ClientBuilder::new()
952                .crypto_provider(TestCryptoProvider::new())
953                .identity_provider(BasicWithCustomProvider::new(BasicIdentityProvider::new()))
954        }
955
956        #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
957        pub async fn with_random_signing_identity(
958            self,
959            identity: &str,
960            cipher_suite: CipherSuite,
961        ) -> Self {
962            let (signing_identity, signer) =
963                get_test_signing_identity(cipher_suite, identity.as_bytes()).await;
964            self.signing_identity(signing_identity, signer, cipher_suite)
965        }
966    }
967}