use std::collections::BTreeMap;
use matrix_sdk_common::deserialized_responses::{VerificationLevel, WithheldCode};
use ruma::{CanonicalJsonError, IdParseError, OwnedDeviceId, OwnedRoomId, OwnedUserId};
use serde::{ser::SerializeMap, Serializer};
use serde_json::Error as SerdeError;
use thiserror::Error;
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
use super::store::CryptoStoreError;
use crate::{olm::SessionExportError, types::SignedKey};
#[cfg(doc)]
use crate::{CollectStrategy, Device, LocalTrust, OtherUserIdentity};
pub type OlmResult<T> = Result<T, OlmError>;
pub type MegolmResult<T> = Result<T, MegolmError>;
#[derive(Error, Debug)]
pub enum OlmError {
#[error(transparent)]
EventError(#[from] EventError),
#[error(transparent)]
JsonError(#[from] SerdeError),
#[error(transparent)]
SessionCreation(#[from] SessionCreationError),
#[error(transparent)]
SessionExport(#[from] SessionExportError),
#[error("failed to read or write to the crypto store {0}")]
Store(#[from] CryptoStoreError),
#[error(
"decryption failed likely because an Olm session from {0} with sender key {1} was wedged"
)]
SessionWedged(OwnedUserId, Curve25519PublicKey),
#[error("decryption failed because an Olm message from {0} with sender key {1} was replayed")]
ReplayedMessage(OwnedUserId, Curve25519PublicKey),
#[error(
"encryption failed because the device does not \
have a valid Olm session with us"
)]
MissingSession,
#[error("encryption failed due to an error collecting the recipient devices: {0}")]
SessionRecipientCollectionError(SessionRecipientCollectionError),
#[error("encryption content is withheld from this: {0}")]
Withheld(WithheldCode),
#[error(
"refusing to decrypt the event because the sender device was not \
verified and 'exclude insecure devices' is enabled."
)]
UnverifiedSenderDevice,
}
#[derive(Error, Debug)]
pub enum MegolmError {
#[error(transparent)]
EventError(#[from] EventError),
#[error(transparent)]
JsonError(#[from] SerdeError),
#[error("Can't find the room key to decrypt the event, withheld code: {0:?}")]
MissingRoomKey(Option<WithheldCode>),
#[error(
"decryption failed because of mismatched identity keys of the sending device and those recorded in the to-device message"
)]
MismatchedIdentityKeys(MismatchedIdentityKeysError),
#[error(transparent)]
Decode(#[from] vodozemac::DecodeError),
#[error(transparent)]
Decryption(#[from] vodozemac::megolm::DecryptionError),
#[error(transparent)]
Store(#[from] CryptoStoreError),
#[error("decryption failed because trust requirement not satisfied: {0}")]
SenderIdentityNotTrusted(VerificationLevel),
#[cfg(feature = "experimental-encrypted-state-events")]
#[error("decryption failed because the state key failed to validate")]
StateKeyVerificationFailed,
}
#[derive(Error, Debug, PartialEq)]
pub struct MismatchedIdentityKeysError {
pub key_ed25519: Box<Ed25519PublicKey>,
pub device_ed25519: Option<Box<Ed25519PublicKey>>,
pub key_curve25519: Box<Curve25519PublicKey>,
pub device_curve25519: Option<Box<Curve25519PublicKey>>,
}
impl std::fmt::Display for MismatchedIdentityKeysError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut ser = f.serialize_struct("MismatchedIdentityKeysError", 4)?;
ser.serialize_entry("key_ed25519", &self.key_ed25519)?;
ser.serialize_entry("device_ed25519", &self.device_ed25519)?;
ser.serialize_entry("key_curve25519", &self.key_curve25519)?;
ser.serialize_entry("device_curve25519", &self.device_curve25519)?;
ser.end()
}
}
impl From<MismatchedIdentityKeysError> for MegolmError {
fn from(value: MismatchedIdentityKeysError) -> Self {
MegolmError::MismatchedIdentityKeys(value)
}
}
impl From<MismatchedIdentityKeysError> for SessionCreationError {
fn from(value: MismatchedIdentityKeysError) -> Self {
SessionCreationError::MismatchedIdentityKeys(value)
}
}
#[derive(Error, Debug)]
pub enum EventError {
#[error("the Encrypted message has been encrypted with a unsupported algorithm.")]
UnsupportedAlgorithm,
#[error("the provided JSON value isn't an object")]
NotAnObject,
#[error("the Encrypted message doesn't contain a ciphertext for our device")]
MissingCiphertext,
#[error("the Encrypted message is missing the signing key of the sender")]
MissingSigningKey,
#[error("the Encrypted message is missing the sender key")]
MissingSenderKey,
#[error(
"the sender of the plaintext doesn't match the sender of the encrypted \
message, got {0}, expected {1}"
)]
MismatchedSender(OwnedUserId, OwnedUserId),
#[error(
"the public key that was part of the message doesn't match the key we \
have stored, expected {0}, got {1}"
)]
MismatchedKeys(Box<Ed25519PublicKey>, Box<Ed25519PublicKey>),
#[error(
"the room id of the room key doesn't match the room id of the \
decrypted event: expected {0}, got {1:?}"
)]
MismatchedRoom(OwnedRoomId, Option<OwnedRoomId>),
#[error("the event included sender_device_keys which were invalid in some way")]
InvalidSenderDeviceKeys,
}
#[derive(Error, Debug)]
pub enum SessionUnpickleError {
#[error("the device keys are missing the signing key")]
MissingSigningKey,
#[error("the device keys are missing the identity key")]
MissingIdentityKey,
}
#[derive(Error, Debug)]
pub enum SignatureError {
#[error("the signature used an unsupported algorithm")]
UnsupportedAlgorithm,
#[error("the ID of the signing key is invalid")]
InvalidKeyId(#[from] IdParseError),
#[error("the signing key is missing from the object that signed the message")]
MissingSigningKey,
#[error("the user id of the signing key differs user id that provided the signature")]
UserIdMismatch,
#[error("the provided JSON value isn't an object")]
NotAnObject,
#[error("the provided JSON object doesn't contain a signatures field")]
NoSignatureFound,
#[error(transparent)]
VerificationError(#[from] vodozemac::SignatureError),
#[error(transparent)]
InvalidKey(#[from] vodozemac::KeyError),
#[error("the given signature is not valid and can't be decoded")]
InvalidSignature,
#[error("the signing key that used to sign the object has changed, old: {0:?}, new: {1:?}")]
SigningKeyChanged(Option<Box<Ed25519PublicKey>>, Option<Box<Ed25519PublicKey>>),
#[error(transparent)]
JsonError(#[from] CanonicalJsonError),
#[error(transparent)]
StoreError(#[from] CryptoStoreError),
}
impl From<SerdeError> for SignatureError {
fn from(e: SerdeError) -> Self {
CanonicalJsonError::SerDe(e).into()
}
}
#[derive(Error, Debug)]
pub enum SessionCreationError {
#[error(
"Failed to create a new Olm session for {0} {1}, the requested \
one-time key isn't a signed curve key"
)]
OneTimeKeyNotSigned(OwnedUserId, OwnedDeviceId),
#[error(
"Tried to create a new Olm session for {0} {1}, but the signed \
one-time key is missing"
)]
OneTimeKeyMissing(OwnedUserId, OwnedDeviceId),
#[error(
"Failed to verify the signature of a one-time key, key: {one_time_key:?}, \
signing_key: {signing_key:?}: {error:?}"
)]
InvalidSignature {
one_time_key: Box<SignedKey>,
signing_key: Option<Box<Ed25519PublicKey>>,
error: Box<SignatureError>,
},
#[error(
"Tried to create an Olm session for {0} {1}, but the device is missing \
a curve25519 key"
)]
DeviceMissingCurveKey(OwnedUserId, OwnedDeviceId),
#[error("Error deserializing the one-time key: {0}")]
InvalidJson(#[from] serde_json::Error),
#[error("The given curve25519 key is not a valid key")]
InvalidCurveKey(#[from] vodozemac::KeyError),
#[error(transparent)]
InboundCreation(#[from] vodozemac::olm::SessionCreationError),
#[error("The given device keys are invalid")]
InvalidDeviceKeys(#[from] SignatureError),
#[error(
"There was a mismatch between the identity keys of the sending device \
and those recorded in the to-device message"
)]
MismatchedIdentityKeys(MismatchedIdentityKeysError),
}
#[derive(Debug, Error)]
pub enum SetRoomSettingsError {
#[error("the new settings would cause a downgrade of encryption security")]
EncryptionDowngrade,
#[error("the new settings are invalid")]
InvalidSettings,
#[error(transparent)]
Store(#[from] CryptoStoreError),
}
#[derive(Error, Debug)]
pub enum SessionRecipientCollectionError {
#[error("one or more verified users have unsigned devices")]
VerifiedUserHasUnsignedDevice(BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>),
#[error("one or more users that were verified have changed their identity")]
VerifiedUserChangedIdentity(Vec<OwnedUserId>),
#[error("Encryption failed because cross-signing is not set up on your account")]
CrossSigningNotSetup,
#[error("Encryption failed because your device is not verified")]
SendingFromUnverifiedDevice,
}