core_crypto/mls/session/
mod.rs1mod 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#[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 pub(crate) conversation_cache: Arc<Mutex<ConversationCache>>,
52}
53
54impl Session {
55 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 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 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 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 pub async fn close(&self) -> crate::mls::Result<()> {
110 self.crypto_provider
111 .close()
112 .await
113 .map_err(OpenMlsError::wrap("closing connection with keystore"))
114 .map_err(Into::into)
115 }
116
117 pub fn database(&self) -> &ImmutableDatabase {
119 &self.database
120 }
121
122 pub async fn reseed(&self, seed: Option<EntropySeed>) -> crate::mls::Result<()> {
124 self.crypto_provider
125 .reseed(seed)
126 .map_err(OpenMlsError::wrap("reseeding mls backend"))
127 .map_err(Into::into)
128 }
129
130 pub(crate) async fn restore_from_history_secret(&self, history_secret: HistorySecret) -> Result<()> {
132 history_secret
134 .key_package
135 .store(&self.crypto_provider)
136 .await
137 .map_err(OpenMlsError::wrap("storing key package encapsulation"))?;
138
139 Ok(())
140 }
141
142 pub fn id(&self) -> ClientId {
144 self.id.clone()
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use core_crypto_keystore::{entities::*, traits::FetchFromDatabase};
151
152 use super::*;
153 use crate::{KeystoreError, mls_provider::CryptoProvider, transaction_context::test_utils::EntitiesCount};
154
155 impl Session {
156 #![allow(missing_docs)]
158
159 pub async fn find_keypackages(&self, backend: &CryptoProvider) -> Result<Vec<openmls::prelude::KeyPackage>> {
160 use core_crypto_keystore::CryptoKeystoreMls as _;
161 let kps = backend
162 .key_store()
163 .mls_fetch_key_packages::<openmls::prelude::KeyPackage>(u32::MAX)
164 .await
165 .map_err(KeystoreError::wrap("fetching mls keypackages"))?;
166 Ok(kps)
167 }
168
169 pub async fn count_entities(&self) -> EntitiesCount {
171 let keystore = &self.database;
172 let credential = keystore.count::<StoredCredential>().await.unwrap();
173 let encryption_keypair = keystore.count::<StoredEncryptionKeyPair>().await.unwrap();
174 let epoch_encryption_keypair = keystore.count::<StoredEpochEncryptionKeypair>().await.unwrap();
175 let enrollment = keystore.count::<StoredE2eiEnrollment>().await.unwrap();
176 let group = keystore.count::<PersistedMlsGroup>().await.unwrap();
177 let hpke_private_key = keystore.count::<StoredHpkePrivateKey>().await.unwrap();
178 let key_package = keystore.count::<StoredKeypackage>().await.unwrap();
179 let pending_group = keystore.count::<PersistedMlsPendingGroup>().await.unwrap();
180 let pending_messages = keystore.count::<MlsPendingMessage>().await.unwrap();
181 let psk_bundle = keystore.count::<StoredPskBundle>().await.unwrap();
182 EntitiesCount {
183 credential,
184 encryption_keypair,
185 epoch_encryption_keypair,
186 enrollment,
187 group,
188 hpke_private_key,
189 key_package,
190 pending_group,
191 pending_messages,
192 psk_bundle,
193 }
194 }
195 }
196}