diff --git a/crypto-ffi/src/core_crypto/mod.rs b/crypto-ffi/src/core_crypto/mod.rs index 2e7e71023f..21aae03c46 100644 --- a/crypto-ffi/src/core_crypto/mod.rs +++ b/crypto-ffi/src/core_crypto/mod.rs @@ -17,7 +17,7 @@ use crate::{CoreCryptoResult, Database}; /// CoreCrypto wraps around MLS and Proteus implementations and provides a transactional interface for each. #[derive(Debug, uniffi::Object)] pub struct CoreCryptoFfi { - pub(crate) inner: core_crypto::CoreCrypto, + pub(crate) inner: Arc, } /// Construct a new `CoreCryptoFfi` instance. diff --git a/crypto/src/ephemeral.rs b/crypto/src/ephemeral.rs index fdef1a4780..5468731694 100644 --- a/crypto/src/ephemeral.rs +++ b/crypto/src/ephemeral.rs @@ -126,7 +126,7 @@ impl CoreCrypto { /// /// This client exposes the full interface of `CoreCrypto`, but it should only be used to decrypt messages. /// Other use is a logic error. - pub async fn history_client(history_secret: HistorySecret) -> Result { + pub async fn history_client(history_secret: HistorySecret) -> Result> { if !history_secret .client_id .starts_with(HISTORY_CLIENT_ID_PREFIX.as_bytes()) diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index 62b5ff49c7..72fbbf46ae 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -122,15 +122,13 @@ impl MlsTransport for CoreCryptoTransportNotImplementedProvider { /// /// As [std::ops::Deref] is implemented, this struct is automatically dereferred to [mls::session::Session] apart from /// `proteus_*` calls -/// -/// This is cheap to clone as all internal members have `Arc` wrappers or are `Copy`. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct CoreCrypto { database: Database, - pki_environment: Arc>>, - mls: Arc>>>, + pki_environment: RwLock>, + mls: RwLock>>, #[cfg(feature = "proteus")] - proteus: Arc>>, + proteus: Mutex>, #[cfg(not(feature = "proteus"))] #[allow(dead_code)] proteus: (), @@ -138,13 +136,14 @@ pub struct CoreCrypto { impl CoreCrypto { /// Create an new CoreCrypto client without any initialized session. - pub fn new(database: Database) -> Self { + pub fn new(database: Database) -> Arc { Self { database, pki_environment: Default::default(), mls: Default::default(), proteus: Default::default(), } + .into() } /// Set the session's PKI Environment diff --git a/crypto/src/proteus.rs b/crypto/src/proteus.rs index 67383d7c56..7c00baa1e8 100644 --- a/crypto/src/proteus.rs +++ b/crypto/src/proteus.rs @@ -601,7 +601,7 @@ mod tests { .await .unwrap(); - let cc: CoreCrypto = CoreCrypto::new(db); + let cc = CoreCrypto::new(db); let context = cc.new_transaction().await.unwrap(); assert!(context.proteus_init().await.is_ok()); assert!(context.proteus_new_prekey(1).await.is_ok()); @@ -624,7 +624,7 @@ mod tests { .await .unwrap(); - let cc: CoreCrypto = CoreCrypto::new(db.clone()); + let cc = CoreCrypto::new(db.clone()); let hooks = Arc::new(DummyPkiEnvironmentHooks); let pki_env = PkiEnvironment::new(hooks, db).await.expect("creating pki environment"); cc.set_pki_environment(Some(pki_env)) diff --git a/crypto/src/test_utils/mod.rs b/crypto/src/test_utils/mod.rs index e84f7dccc8..7b7460dd79 100644 --- a/crypto/src/test_utils/mod.rs +++ b/crypto/src/test_utils/mod.rs @@ -96,7 +96,7 @@ pub struct SessionContext { mls_transport: Arc>>, x509_test_chain: Arc>, history_observer: Arc>>>, - core_crypto: CoreCrypto, + core_crypto: Arc, // We need to store the `TempDir` struct for the duration of the test session, // because its drop implementation takes care of the directory deletion. _db: Option<(Database, Arc)>, @@ -174,7 +174,7 @@ impl SessionContext { pub(crate) async fn new_from_cc( context: &TestContext, - core_crypto: CoreCrypto, + core_crypto: Arc, chain: Option<&X509TestChain>, ) -> Self { let transport = context.transport.clone(); diff --git a/crypto/src/transaction_context/mod.rs b/crypto/src/transaction_context/mod.rs index 03190ff3b7..fb63f3ec8b 100644 --- a/crypto/src/transaction_context/mod.rs +++ b/crypto/src/transaction_context/mod.rs @@ -9,8 +9,6 @@ pub use error::{Error, Result}; use openmls_traits::OpenMlsCryptoProvider as _; use wire_e2e_identity::pki_env::PkiEnvironment; -#[cfg(feature = "proteus")] -use crate::proteus::ProteusCentral; use crate::{ ClientId, ConversationId, CoreCrypto, CredentialFindFilters, CredentialRef, KeystoreError, MlsConversation, MlsError, MlsTransport, RecursiveError, Session, @@ -45,13 +43,9 @@ pub struct TransactionContext { #[derive(Debug, Clone)] enum TransactionContextInner { Valid { - pki_environment: Arc>>, - database: Database, - mls_session: Arc>>>, + core_crypto: Arc, mls_groups: Arc>>, pending_epoch_changes: Arc>>, - #[cfg(feature = "proteus")] - proteus_central: Arc>>, }, Invalid, } @@ -60,15 +54,8 @@ impl CoreCrypto { /// Creates a new transaction. All operations that persist data will be /// buffered in memory and when [TransactionContext::finish] is called, the data will be persisted /// in a single database transaction. - pub async fn new_transaction(&self) -> Result { - TransactionContext::new( - self.database.clone(), - self.pki_environment.clone(), - self.mls.clone(), - #[cfg(feature = "proteus")] - self.proteus.clone(), - ) - .await + pub async fn new_transaction(self: &Arc) -> Result { + TransactionContext::new(self.clone()).await } } @@ -91,27 +78,18 @@ impl HasSessionAndCrypto for TransactionContext { } impl TransactionContext { - async fn new( - keystore: Database, - pki_environment: Arc>>, - mls_session: Arc>>>, - #[cfg(feature = "proteus")] proteus_central: Arc>>, - ) -> Result { - keystore + async fn new(core_crypto: Arc) -> Result { + core_crypto + .database .new_transaction() .await .map_err(MlsError::wrap("creating new transaction"))?; - let mls_groups = Arc::new(RwLock::new(Default::default())); Ok(Self { inner: Arc::new( TransactionContextInner::Valid { - database: keystore, - pki_environment, - mls_session: mls_session.clone(), - mls_groups, + core_crypto, + mls_groups: Default::default(), pending_epoch_changes: Default::default(), - #[cfg(feature = "proteus")] - proteus_central, } .into(), ), @@ -120,7 +98,7 @@ impl TransactionContext { pub(crate) async fn session(&self) -> Result> { match &*self.inner.read().await { - TransactionContextInner::Valid { mls_session, .. } => mls_session.read().await.as_ref().cloned().ok_or( + TransactionContextInner::Valid { core_crypto, .. } => core_crypto.mls.read().await.as_ref().cloned().ok_or( RecursiveError::mls_client("Getting mls session from transaction context")( mls::session::Error::MlsNotInitialized, ) @@ -133,8 +111,8 @@ impl TransactionContext { #[cfg(test)] pub(crate) async fn set_session_if_exists(&self, new_session: Session) { match &*self.inner.read().await { - TransactionContextInner::Valid { mls_session, .. } => { - let mut guard = mls_session.write().await; + TransactionContextInner::Valid { core_crypto, .. } => { + let mut guard = core_crypto.mls.write().await; if guard.as_ref().is_some() { *guard = Some(new_session) @@ -146,14 +124,18 @@ impl TransactionContext { pub(crate) async fn mls_transport(&self) -> Result> { match &*self.inner.read().await { - TransactionContextInner::Valid { mls_session, .. } => { - mls_session.read().await.as_ref().map(|s| s.transport.clone()).ok_or( + TransactionContextInner::Valid { core_crypto, .. } => core_crypto + .mls + .read() + .await + .as_ref() + .map(|s| s.transport.clone()) + .ok_or( RecursiveError::mls_client("Getting mls session from transaction context")( mls::session::Error::MlsNotInitialized, ) .into(), - ) - } + ), TransactionContextInner::Invalid => Err(Error::InvalidTransactionContext), } @@ -162,7 +144,8 @@ impl TransactionContext { /// Clones all references that the [MlsCryptoProvider] comprises. pub async fn mls_provider(&self) -> Result { match &*self.inner.read().await { - TransactionContextInner::Valid { mls_session, .. } => mls_session + TransactionContextInner::Valid { core_crypto, .. } => core_crypto + .mls .read() .await .as_ref() @@ -179,28 +162,32 @@ impl TransactionContext { pub(crate) async fn database(&self) -> Result { match &*self.inner.read().await { - TransactionContextInner::Valid { database, .. } => Ok(database.clone()), + TransactionContextInner::Valid { core_crypto, .. } => Ok(core_crypto.database.clone()), TransactionContextInner::Invalid => Err(Error::InvalidTransactionContext), } } pub(crate) async fn pki_environment(&self) -> Result { match &*self.inner.read().await { - TransactionContextInner::Valid { pki_environment, .. } => { - pki_environment.read().await.as_ref().map(Clone::clone).ok_or( + TransactionContextInner::Valid { core_crypto, .. } => core_crypto + .pki_environment + .read() + .await + .as_ref() + .map(Clone::clone) + .ok_or( RecursiveError::transaction("Getting PKI environment from transaction context")( e2e_identity::Error::PkiEnvironmentUnset, ) .into(), - ) - } + ), TransactionContextInner::Invalid => Err(Error::InvalidTransactionContext), } } pub(crate) async fn pki_environment_option(&self) -> Result> { match &*self.inner.read().await { - TransactionContextInner::Valid { pki_environment, .. } => Ok(pki_environment.read().await.clone()), + TransactionContextInner::Valid { core_crypto, .. } => Ok(core_crypto.pki_environment.read().await.clone()), TransactionContextInner::Invalid => Err(Error::InvalidTransactionContext), } @@ -225,36 +212,28 @@ impl TransactionContext { } } - #[cfg(feature = "proteus")] - pub(crate) async fn proteus_central(&self) -> Result>>> { - match &*self.inner.read().await { - TransactionContextInner::Valid { proteus_central, .. } => Ok(proteus_central.clone()), - TransactionContextInner::Invalid => Err(Error::InvalidTransactionContext), - } - } - /// Commits the transaction, meaning it takes all the enqueued operations and persist them into /// the keystore. After that the internal state is switched to invalid, causing errors if /// something is called from this object. pub async fn finish(&self) -> Result<()> { let mut guard = self.inner.write().await; let TransactionContextInner::Valid { - database, + core_crypto, pending_epoch_changes, - mls_session, .. } = &*guard else { return Err(Error::InvalidTransactionContext); }; - let commit_result = database + let commit_result = core_crypto + .database .commit_transaction() .await .map_err(KeystoreError::wrap("commiting transaction")) .map_err(Into::into); - if let Some(session) = mls_session.read_arc().await.clone() + if let Some(session) = core_crypto.mls.read().await.as_ref() && commit_result.is_ok() { // We need owned values, so we could just clone the conversation ids, but we don't need the events anymore, @@ -276,11 +255,12 @@ impl TransactionContext { pub async fn abort(&self) -> Result<()> { let mut guard = self.inner.write().await; - let TransactionContextInner::Valid { database: keystore, .. } = &*guard else { + let TransactionContextInner::Valid { core_crypto, .. } = &*guard else { return Err(Error::InvalidTransactionContext); }; - let result = keystore + let result = core_crypto + .database .rollback_transaction() .await .map_err(KeystoreError::wrap("rolling back transaction")) @@ -310,8 +290,8 @@ impl TransactionContext { /// Set the `mls_session` Arc (also sets it on the transaction's CoreCrypto instance) pub(crate) async fn set_mls_session(&self, session: Session) -> Result<()> { match &*self.inner.read().await { - TransactionContextInner::Valid { mls_session, .. } => { - let mut guard = mls_session.write().await; + TransactionContextInner::Valid { core_crypto, .. } => { + let mut guard = core_crypto.mls.write().await; *guard = Some(session); Ok(()) } diff --git a/crypto/src/transaction_context/proteus.rs b/crypto/src/transaction_context/proteus.rs index c4db11c479..876ef36d84 100644 --- a/crypto/src/transaction_context/proteus.rs +++ b/crypto/src/transaction_context/proteus.rs @@ -1,6 +1,6 @@ //! This module contains all [super::TransactionContext] methods concerning proteus. -use super::{Error, Result, TransactionContext}; +use super::{Error, Result, TransactionContext, TransactionContextInner}; use crate::{ RecursiveError, group_store::GroupStoreValue, @@ -21,8 +21,10 @@ impl TransactionContext { .await .map_err(RecursiveError::root("getting last resort prekey"))?; - let mutex = self.proteus_central().await?; - let mut guard = mutex.lock().await; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; *guard = Some(proteus_client); Ok(()) } @@ -32,9 +34,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or it will do /// nothing pub async fn proteus_reload_sessions(&self) -> Result<()> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let Some(proteus) = mutex.as_mut() else { return Ok(()) }; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let Some(proteus) = guard.as_mut() else { return Ok(()) }; let keystore = self.database().await?; proteus .reload_sessions(&keystore) @@ -52,9 +56,11 @@ impl TransactionContext { session_id: &str, prekey: &[u8], ) -> Result> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; let session = proteus .session_from_prekey(session_id, prekey) @@ -76,9 +82,11 @@ impl TransactionContext { session_id: &str, envelope: &[u8], ) -> Result<(GroupStoreValue, Vec)> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let mut keystore = self.database().await?; let (session, message) = proteus .session_from_message(&mut keystore, session_id, envelope) @@ -96,9 +104,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_session_save(&self, session_id: &str) -> Result<()> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .session_save(&keystore, session_id) @@ -112,9 +122,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_session_delete(&self, session_id: &str) -> Result<()> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .session_delete(&keystore, session_id) @@ -131,9 +143,11 @@ impl TransactionContext { &self, session_id: &str, ) -> Result>> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .session(session_id, &keystore) @@ -147,9 +161,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_session_exists(&self, session_id: &str) -> Result { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; Ok(proteus.session_exists(session_id, &keystore).await) } @@ -159,9 +175,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_decrypt(&self, session_id: &str, ciphertext: &[u8]) -> Result> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let mut keystore = self.database().await?; proteus .decrypt(&mut keystore, session_id, ciphertext) @@ -175,9 +193,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_encrypt(&self, session_id: &str, plaintext: &[u8]) -> Result> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let mut keystore = self.database().await?; proteus .encrypt(&mut keystore, session_id, plaintext) @@ -196,9 +216,11 @@ impl TransactionContext { sessions: &[impl AsRef], plaintext: &[u8], ) -> Result>> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let mut keystore = self.database().await?; proteus .encrypt_batched(&mut keystore, sessions, plaintext) @@ -212,9 +234,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_new_prekey(&self, prekey_id: u16) -> Result> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .new_prekey(prekey_id, &keystore) @@ -229,9 +253,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_new_prekey_auto(&self) -> Result<(u16, Vec)> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .new_prekey_auto(&keystore) @@ -242,9 +268,11 @@ impl TransactionContext { /// Returns the last resort prekey pub async fn proteus_last_resort_prekey(&self) -> Result> { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus @@ -264,9 +292,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_fingerprint(&self) -> Result { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; Ok(proteus.fingerprint()) } @@ -275,9 +305,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_fingerprint_local(&self, session_id: &str) -> Result { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .fingerprint_local(session_id, &keystore) @@ -291,9 +323,11 @@ impl TransactionContext { /// Warning: The Proteus client **MUST** be initialized with [TransactionContext::proteus_init] first or an error /// will be returned pub async fn proteus_fingerprint_remote(&self, session_id: &str) -> Result { - let arc = self.proteus_central().await?; - let mut mutex = arc.lock().await; - let proteus = mutex.as_mut().ok_or(Error::ProteusNotInitialized)?; + let TransactionContextInner::Valid { core_crypto, .. } = &*self.inner.read().await else { + return Err(Error::InvalidTransactionContext); + }; + let mut guard = core_crypto.proteus.lock().await; + let proteus = guard.as_mut().ok_or(Error::ProteusNotInitialized)?; let keystore = self.database().await?; proteus .fingerprint_remote(session_id, &keystore)