mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-08 16:04:13 -04:00
feat(crypto): Add state event encryption methods to OlmMachine
Signed-off-by: Skye Elliot <actuallyori@gmail.com>
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cfg(feature = "experimental-encrypted-state-events")]
|
||||
use std::borrow::Borrow;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
sync::Arc,
|
||||
@@ -31,6 +33,8 @@ use matrix_sdk_common::{
|
||||
locks::RwLock as StdRwLock,
|
||||
BoxFuture,
|
||||
};
|
||||
#[cfg(feature = "experimental-encrypted-state-events")]
|
||||
use ruma::events::{AnyStateEventContent, StateEventContent};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
dehydrated_device::DehydratedDeviceData,
|
||||
@@ -1102,6 +1106,66 @@ impl OlmMachine {
|
||||
self.inner.group_session_manager.encrypt(room_id, event_type, content).await
|
||||
}
|
||||
|
||||
/// Encrypt a state event for the given room.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `room_id` - The id of the room for which the event should be
|
||||
/// encrypted.
|
||||
///
|
||||
/// * `content` - The plaintext content of the event that should be
|
||||
/// encrypted.
|
||||
///
|
||||
/// * `state_key` - The associated state key of the event.
|
||||
#[cfg(feature = "experimental-encrypted-state-events")]
|
||||
pub async fn encrypt_state_event<C, K>(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
content: C,
|
||||
state_key: K,
|
||||
) -> MegolmResult<Raw<RoomEncryptedEventContent>>
|
||||
where
|
||||
C: StateEventContent,
|
||||
C::StateKey: Borrow<K>,
|
||||
K: AsRef<str>,
|
||||
{
|
||||
let event_type = content.event_type().to_string();
|
||||
let content = Raw::new(&content)?.cast_unchecked();
|
||||
self.encrypt_state_event_raw(room_id, &event_type, state_key.as_ref(), &content).await
|
||||
}
|
||||
|
||||
/// Encrypt a state event for the given state event using its raw JSON
|
||||
/// content and state key.
|
||||
///
|
||||
/// This method is equivalent to [`OlmMachine::encrypt_state_event`]
|
||||
/// method but operates on an arbitrary JSON value instead of strongly-typed
|
||||
/// event content struct.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `room_id` - The id of the room for which the message should be
|
||||
/// encrypted.
|
||||
///
|
||||
/// * `event_type` - The type of the event.
|
||||
///
|
||||
/// * `state_key` - The associated state key of the event.
|
||||
///
|
||||
/// * `content` - The plaintext content of the event that should be
|
||||
/// encrypted as a raw JSON value.
|
||||
#[cfg(feature = "experimental-encrypted-state-events")]
|
||||
pub async fn encrypt_state_event_raw(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
event_type: &str,
|
||||
state_key: &str,
|
||||
content: &Raw<AnyStateEventContent>,
|
||||
) -> MegolmResult<Raw<RoomEncryptedEventContent>> {
|
||||
self.inner
|
||||
.group_session_manager
|
||||
.encrypt_state(room_id, event_type, state_key, content)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Forces the currently active room key, which is used to encrypt messages,
|
||||
/// to be rotated.
|
||||
///
|
||||
|
||||
@@ -26,6 +26,11 @@ use matrix_sdk_common::{
|
||||
executor::spawn,
|
||||
};
|
||||
use matrix_sdk_test::{async_test, message_like_event_content, ruma_response_from_json, test_json};
|
||||
#[cfg(feature = "experimental-encrypted-state-events")]
|
||||
use ruma::events::{
|
||||
room::topic::{OriginalRoomTopicEvent, RoomTopicEventContent},
|
||||
StateEvent,
|
||||
};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
keys::{get_keys, upload_keys},
|
||||
@@ -727,6 +732,84 @@ async fn test_megolm_encryption() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-encrypted-state-events")]
|
||||
#[async_test]
|
||||
async fn test_megolm_state_encryption() {
|
||||
use ruma::events::{AnyStateEvent, EmptyStateKey};
|
||||
|
||||
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");
|
||||
|
||||
let to_device_requests = alice
|
||||
.share_room_key(room_id, iter::once(bob.user_id()), EncryptionSettings::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let event = ToDeviceEvent::new(
|
||||
alice.user_id().to_owned(),
|
||||
to_device_requests_to_content(to_device_requests),
|
||||
);
|
||||
|
||||
let decryption_settings =
|
||||
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
|
||||
|
||||
let group_session = bob
|
||||
.store()
|
||||
.with_transaction(|mut tr| async {
|
||||
let res = bob
|
||||
.decrypt_to_device_event(
|
||||
&mut tr,
|
||||
&event,
|
||||
&mut Changes::default(),
|
||||
&decryption_settings,
|
||||
)
|
||||
.await?;
|
||||
Ok((tr, res))
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.inbound_group_session
|
||||
.unwrap();
|
||||
let sessions = std::slice::from_ref(&group_session);
|
||||
bob.store().save_inbound_group_sessions(sessions).await.unwrap();
|
||||
|
||||
let plaintext = "It is a secret to everybody";
|
||||
|
||||
let content = RoomTopicEventContent::new(plaintext.to_owned());
|
||||
|
||||
let encrypted_content =
|
||||
alice.encrypt_state_event(room_id, content, EmptyStateKey).await.unwrap();
|
||||
|
||||
let event = json!({
|
||||
"event_id": "$xxxxx:example.org",
|
||||
"origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
|
||||
"sender": alice.user_id(),
|
||||
"type": "m.room.encrypted",
|
||||
"content": encrypted_content,
|
||||
});
|
||||
|
||||
let event = json_convert(&event).unwrap();
|
||||
|
||||
let decryption_settings =
|
||||
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
|
||||
|
||||
let decryption_result =
|
||||
bob.try_decrypt_room_event(&event, room_id, &decryption_settings).await.unwrap();
|
||||
assert_let!(RoomEventDecryptionResult::Decrypted(decrypted_event) = decryption_result);
|
||||
let decrypted_event = decrypted_event.event.deserialize().unwrap();
|
||||
|
||||
if let AnyTimelineEvent::State(AnyStateEvent::RoomTopic(StateEvent::Original(
|
||||
OriginalRoomTopicEvent { sender, content, .. },
|
||||
))) = decrypted_event
|
||||
{
|
||||
assert_eq!(&sender, alice.user_id());
|
||||
assert_eq!(&content.topic, plaintext);
|
||||
} else {
|
||||
panic!("Decrypted room event has the wrong type");
|
||||
}
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_withheld_unverified() {
|
||||
let (alice, bob) =
|
||||
|
||||
Reference in New Issue
Block a user