core_crypto/
error.rs

1// Wire
2// Copyright (C) 2022 Wire Swiss GmbH
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see http://www.gnu.org/licenses/.
16
17use crate::mls::conversation::config::MAX_PAST_EPOCHS;
18use crate::prelude::{E2eIdentityError, MlsCredentialType};
19
20#[cfg(all(feature = "cryptobox-migrate", target_family = "wasm"))]
21use rexie;
22
23/// CoreCrypto errors
24#[derive(Debug, thiserror::Error, strum::IntoStaticStr)]
25#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
26#[cfg_attr(feature = "uniffi", uniffi(flat_error))]
27pub enum CryptoError {
28    /// End to end identity error
29    #[error("End to end identity error")]
30    E2eiError(#[from] E2eIdentityError),
31    /// This error is emitted when the requested conversation couldn't be found in our store
32    #[error("Couldn't find conversation")]
33    ConversationNotFound(crate::prelude::ConversationId),
34    /// This error is emitted when the requested conversation already exists with the given if
35    #[error("Conversation already exists")]
36    ConversationAlreadyExists(crate::prelude::ConversationId),
37    /// This error is emitted when the requested client couldn't be found in MLS group
38    #[error("Couldn't find client")]
39    ClientNotFound(crate::prelude::ClientId),
40    /// This error is emitted when a pending proposal couldn't be found in MLS group
41    #[error("Couldn't find pending proposal {0}")]
42    PendingProposalNotFound(crate::mls::proposal::MlsProposalRef),
43    /// This error is emitted when a pending commmit couldn't be found in MLS group
44    #[error("Couldn't find pending commit")]
45    PendingCommitNotFound,
46    /// This error is emitted when we find a malformed (i.e. not uuid) or empty identifier
47    #[error("Malformed or empty identifier found: {0}")]
48    MalformedIdentifier(&'static str),
49    /// The keystore has no knowledge of such client; this shouldn't happen as Client::init is failsafe (find-else-create)
50    #[error("The provided client signature has not been found in the keystore")]
51    ClientSignatureNotFound,
52    /// The keystore already has a stored identity. As such, we cannot create a new raw identity
53    #[error("The keystore already contains a stored identity. Cannot create a new one!")]
54    IdentityAlreadyPresent,
55    /// This error occurs when we cannot find any provisional keypair in the store, indicating that the `generate_raw_keypair` method hasn't been called.
56    #[error(
57        r#"The externally-generated client ID initialization cannot continue - there's no provisional keypair in-store!
58
59        Have you called `CoreCrypto::generate_raw_keypair` ?"#
60    )]
61    NoProvisionalIdentityFound,
62    /// This error occurs when during the MLS external client generation, we end up with more than one client identity in store.
63    ///
64    /// This is usually not possible, unless there's some kind of concurrency issue
65    /// on the consumer (creating an ext-gen client AND a normal one at the same time for instance)
66    #[error(
67        "Somehow CoreCrypto holds more than one MLS identity. Something might've gone very wrong with this client!"
68    )]
69    TooManyIdentitiesPresent,
70    /// !!!! Something went very wrong and one of our locks has been poisoned by an in-thread panic !!!!
71    #[error("One of the locks has been poisoned")]
72    LockPoisonError,
73    /// We have done something terribly wrong
74    #[error("We have done something terribly wrong and it needs to be fixed")]
75    ImplementationError,
76    /// Tried to insert an already existing CredentialBundle
77    #[error("Tried to insert an already existing CredentialBundle")]
78    CredentialBundleConflict,
79    /// The consumer of this library has misused it
80    #[error("The consumer of this library has misused it")]
81    ConsumerError,
82    /// Errors that are sent by our MLS Provider
83    #[error(transparent)]
84    MlsProviderError(#[from] mls_crypto_provider::MlsProviderError),
85    /// Errors that are sent by our Keystore
86    #[error(transparent)]
87    KeyStoreError(#[from] core_crypto_keystore::CryptoKeystoreError),
88    /// MLS Internal Errors
89    #[error(transparent)]
90    MlsError(MlsError),
91    /// UUID-related errors
92    #[cfg(test)]
93    #[error(transparent)]
94    UuidError(#[from] uuid::Error),
95    /// Error when parsing `str`s that are not valid UTF-8
96    #[error(transparent)]
97    Utf8Error(#[from] std::str::Utf8Error),
98    /// Error when parsing `String`s that are not valid UTF-8
99    #[error(transparent)]
100    StringUtf8Error(#[from] std::string::FromUtf8Error),
101    /// Error when trying to coerce ints into Strings
102    #[error(transparent)]
103    ParseIntError(#[from] std::num::ParseIntError),
104    /// Error when trying to convert integer sizes - usually when they don't fit
105    #[error(transparent)]
106    ConvertIntError(#[from] std::num::TryFromIntError),
107    /// Error when trying to decode an hex-encoded string. Usually that means that the length of the hex string is odd - should be always even
108    #[error(transparent)]
109    HexDecodeError(#[from] hex::FromHexError),
110    /// Error when trying to coerce a `Vec<u8>` into a `[u8; N]`
111    #[error("Byte array supplied did not have the expected size {0}")]
112    InvalidByteArrayError(usize),
113    /// Standard I/O Error
114    #[error(transparent)]
115    IoError(#[from] std::io::Error),
116    /// Authorization error
117    #[error("The current client id isn't authorized to perform this action")]
118    Unauthorized,
119    /// Callbacks are not provided
120    #[error("The callbacks needed for CoreCrypto to operate were not set")]
121    CallbacksNotSet,
122    /// External Add Proposal Validation failed
123    #[error("External add proposal validation failed: only users already in the group are allowed")]
124    UnauthorizedExternalAddProposal,
125    /// External Commit sender was not authorized to perform such
126    #[error("External Commit sender was not authorized to perform such")]
127    UnauthorizedExternalCommit,
128    /// A supplied [`openmls::ciphersuite::hash_ref::HashReference`] is not of the expected size: 16
129    #[error("A supplied reference is not of the expected size: 16")]
130    InvalidHashReference,
131    /// Tried to decrypt a message in the wrong epoch
132    #[error("Decrypted an application message from the wrong epoch")]
133    DecryptionError,
134    /// Incoming message is from a prior epoch
135    #[error("Incoming message is from a prior epoch")]
136    StaleMessage,
137    /// Incoming message is a commit for which we have not yet received all the proposals.
138    ///
139    /// Buffering until all proposals have arrived.
140    #[error("Incoming message is a commit for which we have not yet received all the proposals. Buffering until all proposals have arrived.")]
141    BufferedCommit,
142    /// Incoming message is from an epoch too far in the future to buffer.
143    #[error("Incoming message is from an epoch too far in the future to buffer.")]
144    WrongEpoch,
145    /// Incoming message is for a future epoch. We will buffer it until the commit for that epoch arrives
146    #[error("Incoming message is for a future epoch. We will buffer it until the commit for that epoch arrives")]
147    BufferedFutureMessage {
148        /// The epoch of the buffered message
149        message_epoch: u64,
150    },
151    /// Proteus Error Wrapper
152    #[error(transparent)]
153    ProteusError(#[from] ProteusError),
154    /// Cryptobox migration error wrapper
155    #[error(transparent)]
156    CryptoboxMigrationError(#[from] CryptoboxMigrationError),
157    /// The proteus client has been called but has not been initialized yet
158    #[error("Proteus client hasn't been initialized")]
159    ProteusNotInitialized,
160    /// CoreCrypto hasn't been built with the `proteus` feature enabled, meaning proteus isn't built in
161    #[error("CoreCrypto hasn't been built with Proteus support enabled; The feature `{0}` isn't enabled")]
162    ProteusSupportNotEnabled(String),
163    /// A MLS operation was requested but MLS hasn't been initialized on this instance
164    #[error("A MLS operation was requested but MLS hasn't been initialized on this instance")]
165    MlsNotInitialized,
166    /// Decrypted message uses an invalid KeyPackage (probably expired)
167    #[error("Decrypted message uses an invalid KeyPackage")]
168    InvalidKeyPackage,
169    /// Client presented an invalid identity
170    #[error("Client presented an invalid identity")]
171    InvalidIdentity,
172    /// MLS Client was not initialized the right way
173    #[error("MLS Client was not initialized the right way")]
174    IdentityInitializationError,
175    /// Parent group cannot be found
176    #[error("The specified parent group has not been found in the keystore")]
177    ParentGroupNotFound,
178    /// Message epoch is too old
179    #[error("The epoch in which message was encrypted is older than {MAX_PAST_EPOCHS}")]
180    MessageEpochTooOld,
181    /// When looking for a X509 credential for a given ciphersuite and it has not been done
182    #[error("End-to-end identity enrollment has not been done")]
183    E2eiEnrollmentNotDone,
184    /// A Credential was not found locally which is very likely an implementation error
185    #[error("A Credential of type {0:?} was not found locally which is very likely an implementation error")]
186    CredentialNotFound(MlsCredentialType),
187    /// The MLS group is in an invalid state for an unknown reason
188    #[error("The MLS group is in an invalid state for an unknown reason")]
189    InternalMlsError,
190    /// We already decrypted this message once
191    #[error("We already decrypted this message once")]
192    DuplicateMessage,
193    /// This method leaks entities whereas it's not supposed to
194    #[cfg(test)]
195    #[error("This method leaks entities whereas it's not supposed to")]
196    LeakEntities,
197    /// This method does not create new entities whereas it's supposed to
198    #[cfg(test)]
199    #[error("This method does not create new entities whereas it's supposed to")]
200    NoEntityCreated,
201    /// Happens when a client creates a commit, sends it to the DS which accepts it but then client
202    /// clears this pending commit and creates another commit. This is triggered when the client
203    /// tries to decrypt the original commit. This means something is very wrong in the client's
204    /// code and has to be fixed immediately
205    #[error("Happens when a client creates a commit, sends it to the DS which accepts it but then client \
206    clears this pending commit and creates another commit. This is triggered when the client tries to decrypt the original commit.\
207    This means something is very wrong in the client's code and has to be fixed immediately")]
208    ClearingPendingCommitError,
209    /// Tried to decrypt a commit created by self which is likely to have been replayed by the DS
210    #[error("Tried to decrypt a commit created by self which is likely to have been replayed by the DS")]
211    SelfCommitIgnored,
212    /// You tried to join with an external commit but did not merge it yet. We will reapply this message for you when you merge your external commit
213    #[error(
214        "You tried to join with an external commit but did not merge it yet. We will reapply this message for you when you merge your external commit"
215    )]
216    UnmergedPendingGroup,
217    /// see [`x509_cert::der::Error`]
218    #[error(transparent)]
219    X509CertDerError(#[from] x509_cert::der::Error),
220    /// see [`pem::PemError`]
221    #[error(transparent)]
222    PemError(#[from] pem::PemError),
223    /// Domain name not found in the certificate
224    #[error("Could not find domain name in the certificate")]
225    DomainNameNotFound,
226    /// The provided domain name and the one found in the certificate don't match
227    #[error("The provided domain name and the one found in the certificate don't match")]
228    DomainNamesDontMatch,
229    /// A trust anchor with the provided domain name already exists in the group's context
230    /// extensions
231    #[error("A trust anchor with the provided domain name already exists in the group's context extensions")]
232    DuplicateDomainName,
233    /// The certificate chain is invalid or not complete
234    #[error("The certificate chain is invalid or not complete")]
235    InvalidCertificateChain,
236    /// Emtpy trust anchor update
237    #[error("The update anchors parameters can't be empty")]
238    EmptyTrustAnchorUpdate,
239    /// Adding a certificate chain already in the group's context
240    #[error("The certificate chain is already in the group's context")]
241    DuplicateCertificateChain,
242    /// This happens when the DS cannot flag KeyPackages as claimed or not. It this scenario, a client
243    /// requests their old KeyPackages to be deleted but one has already been claimed by another client to create a Welcome.
244    /// In that case the only solution is that the client receiving such a Welcome tries to join the group
245    /// with an External Commit instead
246    #[error("Although this Welcome seems valid, the local KeyPackage it references has already been deleted locally. Join this group with an external commit")]
247    OrphanWelcome,
248    /// The encountered ClientId does not match Wire's definition
249    #[error("The encountered ClientId does not match Wire's definition")]
250    InvalidClientId,
251    /// Json error
252    #[error(transparent)]
253    JsonError(#[from] serde_json::Error),
254    /// The received commit is deemed stale and is from an older epoch
255    #[error("The received commit is deemed stale and is from an older epoch.")]
256    StaleCommit,
257    /// The received proposal is deemed stale and is from an older epoch
258    #[error("The received proposal is deemed stale and is from an older epoch.")]
259    StaleProposal,
260    /// The group lacks an ExternalSender extension whereas it should have at least one
261    #[error("The group lacks an ExternalSender extension whereas it should have at least one")]
262    MissingExternalSenderExtension,
263    /// Not supported for the moment
264    #[error("Not supported for the moment")]
265    Unsupported,
266    /// Invalid Context. This context has been finished and can no longer be used.
267    #[error("This context has already been finished and can no longer be used.")]
268    InvalidContext,
269}
270
271impl From<MlsError> for CryptoError {
272    fn from(err: MlsError) -> Self {
273        match err {
274            MlsError::MlsAddMembersError(openmls::prelude::AddMembersError::KeyPackageVerifyError(
275                openmls::key_packages::errors::KeyPackageVerifyError::InvalidLeafNode(
276                    openmls::prelude::LeafNodeValidationError::InvalidCredential(
277                        openmls::credentials::errors::CredentialError::AuthenticationServiceValidationFailure(
278                            openmls_traits::authentication_service::CredentialAuthenticationStatus::Invalid,
279                        ),
280                    ),
281                ),
282            )) => Self::InvalidIdentity,
283            e => Self::MlsError(e),
284        }
285    }
286}
287
288/// A simpler definition for Result types that the Error is a [CryptoError]
289pub type CryptoResult<T> = Result<T, CryptoError>;
290
291impl CryptoError {
292    /// Returns the proteus error code
293    pub fn proteus_error_code(&self) -> Option<u16> {
294        if let Self::ProteusError(e) = self {
295            e.error_code()
296        } else {
297            None
298        }
299    }
300}
301
302/// MLS-specific error wrapper - see github.com/openmls/openmls for details
303#[derive(Debug, thiserror::Error, strum::IntoStaticStr)]
304pub enum MlsError {
305    /// Welcome error
306    #[error(transparent)]
307    MlsWelcomeError(#[from] openmls::prelude::WelcomeError<core_crypto_keystore::CryptoKeystoreError>),
308    /// Generic error type that indicates unrecoverable errors in the library. See [openmls::error::LibraryError]
309    #[error(transparent)]
310    MlsLibraryError(#[from] openmls::error::LibraryError),
311    /// Create message error
312    #[error(transparent)]
313    MlsInvalidMessageError(#[from] openmls::prelude::CreateMessageError),
314    /// EmptyInput error
315    #[error(transparent)]
316    MlsEmptyInputError(#[from] openmls::prelude::EmptyInputError),
317    /// An error that occurs in methods of a [openmls::credentials::Credential].
318    #[error(transparent)]
319    MlsCredentialError(#[from] openmls::prelude::CredentialError),
320    /// New group error
321    #[error(transparent)]
322    MlsNewGroupError(#[from] openmls::prelude::NewGroupError<core_crypto_keystore::CryptoKeystoreError>),
323    /// Add members error
324    #[error(transparent)]
325    MlsAddMembersError(#[from] openmls::prelude::AddMembersError<core_crypto_keystore::CryptoKeystoreError>),
326    /// Remove members error
327    #[error(transparent)]
328    MlsRemoveMembersError(#[from] openmls::prelude::RemoveMembersError<core_crypto_keystore::CryptoKeystoreError>),
329    /// Parse message error
330    #[error(transparent)]
331    MlsMessageError(#[from] openmls::prelude::ProcessMessageError),
332    /// `KeyPackageBundle` new error
333    #[error(transparent)]
334    MlsKeyPackageBundleNewError(
335        #[from] openmls::prelude::KeyPackageNewError<core_crypto_keystore::CryptoKeystoreError>,
336    ),
337    /// Self update error
338    #[error(transparent)]
339    MlsSelfUpdateError(#[from] openmls::prelude::SelfUpdateError<core_crypto_keystore::CryptoKeystoreError>),
340    /// Group state error
341    #[error(transparent)]
342    MlsMlsGroupStateError(#[from] openmls::prelude::MlsGroupStateError),
343    /// Propose add members error
344    #[error(transparent)]
345    ProposeAddMemberError(#[from] openmls::prelude::ProposeAddMemberError),
346    /// Propose self update error
347    #[error(transparent)]
348    ProposeSelfUpdateError(#[from] openmls::prelude::ProposeSelfUpdateError<core_crypto_keystore::CryptoKeystoreError>),
349    /// Propose remove members error
350    #[error(transparent)]
351    ProposeRemoveMemberError(#[from] openmls::prelude::ProposeRemoveMemberError),
352    /// Commit to pending proposals error
353    #[error(transparent)]
354    MlsCommitToPendingProposalsError(
355        #[from] openmls::prelude::CommitToPendingProposalsError<core_crypto_keystore::CryptoKeystoreError>,
356    ),
357    /// Export public group state error
358    #[error(transparent)]
359    MlsExportGroupInfoError(#[from] openmls::prelude::ExportGroupInfoError),
360    /// Errors that are thrown by TLS serialization crate.
361    #[error(transparent)]
362    MlsTlsCodecError(#[from] tls_codec::Error),
363    /// This type represents all possible errors that can occur when serializing or
364    /// deserializing JSON data.
365    #[error(transparent)]
366    MlsKeystoreSerializationError(#[from] serde_json::Error),
367    /// A wrapper struct for an error string. This can be used when no complex error
368    /// variant is needed.
369    #[error(transparent)]
370    MlsErrorString(#[from] openmls::error::ErrorString),
371    /// External Commit error
372    #[error(transparent)]
373    MlsExternalCommitError(#[from] openmls::prelude::ExternalCommitError),
374    /// OpenMls crypto error
375    #[error(transparent)]
376    MlsCryptoError(#[from] openmls::prelude::CryptoError),
377    /// OpenMls Export Secret error
378    #[error(transparent)]
379    MlsExportSecretError(#[from] openmls::prelude::ExportSecretError),
380    /// OpenMLS merge commit error
381    #[error(transparent)]
382    MlsMergeCommitError(#[from] openmls::prelude::MergeCommitError<core_crypto_keystore::CryptoKeystoreError>),
383    /// OpenMLS keypackage validation error
384    #[error(transparent)]
385    MlsKeyPackageValidationError(#[from] openmls::prelude::KeyPackageVerifyError),
386    /// OpenMLS Commit merge error
387    #[error(transparent)]
388    MlsMergePendingCommitError(
389        #[from] openmls::prelude::MergePendingCommitError<core_crypto_keystore::CryptoKeystoreError>,
390    ),
391    /// OpenMLS encrypt message error
392    #[error(transparent)]
393    MlsEncryptMessageError(#[from] openmls::framing::errors::MlsMessageError),
394    /// OpenMLS delete KeyPackage error
395    #[error(transparent)]
396    MlsDeleteKeyPackageError(
397        #[from] openmls::key_packages::errors::KeyPackageDeleteError<core_crypto_keystore::CryptoKeystoreError>,
398    ),
399    /// OpenMLS update extensions error
400    #[error(transparent)]
401    MlsUpdateExtensionsError(
402        #[from] openmls::prelude::UpdateExtensionsError<core_crypto_keystore::CryptoKeystoreError>,
403    ),
404    /// OpenMLS LeafNode validation error
405    #[error(transparent)]
406    MlsLeafNodeValidationError(#[from] openmls::prelude::LeafNodeValidationError),
407    /// OpenMLS LeafNode validation error
408    #[error(transparent)]
409    RatchetTreeError(#[from] openmls::treesync::RatchetTreeError),
410    /// OpenMLS GroupInfo error
411    #[error(transparent)]
412    GroupInfoError(#[from] openmls::messages::group_info::GroupInfoError),
413}
414
415#[derive(Debug, thiserror::Error, strum::IntoStaticStr)]
416/// Wrapper for Proteus-related errors
417pub enum ProteusError {
418    #[cfg(feature = "proteus")]
419    #[error(transparent)]
420    /// Error when decoding CBOR and/or decrypting Proteus messages
421    ProteusDecodeError(#[from] proteus_wasm::DecodeError),
422    #[cfg(feature = "proteus")]
423    #[error(transparent)]
424    /// Error when encoding CBOR and/or decrypting Proteus messages
425    ProteusEncodeError(#[from] proteus_wasm::EncodeError),
426    #[cfg(feature = "proteus")]
427    #[error(transparent)]
428    /// Various internal Proteus errors
429    ProteusInternalError(#[from] proteus_wasm::error::ProteusError),
430    #[cfg(feature = "proteus")]
431    #[error(transparent)]
432    /// Error when there's a critical error within a proteus Session
433    ProteusSessionError(#[from] proteus_wasm::session::Error<core_crypto_keystore::CryptoKeystoreError>),
434}
435
436impl ProteusError {
437    /// Returns the proteus error code
438    pub fn error_code(&self) -> Option<u16> {
439        cfg_if::cfg_if! {
440            if #[cfg(feature = "proteus")] {
441                use proteus_traits::{ProteusErrorCode as _, ProteusErrorKind};
442                let kind = match self {
443                    ProteusError::ProteusDecodeError(e) => e.code(),
444                    ProteusError::ProteusEncodeError(e) => e.code(),
445                    ProteusError::ProteusSessionError(e) => e.code(),
446                    ProteusError::ProteusInternalError(e) => e.code(),
447                };
448                (kind != ProteusErrorKind::None).then_some(kind as u16)
449            } else {
450                None
451            }
452        }
453    }
454}
455
456#[derive(Debug, thiserror::Error, strum::IntoStaticStr)]
457/// Wrapper for errors that can happen during a Cryptobox migration
458pub enum CryptoboxMigrationError {
459    #[cfg(all(feature = "cryptobox-migrate", target_family = "wasm"))]
460    #[error(transparent)]
461    /// Rexie Error
462    RexieError(rexie::Error),
463    #[cfg(all(feature = "cryptobox-migrate", target_family = "wasm"))]
464    #[error(transparent)]
465    /// IndexedDB Error
466    IdbError(idb::Error),
467    #[cfg(all(feature = "cryptobox-migrate", target_family = "wasm"))]
468    #[error(transparent)]
469    /// Error when parsing/serializing JSON payloads from the WASM boundary
470    JsonParseError(#[from] serde_wasm_bindgen::Error),
471    #[cfg(all(feature = "cryptobox-migrate", target_family = "wasm"))]
472    #[error(transparent)]
473    /// Error when decoding base64
474    Base64DecodeError(#[from] base64::DecodeError),
475    #[error("The targeted value does not possess the targeted key ({0})")]
476    /// Error when trying to fetch a certain key from a structured value
477    MissingKeyInValue(String),
478    #[error("The value cannot be coerced to the {0} type")]
479    /// Error when trying to coerce a certain value to a certain type
480    WrongValueType(String),
481    #[cfg_attr(target_family = "wasm", error("The provided path [{0}] could not be found."))]
482    #[cfg_attr(
483        not(target_family = "wasm"),
484        error("The provided path store [{0}] is either non-existent or has an incorrect shape.")
485    )]
486    /// Error when trying to open a Cryptobox store that doesn't exist
487    ProvidedPathDoesNotExist(String),
488    #[error("The Cryptobox identity at path [{0}] could not be found.")]
489    /// Error when inspecting a Cryptobox store that doesn't contain an Identity
490    IdentityNotFound(String),
491}
492
493#[cfg(all(feature = "cryptobox-migrate", target_family = "wasm"))]
494impl From<rexie::Error> for CryptoboxMigrationError {
495    fn from(e: rexie::Error) -> Self {
496        match e {
497            rexie::Error::IdbError(e) => Self::IdbError(e),
498            _ => Self::RexieError(e),
499        }
500    }
501}