test: Add more olm decryption encryption_info tests

This commit is contained in:
Valere
2025-04-16 12:20:15 +02:00
committed by Damir Jelić
parent 4363105976
commit 4bf103db38
4 changed files with 223 additions and 24 deletions

View File

@@ -18,6 +18,7 @@
use std::collections::BTreeMap;
use as_variant::as_variant;
use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
use matrix_sdk_test::{ruma_response_from_json, test_json};
use ruma::{
api::client::keys::{
@@ -29,14 +30,20 @@ use ruma::{
encryption::OneTimeKey,
events::dummy::ToDeviceDummyEventContent,
serde::Raw,
to_device::DeviceIdOrAllDevices,
user_id, DeviceId, OwnedOneTimeKeyId, TransactionId, UserId,
};
use serde_json::json;
use serde_json::{json, Value};
use crate::{
machine::tests,
store::{Changes, MemoryStore},
types::{events::ToDeviceEvent, requests::AnyOutgoingRequest},
CrossSigningBootstrapRequests, DeviceData, OlmMachine,
types::{
events::ToDeviceEvent,
requests::{AnyOutgoingRequest, ToDeviceRequest},
},
utilities::json_convert,
CrossSigningBootstrapRequests, DeviceData, EncryptionSyncChanges, OlmMachine,
};
/// These keys need to be periodically uploaded to the server.
@@ -172,7 +179,7 @@ pub async fn get_machine_pair_with_session(
}
/// Create a session for the two supplied Olm machines to communicate.
async fn build_session_for_pair(
pub async fn build_session_for_pair(
alice: OlmMachine,
bob: OlmMachine,
mut one_time_keys: BTreeMap<
@@ -276,3 +283,51 @@ pub fn bootstrap_requests_to_keys_query_response(
ruma_response_from_json(&kq_response)
}
/// Encrypt and send a given to device event from the sender to the recipient.
///
/// Simulates the reception by having the recipient machine receiving a sync
/// with the encrypted message.
/// Returns the event as received by the recipient.
pub async fn encrypt_to_device_helper(
sender: &OlmMachine,
recipient: &OlmMachine,
event_type: &str,
event_content: Value,
) -> ProcessedToDeviceEvent {
let device =
sender.get_device(recipient.user_id(), recipient.device_id(), None).await.unwrap().unwrap();
let raw_encrypted = device
.encrypt_event_raw(event_type, &event_content)
.await
.expect("Should have encrypted the content");
let request = ToDeviceRequest::new(
recipient.user_id(),
DeviceIdOrAllDevices::DeviceId(recipient.device_id().to_owned()),
"m.room.encrypted",
raw_encrypted.cast(),
);
let encrypted_event = ToDeviceEvent::new(
sender.user_id().to_owned(),
tests::to_device_requests_to_content(vec![request.clone().into()]),
);
let encrypted_event = json_convert(&encrypted_event).unwrap();
let sync_changes = EncryptionSyncChanges {
to_device_events: vec![encrypted_event],
changed_devices: &Default::default(),
one_time_keys_counts: &Default::default(),
unused_fallback_keys: None,
next_batch_token: None,
};
let (to_devices, _) = recipient
.receive_sync_changes(sync_changes)
.await
.expect("Receive Sync changes should not fail");
to_devices.first().unwrap().clone()
}

View File

@@ -82,6 +82,7 @@ use crate::{
mod decryption_verification_state;
mod interactive_verification;
mod megolm_sender_data;
mod olm_decryption_encryption_info;
mod olm_encryption;
mod room_settings;
mod send_encrypted_to_device;
@@ -837,6 +838,7 @@ async fn test_decrypt_unencrypted_event() {
);
}
/// This only bootstrap cross-signing but it will not sign the current device !!
pub async fn setup_cross_signing_for_machine_test_helper(alice: &OlmMachine, bob: &OlmMachine) {
let CrossSigningBootstrapRequests { upload_signing_keys_req: alice_upload_signing, .. } =
alice.bootstrap_cross_signing(false).await.expect("Expect Alice x-signing key request");

View File

@@ -0,0 +1,149 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use assert_matches2::{assert_let, assert_matches};
use matrix_sdk_common::deserialized_responses::{
ProcessedToDeviceEvent, VerificationLevel, VerificationState,
};
use matrix_sdk_test::async_test;
use ruma::TransactionId;
use serde_json::json;
use crate::{
machine::{
test_helpers::{
bootstrap_requests_to_keys_query_response, build_session_for_pair,
encrypt_to_device_helper, get_machine_pair_with_session,
get_prepared_machine_test_helper,
},
tests,
tests::{
alice_device_id, alice_id,
decryption_verification_state::mark_alice_identity_as_verified_test_helper,
},
},
verification::tests::bob_id,
DeviceData, OlmMachine,
};
#[async_test]
async fn test_to_device_verification_state_signed_device() {
let (alice, bob) = get_machine_pair_with_session(alice_id(), tests::user_id(), false).await;
let bootstrap_requests = alice.bootstrap_cross_signing(false).await.unwrap();
let kq_response = bootstrap_requests_to_keys_query_response(bootstrap_requests);
bob.receive_keys_query_response(&TransactionId::new(), &kq_response).await.unwrap();
let custom_event_type = "m.hello";
let custom_content = json!({
"what": "foo",
});
let received = encrypt_to_device_helper(&alice, &bob, custom_event_type, custom_content).await;
assert_let!(ProcessedToDeviceEvent::Decrypted { encryption_info, .. } = received);
assert_matches!(
encryption_info.verification_state,
VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
);
}
#[async_test]
async fn test_to_device_verification_state_verified() {
let (alice, bob) = get_machine_pair_with_session(alice_id(), tests::user_id(), false).await;
let bootstrap_requests = alice.bootstrap_cross_signing(false).await.unwrap();
let kq_response = bootstrap_requests_to_keys_query_response(bootstrap_requests);
bob.receive_keys_query_response(&TransactionId::new(), &kq_response).await.unwrap();
let bootstrap_requests = bob.bootstrap_cross_signing(false).await.unwrap();
let kq_response = bootstrap_requests_to_keys_query_response(bootstrap_requests);
alice.receive_keys_query_response(&TransactionId::new(), &kq_response).await.unwrap();
mark_alice_identity_as_verified_test_helper(&alice, &bob).await;
let custom_event_type = "m.hello";
let custom_content = json!({
"what": "foo",
});
let received = encrypt_to_device_helper(&alice, &bob, custom_event_type, custom_content).await;
assert_let!(ProcessedToDeviceEvent::Decrypted { encryption_info, .. } = received);
assert_matches!(encryption_info.verification_state, VerificationState::Verified);
}
#[async_test]
async fn test_to_device_verification_state_verification_violation() {
let (alice, bob) = get_machine_pair_with_session(alice_id(), tests::user_id(), false).await;
let bootstrap_requests = alice.bootstrap_cross_signing(false).await.unwrap();
let kq_response = bootstrap_requests_to_keys_query_response(bootstrap_requests);
bob.receive_keys_query_response(&TransactionId::new(), &kq_response).await.unwrap();
// Simulate Alice's cross-signing key changing after having been verified by
// setting the `previously_verified` flag
let alice_identity =
bob.store().get_identity(alice.user_id()).await.unwrap().unwrap().other().unwrap();
alice_identity.mark_as_previously_verified().await.unwrap();
let custom_event_type = "m.hello";
let custom_content = json!({
"what": "foo",
});
let received = encrypt_to_device_helper(&alice, &bob, custom_event_type, custom_content).await;
assert_let!(ProcessedToDeviceEvent::Decrypted { encryption_info, .. } = received);
assert_matches!(
encryption_info.verification_state,
VerificationState::Unverified(VerificationLevel::VerificationViolation)
);
}
#[async_test]
async fn test_to_device_verification_level_none() {
// Arrange
// Set up prepared machine where it is the first time ever bob will receive a
// message from this alice device. So bob is not yet aware of alice device.
let (bob, otk) = get_prepared_machine_test_helper(bob_id(), false).await;
let alice_device = alice_device_id();
let alice = OlmMachine::new(alice_id(), alice_device).await;
// Bob doesn't know yet alice keys. Alice contact in an async way
let bob_device = DeviceData::from_machine_test_helper(&bob).await.unwrap();
alice.store().save_device_data(&[bob_device]).await.unwrap();
// Let alice claim one otk and create the outbound session
let (alice, bob) = build_session_for_pair(alice, bob, otk).await;
// Act
let custom_event_type = "m.hello";
let custom_content = json!({
"what": "foo",
});
let received = encrypt_to_device_helper(&alice, &bob, custom_event_type, custom_content).await;
// The decryption will fail (MissingSigningKey) for custom to-device events if
// the recipient does not know the sender.
// The VerificationLevel::None can only happen for room_key not custom events.
assert_matches!(received, ProcessedToDeviceEvent::UnableToDecrypt { .. });
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use assert_matches2::assert_matches;
use assert_matches2::{assert_let, assert_matches};
use matrix_sdk_common::deserialized_responses::{
AlgorithmInfo, ProcessedToDeviceEvent, VerificationLevel, VerificationState,
};
@@ -88,9 +88,9 @@ async fn test_send_encrypted_to_device() {
assert_eq!(1, decrypted.len());
let processed_event = &decrypted[0];
let ProcessedToDeviceEvent::Decrypted { decrypted_event, .. } = processed_event else {
panic!("Unexpected variant");
};
assert_let!(ProcessedToDeviceEvent::Decrypted { decrypted_event, .. } = processed_event);
let decrypted_event = decrypted_event.deserialize().unwrap();
assert_eq!(decrypted_event.event_type().to_string(), custom_event_type.to_owned());
@@ -210,17 +210,16 @@ async fn test_processed_to_device_variants() {
assert_eq!(4, processed.len());
let processed_event = &processed[0];
let ProcessedToDeviceEvent::Decrypted { encryption_info, .. } = processed_event else {
panic!("Unexpected variant");
};
assert_let!(ProcessedToDeviceEvent::Decrypted { encryption_info, .. } = processed_event);
assert_eq!(alice.user_id().to_owned(), encryption_info.sender);
assert_eq!(Some(alice.device_id().to_owned()), encryption_info.sender_device);
let AlgorithmInfo::OlmV1Curve25519AesSha2 { curve25519_key } = &encryption_info.algorithm_info
else {
panic!("Unexpected algorithm info");
};
assert_let!(
AlgorithmInfo::OlmV1Curve25519AesSha2 { curve25519_key } = &encryption_info.algorithm_info
);
assert_eq!(curve25519_key.to_owned(), alice_curve.to_base64());
assert_eq!(encryption_info.session_id, None);
@@ -230,19 +229,13 @@ async fn test_processed_to_device_variants() {
);
let processed_event = &processed[1];
let ProcessedToDeviceEvent::PlainText(_) = processed_event else {
panic!("Unexpected variant");
};
assert_matches!(processed_event, ProcessedToDeviceEvent::PlainText(_));
let processed_event = &processed[2];
let ProcessedToDeviceEvent::NotProcessed(_) = processed_event else {
panic!("Unexpected variant");
};
assert_matches!(processed_event, ProcessedToDeviceEvent::NotProcessed(_));
let processed_event = &processed[3];
let ProcessedToDeviceEvent::UnableToDecrypt { .. } = processed_event else {
panic!("Unexpected variant");
};
assert_matches!(processed_event, ProcessedToDeviceEvent::UnableToDecrypt { .. });
}
#[async_test]