Skip to content

Commit 9a058b9

Browse files
BillCarsonFrpoljar
andauthored
feat(crypto): Request room keys if the decryption failure is an unknown message index
We automatically request room keys to be forwarded from our other trusted devices if a decryption failure happens because the room key is missing. This patch introduces automatic room key requests for decryption failures if the room key is available but has been ratcheted forward. In other words, we will now request a better version of the given room key automatically as well. Co-authored-by: Damir Jelić <[email protected]>
1 parent 1ef645d commit 9a058b9

File tree

1 file changed

+102
-25
lines changed

1 file changed

+102
-25
lines changed

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

Lines changed: 102 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ use ruma::{
4444
};
4545
use serde_json::{value::to_raw_value, Value};
4646
use tracing::{debug, error, info, trace, warn};
47-
use vodozemac::{megolm::SessionOrdering, Curve25519PublicKey, Ed25519Signature};
47+
use vodozemac::{
48+
megolm::{DecryptionError, SessionOrdering},
49+
Curve25519PublicKey, Ed25519Signature,
50+
};
4851

4952
#[cfg(feature = "backups_v1")]
5053
use crate::backups::BackupMachine;
@@ -1143,8 +1146,6 @@ impl OlmMachine {
11431146

11441147
Ok(TimelineEvent { encryption_info: Some(encryption_info), event: decrypted_event })
11451148
} else {
1146-
self.key_request_machine.create_outgoing_key_request(room_id, event).await?;
1147-
11481149
Err(MegolmError::MissingRoomKey)
11491150
}
11501151
}
@@ -1179,29 +1180,36 @@ impl OlmMachine {
11791180
}
11801181
};
11811182

1182-
self.decrypt_megolm_events(room_id, &event, &content).await.map_err(|e| {
1183-
if let MegolmError::MissingRoomKey = e {
1184-
// TODO log the withheld reason if we have one.
1185-
debug!(
1186-
sender = event.sender.as_str(),
1187-
room_id = room_id.as_str(),
1188-
session_id = content.session_id(),
1189-
algorithm = %content.algorithm(),
1190-
"Failed to decrypt a room event, the room key is missing"
1191-
);
1192-
} else {
1193-
warn!(
1194-
sender = event.sender.as_str(),
1195-
room_id = room_id.as_str(),
1196-
session_id = content.session_id(),
1197-
algorithm = %content.algorithm(),
1198-
error = ?e,
1199-
"Failed to decrypt a room event"
1200-
);
1183+
let result = self.decrypt_megolm_events(room_id, &event, &content).await;
1184+
1185+
if let Err(e) = &result {
1186+
match e {
1187+
MegolmError::MissingRoomKey
1188+
| MegolmError::Decryption(DecryptionError::UnknownMessageIndex(_, _)) => {
1189+
// TODO: log the withheld reason if we have one.
1190+
debug!(
1191+
sender = event.sender.as_str(),
1192+
room_id = room_id.as_str(),
1193+
session_id = content.session_id(),
1194+
algorithm = %content.algorithm(),
1195+
"Failed to decrypt a room event, the room key is missing or has been ratcheted"
1196+
);
1197+
self.key_request_machine.create_outgoing_key_request(room_id, &event).await?;
1198+
}
1199+
_ => {
1200+
warn!(
1201+
sender = event.sender.as_str(),
1202+
room_id = room_id.as_str(),
1203+
session_id = content.session_id(),
1204+
algorithm = %content.algorithm(),
1205+
error = ?e,
1206+
"Failed to decrypt a room event"
1207+
);
1208+
}
12011209
}
1210+
}
12021211

1203-
e
1204-
})
1212+
result
12051213
}
12061214

12071215
/// Update the tracked users.
@@ -1627,7 +1635,7 @@ pub(crate) mod tests {
16271635
},
16281636
utilities::json_convert,
16291637
verification::tests::{outgoing_request_to_event, request_to_event},
1630-
EncryptionSettings, OlmError, ReadOnlyDevice, ToDeviceRequest,
1638+
EncryptionSettings, MegolmError, OlmError, ReadOnlyDevice, ToDeviceRequest,
16311639
};
16321640

16331641
/// These keys need to be periodically uploaded to the server.
@@ -2080,6 +2088,75 @@ pub(crate) mod tests {
20802088
}
20812089
}
20822090

2091+
#[async_test]
2092+
async fn test_query_ratcheted_key() {
2093+
let (alice, bob) = get_machine_pair_with_setup_sessions().await;
2094+
let room_id = room_id!("!test:example.org");
2095+
2096+
// Need a second bob session to check gossiping
2097+
let bob_id = user_id();
2098+
let bob_other_device = device_id!("OTHERBOB");
2099+
let bob_other_machine = OlmMachine::new(bob_id, bob_other_device).await;
2100+
let bob_other_device = ReadOnlyDevice::from_machine(&bob_other_machine).await;
2101+
bob.store.save_devices(&[bob_other_device]).await.unwrap();
2102+
bob.get_device(bob_id, device_id!("OTHERBOB"), None)
2103+
.await
2104+
.unwrap()
2105+
.expect("should exist")
2106+
.set_trust_state(crate::LocalTrust::Verified);
2107+
2108+
alice.create_outbound_group_session_with_defaults(room_id).await.unwrap();
2109+
2110+
let plaintext = "It is a secret to everybody";
2111+
2112+
let content = RoomMessageEventContent::text_plain(plaintext);
2113+
2114+
let content = alice
2115+
.encrypt_room_event(room_id, AnyMessageLikeEventContent::RoomMessage(content.clone()))
2116+
.await
2117+
.unwrap();
2118+
2119+
let room_event = OriginalSyncRoomEncryptedEvent {
2120+
event_id: event_id!("$xxxxx:example.org").to_owned(),
2121+
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
2122+
sender: alice.user_id().to_owned(),
2123+
content: content.deserialize_as().unwrap(),
2124+
unsigned: MessageLikeUnsigned::default(),
2125+
};
2126+
2127+
// should share at index 1
2128+
let to_device_requests = alice
2129+
.share_room_key(room_id, iter::once(bob.user_id()), EncryptionSettings::default())
2130+
.await
2131+
.unwrap();
2132+
2133+
let event = ToDeviceEvent::new(
2134+
alice.user_id().to_owned(),
2135+
to_device_requests_to_content(to_device_requests),
2136+
);
2137+
2138+
let group_session =
2139+
bob.decrypt_to_device_event(&event).await.unwrap().inbound_group_session;
2140+
bob.store.save_inbound_group_sessions(&[group_session.unwrap()]).await.unwrap();
2141+
2142+
let room_event = json_convert(&room_event).unwrap();
2143+
2144+
let decrypt_error = bob.decrypt_room_event(&room_event, room_id).await.unwrap_err();
2145+
2146+
if let MegolmError::Decryption(vodo_error) = decrypt_error {
2147+
if let vodozemac::megolm::DecryptionError::UnknownMessageIndex(_, _) = vodo_error {
2148+
// check that key has been requested
2149+
let outgoing_to_devices =
2150+
bob.key_request_machine.outgoing_to_device_requests().await.unwrap();
2151+
assert_eq!(1, outgoing_to_devices.len());
2152+
} else {
2153+
panic!("Should be UnknownMessageIndex error ")
2154+
}
2155+
} else {
2156+
panic!("Should have been unable to decrypt")
2157+
}
2158+
}
2159+
20832160
#[async_test]
20842161
async fn interactive_verification() {
20852162
let (alice, bob) = get_machine_pair_with_setup_sessions().await;

0 commit comments

Comments
 (0)