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