Skip to main content

core_crypto/mls/session/
mod.rs

1mod credential;
2pub(crate) mod e2e_identity;
3mod epoch_observer;
4mod error;
5mod history_observer;
6pub(crate) mod id;
7pub(crate) mod key_package;
8pub(crate) mod user_id;
9
10use std::sync::Arc;
11
12use async_lock::{Mutex, RwLock};
13pub use epoch_observer::EpochObserver;
14pub(crate) use error::{Error, Result};
15pub use history_observer::HistoryObserver;
16use openmls_traits::OpenMlsCryptoProvider;
17
18use crate::{
19    ClientId, HistorySecret, ImmutableDatabase, LeafError, MlsTransport, OpenMlsError, RecursiveError,
20    mls::{
21        conversation::{Conversation, ConversationIdRef},
22        conversation_cache::ConversationCache,
23    },
24    mls_provider::{CryptoProvider, EntropySeed},
25};
26
27/// A MLS Session enables a user device to communicate via the MLS protocol.
28///
29/// This closely maps to the `Client` term in [RFC 9720], but we avoid that term to avoid ambiguity;
30/// `Client` is very overloaded with distinct meanings.
31///
32/// There is one `Session` per user per device. A session can contain many MLS groups/conversations.
33///
34/// It is cheap to clone a `Session` because everything heavy is wrapped inside an [Arc].
35///
36/// [RFC 9720]: https://www.rfc-editor.org/rfc/rfc9420.html
37#[derive(Clone, derive_more::Debug)]
38pub struct Session {
39    id: ClientId,
40    pub(crate) crypto_provider: CryptoProvider,
41    pub(crate) transport: Arc<dyn MlsTransport + 'static>,
42    database: ImmutableDatabase,
43    #[debug("EpochObserver")]
44    pub(crate) epoch_observer: Arc<RwLock<Option<Arc<dyn EpochObserver + 'static>>>>,
45    #[debug("HistoryObserver")]
46    pub(crate) history_observer: Arc<RwLock<Option<Arc<dyn HistoryObserver + 'static>>>>,
47    /// LRU cache of live MLS conversations.
48    ///
49    /// Shared across transactions for cache reuse;
50    /// cleared on transaction rollback to avoid serving stale state.
51    pub(crate) conversation_cache: Arc<Mutex<ConversationCache>>,
52}
53
54impl Session {
55    /// Create a new `Session`
56    pub fn new(
57        id: ClientId,
58        crypto_provider: CryptoProvider,
59        database: ImmutableDatabase,
60        transport: Arc<dyn MlsTransport>,
61    ) -> Self {
62        Self {
63            id,
64            crypto_provider,
65            transport,
66            database,
67            epoch_observer: Arc::new(RwLock::new(None)),
68            history_observer: Arc::new(RwLock::new(None)),
69            conversation_cache: Arc::new(Mutex::new(ConversationCache::new())),
70        }
71    }
72
73    /// Get an immutable view of an MLS conversation.
74    ///
75    /// This may be faster than
76    /// [crate::transaction_context::TransactionContext::conversation].
77    pub async fn get_raw_conversation(&self, id: &ConversationIdRef) -> Result<Conversation> {
78        Conversation::load(self.clone(), id)
79            .await
80            .map_err(RecursiveError::mls_conversation("getting raw conversation by id"))?
81            .ok_or_else(|| LeafError::ConversationNotFound(id.to_owned()))
82            .map_err(Into::into)
83    }
84
85    /// Checks if a given conversation id exists locally
86    pub async fn conversation_exists(&self, id: &ConversationIdRef) -> Result<bool> {
87        match self.get_raw_conversation(id).await {
88            Ok(_) => Ok(true),
89            Err(Error::Leaf(LeafError::ConversationNotFound(_))) => Ok(false),
90            Err(e) => Err(e),
91        }
92    }
93
94    /// Generates a random byte array of the specified size
95    pub fn random_bytes(&self, len: usize) -> crate::mls::Result<Vec<u8>> {
96        use openmls_traits::random::OpenMlsRand as _;
97        self.crypto_provider
98            .rand()
99            .random_vec(len)
100            .map_err(OpenMlsError::wrap("generating random vector"))
101            .map_err(Into::into)
102    }
103
104    /// Get read-only access to the database.
105    pub fn database(&self) -> &ImmutableDatabase {
106        &self.database
107    }
108
109    /// see [crate::mls_provider::CryptoProvider::reseed]
110    pub async fn reseed(&self, seed: Option<EntropySeed>) -> crate::mls::Result<()> {
111        self.crypto_provider
112            .reseed(seed)
113            .map_err(OpenMlsError::wrap("reseeding mls backend"))
114            .map_err(Into::into)
115    }
116
117    /// Restore from an external [`HistorySecret`].
118    pub(crate) async fn restore_from_history_secret(&self, history_secret: HistorySecret) -> Result<()> {
119        // store the key package
120        history_secret
121            .key_package
122            .store(&self.crypto_provider)
123            .await
124            .map_err(OpenMlsError::wrap("storing key package encapsulation"))?;
125
126        Ok(())
127    }
128
129    /// Retrieves the client's client id. This is free-form and not inspected.
130    pub fn id(&self) -> ClientId {
131        self.id.clone()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use core_crypto_keystore::{entities::*, traits::FetchFromDatabase};
138
139    use super::*;
140    use crate::{KeystoreError, mls_provider::CryptoProvider, transaction_context::test_utils::EntitiesCount};
141
142    impl Session {
143        // test functions are not held to the same documentation standard as proper functions
144        #![allow(missing_docs)]
145
146        pub async fn find_keypackages(&self, backend: &CryptoProvider) -> Result<Vec<openmls::prelude::KeyPackage>> {
147            let kps = backend
148                .key_store()
149                .mls_fetch_key_packages::<openmls::prelude::KeyPackage>(u32::MAX)
150                .await
151                .map_err(KeystoreError::wrap("fetching mls keypackages"))?;
152            Ok(kps)
153        }
154
155        /// Count the entities
156        pub async fn count_entities(&self) -> EntitiesCount {
157            let keystore = &self.database;
158            let credential = keystore.count::<StoredCredential>().await.unwrap();
159            let encryption_keypair = keystore.count::<StoredEncryptionKeyPair>().await.unwrap();
160            let epoch_encryption_keypair = keystore.count::<StoredEpochEncryptionKeypair>().await.unwrap();
161            let group = keystore.count::<PersistedMlsGroup>().await.unwrap();
162            let hpke_private_key = keystore.count::<StoredHpkePrivateKey>().await.unwrap();
163            let key_package = keystore.count::<StoredKeypackage>().await.unwrap();
164            let pending_group = keystore.count::<PersistedMlsPendingGroup>().await.unwrap();
165            let pending_messages = keystore.count::<MlsPendingMessage>().await.unwrap();
166            let psk_bundle = keystore.count::<StoredPskBundle>().await.unwrap();
167            EntitiesCount {
168                credential,
169                encryption_keypair,
170                epoch_encryption_keypair,
171                group,
172                hpke_private_key,
173                key_package,
174                pending_group,
175                pending_messages,
176                psk_bundle,
177            }
178        }
179    }
180}