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