Skip to content

Commit 4d2c261

Browse files
authored
crypto: rewrite check_sender_trust_requirement to use VerificationState (#5044)
There are two reasons for this. Firstly. we've already done a bunch of work to map `SenderData` into a `VerificationState`, and the decision tree from `VerificationState` to allow/reject is simpler than going from `SenderData`, even if we have to fudge it a bit to get the "legacy" flag. (Note that it allows us to get rid of an `unreachable!` panic.) Secondly, `VerficationState` represents the state of an *event*, whereas `SenderData` is about the session as a whole. A session can be fine, whilst events (claiming to be) encrypted with it can be suspect. What we want here is to check a specific message. Currently, this doesn't make any functional difference, but conceptually it's cleaner to check the `VerificationState`. Note that there are a bunch of tests for this method in `matrix-sdk-crypto/src/machine/tests/decryption_verification_state.rs`, called `test_decryption_trust_requirement`.
1 parent d462683 commit 4d2c261

File tree

1 file changed

+70
-37
lines changed
  • crates/matrix-sdk-crypto/src/machine

1 file changed

+70
-37
lines changed

crates/matrix-sdk-crypto/src/machine/mod.rs

Lines changed: 70 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use tracing::trace;
5757
use tracing::{
5858
debug, error,
5959
field::{debug, display},
60-
info, instrument, warn, Span,
60+
info, instrument, trace, warn, Span,
6161
};
6262
use vodozemac::{
6363
megolm::{DecryptionError, SessionOrdering},
@@ -1834,52 +1834,85 @@ impl OlmMachine {
18341834
}
18351835
}
18361836

1837-
/// Check that the sender of a Megolm session satisfies the trust
1837+
/// Check that a Megolm event satisfies the sender trust
18381838
/// requirement from the decryption settings.
1839+
///
1840+
/// If the requirement is not satisfied, returns
1841+
/// [`MegolmError::SenderIdentityNotTrusted`].
18391842
fn check_sender_trust_requirement(
18401843
&self,
18411844
session: &InboundGroupSession,
18421845
encryption_info: &EncryptionInfo,
18431846
trust_requirement: &TrustRequirement,
18441847
) -> MegolmResult<()> {
1845-
/// Get the error from the encryption information.
1846-
fn encryption_info_to_error(encryption_info: &EncryptionInfo) -> MegolmResult<()> {
1847-
// When this is called, the verification state *must* be unverified,
1848-
// otherwise the sender_data would have been SenderVerified
1849-
let VerificationState::Unverified(verification_level) =
1850-
&encryption_info.verification_state
1851-
else {
1852-
unreachable!("inconsistent verification state");
1853-
};
1854-
Err(MegolmError::SenderIdentityNotTrusted(verification_level.clone()))
1855-
}
1848+
trace!(
1849+
verification_state = ?encryption_info.verification_state,
1850+
?trust_requirement, "check_sender_trust_requirement",
1851+
);
18561852

1857-
match trust_requirement {
1858-
TrustRequirement::Untrusted => Ok(()),
1859-
1860-
TrustRequirement::CrossSignedOrLegacy => match &session.sender_data {
1861-
// Reject if the sender was previously verified, but changed
1862-
// their identity and is not verified any more.
1863-
SenderData::VerificationViolation(..) => Err(
1864-
MegolmError::SenderIdentityNotTrusted(VerificationLevel::VerificationViolation),
1865-
),
1866-
SenderData::SenderUnverified(..) => Ok(()),
1867-
SenderData::SenderVerified(..) => Ok(()),
1868-
SenderData::DeviceInfo { legacy_session: true, .. } => Ok(()),
1869-
SenderData::UnknownDevice { legacy_session: true, .. } => Ok(()),
1870-
_ => encryption_info_to_error(encryption_info),
1871-
},
1853+
// VerificationState::Verified is acceptable for all TrustRequirement levels, so
1854+
// let's get that out of the way
1855+
let verification_level = match &encryption_info.verification_state {
1856+
VerificationState::Verified => return Ok(()),
1857+
VerificationState::Unverified(verification_level) => verification_level,
1858+
};
1859+
1860+
let ok = match trust_requirement {
1861+
TrustRequirement::Untrusted => true,
1862+
1863+
TrustRequirement::CrossSignedOrLegacy => {
1864+
// `VerificationLevel::UnsignedDevice` and `VerificationLevel::None` correspond
1865+
// to `SenderData::DeviceInfo` and `SenderData::UnknownDevice`
1866+
// respectively, and those cases may be acceptable if the reason
1867+
// for the lack of data is that the sessions were established
1868+
// before we started collecting SenderData.
1869+
let legacy_session = match session.sender_data {
1870+
SenderData::DeviceInfo { legacy_session, .. } => legacy_session,
1871+
SenderData::UnknownDevice { legacy_session, .. } => legacy_session,
1872+
_ => false,
1873+
};
18721874

1873-
TrustRequirement::CrossSigned => match &session.sender_data {
1874-
// Reject if the sender was previously verified, but changed
1875-
// their identity and is not verified any more.
1876-
SenderData::VerificationViolation(..) => Err(
1877-
MegolmError::SenderIdentityNotTrusted(VerificationLevel::VerificationViolation),
1878-
),
1879-
SenderData::SenderUnverified(..) => Ok(()),
1880-
SenderData::SenderVerified(..) => Ok(()),
1881-
_ => encryption_info_to_error(encryption_info),
1875+
// In the CrossSignedOrLegacy case the following rules apply:
1876+
//
1877+
// 1. Identities we have not yet verified can be decrypted regardless of the
1878+
// legacy state of the session.
1879+
// 2. Devices that aren't signed by the owning identity of the device can only
1880+
// be decrypted if it's a legacy session.
1881+
// 3. If we have no information about the device, we should only decrypt if it's
1882+
// a legacy session.
1883+
// 4. Anything else, should throw an error.
1884+
match (verification_level, legacy_session) {
1885+
// Case 1
1886+
(VerificationLevel::UnverifiedIdentity, _) => true,
1887+
1888+
// Case 2
1889+
(VerificationLevel::UnsignedDevice, true) => true,
1890+
1891+
// Case 3
1892+
(VerificationLevel::None(_), true) => true,
1893+
1894+
// Case 4
1895+
(VerificationLevel::VerificationViolation, _)
1896+
| (VerificationLevel::UnsignedDevice, false)
1897+
| (VerificationLevel::None(_), false) => false,
1898+
}
1899+
}
1900+
1901+
// If cross-signing of identities is required, the only acceptable unverified case
1902+
// is when the identity is signed but not yet verified by us.
1903+
TrustRequirement::CrossSigned => match verification_level {
1904+
VerificationLevel::UnverifiedIdentity => true,
1905+
1906+
VerificationLevel::VerificationViolation
1907+
| VerificationLevel::UnsignedDevice
1908+
| VerificationLevel::None(_) => false,
18821909
},
1910+
};
1911+
1912+
if ok {
1913+
Ok(())
1914+
} else {
1915+
Err(MegolmError::SenderIdentityNotTrusted(verification_level.clone()))
18831916
}
18841917
}
18851918

0 commit comments

Comments
 (0)