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