core_crypto_ffi/error/
core_crypto.rs1use core_crypto::{InnermostErrorMessage as _, RecursiveError};
2
3#[cfg(feature = "proteus")]
4use crate::ProteusError;
5use crate::{MlsError, error::log_error};
6#[cfg(target_family = "wasm")]
7use wasm_bindgen::JsValue;
8
9#[derive(Debug, thiserror::Error)]
10#[cfg_attr(target_family = "wasm", derive(strum::AsRefStr))]
11#[cfg_attr(not(target_family = "wasm"), derive(uniffi::Error))]
12pub enum CoreCryptoError {
13 #[error(transparent)]
14 Mls(#[from] MlsError),
15 #[cfg(feature = "proteus")]
16 #[error(transparent)]
17 Proteus(#[from] ProteusError),
18 #[error("End to end identity error: {0}")]
19 E2ei(String),
20 #[cfg(target_family = "wasm")]
21 #[error(transparent)]
22 SerializationError(#[from] serde_wasm_bindgen::Error),
23 #[cfg(target_family = "wasm")]
24 #[error("Unknown ciphersuite identifier")]
25 UnknownCiphersuite,
26 #[cfg(target_family = "wasm")]
27 #[error("Transaction rolled back due to unexpected JS error: {0:?}")]
28 TransactionFailed(JsValue),
29 #[cfg(not(target_family = "wasm"))]
30 #[error("Transaction rolled back due to unexpected uniffi error: {0:?}")]
31 TransactionFailed(String),
32 #[error("{0}")]
33 Other(String),
34}
35
36#[cfg(not(target_family = "wasm"))]
37impl From<uniffi::UnexpectedUniFFICallbackError> for CoreCryptoError {
38 fn from(value: uniffi::UnexpectedUniFFICallbackError) -> Self {
39 Self::TransactionFailed(value.to_string())
40 }
41}
42
43impl From<RecursiveError> for CoreCryptoError {
44 fn from(error: RecursiveError) -> Self {
45 log_error(&error);
46
47 let innermost = {
48 let mut err: &dyn std::error::Error = &error;
49 while let Some(inner) = err.source() {
50 err = inner;
51 }
52 err
53 };
54
55 if let Some(err) = innermost.downcast_ref::<core_crypto::e2e_identity::Error>() {
57 return CoreCryptoError::E2ei(err.to_string());
58 }
59
60 macro_rules! matches_option {
70 (
72 $val:expr,
73 $pattern:pat $(if $guard:expr)? => $out:expr
74 ) => {
75 match ($val) {
76 $pattern $(if $guard)? => Some($out),
77 _ => None,
78 }
79 };
80 (
82 $val:expr,
83 #[cfg($meta:meta)]
84 $pattern:pat $(if $guard:expr)? => $out:expr
85 ) => {
86 {
87 #[cfg($meta)]
88 let result = match ($val) {
89 $pattern $(if $guard)? => Some($out),
90 _ => None,
91 };
92 #[cfg(not($meta))]
93 let result = None;
94 result
95 }
96 };
97 }
98
99 macro_rules! match_heterogenous {
103 (
104 $err:expr => {
105 $(
106 $( #[cfg($meta:meta)] )?
107 $pattern:pat $(if $guard:expr)? => $var:expr,
108 )*
109 ||=> $default:expr,
110 }
111 ) => {{
112 if false { unreachable!() }
113 $(
114 else if let Some(v) = matches_option!(
115 $err.downcast_ref(),
116 $( #[cfg($meta)] )?
117 Some($pattern) $(if $guard)? => $var
118 ) {
119 v
120 }
121 )*
122 else {
123 $default
124 }
125 }};
126 }
127
128 match_heterogenous!(innermost => {
129 core_crypto::LeafError::ConversationAlreadyExists(id) => MlsError::ConversationAlreadyExists(id.clone()).into(),
130 core_crypto::mls::conversation::Error::BufferedFutureMessage{..} => MlsError::BufferedFutureMessage.into(),
131 core_crypto::mls::conversation::Error::DuplicateMessage => MlsError::DuplicateMessage.into(),
132 core_crypto::mls::conversation::Error::MessageEpochTooOld => MlsError::MessageEpochTooOld.into(),
133 core_crypto::mls::conversation::Error::SelfCommitIgnored => MlsError::SelfCommitIgnored.into(),
134 core_crypto::mls::conversation::Error::StaleCommit => MlsError::StaleCommit.into(),
135 core_crypto::mls::conversation::Error::StaleProposal => MlsError::StaleProposal.into(),
136 core_crypto::mls::conversation::Error::UnbufferedFarFutureMessage => MlsError::WrongEpoch.into(),
137 core_crypto::mls::conversation::Error::BufferedCommit => MlsError::BufferedCommit.into(),
138 core_crypto::mls::conversation::Error::MessageRejected { reason } => MlsError::MessageRejected { reason: reason.clone() }.into(),
139 core_crypto::mls::conversation::Error::OrphanWelcome => MlsError::OrphanWelcome.into(),
140 #[cfg(feature="proteus")]
141 e @ (
142 core_crypto::ProteusErrorKind::ProteusDecodeError(_)
143 | core_crypto::ProteusErrorKind::ProteusEncodeError(_)
144 | core_crypto::ProteusErrorKind::ProteusInternalError(_)
145 | core_crypto::ProteusErrorKind::ProteusSessionError(_)
146 | core_crypto::ProteusErrorKind::Leaf(_)
147 ) => ProteusError::from(e).into(),
148 core_crypto::mls::conversation::Error::BufferedForPendingConversation => MlsError::UnmergedPendingGroup.into(),
152 ||=> MlsError::Other(error.innermost_error_message()).into(),
153 })
154 }
155}
156
157impl From<core_crypto::Error> for CoreCryptoError {
163 fn from(error: core_crypto::Error) -> Self {
164 log_error(&error);
165
166 match error {
167 #[cfg(feature = "proteus")]
168 core_crypto::Error::ProteusNotInitialized => Self::Other(error.to_string()),
169 #[cfg(not(feature = "proteus"))]
170 core_crypto::Error::ProteusNotInitialized => Self::Other("proteus not initialized".into()),
171 #[cfg(feature = "proteus")]
172 core_crypto::Error::Proteus(proteus) => {
173 let error_code = proteus.source.error_code();
174 if let Some(proteus_error) = ProteusError::from_error_code(error_code) {
175 Self::Proteus(proteus_error)
176 } else {
177 Self::Other(format!("unknown proteus error code: {error_code:?}"))
178 }
179 }
180 #[cfg(not(feature = "proteus"))]
181 core_crypto::Error::Proteus(_proteus) => {
182 unreachable!("we don't raise proteus errors when building without proteus")
183 }
184 core_crypto::Error::Mls(mls) => Self::Mls(MlsError::from(mls)),
185 core_crypto::Error::InvalidTransactionContext => Self::Other(error.to_string()),
186 core_crypto::Error::MlsTransportNotProvided => Self::Other(error.to_string()),
187 core_crypto::Error::ErrorDuringMlsTransport(error_message) => Self::Other(error_message),
188 core_crypto::Error::Keystore(keystore_error) => Self::Other(keystore_error.innermost_error_message()),
189 core_crypto::Error::CryptoboxMigration(cryptobox) => Self::Other(cryptobox.innermost_error_message()),
190 core_crypto::Error::Recursive(recursive_error) => recursive_error.into(),
191 core_crypto::Error::FeatureDisabled(_) => Self::Other(error.to_string()),
192 }
193 }
194}
195
196macro_rules! impl_from_via_recursive_error {
204 ($($t:ty),+ $(,)?) => {
205 $(
206 impl From<$t> for CoreCryptoError {
207 fn from(error: $t) -> Self {
208 use core_crypto::ToRecursiveError;
209 error
210 .construct_recursive("this context string does not matter and gets immediately stripped")
211 .into()
212 }
213 }
214 )*
215 };
216}
217
218impl_from_via_recursive_error!(
219 core_crypto::mls::Error,
220 core_crypto::mls::conversation::Error,
221 core_crypto::e2e_identity::Error,
222 core_crypto::transaction_context::Error,
223);
224
225impl CoreCryptoError {
226 pub(crate) fn generic<E>() -> impl FnOnce(E) -> Self
227 where
228 E: ToString,
229 {
230 |err| Self::Other(err.to_string())
231 }
232
233 pub(crate) fn ad_hoc(err: impl ToString) -> Self {
234 Self::Other(err.to_string())
235 }
236
237 #[cfg(target_family = "wasm")]
238 pub(crate) fn variant_name(&self) -> String {
239 let mut out = self.as_ref().to_string() + "Error";
240 match self {
241 Self::Mls(mls) => out += mls.as_ref(),
242 Self::Proteus(proteus) => out += proteus.as_ref(),
243 _ => {}
244 }
245 out
246 }
247
248 #[cfg(target_family = "wasm")]
249 pub(crate) fn stack(&self) -> Vec<String> {
250 let mut stack = Vec::new();
251 let mut err: &dyn std::error::Error = self;
252 stack.push(err.to_string());
253
254 while let Some(source) = err.source() {
255 stack.push(source.to_string());
256 err = source;
257 }
258
259 stack
260 }
261}