crypto: Add methods to manually verify devices and users

This commit is contained in:
Damir Jelić 2021-08-09 17:28:43 +02:00
parent ee838087ca
commit d4fe6f5133
7 changed files with 74 additions and 19 deletions

View file

@ -135,29 +135,42 @@ pub enum SessionUnpicklingError {
SessionTimestampError, SessionTimestampError,
} }
/// Error type describin different errors that happen when we check or create
/// signatures for a Matrix JSON object.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum SignatureError { pub enum SignatureError {
#[error("the signature used a unsupported algorithm")] /// The signature was made using an unsupported algorithm.
#[error("the signature used an unsupported algorithm")]
UnsupportedAlgorithm, UnsupportedAlgorithm,
#[error("the key id of the signing key is invalid")] /// The ID of the signing key isn't a valid key ID.
#[error("the ID of the signing key is invalid")]
InvalidKeyId(#[from] IdentifierError), InvalidKeyId(#[from] IdentifierError),
/// The signing key that should create or check a signature is missing.
#[error("the signing key is missing from the object that signed the message")] #[error("the signing key is missing from the object that signed the message")]
MissingSigningKey, MissingSigningKey,
#[error("the user id of the signing differs from the subkey user id")] /// The user id of signing key differs from the user id that provided the
/// signature.
#[error("the user id of the signing key differs user id that provided the signature")]
UserIdMissmatch, UserIdMissmatch,
/// The provided JSON value that was signed and the signature should be
/// checked isn't a valid JSON object.
#[error("the provided JSON value isn't an object")] #[error("the provided JSON value isn't an object")]
NotAnObject, NotAnObject,
/// The provided JSON value that was signed and the signature should be
/// checked isn't a valid JSON object.
#[error("the provided JSON object doesn't contain a signatures field")] #[error("the provided JSON object doesn't contain a signatures field")]
NoSignatureFound, NoSignatureFound,
/// The signature couldn't be verified.
#[error("the signature didn't match the provided key")] #[error("the signature didn't match the provided key")]
VerificationError, VerificationError,
/// The signed object couldn't be deserialized.
#[error(transparent)] #[error(transparent)]
JsonError(#[from] SerdeError), JsonError(#[from] SerdeError),
} }

View file

