1use crate::{
10 crypto::SignaturePublicKey,
11 extension::ExtensionType,
12 external_client::{ExternalClient, ExternalClientConfig},
13 group::{
14 mls_rules::{DefaultMlsRules, MlsRules},
15 proposal::ProposalType,
16 },
17 protocol_version::ProtocolVersion,
18 CryptoProvider, Sealed,
19};
20use std::{
21 collections::HashMap,
22 fmt::{self, Debug},
23};
24
25pub type ExternalBaseConfig = Config<Missing, DefaultMlsRules, Missing>;
27
28#[derive(Debug)]
98pub struct ExternalClientBuilder<C>(C);
99
100impl Default for ExternalClientBuilder<ExternalBaseConfig> {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106impl ExternalClientBuilder<ExternalBaseConfig> {
107 pub fn new() -> Self {
108 Self(Config(ConfigInner {
109 settings: Default::default(),
110 identity_provider: Missing,
111 mls_rules: DefaultMlsRules::new(),
112 crypto_provider: Missing,
113 signing_data: None,
114 }))
115 }
116}
117
118impl<C: IntoConfig> ExternalClientBuilder<C> {
119 pub fn extension_type(
121 self,
122 type_: ExtensionType,
123 ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
124 self.extension_types(Some(type_))
125 }
126
127 pub fn extension_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
129 where
130 I: IntoIterator<Item = ExtensionType>,
131 {
132 let mut c = self.0.into_config();
133 c.0.settings.extension_types.extend(types);
134 ExternalClientBuilder(c)
135 }
136
137 pub fn custom_proposal_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
139 where
140 I: IntoIterator<Item = ProposalType>,
141 {
142 let mut c = self.0.into_config();
143 c.0.settings.custom_proposal_types.extend(types);
144 ExternalClientBuilder(c)
145 }
146
147 pub fn protocol_version(
152 self,
153 version: ProtocolVersion,
154 ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
155 self.protocol_versions(Some(version))
156 }
157
158 pub fn protocol_versions<I>(self, versions: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
163 where
164 I: IntoIterator<Item = ProtocolVersion>,
165 {
166 let mut c = self.0.into_config();
167 c.0.settings.protocol_versions.extend(versions);
168 ExternalClientBuilder(c)
169 }
170
171 pub fn external_signing_key(
173 self,
174 id: Vec<u8>,
175 key: SignaturePublicKey,
176 ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
177 let mut c = self.0.into_config();
178 c.0.settings.external_signing_keys.insert(id, key);
179 ExternalClientBuilder(c)
180 }
181
182 pub fn max_epoch_jitter(self, max_jitter: u64) -> ExternalClientBuilder<IntoConfigOutput<C>> {
186 let mut c = self.0.into_config();
187 c.0.settings.max_epoch_jitter = Some(max_jitter);
188 ExternalClientBuilder(c)
189 }
190
191 pub fn cache_proposals(
195 self,
196 cache_proposals: bool,
197 ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
198 let mut c = self.0.into_config();
199 c.0.settings.cache_proposals = cache_proposals;
200 ExternalClientBuilder(c)
201 }
202
203 pub fn identity_provider<I>(
205 self,
206 identity_provider: I,
207 ) -> ExternalClientBuilder<WithIdentityProvider<I, C>>
208 where
209 I: IdentityProvider,
210 {
211 let Config(c) = self.0.into_config();
212 ExternalClientBuilder(Config(ConfigInner {
213 settings: c.settings,
214 identity_provider,
215 mls_rules: c.mls_rules,
216 crypto_provider: c.crypto_provider,
217 signing_data: c.signing_data,
218 }))
219 }
220
221 pub fn crypto_provider<Cp>(
225 self,
226 crypto_provider: Cp,
227 ) -> ExternalClientBuilder<WithCryptoProvider<Cp, C>>
228 where
229 Cp: CryptoProvider,
230 {
231 let Config(c) = self.0.into_config();
232 ExternalClientBuilder(Config(ConfigInner {
233 settings: c.settings,
234 identity_provider: c.identity_provider,
235 mls_rules: c.mls_rules,
236 crypto_provider,
237 signing_data: c.signing_data,
238 }))
239 }
240
241 pub fn mls_rules<Pr>(self, mls_rules: Pr) -> ExternalClientBuilder<WithMlsRules<Pr, C>>
249 where
250 Pr: MlsRules,
251 {
252 let Config(c) = self.0.into_config();
253 ExternalClientBuilder(Config(ConfigInner {
254 settings: c.settings,
255 identity_provider: c.identity_provider,
256 mls_rules,
257 crypto_provider: c.crypto_provider,
258 signing_data: c.signing_data,
259 }))
260 }
261
262 pub fn signer(
264 self,
265 signer: SignatureSecretKey,
266 signing_identity: SigningIdentity,
267 ) -> ExternalClientBuilder<IntoConfigOutput<C>> {
268 let mut c = self.0.into_config();
269 c.0.signing_data = Some((signer, signing_identity));
270 ExternalClientBuilder(c)
271 }
272}
273
274impl<C: IntoConfig> ExternalClientBuilder<C>
275where
276 C::IdentityProvider: IdentityProvider + Clone,
277 C::MlsRules: MlsRules + Clone,
278 C::CryptoProvider: CryptoProvider + Clone,
279{
280 pub(crate) fn build_config(self) -> IntoConfigOutput<C> {
281 let mut c = self.0.into_config();
282
283 if c.0.settings.protocol_versions.is_empty() {
284 c.0.settings.protocol_versions = ProtocolVersion::all().collect();
285 }
286
287 c
288 }
289
290 pub fn build(self) -> ExternalClient<IntoConfigOutput<C>> {
295 let mut c = self.build_config();
296 let signing_data = c.0.signing_data.take();
297 ExternalClient::new(c, signing_data)
298 }
299}
300
301#[derive(Debug)]
303pub struct Missing;
304
305pub type WithIdentityProvider<I, C> =
309 Config<I, <C as IntoConfig>::MlsRules, <C as IntoConfig>::CryptoProvider>;
310
311pub type WithMlsRules<Pr, C> =
315 Config<<C as IntoConfig>::IdentityProvider, Pr, <C as IntoConfig>::CryptoProvider>;
316
317pub type WithCryptoProvider<Cp, C> =
321 Config<<C as IntoConfig>::IdentityProvider, <C as IntoConfig>::MlsRules, Cp>;
322
323pub type IntoConfigOutput<C> = Config<
325 <C as IntoConfig>::IdentityProvider,
326 <C as IntoConfig>::MlsRules,
327 <C as IntoConfig>::CryptoProvider,
328>;
329
330impl<Ip, Pr, Cp> ExternalClientConfig for ConfigInner<Ip, Pr, Cp>
331where
332 Ip: IdentityProvider + Clone,
333 Pr: MlsRules + Clone,
334 Cp: CryptoProvider + Clone,
335{
336 type IdentityProvider = Ip;
337 type MlsRules = Pr;
338 type CryptoProvider = Cp;
339
340 fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
341 self.settings.protocol_versions.clone()
342 }
343
344 fn identity_provider(&self) -> Self::IdentityProvider {
345 self.identity_provider.clone()
346 }
347
348 fn crypto_provider(&self) -> Self::CryptoProvider {
349 self.crypto_provider.clone()
350 }
351
352 fn external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey> {
353 self.settings
354 .external_signing_keys
355 .get(external_key_id)
356 .cloned()
357 }
358
359 fn mls_rules(&self) -> Self::MlsRules {
360 self.mls_rules.clone()
361 }
362
363 fn max_epoch_jitter(&self) -> Option<u64> {
364 self.settings.max_epoch_jitter
365 }
366
367 fn cache_proposals(&self) -> bool {
368 self.settings.cache_proposals
369 }
370}
371
372impl<Ip, Mpf, Cp> Sealed for Config<Ip, Mpf, Cp> {}
373
374impl<Ip, Pr, Cp> MlsConfig for Config<Ip, Pr, Cp>
375where
376 Ip: IdentityProvider + Clone,
377 Pr: MlsRules + Clone,
378 Cp: CryptoProvider + Clone,
379{
380 type Output = ConfigInner<Ip, Pr, Cp>;
381
382 fn get(&self) -> &Self::Output {
383 &self.0
384 }
385}
386
387pub trait MlsConfig: Send + Sync + Clone + Sealed {
393 #[doc(hidden)]
394 type Output: ExternalClientConfig;
395
396 #[doc(hidden)]
397 fn get(&self) -> &Self::Output;
398}
399
400impl<T: MlsConfig> ExternalClientConfig for T {
402 type IdentityProvider = <T::Output as ExternalClientConfig>::IdentityProvider;
403 type MlsRules = <T::Output as ExternalClientConfig>::MlsRules;
404 type CryptoProvider = <T::Output as ExternalClientConfig>::CryptoProvider;
405
406 fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
407 self.get().supported_protocol_versions()
408 }
409
410 fn identity_provider(&self) -> Self::IdentityProvider {
411 self.get().identity_provider()
412 }
413
414 fn crypto_provider(&self) -> Self::CryptoProvider {
415 self.get().crypto_provider()
416 }
417
418 fn external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey> {
419 self.get().external_signing_key(external_key_id)
420 }
421
422 fn mls_rules(&self) -> Self::MlsRules {
423 self.get().mls_rules()
424 }
425
426 fn cache_proposals(&self) -> bool {
427 self.get().cache_proposals()
428 }
429
430 fn max_epoch_jitter(&self) -> Option<u64> {
431 self.get().max_epoch_jitter()
432 }
433
434 fn version_supported(&self, version: ProtocolVersion) -> bool {
435 self.get().version_supported(version)
436 }
437}
438
439#[derive(Clone)]
440pub(crate) struct Settings {
441 pub(crate) extension_types: Vec<ExtensionType>,
442 pub(crate) custom_proposal_types: Vec<ProposalType>,
443 pub(crate) protocol_versions: Vec<ProtocolVersion>,
444 pub(crate) external_signing_keys: HashMap<Vec<u8>, SignaturePublicKey>,
445 pub(crate) max_epoch_jitter: Option<u64>,
446 pub(crate) cache_proposals: bool,
447}
448
449impl Debug for Settings {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 f.debug_struct("Settings")
452 .field("extension_types", &self.extension_types)
453 .field("custom_proposal_types", &self.custom_proposal_types)
454 .field("protocol_versions", &self.protocol_versions)
455 .field(
456 "external_signing_keys",
457 &mls_rs_core::debug::pretty_with(|f| {
458 f.debug_map()
459 .entries(
460 self.external_signing_keys
461 .iter()
462 .map(|(k, v)| (mls_rs_core::debug::pretty_bytes(k), v)),
463 )
464 .finish()
465 }),
466 )
467 .field("max_epoch_jitter", &self.max_epoch_jitter)
468 .field("cache_proposals", &self.cache_proposals)
469 .finish()
470 }
471}
472
473impl Default for Settings {
474 fn default() -> Self {
475 Self {
476 cache_proposals: true,
477 extension_types: vec![],
478 protocol_versions: vec![],
479 external_signing_keys: Default::default(),
480 max_epoch_jitter: None,
481 custom_proposal_types: vec![],
482 }
483 }
484}
485
486mod private {
489 use mls_rs_core::{crypto::SignatureSecretKey, identity::SigningIdentity};
490
491 use super::{IntoConfigOutput, Settings};
492
493 #[derive(Clone, Debug)]
494 pub struct Config<Ip, Pr, Cp>(pub(crate) ConfigInner<Ip, Pr, Cp>);
495
496 #[derive(Clone, Debug)]
497 pub struct ConfigInner<Ip, Mpf, Cp> {
498 pub(crate) settings: Settings,
499 pub(crate) identity_provider: Ip,
500 pub(crate) mls_rules: Mpf,
501 pub(crate) crypto_provider: Cp,
502 pub(crate) signing_data: Option<(SignatureSecretKey, SigningIdentity)>,
503 }
504
505 pub trait IntoConfig {
506 type IdentityProvider;
507 type MlsRules;
508 type CryptoProvider;
509
510 fn into_config(self) -> IntoConfigOutput<Self>;
511 }
512
513 impl<Ip, Pr, Cp> IntoConfig for Config<Ip, Pr, Cp> {
514 type IdentityProvider = Ip;
515 type MlsRules = Pr;
516 type CryptoProvider = Cp;
517
518 fn into_config(self) -> Self {
519 self
520 }
521 }
522}
523
524use mls_rs_core::{
525 crypto::SignatureSecretKey,
526 identity::{IdentityProvider, SigningIdentity},
527};
528use private::{Config, ConfigInner, IntoConfig};
529
530#[cfg(test)]
531pub(crate) mod test_utils {
532 use crate::{
533 cipher_suite::CipherSuite, crypto::test_utils::TestCryptoProvider,
534 identity::basic::BasicIdentityProvider,
535 };
536
537 use super::{
538 ExternalBaseConfig, ExternalClientBuilder, WithCryptoProvider, WithIdentityProvider,
539 };
540
541 pub type TestExternalClientConfig = WithIdentityProvider<
542 BasicIdentityProvider,
543 WithCryptoProvider<TestCryptoProvider, ExternalBaseConfig>,
544 >;
545
546 pub type TestExternalClientBuilder = ExternalClientBuilder<TestExternalClientConfig>;
547
548 impl TestExternalClientBuilder {
549 pub fn new_for_test() -> Self {
550 ExternalClientBuilder::new()
551 .crypto_provider(TestCryptoProvider::new())
552 .identity_provider(BasicIdentityProvider::new())
553 }
554
555 pub fn new_for_test_disabling_cipher_suite(cipher_suite: CipherSuite) -> Self {
556 let crypto_provider = TestCryptoProvider::with_enabled_cipher_suites(
557 TestCryptoProvider::all_supported_cipher_suites()
558 .into_iter()
559 .filter(|cs| cs != &cipher_suite)
560 .collect(),
561 );
562
563 ExternalClientBuilder::new()
564 .crypto_provider(crypto_provider)
565 .identity_provider(BasicIdentityProvider::new())
566 }
567 }
568}