core_crypto/transaction_context/
mod.rs1use std::sync::Arc;
5
6use async_lock::{Mutex, MutexGuardArc, RwLock};
7use core_crypto_keystore::{
8 CryptoKeystoreError, Database, UniqueArc,
9 entities::ConsumerData,
10 traits::{EntityDatabaseMutation as _, FetchFromDatabase as _},
11};
12pub use error::{Error, Result};
13use openmls_traits::OpenMlsCryptoProvider as _;
14use wire_e2e_identity::pki_env::PkiEnvironment;
15
16use crate::{
17 ClientId, ConversationId, CoreCrypto, KeystoreError, MlsTransport, OpenMlsError, RecursiveError, Session,
18 mls::{self, conversation_cache::ConversationCache},
19 mls_provider::CryptoProvider,
20};
21pub mod conversation;
22mod credential;
23pub mod e2e_identity;
24mod error;
25mod inner_guard;
26pub mod key_package;
27#[cfg(feature = "proteus")]
28pub mod proteus;
29mod savepoint;
30#[cfg(test)]
31pub mod test_utils;
32
33#[derive(Debug, Clone)]
44pub struct TransactionContext {
45 inner: Arc<RwLock<Option<TransactionContextInner>>>,
46}
47
48#[derive(derive_more::Debug)]
49pub(crate) struct TransactionContextInner {
50 core_crypto: Arc<CoreCrypto>,
51 pending_epoch_changes: Arc<Mutex<Vec<(ConversationId, u64)>>>,
52 touched_conversations: Mutex<Vec<ConversationId>>,
55 #[debug(skip)]
56 transaction: UniqueArc<core_crypto_keystore::Transaction>,
57}
58
59impl CoreCrypto {
60 pub async fn new_transaction(self: &Arc<Self>) -> Result<TransactionContext> {
64 TransactionContext::new(self.clone()).await
65 }
66}
67
68impl TransactionContext {
69 async fn new(core_crypto: Arc<CoreCrypto>) -> Result<Self> {
70 let transaction = core_crypto
71 .database
72 .new_transaction()
73 .await
74 .map_err(KeystoreError::wrap("creating new transaction"))?;
75 Ok(Self {
76 inner: Arc::new(RwLock::new(Some(TransactionContextInner {
77 core_crypto,
78 pending_epoch_changes: Default::default(),
79 touched_conversations: Default::default(),
80 transaction,
81 }))),
82 })
83 }
84
85 pub(crate) async fn session(&self) -> Result<Arc<Session>> {
86 let inner = self.inner()?;
87 inner.core_crypto.mls.read().await.as_ref().cloned().ok_or(
88 RecursiveError::context("Getting mls session from transaction context")(
89 mls::session::Error::MlsNotInitialized,
90 )
91 .into(),
92 )
93 }
94
95 #[cfg(test)]
96 pub(crate) async fn set_session_if_exists(&self, new_session: &Arc<Session>) {
97 let Ok(inner) = self.inner() else {
98 return;
99 };
100 let mut guard = inner.core_crypto.mls.write().await;
101 if guard.as_ref().is_some() {
102 *guard = Some(new_session.clone())
103 }
104 }
105
106 pub(crate) async fn mls_transport(&self) -> Result<Arc<dyn MlsTransport + 'static>> {
107 let inner = self.inner()?;
108 inner
109 .core_crypto
110 .mls
111 .read()
112 .await
113 .as_ref()
114 .map(|s| s.transport.clone())
115 .ok_or(
116 RecursiveError::context("Getting mls session from transaction context")(
117 mls::session::Error::MlsNotInitialized,
118 )
119 .into(),
120 )
121 }
122
123 pub async fn crypto_provider(&self) -> Result<CryptoProvider> {
125 let inner = self.inner()?;
126 inner
127 .core_crypto
128 .mls
129 .read()
130 .await
131 .as_ref()
132 .map(|s| s.crypto_provider.clone())
133 .ok_or(
134 RecursiveError::context("Getting mls session from transaction context")(
135 mls::session::Error::MlsNotInitialized,
136 )
137 .into(),
138 )
139 }
140
141 pub(crate) fn database(&self) -> Result<Arc<Database>> {
142 let inner = self.inner()?;
143 Ok(inner.core_crypto.database.clone())
144 }
145
146 pub(crate) async fn pki_environment(&self) -> Result<Arc<PkiEnvironment>> {
147 let inner = self.inner()?;
148 inner
149 .core_crypto
150 .pki_environment
151 .read()
152 .await
153 .as_ref()
154 .map(Clone::clone)
155 .ok_or(Error::PkiEnvironmentUnset)
156 }
157
158 pub(crate) async fn mls_groups(&self) -> Result<MutexGuardArc<ConversationCache>> {
159 let inner = self.inner()?;
160 let cache = inner
161 .core_crypto
162 .mls
163 .read()
164 .await
165 .as_ref()
166 .map(|session| session.conversation_cache.clone())
167 .ok_or_else(|| {
168 RecursiveError::context("getting mls session from transaction context")(
169 mls::session::Error::MlsNotInitialized,
170 )
171 })?;
172
173 Ok(cache.lock_arc().await)
174 }
175
176 pub(crate) async fn queue_epoch_changed(&self, conversation_id: ConversationId, epoch: u64) -> Result<()> {
177 let inner = self.inner()?;
178 inner.pending_epoch_changes.lock().await.push((conversation_id, epoch));
179 Ok(())
180 }
181
182 pub async fn finish(&self) -> Result<()> {
186 let TransactionContextInner {
187 core_crypto,
188 pending_epoch_changes,
189 transaction: tx,
190 ..
191 } = self.take_inner().await?;
192
193 let commit_result = tx
194 .commit()
195 .await
196 .map_err(KeystoreError::wrap("commiting transaction"))
197 .map_err(Into::into);
198
199 if let Some(session) = core_crypto.mls.read().await.as_ref() {
200 if commit_result.is_ok() {
201 let mut epoch_changes = pending_epoch_changes.lock().await;
204 for (conversation_id, epoch) in epoch_changes.drain(..) {
205 session.notify_epoch_changed(conversation_id, epoch).await;
206 }
207 } else {
208 session.conversation_cache.lock().await.clear();
212 }
213 }
214
215 commit_result
216 }
217
218 pub async fn abort(&self) -> Result<()> {
222 let inner = self.take_inner().await?;
223
224 if let Some(session) = inner.core_crypto.mls.read().await.as_ref() {
227 session.conversation_cache.lock().await.clear();
228 }
229
230 Ok(())
231 }
232
233 pub async fn mls_init(&self, session_id: ClientId, transport: Arc<dyn MlsTransport>) -> Result<()> {
235 let database = self.database()?;
236 let pki_env = self.pki_environment().await.ok();
237 let crypto_provider = CryptoProvider::new_with_pki_env(database.clone(), pki_env);
238 let session = Session::new(session_id.clone(), crypto_provider, database.into(), transport);
239 self.set_mls_session(session).await?;
240
241 Ok(())
242 }
243
244 pub(crate) async fn set_mls_session(&self, session: Arc<Session>) -> Result<()> {
246 let inner = self.inner()?;
247 let mut guard = inner.core_crypto.mls.write().await;
248 *guard = Some(session);
249 Ok(())
250 }
251
252 pub async fn client_id(&self) -> Result<ClientId> {
254 let session = self.session().await?;
255 Ok(session.id())
256 }
257
258 pub async fn random_bytes(&self, len: usize) -> Result<Vec<u8>> {
260 use openmls_traits::random::OpenMlsRand as _;
261 self.crypto_provider()
262 .await?
263 .rand()
264 .random_vec(len)
265 .map_err(OpenMlsError::wrap("generating random vector"))
266 .map_err(Into::into)
267 }
268
269 pub fn set_data(&self, data: Vec<u8>) -> Result<()> {
273 let inner = self.inner()?;
274 ConsumerData::from(data)
275 .save(inner.transaction())
276 .map_err(KeystoreError::wrap("saving consumer data"))?;
277 Ok(())
278 }
279
280 pub async fn get_data(&self) -> Result<Option<Vec<u8>>> {
283 let inner = self.inner()?;
284 match inner.transaction.get_unique::<ConsumerData>().await {
285 Ok(maybe_data) => Ok(maybe_data.map(Arc::unwrap_or_clone).map(Into::into)),
286 Err(CryptoKeystoreError::NotFound(..)) => Ok(None),
287 Err(err) => Err(KeystoreError::wrap("finding unique consumer data")(err).into()),
288 }
289 }
290}
291
292impl TransactionContextInner {
293 pub(crate) fn transaction(&self) -> &core_crypto_keystore::Transaction {
294 &self.transaction
295 }
296}