core_crypto/mls/session/
config.rs

1use core_crypto_keystore::ConnectionType;
2use mls_crypto_provider::{DatabaseKey, EntropySeed};
3use typed_builder::TypedBuilder;
4
5use crate::{
6    MlsError,
7    mls::{
8        ciphersuite::MlsCiphersuite,
9        error::{Error, Result},
10    },
11    prelude::{ClientId, INITIAL_KEYING_MATERIAL_COUNT},
12};
13
14/// Configuration parameters for [Session][crate::mls::session::Session]
15///
16/// These can be set directly or via the [builder][SessionConfig::builder].
17#[derive(Debug, Clone, TypedBuilder)]
18pub struct SessionConfig<'a> {
19    /// Connection type for the database
20    pub db_connection_type: ConnectionType<'a>,
21    /// Database key to be used to instantiate the [MlsCryptoProvider][mls_crypto_provider::MlsCryptoProvider]
22    pub database_key: DatabaseKey,
23    /// Identifier for the client to be used by [Session][crate::mls::session::Session]
24    ///
25    /// If set, this initializes the MLS session. Otherwise, the session is left uninitialized.
26    #[builder(default, setter(strip_option(fallback = client_id_opt)))]
27    pub client_id: Option<ClientId>,
28    /// Entropy pool seed for the internal PRNG
29    #[builder(default, setter(strip_option(fallback = external_entropy_opt)))]
30    pub external_entropy: Option<&'a [u8]>,
31    /// All supported ciphersuites in this session
32    #[builder(default, setter(transform = |iter: impl IntoIterator<Item = MlsCiphersuite>| iter.into_iter().collect()))]
33    pub ciphersuites: Vec<MlsCiphersuite>,
34    /// Number of [openmls::prelude::KeyPackage] to create when creating a MLS client.
35    ///
36    /// Defaults to [crate::prelude::INITIAL_KEYING_MATERIAL_COUNT].
37    #[builder(default)]
38    pub nb_key_packages: Option<usize>,
39}
40impl<'a, Key, ClientId, ExternalEntropy, Ciphersuites, KPs>
41    SessionConfigBuilder<'a, ((), Key, ClientId, ExternalEntropy, Ciphersuites, KPs)>
42{
43    /// Use an in-memory database
44    pub fn in_memory(
45        self,
46    ) -> SessionConfigBuilder<'a, ((ConnectionType<'a>,), Key, ClientId, ExternalEntropy, Ciphersuites, KPs)> {
47        self.db_connection_type(ConnectionType::InMemory)
48    }
49
50    /// Use a persistent database at the given path
51    pub fn persistent(
52        self,
53        path: &'a str,
54    ) -> SessionConfigBuilder<'a, ((ConnectionType<'a>,), Key, ClientId, ExternalEntropy, Ciphersuites, KPs)> {
55        self.db_connection_type(ConnectionType::Persistent(path))
56    }
57}
58
59/// Validated configuration parameters for [Session][crate::mls::session::Session].
60///
61/// These can not be constructed directly, only via [SessionConfig].
62#[derive(Debug)]
63pub struct ValidatedSessionConfig<'a> {
64    pub(super) db_connection_type: ConnectionType<'a>,
65    pub(super) database_key: DatabaseKey,
66    pub(super) client_id: Option<ClientId>,
67    pub(super) external_entropy: Option<EntropySeed>,
68    pub(super) ciphersuites: Vec<MlsCiphersuite>,
69    pub(super) nb_key_packages: usize,
70}
71
72impl<'a> SessionConfig<'a> {
73    /// Validate this configuration to produce a validated configuration.
74    ///
75    /// This can then be passed to [Session::try_new][crate::mls::session::Session::try_new].
76    pub fn validate(self) -> Result<ValidatedSessionConfig<'a>> {
77        let Self {
78            db_connection_type,
79            database_key,
80            client_id,
81            external_entropy,
82            ciphersuites,
83            nb_key_packages,
84        } = self;
85
86        if let ConnectionType::Persistent(path) = &db_connection_type
87            && path.trim().is_empty()
88        {
89            return Err(Error::MalformedIdentifier("persistent db path"));
90        }
91        if let Some(client_id) = &client_id
92            && client_id.is_empty()
93        {
94            return Err(Error::MalformedIdentifier("client_id"));
95        }
96
97        if client_id.is_some() && ciphersuites.is_empty() {
98            return Err(Error::MalformedIdentifier(
99                "ciphersuites must be non-empty if initializing (i.e. client_id is set)",
100            ));
101        }
102
103        let external_entropy = external_entropy
104            .map(EntropySeed::try_from_slice)
105            .transpose()
106            .map_err(MlsError::wrap("gathering external entropy"))?;
107
108        let nb_key_packages = nb_key_packages.unwrap_or(INITIAL_KEYING_MATERIAL_COUNT);
109
110        Ok(ValidatedSessionConfig {
111            db_connection_type,
112            database_key,
113            client_id,
114            external_entropy,
115            ciphersuites,
116            nb_key_packages,
117        })
118    }
119}
120
121impl<'a> TryFrom<SessionConfig<'a>> for ValidatedSessionConfig<'a> {
122    type Error = Error;
123
124    fn try_from(value: SessionConfig<'a>) -> std::result::Result<Self, Self::Error> {
125        value.validate()
126    }
127}