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 
9 use crate::{
10     cipher_suite::CipherSuite,
11     client::Client,
12     client_config::ClientConfig,
13     extension::{ExtensionType, MlsExtension},
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")]
30 use crate::time::MlsTime;
31 
32 use alloc::vec::Vec;
33 
34 #[cfg(feature = "sqlite")]
35 use 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")]
44 pub use crate::group::padding::PaddingMode;
45 
46 /// Base client configuration type when instantiating `ClientBuilder`
47 pub 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`
57 pub type BaseInMemoryConfig = Config<
58     InMemoryKeyPackageStorage,
59     InMemoryPreSharedKeyStorage,
60     InMemoryGroupStateStorage,
61     Missing,
62     Missing,
63     Missing,
64 >;
65 
66 pub type EmptyConfig = Config<Missing, Missing, Missing, Missing, Missing, Missing>;
67 
68 /// Base client configuration that is backed by SQLite storage.
69 #[cfg(feature = "sqlite")]
70 pub 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)]
178 pub struct ClientBuilder<C>(C);
179 
180 impl Default for ClientBuilder<BaseConfig> {
default() -> Self181     fn default() -> Self {
182         Self::new()
183     }
184 }
185 
186 impl<C> ClientBuilder<C> {
from_config(c: C) -> Self187     pub(crate) fn from_config(c: C) -> Self {
188         Self(c)
189     }
190 }
191 
192 impl ClientBuilder<BaseConfig> {
193     /// Create a new client builder with default in-memory providers
new() -> Self194     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 
210 impl ClientBuilder<EmptyConfig> {
new_empty() -> Self211     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")]
228 impl ClientBuilder<BaseSqlConfig> {
229     /// Create a new client builder with SQLite storage providers.
new_sqlite<CS: ConnectionStrategy>( storage: SqLiteDataStorageEngine<CS>, ) -> Result<Self, SqLiteDataStorageError>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 
248 impl<C: IntoConfig> ClientBuilder<C> {
249     /// Add an extension type to the list of extension types supported by the client.
extension_type(self, type_: ExtensionType) -> ClientBuilder<IntoConfigOutput<C>>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.
extension_types<I>(self, types: I) -> ClientBuilder<IntoConfigOutput<C>> where I: IntoIterator<Item = ExtensionType>,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.
custom_proposal_type(self, type_: ProposalType) -> ClientBuilder<IntoConfigOutput<C>>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.
custom_proposal_types<I>(self, types: I) -> ClientBuilder<IntoConfigOutput<C>> where I: IntoIterator<Item = ProposalType>,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.
protocol_version(self, version: ProtocolVersion) -> ClientBuilder<IntoConfigOutput<C>>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.
protocol_versions<I>(self, versions: I) -> ClientBuilder<IntoConfigOutput<C>> where I: IntoIterator<Item = ProtocolVersion>,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     /// Add a key package extension to the list of key package extensions supported by the client.
key_package_extension<T>( self, extension: T, ) -> Result<ClientBuilder<IntoConfigOutput<C>>, ExtensionError> where T: MlsExtension, Self: Sized,301     pub fn key_package_extension<T>(
302         self,
303         extension: T,
304     ) -> Result<ClientBuilder<IntoConfigOutput<C>>, ExtensionError>
305     where
306         T: MlsExtension,
307         Self: Sized,
308     {
309         let mut c = self.0.into_config();
310         c.0.settings.key_package_extensions.set_from(extension)?;
311         Ok(ClientBuilder(c))
312     }
313 
314     /// Add multiple key package extensions to the list of key package extensions supported by the
315     /// client.
key_package_extensions( self, extensions: ExtensionList, ) -> ClientBuilder<IntoConfigOutput<C>>316     pub fn key_package_extensions(
317         self,
318         extensions: ExtensionList,
319     ) -> ClientBuilder<IntoConfigOutput<C>> {
320         let mut c = self.0.into_config();
321         c.0.settings.key_package_extensions.append(extensions);
322         ClientBuilder(c)
323     }
324 
325     /// Add a leaf node extension to the list of leaf node extensions supported by the client.
leaf_node_extension<T>( self, extension: T, ) -> Result<ClientBuilder<IntoConfigOutput<C>>, ExtensionError> where T: MlsExtension, Self: Sized,326     pub fn leaf_node_extension<T>(
327         self,
328         extension: T,
329     ) -> Result<ClientBuilder<IntoConfigOutput<C>>, ExtensionError>
330     where
331         T: MlsExtension,
332         Self: Sized,
333     {
334         let mut c = self.0.into_config();
335         c.0.settings.leaf_node_extensions.set_from(extension)?;
336         Ok(ClientBuilder(c))
337     }
338 
339     /// Add multiple leaf node extensions to the list of leaf node extensions supported by the
340     /// client.
leaf_node_extensions( self, extensions: ExtensionList, ) -> ClientBuilder<IntoConfigOutput<C>>341     pub fn leaf_node_extensions(
342         self,
343         extensions: ExtensionList,
344     ) -> ClientBuilder<IntoConfigOutput<C>> {
345         let mut c = self.0.into_config();
346         c.0.settings.leaf_node_extensions.append(extensions);
347         ClientBuilder(c)
348     }
349 
350     /// Set the lifetime duration in seconds of key packages generated by the client.
key_package_lifetime(self, duration_in_s: u64) -> ClientBuilder<IntoConfigOutput<C>>351     pub fn key_package_lifetime(self, duration_in_s: u64) -> ClientBuilder<IntoConfigOutput<C>> {
352         let mut c = self.0.into_config();
353         c.0.settings.lifetime_in_s = duration_in_s;
354         ClientBuilder(c)
355     }
356 
357     /// Set the key package repository to be used by the client.
358     ///
359     /// By default, an in-memory repository is used.
key_package_repo<K>(self, key_package_repo: K) -> ClientBuilder<WithKeyPackageRepo<K, C>> where K: KeyPackageStorage,360     pub fn key_package_repo<K>(self, key_package_repo: K) -> ClientBuilder<WithKeyPackageRepo<K, C>>
361     where
362         K: KeyPackageStorage,
363     {
364         let Config(c) = self.0.into_config();
365 
366         ClientBuilder(Config(ConfigInner {
367             settings: c.settings,
368             key_package_repo,
369             psk_store: c.psk_store,
370             group_state_storage: c.group_state_storage,
371             identity_provider: c.identity_provider,
372             mls_rules: c.mls_rules,
373             crypto_provider: c.crypto_provider,
374             signer: c.signer,
375             signing_identity: c.signing_identity,
376             version: c.version,
377         }))
378     }
379 
380     /// Set the PSK store to be used by the client.
381     ///
382     /// By default, an in-memory store is used.
psk_store<P>(self, psk_store: P) -> ClientBuilder<WithPskStore<P, C>> where P: PreSharedKeyStorage,383     pub fn psk_store<P>(self, psk_store: P) -> ClientBuilder<WithPskStore<P, C>>
384     where
385         P: PreSharedKeyStorage,
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,
393             group_state_storage: c.group_state_storage,
394             identity_provider: c.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 group state storage to be used by the client.
404     ///
405     /// By default, an in-memory storage is used.
group_state_storage<G>( self, group_state_storage: G, ) -> ClientBuilder<WithGroupStateStorage<G, C>> where G: GroupStateStorage,406     pub fn group_state_storage<G>(
407         self,
408         group_state_storage: G,
409     ) -> ClientBuilder<WithGroupStateStorage<G, C>>
410     where
411         G: GroupStateStorage,
412     {
413         let Config(c) = self.0.into_config();
414 
415         ClientBuilder(Config(ConfigInner {
416             settings: c.settings,
417             key_package_repo: c.key_package_repo,
418             psk_store: c.psk_store,
419             group_state_storage,
420             identity_provider: c.identity_provider,
421             crypto_provider: c.crypto_provider,
422             mls_rules: c.mls_rules,
423             signer: c.signer,
424             signing_identity: c.signing_identity,
425             version: c.version,
426         }))
427     }
428 
429     /// Set the identity validator to be used by the client.
identity_provider<I>( self, identity_provider: I, ) -> ClientBuilder<WithIdentityProvider<I, C>> where I: IdentityProvider,430     pub fn identity_provider<I>(
431         self,
432         identity_provider: I,
433     ) -> ClientBuilder<WithIdentityProvider<I, C>>
434     where
435         I: IdentityProvider,
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,
445             mls_rules: c.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 crypto provider to be used by the client.
crypto_provider<Cp>( self, crypto_provider: Cp, ) -> ClientBuilder<WithCryptoProvider<Cp, C>> where Cp: CryptoProvider,454     pub fn crypto_provider<Cp>(
455         self,
456         crypto_provider: Cp,
457     ) -> ClientBuilder<WithCryptoProvider<Cp, C>>
458     where
459         Cp: CryptoProvider,
460     {
461         let Config(c) = self.0.into_config();
462 
463         ClientBuilder(Config(ConfigInner {
464             settings: c.settings,
465             key_package_repo: c.key_package_repo,
466             psk_store: c.psk_store,
467             group_state_storage: c.group_state_storage,
468             identity_provider: c.identity_provider,
469             mls_rules: c.mls_rules,
470             crypto_provider,
471             signer: c.signer,
472             signing_identity: c.signing_identity,
473             version: c.version,
474         }))
475     }
476 
477     /// Set the user-defined proposal rules to be used by the client.
478     ///
479     /// User-defined rules are used when sending and receiving commits before
480     /// enforcing general MLS protocol rules. If the rule set returns an error when
481     /// receiving a commit, the entire commit is considered invalid. If the
482     /// rule set would return an error when sending a commit, individual proposals
483     /// may be filtered out to compensate.
mls_rules<Pr>(self, mls_rules: Pr) -> ClientBuilder<WithMlsRules<Pr, C>> where Pr: MlsRules,484     pub fn mls_rules<Pr>(self, mls_rules: Pr) -> ClientBuilder<WithMlsRules<Pr, C>>
485     where
486         Pr: MlsRules,
487     {
488         let Config(c) = self.0.into_config();
489 
490         ClientBuilder(Config(ConfigInner {
491             settings: c.settings,
492             key_package_repo: c.key_package_repo,
493             psk_store: c.psk_store,
494             group_state_storage: c.group_state_storage,
495             identity_provider: c.identity_provider,
496             mls_rules,
497             crypto_provider: c.crypto_provider,
498             signer: c.signer,
499             signing_identity: c.signing_identity,
500             version: c.version,
501         }))
502     }
503 
504     /// Set the protocol version used by the client. By default, the client uses version MLS 1.0
used_protocol_version( self, version: ProtocolVersion, ) -> ClientBuilder<IntoConfigOutput<C>>505     pub fn used_protocol_version(
506         self,
507         version: ProtocolVersion,
508     ) -> ClientBuilder<IntoConfigOutput<C>> {
509         let mut c = self.0.into_config();
510         c.0.version = version;
511         ClientBuilder(c)
512     }
513 
514     /// Set the signing identity used by the client as well as the matching signer and cipher suite.
515     /// This must be called in order to create groups and key packages.
signing_identity( self, signing_identity: SigningIdentity, signer: SignatureSecretKey, cipher_suite: CipherSuite, ) -> ClientBuilder<IntoConfigOutput<C>>516     pub fn signing_identity(
517         self,
518         signing_identity: SigningIdentity,
519         signer: SignatureSecretKey,
520         cipher_suite: CipherSuite,
521     ) -> ClientBuilder<IntoConfigOutput<C>> {
522         let mut c = self.0.into_config();
523         c.0.signer = Some(signer);
524         c.0.signing_identity = Some((signing_identity, cipher_suite));
525         ClientBuilder(c)
526     }
527 
528     /// Set the signer used by the client. This must be called in order to join groups.
signer(self, signer: SignatureSecretKey) -> ClientBuilder<IntoConfigOutput<C>>529     pub fn signer(self, signer: SignatureSecretKey) -> ClientBuilder<IntoConfigOutput<C>> {
530         let mut c = self.0.into_config();
531         c.0.signer = Some(signer);
532         ClientBuilder(c)
533     }
534 
535     #[cfg(any(test, feature = "test_util"))]
key_package_not_before( self, key_package_not_before: u64, ) -> ClientBuilder<IntoConfigOutput<C>>536     pub(crate) fn key_package_not_before(
537         self,
538         key_package_not_before: u64,
539     ) -> ClientBuilder<IntoConfigOutput<C>> {
540         let mut c = self.0.into_config();
541         c.0.settings.key_package_not_before = Some(key_package_not_before);
542         ClientBuilder(c)
543     }
544 }
545 
546 impl<C: IntoConfig> ClientBuilder<C>
547 where
548     C::KeyPackageRepository: KeyPackageStorage + Clone,
549     C::PskStore: PreSharedKeyStorage + Clone,
550     C::GroupStateStorage: GroupStateStorage + Clone,
551     C::IdentityProvider: IdentityProvider + Clone,
552     C::MlsRules: MlsRules + Clone,
553     C::CryptoProvider: CryptoProvider + Clone,
554 {
build_config(self) -> IntoConfigOutput<C>555     pub(crate) fn build_config(self) -> IntoConfigOutput<C> {
556         let mut c = self.0.into_config();
557 
558         if c.0.settings.protocol_versions.is_empty() {
559             c.0.settings.protocol_versions = ProtocolVersion::all().collect();
560         }
561 
562         c
563     }
564 
565     /// Build a client.
566     ///
567     /// See [`ClientBuilder`] documentation if the return type of this function needs to be spelled
568     /// out.
build(self) -> Client<IntoConfigOutput<C>>569     pub fn build(self) -> Client<IntoConfigOutput<C>> {
570         let mut c = self.build_config();
571         let version = c.0.version;
572         let signer = c.0.signer.take();
573         let signing_identity = c.0.signing_identity.take();
574 
575         Client::new(c, signer, signing_identity, version)
576     }
577 }
578 
579 impl<C: IntoConfig<PskStore = InMemoryPreSharedKeyStorage>> ClientBuilder<C> {
580     /// Add a PSK to the in-memory PSK store.
psk( self, psk_id: ExternalPskId, psk: PreSharedKey, ) -> ClientBuilder<IntoConfigOutput<C>>581     pub fn psk(
582         self,
583         psk_id: ExternalPskId,
584         psk: PreSharedKey,
585     ) -> ClientBuilder<IntoConfigOutput<C>> {
586         let mut c = self.0.into_config();
587         c.0.psk_store.insert(psk_id, psk);
588         ClientBuilder(c)
589     }
590 }
591 
592 /// Marker type for required `ClientBuilder` services that have not been specified yet.
593 #[derive(Debug)]
594 pub struct Missing;
595 
596 /// Change the key package repository used by a client configuration.
597 ///
598 /// See [`ClientBuilder::key_package_repo`].
599 pub type WithKeyPackageRepo<K, C> = Config<
600     K,
601     <C as IntoConfig>::PskStore,
602     <C as IntoConfig>::GroupStateStorage,
603     <C as IntoConfig>::IdentityProvider,
604     <C as IntoConfig>::MlsRules,
605     <C as IntoConfig>::CryptoProvider,
606 >;
607 
608 /// Change the PSK store used by a client configuration.
609 ///
610 /// See [`ClientBuilder::psk_store`].
611 pub type WithPskStore<P, C> = Config<
612     <C as IntoConfig>::KeyPackageRepository,
613     P,
614     <C as IntoConfig>::GroupStateStorage,
615     <C as IntoConfig>::IdentityProvider,
616     <C as IntoConfig>::MlsRules,
617     <C as IntoConfig>::CryptoProvider,
618 >;
619 
620 /// Change the group state storage used by a client configuration.
621 ///
622 /// See [`ClientBuilder::group_state_storage`].
623 pub type WithGroupStateStorage<G, C> = Config<
624     <C as IntoConfig>::KeyPackageRepository,
625     <C as IntoConfig>::PskStore,
626     G,
627     <C as IntoConfig>::IdentityProvider,
628     <C as IntoConfig>::MlsRules,
629     <C as IntoConfig>::CryptoProvider,
630 >;
631 
632 /// Change the identity validator used by a client configuration.
633 ///
634 /// See [`ClientBuilder::identity_provider`].
635 pub type WithIdentityProvider<I, C> = Config<
636     <C as IntoConfig>::KeyPackageRepository,
637     <C as IntoConfig>::PskStore,
638     <C as IntoConfig>::GroupStateStorage,
639     I,
640     <C as IntoConfig>::MlsRules,
641     <C as IntoConfig>::CryptoProvider,
642 >;
643 
644 /// Change the proposal rules used by a client configuration.
645 ///
646 /// See [`ClientBuilder::mls_rules`].
647 pub type WithMlsRules<Pr, C> = Config<
648     <C as IntoConfig>::KeyPackageRepository,
649     <C as IntoConfig>::PskStore,
650     <C as IntoConfig>::GroupStateStorage,
651     <C as IntoConfig>::IdentityProvider,
652     Pr,
653     <C as IntoConfig>::CryptoProvider,
654 >;
655 
656 /// Change the crypto provider used by a client configuration.
657 ///
658 /// See [`ClientBuilder::crypto_provider`].
659 pub type WithCryptoProvider<Cp, C> = Config<
660     <C as IntoConfig>::KeyPackageRepository,
661     <C as IntoConfig>::PskStore,
662     <C as IntoConfig>::GroupStateStorage,
663     <C as IntoConfig>::IdentityProvider,
664     <C as IntoConfig>::MlsRules,
665     Cp,
666 >;
667 
668 /// Helper alias for `Config`.
669 pub type IntoConfigOutput<C> = Config<
670     <C as IntoConfig>::KeyPackageRepository,
671     <C as IntoConfig>::PskStore,
672     <C as IntoConfig>::GroupStateStorage,
673     <C as IntoConfig>::IdentityProvider,
674     <C as IntoConfig>::MlsRules,
675     <C as IntoConfig>::CryptoProvider,
676 >;
677 
678 /// Helper alias to make a `Config` from a `ClientConfig`
679 pub type MakeConfig<C> = Config<
680     <C as ClientConfig>::KeyPackageRepository,
681     <C as ClientConfig>::PskStore,
682     <C as ClientConfig>::GroupStateStorage,
683     <C as ClientConfig>::IdentityProvider,
684     <C as ClientConfig>::MlsRules,
685     <C as ClientConfig>::CryptoProvider,
686 >;
687 
688 impl<Kpr, Ps, Gss, Ip, Pr, Cp> ClientConfig for ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp>
689 where
690     Kpr: KeyPackageStorage + Clone,
691     Ps: PreSharedKeyStorage + Clone,
692     Gss: GroupStateStorage + Clone,
693     Ip: IdentityProvider + Clone,
694     Pr: MlsRules + Clone,
695     Cp: CryptoProvider + Clone,
696 {
697     type KeyPackageRepository = Kpr;
698     type PskStore = Ps;
699     type GroupStateStorage = Gss;
700     type IdentityProvider = Ip;
701     type MlsRules = Pr;
702     type CryptoProvider = Cp;
703 
supported_extensions(&self) -> Vec<ExtensionType>704     fn supported_extensions(&self) -> Vec<ExtensionType> {
705         self.settings.extension_types.clone()
706     }
707 
supported_protocol_versions(&self) -> Vec<ProtocolVersion>708     fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
709         self.settings.protocol_versions.clone()
710     }
711 
key_package_repo(&self) -> Self::KeyPackageRepository712     fn key_package_repo(&self) -> Self::KeyPackageRepository {
713         self.key_package_repo.clone()
714     }
715 
mls_rules(&self) -> Self::MlsRules716     fn mls_rules(&self) -> Self::MlsRules {
717         self.mls_rules.clone()
718     }
719 
secret_store(&self) -> Self::PskStore720     fn secret_store(&self) -> Self::PskStore {
721         self.psk_store.clone()
722     }
723 
group_state_storage(&self) -> Self::GroupStateStorage724     fn group_state_storage(&self) -> Self::GroupStateStorage {
725         self.group_state_storage.clone()
726     }
727 
identity_provider(&self) -> Self::IdentityProvider728     fn identity_provider(&self) -> Self::IdentityProvider {
729         self.identity_provider.clone()
730     }
731 
crypto_provider(&self) -> Self::CryptoProvider732     fn crypto_provider(&self) -> Self::CryptoProvider {
733         self.crypto_provider.clone()
734     }
735 
key_package_extensions(&self) -> ExtensionList736     fn key_package_extensions(&self) -> ExtensionList {
737         self.settings.key_package_extensions.clone()
738     }
739 
leaf_node_extensions(&self) -> ExtensionList740     fn leaf_node_extensions(&self) -> ExtensionList {
741         self.settings.leaf_node_extensions.clone()
742     }
743 
lifetime(&self) -> Lifetime744     fn lifetime(&self) -> Lifetime {
745         #[cfg(feature = "std")]
746         let now_timestamp = MlsTime::now().seconds_since_epoch();
747 
748         #[cfg(not(feature = "std"))]
749         let now_timestamp = 0;
750 
751         #[cfg(test)]
752         let now_timestamp = self
753             .settings
754             .key_package_not_before
755             .unwrap_or(now_timestamp);
756 
757         Lifetime {
758             not_before: now_timestamp,
759             not_after: now_timestamp + self.settings.lifetime_in_s,
760         }
761     }
762 
supported_custom_proposals(&self) -> Vec<crate::group::proposal::ProposalType>763     fn supported_custom_proposals(&self) -> Vec<crate::group::proposal::ProposalType> {
764         self.settings.custom_proposal_types.clone()
765     }
766 }
767 
768 impl<Kpr, Ps, Gss, Ip, Pr, Cp> Sealed for Config<Kpr, Ps, Gss, Ip, Pr, Cp> {}
769 
770 impl<Kpr, Ps, Gss, Ip, Pr, Cp> MlsConfig for Config<Kpr, Ps, Gss, Ip, Pr, Cp>
771 where
772     Kpr: KeyPackageStorage + Clone,
773 
774     Ps: PreSharedKeyStorage + Clone,
775     Gss: GroupStateStorage + Clone,
776     Ip: IdentityProvider + Clone,
777     Pr: MlsRules + Clone,
778     Cp: CryptoProvider + Clone,
779 {
780     type Output = ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp>;
781 
get(&self) -> &Self::Output782     fn get(&self) -> &Self::Output {
783         &self.0
784     }
785 }
786 
787 /// Helper trait to allow consuming crates to easily write a client type as `Client<impl MlsConfig>`
788 ///
789 /// It is not meant to be implemented by consuming crates. `T: MlsConfig` implies `T: ClientConfig`.
790 pub trait MlsConfig: Clone + Send + Sync + Sealed {
791     #[doc(hidden)]
792     type Output: ClientConfig;
793 
794     #[doc(hidden)]
get(&self) -> &Self::Output795     fn get(&self) -> &Self::Output;
796 }
797 
798 /// Blanket implementation so that `T: MlsConfig` implies `T: ClientConfig`
799 impl<T: MlsConfig> ClientConfig for T {
800     type KeyPackageRepository = <T::Output as ClientConfig>::KeyPackageRepository;
801     type PskStore = <T::Output as ClientConfig>::PskStore;
802     type GroupStateStorage = <T::Output as ClientConfig>::GroupStateStorage;
803     type IdentityProvider = <T::Output as ClientConfig>::IdentityProvider;
804     type MlsRules = <T::Output as ClientConfig>::MlsRules;
805     type CryptoProvider = <T::Output as ClientConfig>::CryptoProvider;
806 
supported_extensions(&self) -> Vec<ExtensionType>807     fn supported_extensions(&self) -> Vec<ExtensionType> {
808         self.get().supported_extensions()
809     }
810 
supported_custom_proposals(&self) -> Vec<ProposalType>811     fn supported_custom_proposals(&self) -> Vec<ProposalType> {
812         self.get().supported_custom_proposals()
813     }
814 
supported_protocol_versions(&self) -> Vec<ProtocolVersion>815     fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
816         self.get().supported_protocol_versions()
817     }
818 
key_package_repo(&self) -> Self::KeyPackageRepository819     fn key_package_repo(&self) -> Self::KeyPackageRepository {
820         self.get().key_package_repo()
821     }
822 
mls_rules(&self) -> Self::MlsRules823     fn mls_rules(&self) -> Self::MlsRules {
824         self.get().mls_rules()
825     }
826 
secret_store(&self) -> Self::PskStore827     fn secret_store(&self) -> Self::PskStore {
828         self.get().secret_store()
829     }
830 
group_state_storage(&self) -> Self::GroupStateStorage831     fn group_state_storage(&self) -> Self::GroupStateStorage {
832         self.get().group_state_storage()
833     }
834 
identity_provider(&self) -> Self::IdentityProvider835     fn identity_provider(&self) -> Self::IdentityProvider {
836         self.get().identity_provider()
837     }
838 
crypto_provider(&self) -> Self::CryptoProvider839     fn crypto_provider(&self) -> Self::CryptoProvider {
840         self.get().crypto_provider()
841     }
842 
key_package_extensions(&self) -> ExtensionList843     fn key_package_extensions(&self) -> ExtensionList {
844         self.get().key_package_extensions()
845     }
846 
leaf_node_extensions(&self) -> ExtensionList847     fn leaf_node_extensions(&self) -> ExtensionList {
848         self.get().leaf_node_extensions()
849     }
850 
lifetime(&self) -> Lifetime851     fn lifetime(&self) -> Lifetime {
852         self.get().lifetime()
853     }
854 
capabilities(&self) -> Capabilities855     fn capabilities(&self) -> Capabilities {
856         self.get().capabilities()
857     }
858 
version_supported(&self, version: ProtocolVersion) -> bool859     fn version_supported(&self, version: ProtocolVersion) -> bool {
860         self.get().version_supported(version)
861     }
862 
supported_credential_types(&self) -> Vec<CredentialType>863     fn supported_credential_types(&self) -> Vec<CredentialType> {
864         self.get().supported_credential_types()
865     }
866 }
867 
868 #[derive(Clone, Debug)]
869 pub(crate) struct Settings {
870     pub(crate) extension_types: Vec<ExtensionType>,
871     pub(crate) protocol_versions: Vec<ProtocolVersion>,
872     pub(crate) custom_proposal_types: Vec<ProposalType>,
873     pub(crate) key_package_extensions: ExtensionList,
874     pub(crate) leaf_node_extensions: ExtensionList,
875     pub(crate) lifetime_in_s: u64,
876     #[cfg(any(test, feature = "test_util"))]
877     pub(crate) key_package_not_before: Option<u64>,
878 }
879 
880 impl Default for Settings {
default() -> Self881     fn default() -> Self {
882         Self {
883             extension_types: Default::default(),
884             protocol_versions: Default::default(),
885             key_package_extensions: Default::default(),
886             leaf_node_extensions: Default::default(),
887             lifetime_in_s: 365 * 24 * 3600,
888             custom_proposal_types: Default::default(),
889             #[cfg(any(test, feature = "test_util"))]
890             key_package_not_before: None,
891         }
892     }
893 }
894 
recreate_config<T: ClientConfig>( c: T, signer: Option<SignatureSecretKey>, signing_identity: Option<(SigningIdentity, CipherSuite)>, version: ProtocolVersion, ) -> MakeConfig<T>895 pub(crate) fn recreate_config<T: ClientConfig>(
896     c: T,
897     signer: Option<SignatureSecretKey>,
898     signing_identity: Option<(SigningIdentity, CipherSuite)>,
899     version: ProtocolVersion,
900 ) -> MakeConfig<T> {
901     Config(ConfigInner {
902         settings: Settings {
903             extension_types: c.supported_extensions(),
904             protocol_versions: c.supported_protocol_versions(),
905             custom_proposal_types: c.supported_custom_proposals(),
906             key_package_extensions: c.key_package_extensions(),
907             leaf_node_extensions: c.leaf_node_extensions(),
908             lifetime_in_s: {
909                 let l = c.lifetime();
910                 l.not_after - l.not_before
911             },
912             #[cfg(any(test, feature = "test_util"))]
913             key_package_not_before: None,
914         },
915         key_package_repo: c.key_package_repo(),
916         psk_store: c.secret_store(),
917         group_state_storage: c.group_state_storage(),
918         identity_provider: c.identity_provider(),
919         mls_rules: c.mls_rules(),
920         crypto_provider: c.crypto_provider(),
921         signer,
922         signing_identity,
923         version,
924     })
925 }
926 
927 /// Definitions meant to be private that are inaccessible outside this crate. They need to be marked
928 /// `pub` because they appear in public definitions.
929 mod private {
930     use mls_rs_core::{
931         crypto::{CipherSuite, SignatureSecretKey},
932         identity::SigningIdentity,
933         protocol_version::ProtocolVersion,
934     };
935 
936     use crate::client_builder::{IntoConfigOutput, Settings};
937 
938     #[derive(Clone, Debug)]
939     pub struct Config<Kpr, Ps, Gss, Ip, Pr, Cp>(pub(crate) ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp>);
940 
941     #[derive(Clone, Debug)]
942     pub struct ConfigInner<Kpr, Ps, Gss, Ip, Pr, Cp> {
943         pub(crate) settings: Settings,
944         pub(crate) key_package_repo: Kpr,
945         pub(crate) psk_store: Ps,
946         pub(crate) group_state_storage: Gss,
947         pub(crate) identity_provider: Ip,
948         pub(crate) mls_rules: Pr,
949         pub(crate) crypto_provider: Cp,
950         pub(crate) signer: Option<SignatureSecretKey>,
951         pub(crate) signing_identity: Option<(SigningIdentity, CipherSuite)>,
952         pub(crate) version: ProtocolVersion,
953     }
954 
955     pub trait IntoConfig {
956         type KeyPackageRepository;
957         type PskStore;
958         type GroupStateStorage;
959         type IdentityProvider;
960         type MlsRules;
961         type CryptoProvider;
962 
into_config(self) -> IntoConfigOutput<Self>963         fn into_config(self) -> IntoConfigOutput<Self>;
964     }
965 
966     impl<Kpr, Ps, Gss, Ip, Pr, Cp> IntoConfig for Config<Kpr, Ps, Gss, Ip, Pr, Cp> {
967         type KeyPackageRepository = Kpr;
968         type PskStore = Ps;
969         type GroupStateStorage = Gss;
970         type IdentityProvider = Ip;
971         type MlsRules = Pr;
972         type CryptoProvider = Cp;
973 
into_config(self) -> Self974         fn into_config(self) -> Self {
975             self
976         }
977     }
978 }
979 
980 use mls_rs_core::{
981     crypto::{CryptoProvider, SignatureSecretKey},
982     extension::{ExtensionError, ExtensionList},
983     group::GroupStateStorage,
984     identity::IdentityProvider,
985     key_package::KeyPackageStorage,
986     psk::PreSharedKeyStorage,
987 };
988 use private::{Config, ConfigInner, IntoConfig};
989 
990 #[cfg(test)]
991 pub(crate) mod test_utils {
992     use crate::{
993         client_builder::{BaseConfig, ClientBuilder, WithIdentityProvider},
994         crypto::test_utils::TestCryptoProvider,
995         identity::{
996             basic::BasicIdentityProvider,
997             test_utils::{get_test_signing_identity, BasicWithCustomProvider},
998         },
999         CipherSuite,
1000     };
1001 
1002     use super::WithCryptoProvider;
1003 
1004     pub type TestClientConfig = WithIdentityProvider<
1005         BasicWithCustomProvider,
1006         WithCryptoProvider<TestCryptoProvider, BaseConfig>,
1007     >;
1008 
1009     pub type TestClientBuilder = ClientBuilder<TestClientConfig>;
1010 
1011     impl TestClientBuilder {
new_for_test() -> Self1012         pub fn new_for_test() -> Self {
1013             ClientBuilder::new()
1014                 .crypto_provider(TestCryptoProvider::new())
1015                 .identity_provider(BasicWithCustomProvider::new(BasicIdentityProvider::new()))
1016         }
1017 
1018         #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
with_random_signing_identity( self, identity: &str, cipher_suite: CipherSuite, ) -> Self1019         pub async fn with_random_signing_identity(
1020             self,
1021             identity: &str,
1022             cipher_suite: CipherSuite,
1023         ) -> Self {
1024             let (signing_identity, signer) =
1025                 get_test_signing_identity(cipher_suite, identity.as_bytes()).await;
1026             self.signing_identity(signing_identity, signer, cipher_suite)
1027         }
1028     }
1029 }
1030