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,
}
/// Error type describin different errors that happen when we check or create
/// signatures for a Matrix JSON object.
#[derive(Error, Debug)]
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,
#[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),
/// 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")]
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,
/// 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")]
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")]
NoSignatureFound,
/// The signature couldn't be verified.
#[error("the signature didn't match the provided key")]
VerificationError,
/// The signed object couldn't be deserialized.
#[error(transparent)]
JsonError(#[from] SerdeError),
}

View file

@ -25,6 +25,7 @@ use std::{
use atomic::Atomic;
use matrix_sdk_common::locks::Mutex;
use ruma::{
api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest,
encryption::{DeviceKeys, SignedKey},
events::{
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
@ -103,7 +104,6 @@ where
/// A device represents a E2EE capable client of an user.
pub struct Device {
pub(crate) inner: ReadOnlyDevice,
pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
pub(crate) verification_machine: VerificationMachine,
pub(crate) own_identity: Option<ReadOnlyOwnUserIdentity>,
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)
}
/// 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.
///
/// 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> {
self.inner.get(device_id).map(|d| Device {
inner: d.clone(),
private_identity: self.private_identity.clone(),
verification_machine: self.verification_machine.clone(),
own_identity: self.own_identity.clone(),
device_owner_identity: self.device_owner_identity.clone(),
@ -302,7 +328,6 @@ impl UserDevices {
pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
self.inner.values().map(move |d| Device {
inner: d.clone(),
private_identity: self.private_identity.clone(),
verification_machine: self.verification_machine.clone(),
own_identity: self.own_identity.clone(),
device_owner_identity: self.device_owner_identity.clone(),

View file

@ -23,6 +23,7 @@ use std::{
};
use ruma::{
api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest,
encryption::{CrossSigningKey, DeviceKeys, KeyUsage},
events::{
key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
@ -33,8 +34,6 @@ use serde::{Deserialize, Serialize};
use serde_json::to_value;
use super::{atomic_bool_deserializer, atomic_bool_serializer};
#[cfg(test)]
use crate::olm::PrivateCrossSigningIdentity;
use crate::{
error::SignatureError, olm::Utility, verification::VerificationMachine, CryptoStoreError,
OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest,
@ -190,6 +189,30 @@ impl UserIdentity {
.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
/// content has been sent out.
pub async fn request_verification(
@ -667,7 +690,7 @@ impl ReadOnlyUserIdentity {
}
#[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 self_signing_key =
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.
///
/// Returns a `SignatureError` if we failed to update the identity.
pub fn update(
pub(crate) fn update(
&mut self,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
@ -974,7 +997,6 @@ pub(crate) mod test {
let first = Device {
inner: first,
verification_machine: verification_machine.clone(),
private_identity: private_identity.clone(),
own_identity: Some(identity.clone()),
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
};
@ -982,7 +1004,6 @@ pub(crate) mod test {
let second = Device {
inner: second,
verification_machine,
private_identity,
own_identity: Some(identity.clone()),
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
};
@ -1019,7 +1040,6 @@ pub(crate) mod test {
let mut device = Device {
inner: device,
verification_machine: verification_machine.clone(),
private_identity: id.clone(),
own_identity: Some(public_identity.clone()),
device_owner_identity: Some(public_identity.clone().into()),
};

View file

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

View file

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

View file

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

View file

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