Skip to content

Commit

Permalink
shields: Put the Code inside the Colour instead the Colour inside the…
Browse files Browse the repository at this point in the history
… Code.
  • Loading branch information
pixlwave committed Aug 1, 2024
1 parent 187f758 commit db9d8e5
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 135 deletions.
32 changes: 6 additions & 26 deletions bindings/matrix-sdk-crypto-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ pub use error::{
use js_int::UInt;
pub use logger::{set_logger, Logger};
pub use machine::{KeyRequestPair, OlmMachine, SignatureVerification};
use matrix_sdk_common::deserialized_responses::{
ShieldState as RustShieldState, ShieldStateColor as RustShieldStateColor,
};
use matrix_sdk_common::deserialized_responses::ShieldState as RustShieldState;
use matrix_sdk_crypto::{
olm::{IdentityKeys, InboundGroupSession, SenderData, Session},
store::{Changes, CryptoStore, PendingChanges, RoomSettings as RustRoomSettings},
Expand Down Expand Up @@ -733,36 +731,18 @@ pub struct ShieldState {

impl From<RustShieldState> for ShieldState {
fn from(value: RustShieldState) -> Self {
match &value {
RustShieldState::AuthenticityNotGuaranteed { color } => {
Self { color: color.into(), message: value.message().map(ToOwned::to_owned) }
}
RustShieldState::UnknownDevice { color } => {
Self { color: color.into(), message: value.message().map(ToOwned::to_owned) }
}
RustShieldState::UnsignedDevice { color } => {
Self { color: color.into(), message: value.message().map(ToOwned::to_owned) }
}
RustShieldState::UnverifiedIdentity { color } => {
Self { color: color.into(), message: value.message().map(ToOwned::to_owned) }
match value {
RustShieldState::Red { code, message } => {
Self { color: ShieldColor::Red, message: Some(message.to_owned()) }
}
RustShieldState::SentInClear { color } => {
Self { color: color.into(), message: value.message().map(ToOwned::to_owned) }
RustShieldState::Grey { code, message } => {
Self { color: ShieldColor::Grey, message: Some(message.to_owned()) }
}
RustShieldState::None => Self { color: ShieldColor::None, message: None },
}
}
}

impl From<&RustShieldStateColor> for ShieldColor {
fn from(value: &RustShieldStateColor) -> Self {
match value {
RustShieldStateColor::Red => ShieldColor::Red,
RustShieldStateColor::Grey => ShieldColor::Grey,
}
}
}

/// Struct representing the state of our private cross signing keys, it shows
/// which private cross signing keys we have locally stored.
#[derive(Debug, Clone, uniffi::Record)]
Expand Down
31 changes: 27 additions & 4 deletions bindings/matrix-sdk-ffi/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use matrix_sdk::{
AttachmentConfig, AttachmentInfo, BaseAudioInfo, BaseFileInfo, BaseImageInfo,
BaseThumbnailInfo, BaseVideoInfo, Thumbnail,
},
deserialized_responses::ShieldState,
deserialized_responses::{ShieldState as RustShieldState, ShieldStateCode},
Error,
};
use matrix_sdk_ui::timeline::{
Expand Down Expand Up @@ -923,9 +923,32 @@ impl From<&matrix_sdk_ui::timeline::EventSendState> for EventSendState {
}
}

#[uniffi::export]
pub fn message_for_shield_state(shield_state: ShieldState) -> Option<String> {
shield_state.message().map(ToOwned::to_owned)
/// Recommended decorations for decrypted messages, representing the message's
/// authenticity properties.
#[derive(uniffi::Enum)]
pub enum ShieldState {
/// A red shield with a tooltip containing the associated message should be
/// presented.
Red { code: ShieldStateCode, message: String },
/// A grey shield with a tooltip containing the associated message should be
/// presented.
Grey { code: ShieldStateCode, message: String },
/// No shield should be presented.
None,
}

impl From<RustShieldState> for ShieldState {
fn from(value: RustShieldState) -> Self {
match value {
RustShieldState::Red { code, message } => {
Self::Red { code, message: message.to_owned() }
}
RustShieldState::Grey { code, message } => {
Self::Grey { code, message: message.to_owned() }
}
RustShieldState::None => Self::None,
}
}
}

#[derive(uniffi::Object)]
Expand Down
120 changes: 58 additions & 62 deletions crates/matrix-sdk-common/src/deserialized_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,24 @@ impl VerificationState {
pub fn to_shield_state_strict(&self) -> ShieldState {
match self {
VerificationState::Verified => ShieldState::None,
VerificationState::Unverified(level) => {
let red = ShieldStateColor::Red;
match level {
VerificationLevel::UnverifiedIdentity | VerificationLevel::UnsignedDevice => {
ShieldState::UnverifiedIdentity { color: red }
VerificationState::Unverified(level) => match level {
VerificationLevel::UnverifiedIdentity | VerificationLevel::UnsignedDevice => {
ShieldState::Red {
code: ShieldStateCode::UnverifiedIdentity,
message: UNVERIFIED_IDENTITY,
}
VerificationLevel::None(link) => match link {
DeviceLinkProblem::MissingDevice => {
ShieldState::UnknownDevice { color: red }
}
DeviceLinkProblem::InsecureSource => {
ShieldState::AuthenticityNotGuaranteed { color: red }
}
},
}
}
VerificationLevel::None(link) => match link {
DeviceLinkProblem::MissingDevice => ShieldState::Red {
code: ShieldStateCode::UnknownDevice,
message: UNKNOWN_DEVICE,
},
DeviceLinkProblem::InsecureSource => ShieldState::Red {
code: ShieldStateCode::AuthenticityNotGuaranteed,
message: AUTHENTICITY_NOT_GUARANTEED,
},
},
},
}
}

Expand All @@ -126,19 +128,28 @@ impl VerificationState {
}
VerificationLevel::UnsignedDevice => {
// This is a high warning. The sender hasn't verified his own device.
ShieldState::UnsignedDevice { color: ShieldStateColor::Red }
ShieldState::Red {
code: ShieldStateCode::UnsignedDevice,
message: UNSIGNED_DEVICE,
}
}
VerificationLevel::None(link) => match link {
DeviceLinkProblem::MissingDevice => {
// Have to warn as it could have been a temporary injected device.
// Notice that the device might just not be known at this time, so callers
// should retry when there is a device change for that user.
ShieldState::UnknownDevice { color: ShieldStateColor::Red }
ShieldState::Red {
code: ShieldStateCode::UnknownDevice,
message: UNKNOWN_DEVICE,
}
}
DeviceLinkProblem::InsecureSource => {
// In legacy mode, we tone down this warning as it is quite common and
// mostly noise (due to legacy backup and lack of trusted forwards).
ShieldState::AuthenticityNotGuaranteed { color: ShieldStateColor::Grey }
ShieldState::Grey {
code: ShieldStateCode::AuthenticityNotGuaranteed,
message: AUTHENTICITY_NOT_GUARANTEED,
}
}
},
},
Expand Down Expand Up @@ -170,7 +181,7 @@ pub enum VerificationLevel {
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum DeviceLinkProblem {
/// The device is missing, either because it was deleted, or you haven't
/// yet downloaded it or the server is erroneously omitting it (federation
/// yet downoaled it or the server is erroneously omitting it (federation
/// lag).
MissingDevice,
/// The key was obtained from an insecure source: imported from a file,
Expand All @@ -181,56 +192,41 @@ pub enum DeviceLinkProblem {
/// Recommended decorations for decrypted messages, representing the message's
/// authenticity properties.
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ShieldState {
/// Not enough information available to check the authenticity.
AuthenticityNotGuaranteed { color: ShieldStateColor },
/// The sending device isn't yet known by the Client.
UnknownDevice { color: ShieldStateColor },
/// The sending device hasn't been verified by the sender.
UnsignedDevice { color: ShieldStateColor },
/// The sender hasn't been verified by the Client's user.
UnverifiedIdentity { color: ShieldStateColor },
/// An unencrypted event in an encrypted room.
SentInClear { color: ShieldStateColor },
/// The event is trusted as authentic.
/// A red shield with a tooltip containing the associated message should be
/// presented.
Red {
/// A machine-readable representation.
code: ShieldStateCode,
/// A human readable description.
message: &'static str,
},
/// A grey shield with a tooltip containing the associated message should be
/// presented.
Grey {
/// A machine-readable representation.
code: ShieldStateCode,
/// A human readable description.
message: &'static str,
},
/// No shield should be presented.
None,
}

impl ShieldState {
/// The message (in English) that should be presented.
pub fn message(&self) -> Option<&str> {
match self {
ShieldState::AuthenticityNotGuaranteed { .. } => Some(AUTHENTICITY_NOT_GUARANTEED),
ShieldState::UnknownDevice { .. } => Some(UNKNOWN_DEVICE),
ShieldState::UnsignedDevice { .. } => Some(UNSIGNED_DEVICE),
ShieldState::UnverifiedIdentity { .. } => Some(UNVERIFIED_IDENTITY),
ShieldState::SentInClear { .. } => Some(SENT_IN_CLEAR),
ShieldState::None => None,
}
}

/// A helper method to get the color that should be used.
pub fn color(&self) -> Option<&ShieldStateColor> {
match self {
ShieldState::AuthenticityNotGuaranteed { color } => Some(color),
ShieldState::UnknownDevice { color } => Some(color),
ShieldState::UnsignedDevice { color } => Some(color),
ShieldState::UnverifiedIdentity { color } => Some(color),
ShieldState::SentInClear { color } => Some(color),
ShieldState::None => None,
}
}
}

/// The color of the shield that should be presented to the user.
/// A machine-readable representation of the authenticity for a `ShieldState`.
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ShieldStateColor {
/// A red shield should be presented.
Red,
/// A grey shield should be presented.
Grey,
pub enum ShieldStateCode {
/// Not enough information available to check the authenticity.
AuthenticityNotGuaranteed,
/// The sending device isn't yet known by the Client.
UnknownDevice,
/// The sending device hasn't been verified by the sender.
UnsignedDevice,
/// The sender hasn't been verified by the Client's user.
UnverifiedIdentity,
/// An unencrypted event in an encrypted room.
SentInClear,
}

/// The algorithm specific information of a decrypted event.
Expand Down
56 changes: 17 additions & 39 deletions crates/matrix-sdk-crypto/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2388,8 +2388,8 @@ pub(crate) mod tests {
use futures_util::{pin_mut, FutureExt, StreamExt};
use itertools::Itertools;
use matrix_sdk_common::deserialized_responses::{
DeviceLinkProblem, EncryptionInfo, ShieldState, ShieldStateColor, UnableToDecryptInfo,
UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel, VerificationState,
DeviceLinkProblem, ShieldState, UnableToDecryptInfo, UnsignedDecryptionResult,
UnsignedEventLocation, VerificationLevel, VerificationState,
};
use matrix_sdk_test::{async_test, message_like_event_content, test_json};
use ruma::{
Expand Down Expand Up @@ -3346,13 +3346,15 @@ pub(crate) mod tests {

#[async_test]
async fn test_decryption_verification_state() {
let assert_shield = |info: EncryptionInfo, strict: ShieldState, lax: ShieldState| {
let info_lax = info.verification_state.to_shield_state_lax();
let info_strict = info.verification_state.to_shield_state_strict();
macro_rules! assert_shield {
($foo: ident, $strict: ident, $lax: ident) => {
let lax = $foo.verification_state.to_shield_state_lax();
let strict = $foo.verification_state.to_shield_state_strict();

assert_eq!(info_lax, lax);
assert_eq!(info_strict, strict);
};
assert_matches!(lax, ShieldState::$lax { .. });
assert_matches!(strict, ShieldState::$strict { .. });
};
}
let (alice, bob) =
get_machine_pair_with_setup_sessions_test_helper(alice_id(), user_id(), false).await;
let room_id = room_id!("!test:example.org");
Expand Down Expand Up @@ -3409,23 +3411,15 @@ pub(crate) mod tests {
encryption_info.verification_state
);

assert_shield(
encryption_info,
ShieldState::UnverifiedIdentity { color: ShieldStateColor::Red },
ShieldState::UnsignedDevice { color: ShieldStateColor::Red },
);
assert_shield!(encryption_info, Red, Red);

// get_room_event_encryption_info should return the same information
let encryption_info = bob.get_room_event_encryption_info(&event, room_id).await.unwrap();
assert_eq!(
VerificationState::Unverified(VerificationLevel::UnsignedDevice),
encryption_info.verification_state
);
assert_shield(
encryption_info,
ShieldState::UnverifiedIdentity { color: ShieldStateColor::Red },
ShieldState::UnsignedDevice { color: ShieldStateColor::Red },
);
assert_shield!(encryption_info, Red, Red);

// Local trust state has no effect
bob.get_device(alice.user_id(), alice_device_id(), None)
Expand All @@ -3440,11 +3434,7 @@ pub(crate) mod tests {
VerificationState::Unverified(VerificationLevel::UnsignedDevice),
encryption_info.verification_state
);
assert_shield(
encryption_info,
ShieldState::UnverifiedIdentity { color: ShieldStateColor::Red },
ShieldState::UnsignedDevice { color: ShieldStateColor::Red },
);
assert_shield!(encryption_info, Red, Red);

setup_cross_signing_for_machine_test_helper(&alice, &bob).await;
let bob_id_from_alice = alice.get_identity(bob.user_id(), None).await.unwrap();
Expand All @@ -3459,11 +3449,7 @@ pub(crate) mod tests {
VerificationState::Unverified(VerificationLevel::UnsignedDevice),
encryption_info.verification_state
);
assert_shield(
encryption_info,
ShieldState::UnverifiedIdentity { color: ShieldStateColor::Red },
ShieldState::UnsignedDevice { color: ShieldStateColor::Red },
);
assert_shield!(encryption_info, Red, Red);

// Let alice sign her device
sign_alice_device_for_machine_test_helper(&alice, &bob).await;
Expand All @@ -3475,11 +3461,7 @@ pub(crate) mod tests {
encryption_info.verification_state
);

assert_shield(
encryption_info,
ShieldState::UnverifiedIdentity { color: ShieldStateColor::Red },
ShieldState::None,
);
assert_shield!(encryption_info, Red, None);

// Given alice is verified
mark_alice_identity_as_verified_test_helper(&alice, &bob).await;
Expand All @@ -3494,7 +3476,7 @@ pub(crate) mod tests {
let encryption_info = bob.get_room_event_encryption_info(&event, room_id).await.unwrap();
// Then it should say Verified
assert_eq!(VerificationState::Verified, encryption_info.verification_state);
assert_shield(encryption_info, ShieldState::None, ShieldState::None);
assert_shield!(encryption_info, None, None);

// And the updated SenderData should have been saved into the store.
let session = load_session(&bob, room_id, &event).await.unwrap().unwrap();
Expand All @@ -3515,11 +3497,7 @@ pub(crate) mod tests {
encryption_info.verification_state
);

assert_shield(
encryption_info,
ShieldState::AuthenticityNotGuaranteed { color: ShieldStateColor::Red },
ShieldState::AuthenticityNotGuaranteed { color: ShieldStateColor::Grey },
);
assert_shield!(encryption_info, Red, Grey);
}

async fn load_session(
Expand Down
Loading

0 comments on commit db9d8e5

Please sign in to comment.