matrix_sdk_crypto/error.rs
1// Copyright 2020 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16
17use matrix_sdk_common::deserialized_responses::{VerificationLevel, WithheldCode};
18use ruma::{CanonicalJsonError, IdParseError, OwnedDeviceId, OwnedRoomId, OwnedUserId};
19use serde::{ser::SerializeMap, Serializer};
20use serde_json::Error as SerdeError;
21use thiserror::Error;
22use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
23
24use super::store::CryptoStoreError;
25use crate::{olm::SessionExportError, types::SignedKey};
26#[cfg(doc)]
27use crate::{CollectStrategy, Device, LocalTrust, OtherUserIdentity};
28
29pub type OlmResult<T> = Result<T, OlmError>;
30pub type MegolmResult<T> = Result<T, MegolmError>;
31
32/// Error representing a failure during a device to device cryptographic
33/// operation.
34#[derive(Error, Debug)]
35pub enum OlmError {
36 /// The event that should have been decrypted is malformed.
37 #[error(transparent)]
38 EventError(#[from] EventError),
39
40 /// The received decrypted event couldn't be deserialized.
41 #[error(transparent)]
42 JsonError(#[from] SerdeError),
43
44 /// The received room key couldn't be converted into a valid Megolm session.
45 #[error(transparent)]
46 SessionCreation(#[from] SessionCreationError),
47
48 /// The room key that should be exported can't be converted into a
49 /// `m.forwarded_room_key` event.
50 #[error(transparent)]
51 SessionExport(#[from] SessionExportError),
52
53 /// The storage layer returned an error.
54 #[error("failed to read or write to the crypto store {0}")]
55 Store(#[from] CryptoStoreError),
56
57 /// The session with a device has become corrupted.
58 #[error(
59 "decryption failed likely because an Olm session from {0} with sender key {1} was wedged"
60 )]
61 SessionWedged(OwnedUserId, Curve25519PublicKey),
62
63 /// An Olm message got replayed while the Olm ratchet has already moved
64 /// forward.
65 #[error("decryption failed because an Olm message from {0} with sender key {1} was replayed")]
66 ReplayedMessage(OwnedUserId, Curve25519PublicKey),
67
68 /// Encryption failed because the device does not have a valid Olm session
69 /// with us.
70 #[error(
71 "encryption failed because the device does not \
72 have a valid Olm session with us"
73 )]
74 MissingSession,
75
76 /// Encryption failed due to an error collecting the recipient devices.
77 #[error("encryption failed due to an error collecting the recipient devices: {0}")]
78 SessionRecipientCollectionError(SessionRecipientCollectionError),
79}
80
81/// Error representing a failure during a group encryption operation.
82#[derive(Error, Debug)]
83pub enum MegolmError {
84 /// The event that should have been decrypted is malformed.
85 #[error(transparent)]
86 EventError(#[from] EventError),
87
88 /// The received decrypted event couldn't be deserialized.
89 #[error(transparent)]
90 JsonError(#[from] SerdeError),
91
92 /// Decryption failed because we're missing the room key that was used to
93 /// encrypt the event.
94 #[error("Can't find the room key to decrypt the event, withheld code: {0:?}")]
95 MissingRoomKey(Option<WithheldCode>),
96
97 /// Decryption failed because of a mismatch between the identity keys of the
98 /// device we received the room key from and the identity keys recorded in
99 /// the plaintext of the room key to-device message.
100 #[error(
101 "decryption failed because of mismatched identity keys of the sending device and those recorded in the to-device message"
102 )]
103 MismatchedIdentityKeys(MismatchedIdentityKeysError),
104
105 /// The encrypted megolm message couldn't be decoded.
106 #[error(transparent)]
107 Decode(#[from] vodozemac::DecodeError),
108
109 /// The event could not have been decrypted.
110 #[error(transparent)]
111 Decryption(#[from] vodozemac::megolm::DecryptionError),
112
113 /// The storage layer returned an error.
114 #[error(transparent)]
115 Store(#[from] CryptoStoreError),
116
117 /// An encrypted message wasn't decrypted, because the sender's
118 /// cross-signing identity did not satisfy the requested
119 /// [`crate::TrustRequirement`].
120 ///
121 /// The nested value is the sender's current verification level.
122 #[error("decryption failed because trust requirement not satisfied: {0}")]
123 SenderIdentityNotTrusted(VerificationLevel),
124}
125
126/// Decryption failed because of a mismatch between the identity keys of the
127/// device we received the room key from and the identity keys recorded in
128/// the plaintext of the room key to-device message.
129#[derive(Error, Debug, PartialEq)]
130pub struct MismatchedIdentityKeysError {
131 /// The Ed25519 key recorded in the room key's to-device message.
132 pub key_ed25519: Box<Ed25519PublicKey>,
133 /// The Ed25519 identity key of the device sending the room key.
134 pub device_ed25519: Option<Box<Ed25519PublicKey>>,
135 /// The Curve25519 key recorded in the room key's to-device message.
136 pub key_curve25519: Box<Curve25519PublicKey>,
137 /// The Curve25519 identity key of the device sending the room key.
138 pub device_curve25519: Option<Box<Curve25519PublicKey>>,
139}
140
141impl std::fmt::Display for MismatchedIdentityKeysError {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 let mut ser = f.serialize_struct("MismatchedIdentityKeysError", 4)?;
144 ser.serialize_entry("key_ed25519", &self.key_ed25519)?;
145 ser.serialize_entry("device_ed25519", &self.device_ed25519)?;
146 ser.serialize_entry("key_curve25519", &self.key_curve25519)?;
147 ser.serialize_entry("device_curve25519", &self.device_curve25519)?;
148 ser.end()
149 }
150}
151
152impl From<MismatchedIdentityKeysError> for MegolmError {
153 fn from(value: MismatchedIdentityKeysError) -> Self {
154 MegolmError::MismatchedIdentityKeys(value)
155 }
156}
157
158impl From<MismatchedIdentityKeysError> for SessionCreationError {
159 fn from(value: MismatchedIdentityKeysError) -> Self {
160 SessionCreationError::MismatchedIdentityKeys(value)
161 }
162}
163
164/// Error that occurs when decrypting an event that is malformed.
165#[derive(Error, Debug)]
166pub enum EventError {
167 /// The Encrypted message has been encrypted with a unsupported algorithm.
168 #[error("the Encrypted message has been encrypted with a unsupported algorithm.")]
169 UnsupportedAlgorithm,
170
171 /// The provided JSON value isn't an object.
172 #[error("the provided JSON value isn't an object")]
173 NotAnObject,
174
175 /// The Encrypted message doesn't contain a ciphertext for our device.
176 #[error("the Encrypted message doesn't contain a ciphertext for our device")]
177 MissingCiphertext,
178
179 /// The Encrypted message is missing the signing key of the sender.
180 #[error("the Encrypted message is missing the signing key of the sender")]
181 MissingSigningKey,
182
183 /// The Encrypted message is missing the sender key.
184 #[error("the Encrypted message is missing the sender key")]
185 MissingSenderKey,
186
187 /// The sender of the plaintext doesn't match the sender of the encrypted
188 /// message.
189 #[error(
190 "the sender of the plaintext doesn't match the sender of the encrypted \
191 message, got {0}, expected {1}"
192 )]
193 MismatchedSender(OwnedUserId, OwnedUserId),
194
195 /// The public key that was part of the message doesn't match the key we
196 /// have stored.
197 #[error(
198 "the public key that was part of the message doesn't match the key we \
199 have stored, expected {0}, got {1}"
200 )]
201 MismatchedKeys(Box<Ed25519PublicKey>, Box<Ed25519PublicKey>),
202
203 /// The room ID of the room key doesn't match the room ID of the decrypted
204 /// event.
205 #[error(
206 "the room id of the room key doesn't match the room id of the \
207 decrypted event: expected {0}, got {1:?}"
208 )]
209 MismatchedRoom(OwnedRoomId, Option<OwnedRoomId>),
210
211 /// The event includes `sender_device_keys` as per [MSC4147], but the
212 /// signature was invalid, or the ed25519 or curve25519 key did not
213 /// match other data in the event.
214 ///
215 /// [MSC4147]: https://github.com/matrix-org/matrix-spec-proposals/pull/4147
216 #[error("the event included sender_device_keys which were invalid in some way")]
217 InvalidSenderDeviceKeys,
218}
219
220/// Error type describing different errors that can happen when we create an
221/// Olm session from a pickle.
222#[derive(Error, Debug)]
223pub enum SessionUnpickleError {
224 /// The device keys are missing the signing key
225 #[error("the device keys are missing the signing key")]
226 MissingSigningKey,
227
228 /// The device keys are missing the identity key
229 #[error("the device keys are missing the identity key")]
230 MissingIdentityKey,
231}
232
233/// Error type describing different errors that happen when we check or create
234/// signatures for a Matrix JSON object.
235#[derive(Error, Debug)]
236pub enum SignatureError {
237 /// The signature was made using an unsupported algorithm.
238 #[error("the signature used an unsupported algorithm")]
239 UnsupportedAlgorithm,
240
241 /// The ID of the signing key isn't a valid key ID.
242 #[error("the ID of the signing key is invalid")]
243 InvalidKeyId(#[from] IdParseError),
244
245 /// The signing key that should create or check a signature is missing.
246 #[error("the signing key is missing from the object that signed the message")]
247 MissingSigningKey,
248
249 /// The user id of signing key differs from the user id that provided the
250 /// signature.
251 #[error("the user id of the signing key differs user id that provided the signature")]
252 UserIdMismatch,
253
254 /// The provided JSON value that was signed and the signature should be
255 /// checked isn't a valid JSON object.
256 #[error("the provided JSON value isn't an object")]
257 NotAnObject,
258
259 /// The provided JSON value that was signed and the signature should be
260 /// checked isn't a valid JSON object.
261 #[error("the provided JSON object doesn't contain a signatures field")]
262 NoSignatureFound,
263
264 /// The signature couldn't be verified.
265 #[error(transparent)]
266 VerificationError(#[from] vodozemac::SignatureError),
267
268 /// The public key isn't a valid ed25519 key.
269 #[error(transparent)]
270 InvalidKey(#[from] vodozemac::KeyError),
271
272 /// The signature could not be decoded.
273 #[error("the given signature is not valid and can't be decoded")]
274 InvalidSignature,
275
276 /// The signing key that used to sign the object has been changed.
277 #[error("the signing key that used to sign the object has changed, old: {0:?}, new: {1:?}")]
278 SigningKeyChanged(Option<Box<Ed25519PublicKey>>, Option<Box<Ed25519PublicKey>>),
279
280 /// The signed object couldn't be deserialized.
281 #[error(transparent)]
282 JsonError(#[from] CanonicalJsonError),
283
284 /// The store ran into an error.
285 #[error(transparent)]
286 StoreError(#[from] CryptoStoreError),
287}
288
289impl From<SerdeError> for SignatureError {
290 fn from(e: SerdeError) -> Self {
291 CanonicalJsonError::SerDe(e).into()
292 }
293}
294
295/// Error that occurs when a room key can't be converted into a valid Megolm
296/// session.
297#[derive(Error, Debug)]
298pub enum SessionCreationError {
299 /// The requested one-time key isn't a signed curve key.
300 #[error(
301 "Failed to create a new Olm session for {0} {1}, the requested \
302 one-time key isn't a signed curve key"
303 )]
304 OneTimeKeyNotSigned(OwnedUserId, OwnedDeviceId),
305
306 /// The signed one-time key is missing.
307 #[error(
308 "Tried to create a new Olm session for {0} {1}, but the signed \
309 one-time key is missing"
310 )]
311 OneTimeKeyMissing(OwnedUserId, OwnedDeviceId),
312
313 /// Failed to verify the one-time key signatures.
314 #[error(
315 "Failed to verify the signature of a one-time key, key: {one_time_key:?}, \
316 signing_key: {signing_key:?}: {error:?}"
317 )]
318 InvalidSignature {
319 /// The one-time key that failed the signature verification.
320 one_time_key: Box<SignedKey>,
321 /// The key that was used to verify the signature.
322 signing_key: Option<Box<Ed25519PublicKey>>,
323 /// The exact error describing why the signature verification failed.
324 error: Box<SignatureError>,
325 },
326
327 /// The user's device is missing a curve25519 key.
328 #[error(
329 "Tried to create an Olm session for {0} {1}, but the device is missing \
330 a curve25519 key"
331 )]
332 DeviceMissingCurveKey(OwnedUserId, OwnedDeviceId),
333
334 /// Error deserializing the one-time key.
335 #[error("Error deserializing the one-time key: {0}")]
336 InvalidJson(#[from] serde_json::Error),
337
338 /// The given curve25519 key is not a valid key.
339 #[error("The given curve25519 key is not a valid key")]
340 InvalidCurveKey(#[from] vodozemac::KeyError),
341
342 /// Error when creating an Olm Session from an incoming Olm message.
343 #[error(transparent)]
344 InboundCreation(#[from] vodozemac::olm::SessionCreationError),
345
346 /// The given device keys are invalid.
347 #[error("The given device keys are invalid")]
348 InvalidDeviceKeys(#[from] SignatureError),
349
350 /// There was a mismatch between the identity keys of the device we received
351 /// the room key from and the identity keys recorded in the plaintext of the
352 /// room key to-device message.
353 #[error(
354 "There was a mismatch between the identity keys of the sending device \
355 and those recorded in the to-device message"
356 )]
357 MismatchedIdentityKeys(MismatchedIdentityKeysError),
358}
359
360/// Errors that can be returned by
361/// [`crate::machine::OlmMachine::set_room_settings`].
362#[derive(Debug, Error)]
363pub enum SetRoomSettingsError {
364 /// The changes are rejected because they conflict with the previous
365 /// settings for this room.
366 #[error("the new settings would cause a downgrade of encryption security")]
367 EncryptionDowngrade,
368
369 /// The changes are rejected because we would be unable to use them to
370 /// encrypt events.
371 #[error("the new settings are invalid")]
372 InvalidSettings,
373
374 /// The store ran into an error.
375 #[error(transparent)]
376 Store(#[from] CryptoStoreError),
377}
378
379/// Error representing a problem when collecting the recipient devices for the
380/// room key, during an encryption operation.
381#[derive(Error, Debug)]
382pub enum SessionRecipientCollectionError {
383 /// One or more verified users has one or more unsigned devices.
384 ///
385 /// Happens only with [`CollectStrategy::ErrorOnVerifiedUserProblem`].
386 ///
387 /// In order to resolve this, the caller can set the trust level of the
388 /// affected devices to [`LocalTrust::Ignored`] or
389 /// [`LocalTrust::BlackListed`] (see [`Device::set_local_trust`]), and
390 /// then retry the encryption operation.
391 #[error("one or more verified users have unsigned devices")]
392 VerifiedUserHasUnsignedDevice(BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>),
393
394 /// One or more users was previously verified, but they have changed their
395 /// identity.
396 ///
397 /// Happens only with [`CollectStrategy::ErrorOnVerifiedUserProblem`] or
398 /// [`CollectStrategy::IdentityBasedStrategy`].
399 ///
400 /// In order to resolve this, the user can:
401 ///
402 /// * re-verify the problematic recipients, or
403 ///
404 /// * withdraw verification of the problematic recipients with
405 /// [`OtherUserIdentity::withdraw_verification`], or
406 ///
407 /// * set the trust level of all of the devices belonging to the problematic
408 /// recipients to [`LocalTrust::Ignored`] or [`LocalTrust::BlackListed`]
409 /// (see [`Device::set_local_trust`]).
410 ///
411 /// The caller can then retry the encryption operation.
412 #[error("one or more users that were verified have changed their identity")]
413 VerifiedUserChangedIdentity(Vec<OwnedUserId>),
414
415 /// Cross-signing has not been configured on our own identity.
416 ///
417 /// Happens only with [`CollectStrategy::IdentityBasedStrategy`].
418 /// (Cross-signing is required for encryption when using
419 /// `IdentityBasedStrategy`.) Apps should detect this condition and prevent
420 /// sending in the UI rather than waiting for this error to be returned when
421 /// encrypting.
422 #[error("Encryption failed because cross-signing is not set up on your account")]
423 CrossSigningNotSetup,
424
425 /// The current device has not been cross-signed by our own identity.
426 ///
427 /// Happens only with [`CollectStrategy::IdentityBasedStrategy`].
428 /// (Cross-signing is required for encryption when using
429 /// `IdentityBasedStrategy`.) Apps should detect this condition and prevent
430 /// sending in the UI rather than waiting for this error to be returned when
431 /// encrypting.
432 #[error("Encryption failed because your device is not verified")]
433 SendingFromUnverifiedDevice,
434}