Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(crypto): Add support for master key local pinning #3639

Merged
merged 11 commits into from
Aug 2, 2024
128 changes: 127 additions & 1 deletion crates/matrix-sdk-crypto/src/identities/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ impl IdentityManager {
*changed_private_identity = self.check_private_identity(&identity).await;
Ok(identity.into())
} else {
// First time seen, create the identity. The current MSK will be pinned.
BillCarsonFr marked this conversation as resolved.
Show resolved Hide resolved
let identity = OtherUserIdentityData::new(master_key, self_signing)?;
Ok(identity.into())
}
Expand Down Expand Up @@ -1338,7 +1339,7 @@ pub(crate) mod tests {
use std::ops::Deref;

use futures_util::pin_mut;
use matrix_sdk_test::{async_test, response_from_file};
use matrix_sdk_test::{async_test, response_from_file, test_json};
use ruma::{
api::{client::keys::get_keys::v3::Response as KeysQueryResponse, IncomingResponse},
device_id, user_id, TransactionId,
Expand Down Expand Up @@ -1897,4 +1898,129 @@ pub(crate) mod tests {

manager.store.get_device_data(other_user, device_id!("OBEBOSKTBE")).await.unwrap().unwrap();
}

#[async_test]
async fn test_manager_identity_updates() {
use test_json::keys_query_sets::IdentityChangeDataSet as DataSet;

let manager = manager_test_helper(user_id(), device_id()).await;
let other_user = DataSet::user_id();
let devices = manager.store.get_user_devices(other_user).await.unwrap();
assert_eq!(devices.devices().count(), 0);

let identity = manager.store.get_user_identity(other_user).await.unwrap();
assert!(identity.is_none());

manager
.receive_keys_query_response(
&TransactionId::new(),
&DataSet::key_query_with_identity_a(),
)
.await
.unwrap();

let identity = manager.store.get_user_identity(other_user).await.unwrap().unwrap();
let other_identity = identity.other().unwrap();

// There should be now an identity and no pin violation (pinned msk is the
// current one)
BillCarsonFr marked this conversation as resolved.
Show resolved Hide resolved
assert!(!other_identity.has_pin_violation());
let first_device = manager
.store
.get_device_data(other_user, DataSet::first_device_id())
.await
.unwrap()
.unwrap();
assert!(first_device.is_cross_signed_by_owner(&identity));

// We receive a new keys update for that user, with a new identity
manager
.receive_keys_query_response(
&TransactionId::new(),
&DataSet::key_query_with_identity_b(),
)
.await
.unwrap();

let identity = manager.store.get_user_identity(other_user).await.unwrap().unwrap();
let other_identity = identity.other().unwrap();

// The previous known identity has been replaced, there should be a pin
// violation
assert!(other_identity.has_pin_violation());

let second_device = manager
.store
.get_device_data(other_user, DataSet::second_device_id())
.await
.unwrap()
.unwrap();

// There is a new device signed by the new identity
assert!(second_device.is_cross_signed_by_owner(&identity));

// The first device should not be signed by the new identity
let first_device = manager
.store
.get_device_data(other_user, DataSet::first_device_id())
.await
.unwrap()
.unwrap();
assert!(!first_device.is_cross_signed_by_owner(&identity));

let remember_previous_identity = other_identity.clone();
// We receive updated keys for that user, with no identity anymore.
// Notice that there is no server API to delete identity, but we want to test
// here that a home server cannot clear the identity and serve a new one
// after that would get automatically approved.
BillCarsonFr marked this conversation as resolved.
Show resolved Hide resolved
manager
.receive_keys_query_response(
&TransactionId::new(),
&DataSet::key_query_with_identity_no_identity(),
)
.await
.unwrap();

let identity = manager.store.get_user_identity(other_user).await.unwrap().unwrap();
let other_identity = identity.other().unwrap();

assert_eq!(other_identity, &remember_previous_identity);
assert!(other_identity.has_pin_violation());
}

#[async_test]
async fn test_manager_resolve_identity_mismatch() {
use test_json::keys_query_sets::IdentityChangeDataSet as DataSet;

let manager = manager_test_helper(user_id(), device_id()).await;
let other_user = DataSet::user_id();

manager
.receive_keys_query_response(
&TransactionId::new(),
&DataSet::key_query_with_identity_a(),
)
.await
.unwrap();

// We receive a new keys update for that user, with a new identity
manager
.receive_keys_query_response(
&TransactionId::new(),
&DataSet::key_query_with_identity_b(),
)
.await
.unwrap();

let identity = manager.store.get_user_identity(other_user).await.unwrap().unwrap();
let other_identity = identity.other().unwrap();

// We have a new identity now, so there should be a pin violation
assert!(other_identity.has_pin_violation());

// Resolve the misatch by pinning the new identity
BillCarsonFr marked this conversation as resolved.
Show resolved Hide resolved
other_identity.pin();

assert!(!other_identity.has_pin_violation());
}
}
Loading
Loading