@ -25,6 +25,7 @@ use std::{
use atomic::Atomic; use atomic::Atomic;
use matrix_sdk_common::locks::Mutex; use matrix_sdk_common::locks::Mutex;
use ruma::{ use ruma::{
api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest,
encryption::{DeviceKeys, SignedKey}, encryption::{DeviceKeys, SignedKey},
events::{ events::{
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
@ -103,7 +104,6 @@ where
/// A device represents a E2EE capable client of an user. /// A device represents a E2EE capable client of an user.
pub struct Device { pub struct Device {
pub(crate) inner: ReadOnlyDevice, pub(crate) inner: ReadOnlyDevice,
pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
pub(crate) verification_machine: VerificationMachine, pub(crate) verification_machine: VerificationMachine,
pub(crate) own_identity: Option<ReadOnlyOwnUserIdentity>, pub(crate) own_identity: Option<ReadOnlyOwnUserIdentity>,
pub(crate) device_owner_identity: Option<ReadOnlyUserIdentities>, pub(crate) device_owner_identity: Option<ReadOnlyUserIdentities>,
@ -204,6 +204,33 @@ impl Device {
self.inner.is_cross_signing_trusted(&self.own_identity, &self.device_owner_identity) self.inner.is_cross_signing_trusted(&self.own_identity, &self.device_owner_identity)
} }
/// Manually verify this device.
///
/// This method will attempt to sign the device using our private cross
/// signing key.
///
/// This method will always fail if the device belongs to someone else, we
/// can only sign our own devices.
///
/// It can also fail if we don't have the private part of our self-signing
/// key.
///
/// Returns a request that needs to be sent out for the device to be marked
/// as verified.
pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
if self.user_id() == self.verification_machine.own_user_id() {
Ok(self
.verification_machine
.private_identity
.lock()
.await
.sign_device(&self.inner)
.await?)
} else {
Err(SignatureError::UserIdMissmatch)
}
}
/// Set the local trust state of the device to the given state. /// Set the local trust state of the device to the given state.
/// ///
/// This won't affect any cross signing trust state, this only sets a flag /// This won't affect any cross signing trust state, this only sets a flag
@ -280,7 +307,6 @@ impl UserDevices {
pub fn get(&self, device_id: &DeviceId) -> Option<Device> { pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
self.inner.get(device_id).map(|d| Device { self.inner.get(device_id).map(|d| Device {
inner: d.clone(), inner: d.clone(),
private_identity: self.private_identity.clone(),
verification_machine: self.verification_machine.clone(), verification_machine: self.verification_machine.clone(),
own_identity: self.own_identity.clone(), own_identity: self.own_identity.clone(),
device_owner_identity: self.device_owner_identity.clone(), device_owner_identity: self.device_owner_identity.clone(),
@ -302,7 +328,6 @@ impl UserDevices {
pub fn devices(&self) -> impl Iterator<Item = Device> + '_ { pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
self.inner.values().map(move |d| Device { self.inner.values().map(move |d| Device {
inner: d.clone(), inner: d.clone(),
private_identity: self.private_identity.clone(),
verification_machine: self.verification_machine.clone(), verification_machine: self.verification_machine.clone(),
own_identity: self.own_identity.clone(), own_identity: self.own_identity.clone(),
device_owner_identity: self.device_owner_identity.clone(), device_owner_identity: self.device_owner_identity.clone(),

View file

@ -23,6 +23,7 @@ use std::{
}; };
use ruma::{ use ruma::{
api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest,
encryption::{CrossSigningKey, DeviceKeys, KeyUsage}, encryption::{CrossSigningKey, DeviceKeys, KeyUsage},
events::{ events::{
key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent, key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
@ -33,8 +34,6 @@ use serde::{Deserialize, Serialize};
use serde_json::to_value; use serde_json::to_value;
use super::{atomic_bool_deserializer, atomic_bool_serializer}; use super::{atomic_bool_deserializer, atomic_bool_serializer};
#[cfg(test)]
use crate::olm::PrivateCrossSigningIdentity;
use crate::{ use crate::{
error::SignatureError, olm::Utility, verification::VerificationMachine, CryptoStoreError, error::SignatureError, olm::Utility, verification::VerificationMachine, CryptoStoreError,
OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest, OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest,
@ -190,6 +189,30 @@ impl UserIdentity {
.unwrap_or(false) .unwrap_or(false)
} }
/// Manually verify this user.
///
/// This method will attempt to sign the user identity using our private
/// cross signing key.
///
/// This method fails if we don't have the private part of our user-signing
/// key.
///
/// Returns a request that needs to be sent out for the user to be marked
/// as verified.
pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
if self.user_id() == self.verification_machine.own_user_id() {
Ok(self
.verification_machine
.private_identity
.lock()
.await
.sign_user(&self.inner)
.await?)
} else {
Err(SignatureError::UserIdMissmatch)
}
}
/// Create a `VerificationRequest` object after the verification request /// Create a `VerificationRequest` object after the verification request
/// content has been sent out. /// content has been sent out.
pub async fn request_verification( pub async fn request_verification(
@ -667,7 +690,7 @@ impl ReadOnlyUserIdentity {
} }
#[cfg(test)] #[cfg(test)]
pub async fn from_private(identity: &PrivateCrossSigningIdentity) -> Self { pub async fn from_private(identity: &crate::olm::PrivateCrossSigningIdentity) -> Self {
let master_key = identity.master_key.lock().await.as_ref().unwrap().public_key.clone(); let master_key = identity.master_key.lock().await.as_ref().unwrap().public_key.clone();
let self_signing_key = let self_signing_key =
identity.self_signing_key.lock().await.as_ref().unwrap().public_key.clone(); identity.self_signing_key.lock().await.as_ref().unwrap().public_key.clone();
@ -699,7 +722,7 @@ impl ReadOnlyUserIdentity {
/// * `self_signing_key` - The new self signing key of user identity. /// * `self_signing_key` - The new self signing key of user identity.
/// ///
/// Returns a `SignatureError` if we failed to update the identity. /// Returns a `SignatureError` if we failed to update the identity.
pub fn update( pub(crate) fn update(
&mut self, &mut self,
master_key: MasterPubkey, master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey, self_signing_key: SelfSigningPubkey,
@ -974,7 +997,6 @@ pub(crate) mod test {
let first = Device { let first = Device {
inner: first, inner: first,
verification_machine: verification_machine.clone(), verification_machine: verification_machine.clone(),
private_identity: private_identity.clone(),
own_identity: Some(identity.clone()), own_identity: Some(identity.clone()),
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())), device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
}; };
@ -982,7 +1004,6 @@ pub(crate) mod test {
let second = Device { let second = Device {
inner: second, inner: second,
verification_machine, verification_machine,
private_identity,
own_identity: Some(identity.clone()), own_identity: Some(identity.clone()),
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())), device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
}; };
@ -1019,7 +1040,6 @@ pub(crate) mod test {
let mut device = Device { let mut device = Device {
inner: device, inner: device,
verification_machine: verification_machine.clone(), verification_machine: verification_machine.clone(),
private_identity: id.clone(),
own_identity: Some(public_identity.clone()), own_identity: Some(public_identity.clone()),
device_owner_identity: Some(public_identity.clone().into()), device_owner_identity: Some(public_identity.clone().into()),
}; };

View file

@ -39,7 +39,7 @@ pub mod store;
mod utilities; mod utilities;
mod verification; mod verification;
pub use error::{MegolmError, OlmError}; pub use error::{MegolmError, OlmError, SignatureError};
pub use file_encryption::{ pub use file_encryption::{
decrypt_key_export, encrypt_key_export, AttachmentDecryptor, AttachmentEncryptor, decrypt_key_export, encrypt_key_export, AttachmentDecryptor, AttachmentEncryptor,
DecryptorError, EncryptionInfo, KeyExportError, DecryptorError, EncryptionInfo, KeyExportError,

View file

@ -330,7 +330,6 @@ impl Store {
Ok(self.inner.get_device(user_id, device_id).await?.map(|d| Device { Ok(self.inner.get_device(user_id, device_id).await?.map(|d| Device {
inner: d, inner: d,
private_identity: self.identity.clone(),
verification_machine: self.verification_machine.clone(), verification_machine: self.verification_machine.clone(),
own_identity, own_identity,
device_owner_identity, device_owner_identity,

View file

@ -46,7 +46,7 @@ use crate::{
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct VerificationMachine { pub struct VerificationMachine {
private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>, pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
pub(crate) store: VerificationStore, pub(crate) store: VerificationStore,
verifications: VerificationCache, verifications: VerificationCache,
requests: Arc<DashMap<UserId, DashMap<String, VerificationRequest>>>, requests: Arc<DashMap<UserId, DashMap<String, VerificationRequest>>>,

View file

@ -143,7 +143,6 @@ impl Sas {
self.inner.lock().unwrap().set_creation_time(time) self.inner.lock().unwrap().set_creation_time(time)
} }
#[allow(clippy::too_many_arguments)]
fn start_helper( fn start_helper(
inner_sas: InnerSas, inner_sas: InnerSas,
private_identity: PrivateCrossSigningIdentity, private_identity: PrivateCrossSigningIdentity,
@ -159,7 +158,7 @@ impl Sas {
let identities = IdentitiesBeingVerified { let identities = IdentitiesBeingVerified {
private_identity, private_identity,
store: store.clone(), store,
device_being_verified: other_device, device_being_verified: other_device,
identity_being_verified: other_identity, identity_being_verified: other_identity,
}; };
@ -184,7 +183,6 @@ impl Sas {
/// ///
/// Returns the new `Sas` object and a `StartEventContent` that needs to be /// Returns the new `Sas` object and a `StartEventContent` that needs to be
/// sent out through the server to the other device. /// sent out through the server to the other device.
#[allow(clippy::too_many_arguments)]
pub(crate) fn start( pub(crate) fn start(
private_identity: PrivateCrossSigningIdentity, private_identity: PrivateCrossSigningIdentity,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,