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