use std::{
collections::HashMap,
ops::Deref,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use as_variant::as_variant;
use ruma::{
api::client::keys::upload_signatures::v3::Request as SignatureUploadRequest,
events::{
key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
},
DeviceId, EventId, OwnedDeviceId, OwnedUserId, RoomId, UserId,
};
use serde::{Deserialize, Serialize};
use tracing::error;
use super::{atomic_bool_deserializer, atomic_bool_serializer};
use crate::{
error::SignatureError,
store::{Changes, IdentityChanges, Store},
types::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
verification::VerificationMachine,
CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest,
};
#[derive(Debug, Clone)]
pub enum UserIdentities {
Own(OwnUserIdentity),
Other(UserIdentity),
}
impl UserIdentities {
pub fn own(self) -> Option<OwnUserIdentity> {
as_variant!(self, Self::Own)
}
pub fn other(self) -> Option<UserIdentity> {
as_variant!(self, Self::Other)
}
pub fn user_id(&self) -> &UserId {
match self {
UserIdentities::Own(u) => u.user_id(),
UserIdentities::Other(u) => u.user_id(),
}
}
pub(crate) fn new(
store: Store,
identity: ReadOnlyUserIdentities,
verification_machine: VerificationMachine,
own_identity: Option<ReadOnlyOwnUserIdentity>,
) -> Self {
match identity {
ReadOnlyUserIdentities::Own(i) => {
Self::Own(OwnUserIdentity { inner: i, verification_machine, store })
}
ReadOnlyUserIdentities::Other(i) => {
Self::Other(UserIdentity { inner: i, own_identity, verification_machine })
}
}
}
}
impl From<OwnUserIdentity> for UserIdentities {
fn from(i: OwnUserIdentity) -> Self {
Self::Own(i)
}
}
impl From<UserIdentity> for UserIdentities {
fn from(i: UserIdentity) -> Self {
Self::Other(i)
}
}
#[derive(Debug, Clone)]
pub struct OwnUserIdentity {
pub(crate) inner: ReadOnlyOwnUserIdentity,
pub(crate) verification_machine: VerificationMachine,
store: Store,
}
impl Deref for OwnUserIdentity {
type Target = ReadOnlyOwnUserIdentity;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl OwnUserIdentity {
pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
self.mark_as_verified();
let changes = Changes {
identities: IdentityChanges {
changed: vec![self.inner.clone().into()],
new: vec![],
unchanged: vec![],
},
..Default::default()
};
if let Err(e) = self.verification_machine.store.save_changes(changes).await {
error!(error = ?e, "Couldn't store our own user identity after marking it as verified");
}
let cache = self.store.cache().await?;
let account = cache.account().await?;
account.sign_master_key(self.master_key.clone())
}
pub async fn request_verification(
&self,
) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> {
self.request_verification_helper(None).await
}
pub async fn request_verification_with_methods(
&self,
methods: Vec<VerificationMethod>,
) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> {
self.request_verification_helper(Some(methods)).await
}
pub async fn trusts_our_own_device(&self) -> Result<bool, CryptoStoreError> {
Ok(if let Some(signatures) = self.verification_machine.store.device_signatures().await? {
let mut device_keys = self.store.cache().await?.account().await?.device_keys();
device_keys.signatures = signatures;
self.inner.self_signing_key().verify_device_keys(&device_keys).is_ok()
} else {
false
})
}
async fn request_verification_helper(
&self,
methods: Option<Vec<VerificationMethod>>,
) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> {
let all_devices = self.verification_machine.store.get_user_devices(self.user_id()).await?;
let devices = self
.inner
.filter_devices_to_request(all_devices, self.verification_machine.own_device_id());
Ok(self
.verification_machine
.request_to_device_verification(self.user_id(), devices, methods)
.await)
}
}
#[derive(Debug, Clone)]
pub struct UserIdentity {
pub(crate) inner: ReadOnlyUserIdentity,
pub(crate) own_identity: Option<ReadOnlyOwnUserIdentity>,
pub(crate) verification_machine: VerificationMachine,
}
impl Deref for UserIdentity {
type Target = ReadOnlyUserIdentity;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl UserIdentity {
pub fn is_verified(&self) -> bool {
self.own_identity.as_ref().is_some_and(|o| o.is_identity_signed(&self.inner).is_ok())
}
pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
if self.user_id() != self.verification_machine.own_user_id() {
Ok(self
.verification_machine
.store
.private_identity
.lock()
.await
.sign_user(&self.inner)
.await?)
} else {
Err(SignatureError::UserIdMismatch)
}
}
pub async fn request_verification(
&self,
room_id: &RoomId,
request_event_id: &EventId,
methods: Option<Vec<VerificationMethod>>,
) -> VerificationRequest {
self.verification_machine
.request_verification(&self.inner, room_id, request_event_id, methods)
.await
}
pub async fn verification_request_content(
&self,
methods: Option<Vec<VerificationMethod>>,
) -> KeyVerificationRequestEventContent {
VerificationRequest::request(
self.verification_machine.own_user_id(),
self.verification_machine.own_device_id(),
self.user_id(),
methods,
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReadOnlyUserIdentities {
Own(ReadOnlyOwnUserIdentity),
Other(ReadOnlyUserIdentity),
}
impl From<ReadOnlyOwnUserIdentity> for ReadOnlyUserIdentities {
fn from(identity: ReadOnlyOwnUserIdentity) -> Self {
ReadOnlyUserIdentities::Own(identity)
}
}
impl From<ReadOnlyUserIdentity> for ReadOnlyUserIdentities {
fn from(identity: ReadOnlyUserIdentity) -> Self {
ReadOnlyUserIdentities::Other(identity)
}
}
impl ReadOnlyUserIdentities {
pub fn user_id(&self) -> &UserId {
match self {
ReadOnlyUserIdentities::Own(i) => i.user_id(),
ReadOnlyUserIdentities::Other(i) => i.user_id(),
}
}
pub fn master_key(&self) -> &MasterPubkey {
match self {
ReadOnlyUserIdentities::Own(i) => i.master_key(),
ReadOnlyUserIdentities::Other(i) => i.master_key(),
}
}
pub fn self_signing_key(&self) -> &SelfSigningPubkey {
match self {
ReadOnlyUserIdentities::Own(i) => &i.self_signing_key,
ReadOnlyUserIdentities::Other(i) => &i.self_signing_key,
}
}
pub fn user_signing_key(&self) -> Option<&UserSigningPubkey> {
match self {
ReadOnlyUserIdentities::Own(i) => Some(&i.user_signing_key),
ReadOnlyUserIdentities::Other(_) => None,
}
}
pub fn own(&self) -> Option<&ReadOnlyOwnUserIdentity> {
as_variant!(self, Self::Own)
}
pub(crate) fn into_own(self) -> Option<ReadOnlyOwnUserIdentity> {
as_variant!(self, Self::Own)
}
pub fn other(&self) -> Option<&ReadOnlyUserIdentity> {
as_variant!(self, Self::Other)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ReadOnlyUserIdentity {
user_id: OwnedUserId,
pub(crate) master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
}
impl PartialEq for ReadOnlyUserIdentity {
fn eq(&self, other: &Self) -> bool {
self.user_id == other.user_id
&& self.master_key == other.master_key
&& self.self_signing_key == other.self_signing_key
&& self.master_key.signatures() == other.master_key.signatures()
}
}
impl ReadOnlyUserIdentity {
pub(crate) fn new(
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
) -> Result<Self, SignatureError> {
master_key.verify_subkey(&self_signing_key)?;
Ok(Self { user_id: master_key.user_id().into(), master_key, self_signing_key })
}
#[cfg(test)]
pub(crate) async fn from_private(identity: &crate::olm::PrivateCrossSigningIdentity) -> Self {
let master_key = identity.master_key.lock().await.as_ref().unwrap().public_key.clone();
let self_signing_key =
identity.self_signing_key.lock().await.as_ref().unwrap().public_key.clone();
Self { user_id: identity.user_id().into(), master_key, self_signing_key }
}
pub fn user_id(&self) -> &UserId {
&self.user_id
}
pub fn master_key(&self) -> &MasterPubkey {
&self.master_key
}
pub fn self_signing_key(&self) -> &SelfSigningPubkey {
&self.self_signing_key
}
pub(crate) fn update(
&mut self,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
) -> Result<bool, SignatureError> {
master_key.verify_subkey(&self_signing_key)?;
let new = Self::new(master_key, self_signing_key)?;
let changed = new != *self;
*self = new;
Ok(changed)
}
pub(crate) fn is_device_signed(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> {
if self.user_id() != device.user_id() {
return Err(SignatureError::UserIdMismatch);
}
self.self_signing_key.verify_device(device)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadOnlyOwnUserIdentity {
user_id: OwnedUserId,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
user_signing_key: UserSigningPubkey,
#[serde(
serialize_with = "atomic_bool_serializer",
deserialize_with = "atomic_bool_deserializer"
)]
verified: Arc<AtomicBool>,
}
impl PartialEq for ReadOnlyOwnUserIdentity {
fn eq(&self, other: &Self) -> bool {
self.user_id == other.user_id
&& self.master_key == other.master_key
&& self.self_signing_key == other.self_signing_key
&& self.user_signing_key == other.user_signing_key
&& self.is_verified() == other.is_verified()
&& self.master_key.signatures() == other.master_key.signatures()
}
}
impl ReadOnlyOwnUserIdentity {
pub(crate) fn new(
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
user_signing_key: UserSigningPubkey,
) -> Result<Self, SignatureError> {
master_key.verify_subkey(&self_signing_key)?;
master_key.verify_subkey(&user_signing_key)?;
Ok(Self {
user_id: master_key.user_id().into(),
master_key,
self_signing_key,
user_signing_key,
verified: Arc::new(AtomicBool::new(false)),
})
}
#[cfg(test)]
pub(crate) async fn from_private(identity: &crate::olm::PrivateCrossSigningIdentity) -> Self {
let master_key = identity.master_key.lock().await.as_ref().unwrap().public_key.clone();
let self_signing_key =
identity.self_signing_key.lock().await.as_ref().unwrap().public_key.clone();
let user_signing_key =
identity.user_signing_key.lock().await.as_ref().unwrap().public_key.clone();
Self {
user_id: identity.user_id().into(),
master_key,
self_signing_key,
user_signing_key,
verified: Arc::new(AtomicBool::new(false)),
}
}
pub fn user_id(&self) -> &UserId {
&self.user_id
}
pub fn master_key(&self) -> &MasterPubkey {
&self.master_key
}
pub fn self_signing_key(&self) -> &SelfSigningPubkey {
&self.self_signing_key
}
pub fn user_signing_key(&self) -> &UserSigningPubkey {
&self.user_signing_key
}
pub(crate) fn is_identity_signed(
&self,
identity: &ReadOnlyUserIdentity,
) -> Result<(), SignatureError> {
self.user_signing_key.verify_master_key(&identity.master_key)
}
pub(crate) fn is_device_signed(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> {
if self.user_id() != device.user_id() {
return Err(SignatureError::UserIdMismatch);
}
self.self_signing_key.verify_device(device)
}
pub fn mark_as_verified(&self) {
self.verified.store(true, Ordering::SeqCst)
}
#[cfg(test)]
pub fn mark_as_unverified(&self) {
self.verified.store(false, Ordering::SeqCst)
}
pub fn is_verified(&self) -> bool {
self.verified.load(Ordering::SeqCst)
}
pub(crate) fn update(
&mut self,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
user_signing_key: UserSigningPubkey,
) -> Result<bool, SignatureError> {
master_key.verify_subkey(&self_signing_key)?;
master_key.verify_subkey(&user_signing_key)?;
let old = self.clone();
self.self_signing_key = self_signing_key;
self.user_signing_key = user_signing_key;
if self.master_key != master_key {
self.verified.store(false, Ordering::SeqCst);
}
self.master_key = master_key;
Ok(old != *self)
}
fn filter_devices_to_request(
&self,
devices: HashMap<OwnedDeviceId, ReadOnlyDevice>,
own_device_id: &DeviceId,
) -> Vec<OwnedDeviceId> {
devices
.into_iter()
.filter_map(|(device_id, device)| {
(device_id != own_device_id && self.is_device_signed(&device).is_ok())
.then_some(device_id)
})
.collect()
}
}
#[cfg(any(test, feature = "testing"))]
pub(crate) mod testing {
#![allow(dead_code)]
use ruma::{api::client::keys::get_keys::v3::Response as KeyQueryResponse, user_id};
use super::{ReadOnlyOwnUserIdentity, ReadOnlyUserIdentity};
#[cfg(test)]
use crate::{identities::manager::testing::other_user_id, olm::PrivateCrossSigningIdentity};
use crate::{
identities::{
manager::testing::{other_key_query, own_key_query},
ReadOnlyDevice,
},
types::CrossSigningKey,
};
pub fn device(response: &KeyQueryResponse) -> (ReadOnlyDevice, ReadOnlyDevice) {
let mut devices = response.device_keys.values().next().unwrap().values();
let first =
ReadOnlyDevice::try_from(&devices.next().unwrap().deserialize_as().unwrap()).unwrap();
let second =
ReadOnlyDevice::try_from(&devices.next().unwrap().deserialize_as().unwrap()).unwrap();
(first, second)
}
pub fn own_identity(response: &KeyQueryResponse) -> ReadOnlyOwnUserIdentity {
let user_id = user_id!("@example:localhost");
let master_key: CrossSigningKey =
response.master_keys.get(user_id).unwrap().deserialize_as().unwrap();
let user_signing: CrossSigningKey =
response.user_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
let self_signing: CrossSigningKey =
response.self_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
ReadOnlyOwnUserIdentity::new(
master_key.try_into().unwrap(),
self_signing.try_into().unwrap(),
user_signing.try_into().unwrap(),
)
.unwrap()
}
pub fn get_own_identity() -> ReadOnlyOwnUserIdentity {
own_identity(&own_key_query())
}
#[cfg(test)]
pub async fn get_other_own_identity() -> ReadOnlyOwnUserIdentity {
let private_identity = PrivateCrossSigningIdentity::new(other_user_id().into());
ReadOnlyOwnUserIdentity::from_private(&private_identity).await
}
pub fn get_other_identity() -> ReadOnlyUserIdentity {
let user_id = user_id!("@example2:localhost");
let response = other_key_query();
let master_key: CrossSigningKey =
response.master_keys.get(user_id).unwrap().deserialize_as().unwrap();
let self_signing: CrossSigningKey =
response.self_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
ReadOnlyUserIdentity::new(master_key.try_into().unwrap(), self_signing.try_into().unwrap())
.unwrap()
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::{collections::HashMap, sync::Arc};
use assert_matches::assert_matches;
use matrix_sdk_test::async_test;
use ruma::{device_id, user_id};
use serde_json::{json, Value};
use tokio::sync::Mutex;
use super::{
testing::{device, get_other_identity, get_own_identity},
ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities,
};
use crate::{
identities::{manager::testing::own_key_query, Device},
olm::{Account, PrivateCrossSigningIdentity},
store::{CryptoStoreWrapper, MemoryStore},
types::{CrossSigningKey, MasterPubkey, SelfSigningPubkey, Signatures, UserSigningPubkey},
verification::VerificationMachine,
};
#[test]
fn own_identity_create() {
let user_id = user_id!("@example:localhost");
let response = own_key_query();
let master_key: CrossSigningKey =
response.master_keys.get(user_id).unwrap().deserialize_as().unwrap();
let user_signing: CrossSigningKey =
response.user_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
let self_signing: CrossSigningKey =
response.self_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
ReadOnlyOwnUserIdentity::new(
master_key.try_into().unwrap(),
self_signing.try_into().unwrap(),
user_signing.try_into().unwrap(),
)
.unwrap();
}
#[test]
fn own_identity_partial_equality() {
let user_id = user_id!("@example:localhost");
let response = own_key_query();
let master_key: CrossSigningKey =
response.master_keys.get(user_id).unwrap().deserialize_as().unwrap();
let user_signing: CrossSigningKey =
response.user_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
let self_signing: CrossSigningKey =
response.self_signing_keys.get(user_id).unwrap().deserialize_as().unwrap();
let identity = ReadOnlyOwnUserIdentity::new(
master_key.clone().try_into().unwrap(),
self_signing.clone().try_into().unwrap(),
user_signing.clone().try_into().unwrap(),
)
.unwrap();
let mut master_key_updated_signature = master_key.clone();
master_key_updated_signature.signatures = Signatures::new();
let updated_identity = ReadOnlyOwnUserIdentity::new(
master_key_updated_signature.try_into().unwrap(),
self_signing.try_into().unwrap(),
user_signing.try_into().unwrap(),
)
.unwrap();
assert_ne!(identity, updated_identity);
assert_eq!(identity.master_key(), updated_identity.master_key());
}
#[test]
fn other_identity_create() {
get_other_identity();
}
#[test]
fn own_identity_check_signatures() {
let response = own_key_query();
let identity = get_own_identity();
let (first, second) = device(&response);
identity.is_device_signed(&first).unwrap_err();
identity.is_device_signed(&second).unwrap();
let private_identity =
Arc::new(Mutex::new(PrivateCrossSigningIdentity::empty(second.user_id())));
let verification_machine = VerificationMachine::new(
Account::with_device_id(second.user_id(), second.device_id()).static_data,
private_identity,
Arc::new(CryptoStoreWrapper::new(second.user_id(), MemoryStore::new())),
);
let first = Device {
inner: first,
verification_machine: verification_machine.clone(),
own_identity: Some(identity.clone()),
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
};
let second = Device {
inner: second,
verification_machine,
own_identity: Some(identity.clone()),
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
};
assert!(!second.is_locally_trusted());
assert!(!second.is_cross_signing_trusted());
assert!(!first.is_locally_trusted());
assert!(!first.is_cross_signing_trusted());
identity.mark_as_verified();
assert!(second.is_verified());
assert!(!first.is_verified());
}
#[async_test]
async fn own_device_with_private_identity() {
let response = own_key_query();
let (_, device) = device(&response);
let account = Account::with_device_id(device.user_id(), device.device_id());
let (identity, _, _) = PrivateCrossSigningIdentity::with_account(&account).await;
let id = Arc::new(Mutex::new(identity.clone()));
let verification_machine = VerificationMachine::new(
Account::with_device_id(device.user_id(), device.device_id()).static_data,
id.clone(),
Arc::new(CryptoStoreWrapper::new(device.user_id(), MemoryStore::new())),
);
let public_identity = identity.to_public_identity().await.unwrap();
let mut device = Device {
inner: device,
verification_machine: verification_machine.clone(),
own_identity: Some(public_identity.clone()),
device_owner_identity: Some(public_identity.clone().into()),
};
assert!(!device.is_verified());
let mut device_keys = device.as_device_keys().to_owned();
identity.sign_device_keys(&mut device_keys).await.unwrap();
device.inner.update_device(&device_keys).expect("Couldn't update newly signed device keys");
assert!(device.is_verified());
}
#[test]
fn cannot_instantiate_keys_with_incorrect_usage() {
let user_id = user_id!("@example:localhost");
let response = own_key_query();
let master_key = response.master_keys.get(user_id).unwrap();
let mut master_key_json: Value = master_key.deserialize_as().unwrap();
let self_signing_key = response.self_signing_keys.get(user_id).unwrap();
let mut self_signing_key_json: Value = self_signing_key.deserialize_as().unwrap();
let user_signing_key = response.user_signing_keys.get(user_id).unwrap();
let mut user_signing_key_json: Value = user_signing_key.deserialize_as().unwrap();
let usage = master_key_json.get_mut("usage").unwrap();
*usage = json!([]);
let usage = self_signing_key_json.get_mut("usage").unwrap();
*usage = json!([]);
let usage = user_signing_key_json.get_mut("usage").unwrap();
*usage = json!([]);
assert_matches!(serde_json::from_value::<MasterPubkey>(master_key_json.clone()), Err(_));
assert_matches!(
serde_json::from_value::<SelfSigningPubkey>(self_signing_key_json.clone()),
Err(_)
);
assert_matches!(
serde_json::from_value::<UserSigningPubkey>(user_signing_key_json.clone()),
Err(_)
);
let usage = master_key_json.get_mut("usage").unwrap();
*usage = json!(["master", "user_signing"]);
let usage = self_signing_key_json.get_mut("usage").unwrap();
*usage = json!(["self_signing", "user_signing"]);
let usage = user_signing_key_json.get_mut("usage").unwrap();
*usage = json!(["user_signing", "self_signing"]);
assert_matches!(serde_json::from_value::<MasterPubkey>(master_key_json.clone()), Err(_));
assert_matches!(
serde_json::from_value::<SelfSigningPubkey>(self_signing_key_json.clone()),
Err(_)
);
assert_matches!(
serde_json::from_value::<UserSigningPubkey>(user_signing_key_json.clone()),
Err(_)
);
}
#[test]
fn filter_devices_to_request() {
let response = own_key_query();
let identity = get_own_identity();
let (first, second) = device(&response);
let second_device_id = second.device_id().to_owned();
let unknown_device_id = device_id!("UNKNOWN");
let devices = HashMap::from([
(first.device_id().to_owned(), first),
(second.device_id().to_owned(), second),
]);
assert_eq!(identity.filter_devices_to_request(devices.clone(), &second_device_id).len(), 0);
assert_eq!(
identity.filter_devices_to_request(devices, unknown_device_id),
[second_device_id]
);
}
}