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