// 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 std::{ convert::TryFrom, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, }; use serde::{Deserialize, Serialize}; use serde_json::to_value; use matrix_sdk_common::{ api::r0::keys::CrossSigningKey, identifiers::{DeviceKeyId, UserId}, }; use crate::{error::SignatureError, verify_json, ReadOnlyDevice}; /// Wrapper for a cross signing key marking it as the master key. /// /// Master keys are used to sign other cross signing keys, the self signing and /// user signing keys of an user will be signed by their master key. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MasterPubkey(Arc); /// Wrapper for a cross signing key marking it as a self signing key. /// /// Self signing keys are used to sign the user's own devices. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SelfSigningPubkey(Arc); /// Wrapper for a cross signing key marking it as a user signing key. /// /// User signing keys are used to sign the master keys of other users. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserSigningPubkey(Arc); impl PartialEq for MasterPubkey { fn eq(&self, other: &MasterPubkey) -> bool { self.0.user_id == other.0.user_id && self.0.keys == other.0.keys } } impl From<&CrossSigningKey> for MasterPubkey { fn from(key: &CrossSigningKey) -> Self { Self(Arc::new(key.clone())) } } impl From<&CrossSigningKey> for SelfSigningPubkey { fn from(key: &CrossSigningKey) -> Self { Self(Arc::new(key.clone())) } } impl From<&CrossSigningKey> for UserSigningPubkey { fn from(key: &CrossSigningKey) -> Self { Self(Arc::new(key.clone())) } } impl<'a> From<&'a SelfSigningPubkey> for CrossSigningSubKeys<'a> { fn from(key: &'a SelfSigningPubkey) -> Self { CrossSigningSubKeys::SelfSigning(key) } } impl<'a> From<&'a UserSigningPubkey> for CrossSigningSubKeys<'a> { fn from(key: &'a UserSigningPubkey) -> Self { CrossSigningSubKeys::UserSigning(key) } } /// Enum over the cross signing sub-keys. enum CrossSigningSubKeys<'a> { /// The self signing subkey. SelfSigning(&'a SelfSigningPubkey), /// The user signing subkey. UserSigning(&'a UserSigningPubkey), } impl<'a> CrossSigningSubKeys<'a> { /// Get the id of the user that owns this cross signing subkey. fn user_id(&self) -> &UserId { match self { CrossSigningSubKeys::SelfSigning(key) => &key.0.user_id, CrossSigningSubKeys::UserSigning(key) => &key.0.user_id, } } /// Get the `CrossSigningKey` from an sub-keys enum fn cross_signing_key(&self) -> &CrossSigningKey { match self { CrossSigningSubKeys::SelfSigning(key) => &key.0, CrossSigningSubKeys::UserSigning(key) => &key.0, } } } impl MasterPubkey { /// Get the user id of the master key's owner. pub fn user_id(&self) -> &UserId { &self.0.user_id } /// Get the master key with the given key id. /// /// # Arguments /// /// * `key_id` - The id of the key that should be fetched. pub fn get_key(&self, key_id: &DeviceKeyId) -> Option<&str> { self.0.keys.get(key_id.as_str()).map(|k| k.as_str()) } /// Check if the given cross signing sub-key is signed by the master key. /// /// # Arguments /// /// * `subkey` - The subkey that should be checked for a valid signature. /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. fn verify_subkey<'a>( &self, subkey: impl Into>, ) -> Result<(), SignatureError> { let (key_id, key) = self .0 .keys .iter() .next() .ok_or(SignatureError::MissingSigningKey)?; let key_id = DeviceKeyId::try_from(key_id.as_str())?; // FIXME `KeyUsage is missing PartialEq. // if self.0.usage.contains(&KeyUsage::Master) { // return Err(SignatureError::MissingSigningKey); // } let subkey: CrossSigningSubKeys = subkey.into(); if &self.0.user_id != subkey.user_id() { return Err(SignatureError::UserIdMissmatch); } verify_json( &self.0.user_id, &key_id, key, &mut to_value(subkey.cross_signing_key()).map_err(|_| SignatureError::NotAnObject)?, ) } } impl UserSigningPubkey { /// Get the user id of the user signing key's owner. pub fn user_id(&self) -> &UserId { &self.0.user_id } /// Check if the given master key is signed by this user signing key. /// /// # Arguments /// /// * `master_key` - The master key that should be checked for a valid /// signature. /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. fn verify_master_key(&self, master_key: &MasterPubkey) -> Result<(), SignatureError> { let (key_id, key) = self .0 .keys .iter() .next() .ok_or(SignatureError::MissingSigningKey)?; // TODO check that the usage is OK. verify_json( &self.0.user_id, &DeviceKeyId::try_from(key_id.as_str())?, key, &mut to_value(&*master_key.0).map_err(|_| SignatureError::NotAnObject)?, ) } } impl SelfSigningPubkey { /// Get the user id of the self signing key's owner. pub fn user_id(&self) -> &UserId { &self.0.user_id } /// Check if the given device is signed by this self signing key. /// /// # Arguments /// /// * `device` - The device that should be checked for a valid signature. /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. fn verify_device(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> { let (key_id, key) = self .0 .keys .iter() .next() .ok_or(SignatureError::MissingSigningKey)?; // TODO check that the usage is OK. verify_json( &self.0.user_id, &DeviceKeyId::try_from(key_id.as_str())?, key, &mut device.as_signature_message(), ) } } /// Enum over the different user identity types we can have. #[derive(Debug, Clone)] pub enum UserIdentities { /// Our own user identity. Own(OwnUserIdentity), /// Identities of other users. Other(UserIdentity), } impl UserIdentities { /// The unique user id of this identity. pub fn user_id(&self) -> &UserId { match self { UserIdentities::Own(i) => i.user_id(), UserIdentities::Other(i) => i.user_id(), } } /// Get the master key of the identity. pub fn master_key(&self) -> &MasterPubkey { match self { UserIdentities::Own(i) => i.master_key(), UserIdentities::Other(i) => i.master_key(), } } /// Destructure the enum into an `OwnUserIdentity` if it's of the correct /// type. pub fn own(&self) -> Option<&OwnUserIdentity> { match self { UserIdentities::Own(i) => Some(i), _ => None, } } } impl PartialEq for UserIdentities { fn eq(&self, other: &UserIdentities) -> bool { self.user_id() == other.user_id() } } /// Struct representing a cross signing identity of a user. /// /// This is the user identity of a user that isn't our own. Other users will /// only contain a master key and a self signing key, meaning that only device /// signatures can be checked with this identity. #[derive(Debug, Clone, Deserialize, Serialize)] pub struct UserIdentity { user_id: Arc, master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, } impl UserIdentity { /// Create a new user identity with the given master and self signing key. /// /// # Arguments /// /// * `master_key` - The master key of the user identity. /// /// * `self signing key` - The self signing key of user identity. /// /// Returns a `SignatureError` if the self signing key fails to be correctly /// verified by the given master key. pub fn new( master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, ) -> Result { master_key.verify_subkey(&self_signing_key)?; Ok(Self { user_id: Arc::new(master_key.0.user_id.clone()), master_key, self_signing_key, }) } /// Get the user id of this identity. pub fn user_id(&self) -> &UserId { &self.user_id } /// Get the public master key of the identity. pub fn master_key(&self) -> &MasterPubkey { &self.master_key } /// Update the identity with a new master key and self signing key. /// /// # Arguments /// /// * `master_key` - The new master key of the user identity. /// /// * `self_signing_key` - The new self signing key of user identity. /// /// Returns a `SignatureError` if we failed to update the identity. pub fn update( &mut self, master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, ) -> Result<(), SignatureError> { master_key.verify_subkey(&self_signing_key)?; self.master_key = master_key; self.self_signing_key = self_signing_key; Ok(()) } /// Check if the given device has been signed by this identity. /// /// The user_id of the user identity and the user_id of the device need to /// match for the signature check to succeed as we don't trust users to sign /// devices of other users. /// /// # Arguments /// /// * `device` - The device that should be checked for a valid signature. /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. pub fn is_device_signed(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> { if self.user_id() != device.user_id() { return Err(SignatureError::UserIdMissmatch); } self.self_signing_key.verify_device(device) } } /// Struct representing a cross signing identity of our own user. /// /// This is the user identity of our own user. This user identity will contain a /// master key, self signing key as well as a user signing key. /// /// This identity can verify other identities as well as devices belonging to /// the identity. #[derive(Debug, Clone)] pub struct OwnUserIdentity { user_id: Arc, master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, user_signing_key: UserSigningPubkey, verified: Arc, } impl OwnUserIdentity { /// Create a new own user identity with the given master, self signing, and /// user signing key. /// /// # Arguments /// /// * `master_key` - The master key of the user identity. /// /// * `self_signing_key` - The self signing key of user identity. /// /// * `user_signing_key` - The user signing key of user identity. /// /// Returns a `SignatureError` if the self signing key fails to be correctly /// verified by the given master key. pub fn new( master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, user_signing_key: UserSigningPubkey, ) -> Result { master_key.verify_subkey(&self_signing_key)?; master_key.verify_subkey(&user_signing_key)?; Ok(Self { user_id: Arc::new(master_key.0.user_id.clone()), master_key, self_signing_key, user_signing_key, verified: Arc::new(AtomicBool::new(false)), }) } /// Get the user id of this identity. pub fn user_id(&self) -> &UserId { &self.user_id } /// Get the public master key of the identity. pub fn master_key(&self) -> &MasterPubkey { &self.master_key } /// Check if the given identity has been signed by this identity. /// /// # Arguments /// /// * `identity` - The identity of another user that we want to check if /// it's has been signed. /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. pub fn is_identity_signed(&self, identity: &UserIdentity) -> Result<(), SignatureError> { self.user_signing_key .verify_master_key(&identity.master_key) } /// Check if the given device has been signed by this identity. /// /// Only devices of our own user should be checked with this method, if a /// device of a different user is given the signature check will always fail /// even if a valid signature exists. /// /// # Arguments /// /// * `device` - The device that should be checked for a valid signature. /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. pub fn is_device_signed(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> { if self.user_id() != device.user_id() { return Err(SignatureError::UserIdMissmatch); } self.self_signing_key.verify_device(device) } /// Mark our identity as verified. pub fn mark_as_verified(&self) { self.verified.store(true, Ordering::SeqCst) } /// Check if our identity is verified. pub fn is_verified(&self) -> bool { self.verified.load(Ordering::SeqCst) } /// Update the identity with a new master key and self signing key. /// /// Note: This will reset the verification state if the master keys differ. /// /// # Arguments /// /// * `master_key` - The new master key of the user identity. /// /// * `self_signing_key` - The new self signing key of user identity. /// /// * `user_signing_key` - The new user signing key of user identity. /// /// Returns a `SignatureError` if we failed to update the identity. pub fn update( &mut self, master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, user_signing_key: UserSigningPubkey, ) -> Result<(), SignatureError> { master_key.verify_subkey(&self_signing_key)?; master_key.verify_subkey(&user_signing_key)?; 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(()) } } #[cfg(test)] mod test { use serde_json::json; use std::{convert::TryFrom, sync::Arc}; use matrix_sdk_common::{ api::r0::keys::get_keys::Response as KeyQueryResponse, identifiers::user_id, }; use crate::{ identities::{Device, ReadOnlyDevice}, machine::test::response_from_file, olm::Account, store::memorystore::MemoryStore, verification::VerificationMachine, }; use super::{OwnUserIdentity, UserIdentities, UserIdentity}; fn other_key_query() -> KeyQueryResponse { let data = response_from_file(&json!({ "device_keys": { "@example2:localhost": { "SKISMLNIMH": { "algorithms": ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"], "device_id": "SKISMLNIMH", "keys": { "curve25519:SKISMLNIMH": "qO9xFazIcW8dE0oqHGMojGgJwbBpMOhGnIfJy2pzvmI", "ed25519:SKISMLNIMH": "y3wV3AoyIGREqrJJVH8DkQtlwHBUxoZ9ApP76kFgXQ8" }, "signatures": { "@example2:localhost": { "ed25519:SKISMLNIMH": "YwbT35rbjKoYFZVU1tQP8MsL06+znVNhNzUMPt6jTEYRBFoC4GDq9hQEJBiFSq37r1jvLMteggVAWw37fs1yBA", "ed25519:ZtFrSkJ1qB8Jph/ql9Eo/lKpIYCzwvKAKXfkaS4XZNc": "PWuuTE/aTkp1EJQkPHhRx2BxbF+wjMIDFxDRp7JAerlMkDsNFUTfRRusl6vqROPU36cl+yY8oeJTZGFkU6+pBQ" } }, "user_id": "@example2:localhost", "unsigned": { "device_display_name": "Riot Desktop (Linux)" } } } }, "failures": {}, "master_keys": { "@example2:localhost": { "user_id": "@example2:localhost", "usage": ["master"], "keys": { "ed25519:kC/HmRYw4HNqUp/i4BkwYENrf+hd9tvdB7A1YOf5+Do": "kC/HmRYw4HNqUp/i4BkwYENrf+hd9tvdB7A1YOf5+Do" }, "signatures": { "@example2:localhost": { "ed25519:SKISMLNIMH": "KdUZqzt8VScGNtufuQ8lOf25byYLWIhmUYpPENdmM8nsldexD7vj+Sxoo7PknnTX/BL9h2N7uBq0JuykjunCAw" } } } }, "self_signing_keys": { "@example2:localhost": { "user_id": "@example2:localhost", "usage": ["self_signing"], "keys": { "ed25519:ZtFrSkJ1qB8Jph/ql9Eo/lKpIYCzwvKAKXfkaS4XZNc": "ZtFrSkJ1qB8Jph/ql9Eo/lKpIYCzwvKAKXfkaS4XZNc" }, "signatures": { "@example2:localhost": { "ed25519:kC/HmRYw4HNqUp/i4BkwYENrf+hd9tvdB7A1YOf5+Do": "W/O8BnmiUETPpH02mwYaBgvvgF/atXnusmpSTJZeUSH/vHg66xiZOhveQDG4cwaW8iMa+t9N4h1DWnRoHB4mCQ" } } } }, "user_signing_keys": {} })); KeyQueryResponse::try_from(data).expect("Can't parse the keys upload response") } fn own_key_query() -> KeyQueryResponse { let data = response_from_file(&json!({ "device_keys": { "@example:localhost": { "WSKKLTJZCL": { "algorithms": [ "m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2" ], "device_id": "WSKKLTJZCL", "keys": { "curve25519:WSKKLTJZCL": "wnip2tbJBJxrFayC88NNJpm61TeSNgYcqBH4T9yEDhU", "ed25519:WSKKLTJZCL": "lQ+eshkhgKoo+qp9Qgnj3OX5PBoWMU5M9zbuEevwYqE" }, "signatures": { "@example:localhost": { "ed25519:WSKKLTJZCL": "SKpIUnq7QK0xleav0PrIQyKjVm+TgZr7Yi8cKjLeZDtkgyToE2d4/e3Aj79dqOlLB92jFVE4d1cM/Ry04wFwCA", "ed25519:0C8lCBxrvrv/O7BQfsKnkYogHZX3zAgw3RfJuyiq210": "9UGu1iC5YhFCdELGfB29YaV+QE0t/X5UDSsPf4QcdZyXIwyp9zBbHX2lh9vWudNQ+akZpaq7ZRaaM+4TCnw/Ag" } }, "user_id": "@example:localhost", "unsigned": { "device_display_name": "Cross signing capable" } }, "LVWOVGOXME": { "algorithms": [ "m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2" ], "device_id": "LVWOVGOXME", "keys": { "curve25519:LVWOVGOXME": "KMfWKUhnDW1D11hNzATs/Ax1FQRsJxKCWzq0NyGtIiI", "ed25519:LVWOVGOXME": "k+NC3L7CBD6fBClcHBrKLOkqCyGNSKhWXiH5Q2STRnA" }, "signatures": { "@example:localhost": { "ed25519:LVWOVGOXME": "39Ir5Bttpc5+bQwzLj7rkjm5E5/cp/JTbMJ/t0enj6J5w9MXVBFOUqqM2hpaRaRwILMMpwYbJ8IOGjl0Y/MGAw" } }, "user_id": "@example:localhost", "unsigned": { "device_display_name": "Non-cross signing" } } } }, "failures": {}, "master_keys": { "@example:localhost": { "user_id": "@example:localhost", "usage": [ "master" ], "keys": { "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0" }, "signatures": { "@example:localhost": { "ed25519:WSKKLTJZCL": "ZzJp1wtmRdykXAUEItEjNiFlBrxx8L6/Vaen9am8AuGwlxxJtOkuY4m+4MPLvDPOgavKHLsrRuNLAfCeakMlCQ" } } } }, "self_signing_keys": { "@example:localhost": { "user_id": "@example:localhost", "usage": [ "self_signing" ], "keys": { "ed25519:0C8lCBxrvrv/O7BQfsKnkYogHZX3zAgw3RfJuyiq210": "0C8lCBxrvrv/O7BQfsKnkYogHZX3zAgw3RfJuyiq210" }, "signatures": { "@example:localhost": { "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "AC7oDUW4rUhtInwb4lAoBJ0wAuu4a5k+8e34B5+NKsDB8HXRwgVwUWN/MRWc/sJgtSbVlhzqS9THEmQQ1C51Bw" } } } }, "user_signing_keys": { "@example:localhost": { "user_id": "@example:localhost", "usage": [ "user_signing" ], "keys": { "ed25519:DU9z4gBFKFKCk7a13sW9wjT0Iyg7Hqv5f0BPM7DEhPo": "DU9z4gBFKFKCk7a13sW9wjT0Iyg7Hqv5f0BPM7DEhPo" }, "signatures": { "@example:localhost": { "ed25519:rJ2TAGkEOP6dX41Ksll6cl8K3J48l8s/59zaXyvl2p0": "C4L2sx9frGqj8w41KyynHGqwUbbwBYRZpYCB+6QWnvQFA5Oi/1PJj8w5anwzEsoO0TWmLYmf7FXuAGewanOWDg" } } } } })); KeyQueryResponse::try_from(data).expect("Can't parse the keys upload response") } fn device(response: &KeyQueryResponse) -> (ReadOnlyDevice, ReadOnlyDevice) { let mut devices = response.device_keys.values().next().unwrap().values(); let first = ReadOnlyDevice::try_from(devices.next().unwrap()).unwrap(); let second = ReadOnlyDevice::try_from(devices.next().unwrap()).unwrap(); (first, second) } fn own_identity(response: &KeyQueryResponse) -> OwnUserIdentity { let user_id = user_id!("@example:localhost"); let master_key = response.master_keys.get(&user_id).unwrap(); let user_signing = response.user_signing_keys.get(&user_id).unwrap(); let self_signing = response.self_signing_keys.get(&user_id).unwrap(); OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap() } #[test] fn own_identity_create() { let user_id = user_id!("@example:localhost"); let response = own_key_query(); let master_key = response.master_keys.get(&user_id).unwrap(); let user_signing = response.user_signing_keys.get(&user_id).unwrap(); let self_signing = response.self_signing_keys.get(&user_id).unwrap(); OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap(); } #[test] fn other_identity_create() { let user_id = user_id!("@example2:localhost"); let response = other_key_query(); let master_key = response.master_keys.get(&user_id).unwrap(); let self_signing = response.self_signing_keys.get(&user_id).unwrap(); UserIdentity::new(master_key.into(), self_signing.into()).unwrap(); } #[test] fn own_identity_check_signatures() { let response = own_key_query(); let identity = own_identity(&response); let (first, second) = device(&response); assert!(identity.is_device_signed(&first).is_err()); assert!(identity.is_device_signed(&second).is_ok()); let verification_machine = VerificationMachine::new( Account::new(second.user_id(), second.device_id()), Arc::new(Box::new(MemoryStore::new())), ); let first = Device { inner: first, verification_machine: verification_machine.clone(), own_identity: Some(identity.clone()), device_owner_identity: Some(UserIdentities::Own(identity.clone())), }; let second = Device { inner: second, verification_machine, own_identity: Some(identity.clone()), device_owner_identity: Some(UserIdentities::Own(identity.clone())), }; assert!(!second.trust_state()); assert!(!second.is_trusted()); assert!(!first.trust_state()); assert!(!first.is_trusted()); identity.mark_as_verified(); assert!(second.trust_state()); assert!(!first.trust_state()); } }