Skip to main content

core_crypto_keystore/
error.rs

1pub use rusqlite::{Error as RusqliteError, ErrorCode as RusqliteErrorCode};
2
3/// Error type to represent various errors that can happen in the KeyStore
4#[derive(Debug, thiserror::Error)]
5pub enum CryptoKeystoreError {
6    #[error("The given key doesn't contain valid utf-8")]
7    KeyReprError(#[from] std::str::Utf8Error),
8    #[error("A transaction must be in progress to perform this operation.")]
9    MutatingOperationWithoutTransaction,
10    #[error("cannot open a new transaction as there exists another transaction currently in progress")]
11    TransactionInProgress,
12    #[error("the transaction was unexpectedly rolled back by sqlite")]
13    UnexpectedRollback,
14    #[error("failed to operate the cross-process transaction lock at {path}")]
15    TransactionLock {
16        path: String,
17        #[source]
18        source: std::io::Error,
19    },
20    #[error(transparent)]
21    TryFromSliceError(#[from] std::array::TryFromSliceError),
22    #[error("One of the Keystore locks has been poisoned")]
23    LockPoisonError,
24    #[error("The keystore has run out of keypackage bundles!")]
25    OutOfKeyPackageBundles,
26    #[error("Incorrect API usage: {0}")]
27    IncorrectApiUsage(&'static str),
28    #[error("The credential tied to this signature keypair is different from the provided one")]
29    SignatureKeyPairDoesNotBelongToCredential,
30    #[error("The uniqueness constraint has been violated for {0}")]
31    AlreadyExists(&'static str),
32    #[error("The provided buffer is too big to be persisted in the store")]
33    BlobTooBig,
34    #[error("This database connection has been closed")]
35    Closed,
36    #[error(transparent)]
37    KeyStoreValueTransformError(#[from] postcard::Error),
38    #[error(transparent)]
39    IoError(#[from] std::io::Error),
40    #[cfg(not(target_os = "unknown"))]
41    #[error(transparent)]
42    TimeError(#[from] std::time::SystemTimeError),
43    #[error("aead::Error: {0}")]
44    AesGcmError(&'static str),
45    #[cfg(target_os = "unknown")]
46    #[error("{0}")]
47    SerdeWasmBindgenError(String),
48    #[error(transparent)]
49    DbError(#[from] RusqliteError),
50    #[error(transparent)]
51    DbMigrationError(#[from] Box<refinery::Error>),
52    #[cfg(test)]
53    #[error(transparent)]
54    MlsKeyPackageIdError(#[from] openmls::prelude::KeyPackageIdError),
55    #[cfg(test)]
56    #[error(transparent)]
57    MlsExtensionError(#[from] openmls::prelude::ExtensionError),
58    #[error("Invalid database key size, expected {expected}, got {actual}")]
59    InvalidDbKeySize { expected: usize, actual: usize },
60    #[cfg(feature = "proteus-keystore")]
61    #[error("Invalid key [{key}] size, expected {expected}, got {actual}")]
62    InvalidKeySize {
63        expected: usize,
64        actual: usize,
65        key: &'static str,
66    },
67    #[cfg(feature = "proteus-keystore")]
68    #[error(transparent)]
69    ParseIntError(#[from] std::num::ParseIntError),
70    #[cfg(feature = "proteus-keystore")]
71    #[error("Could not find a free prekey id")]
72    NoFreePrekeyId,
73    #[error("{0}")]
74    MlsKeyStoreError(String),
75    #[error(transparent)]
76    HexDecodeError(#[from] hex::FromHexError),
77    #[error(transparent)]
78    FromUtf8Error(#[from] std::string::FromUtf8Error),
79    #[cfg(target_os = "ios")]
80    #[error(transparent)]
81    HexSaltDecodeError(hex::FromHexError),
82    #[cfg(target_os = "ios")]
83    #[error(transparent)]
84    SecurityFrameworkError(#[from] security_framework::base::Error),
85    #[error("Not implemented (and probably never will)")]
86    NotImplemented,
87    #[error("Failed getting current timestamp")]
88    TimestampError,
89    #[error("Could not find {0} in keystore with value {1}")]
90    NotFound(&'static str, String),
91    #[error(transparent)]
92    SerdeJsonError(#[from] serde_json::Error),
93    #[cfg(target_os = "unknown")]
94    #[error(transparent)]
95    IdbError(#[from] idb::Error),
96    #[cfg(target_os = "unknown")]
97    #[error("Migration from version {0} is not supported")]
98    MigrationNotSupported(u32),
99    #[cfg(target_os = "unknown")]
100    #[error("CoreCrypto 10.x databases are unsupported and must be discarded")]
101    CoreCrypto10DatabaseUnsupported,
102    #[error("The migration failed: {0}")]
103    MigrationFailed(String),
104    #[cfg(target_os = "unknown")]
105    #[error("{context}")]
106    OpfsError {
107        context: &'static str,
108        #[source]
109        error: rsqlite_vfs::VfsError,
110    },
111    #[error("The database includes migrations newer than this version of CC knows about")]
112    DatabaseFromTheFuture,
113}
114
115impl CryptoKeystoreError {
116    /// If the produced rusqlite error came from a uniqueness violation, then map it to our
117    /// AlreadyExists variant. Otherwise pass it through.
118    ///
119    /// Only `UNIQUE` and `PRIMARY KEY` conflicts count. Other constraint violations, such as a
120    /// foreign key with no parent row, mean something else went wrong.
121    pub(crate) fn map_already_exists(table_name: &'static str) -> impl FnOnce(rusqlite::Error) -> Self {
122        move |err| {
123            if let rusqlite::Error::SqliteFailure(inner, _) = err
124                && matches!(
125                    inner.extended_code,
126                    rusqlite::ffi::SQLITE_CONSTRAINT_UNIQUE | rusqlite::ffi::SQLITE_CONSTRAINT_PRIMARYKEY
127                )
128            {
129                Self::AlreadyExists(table_name)
130            } else {
131                err.into()
132            }
133        }
134    }
135
136    #[cfg(target_os = "unknown")]
137    pub(crate) fn opfs(context: &'static str) -> impl FnOnce(rsqlite_vfs::VfsError) -> Self {
138        move |error| Self::OpfsError { context, error }
139    }
140}
141
142#[cfg(target_os = "unknown")]
143#[expect(clippy::from_over_into)]
144impl Into<wasm_bindgen::JsValue> for CryptoKeystoreError {
145    fn into(self) -> wasm_bindgen::JsValue {
146        wasm_bindgen::JsValue::from_str(&self.to_string())
147    }
148}
149
150#[cfg(target_os = "unknown")]
151impl From<serde_wasm_bindgen::Error> for CryptoKeystoreError {
152    fn from(jsv: serde_wasm_bindgen::Error) -> Self {
153        Self::SerdeWasmBindgenError(jsv.to_string())
154    }
155}
156
157#[cfg(feature = "proteus-keystore")]
158impl proteus_traits::ProteusErrorCode for CryptoKeystoreError {
159    fn code(&self) -> proteus_traits::ProteusErrorKind {
160        use proteus_traits::ProteusErrorKind;
161        match self {
162            CryptoKeystoreError::KeyReprError(_) => ProteusErrorKind::DecodeError,
163            CryptoKeystoreError::TryFromSliceError(_) => ProteusErrorKind::DecodeError,
164            CryptoKeystoreError::LockPoisonError => ProteusErrorKind::OtherSystemError,
165            CryptoKeystoreError::BlobTooBig => ProteusErrorKind::IoError,
166            CryptoKeystoreError::KeyStoreValueTransformError(_) => ProteusErrorKind::DecodeError,
167            CryptoKeystoreError::IoError(_) => ProteusErrorKind::IoError,
168            CryptoKeystoreError::DbError(_) => ProteusErrorKind::IoError,
169            CryptoKeystoreError::DbMigrationError(_) => ProteusErrorKind::IoError,
170            CryptoKeystoreError::InvalidKeySize { .. } => ProteusErrorKind::InvalidArrayLen,
171            CryptoKeystoreError::ParseIntError(_) => ProteusErrorKind::DecodeError,
172            CryptoKeystoreError::HexDecodeError(_) => ProteusErrorKind::DecodeError,
173            CryptoKeystoreError::FromUtf8Error(_) => ProteusErrorKind::DecodeError,
174            _ => unreachable!(),
175        }
176    }
177}
178
179/// A specialized Result for the KeyStore functions
180pub type CryptoKeystoreResult<T> = Result<T, CryptoKeystoreError>;