Merge branch 'verification-improvements'
commit
e919a82b2c
|
@ -58,7 +58,7 @@ async fn print_devices(user_id: &UserId, client: &Client) {
|
||||||
" {:<10} {:<30} {:<}",
|
" {:<10} {:<30} {:<}",
|
||||||
device.device_id(),
|
device.device_id(),
|
||||||
device.display_name().as_deref().unwrap_or_default(),
|
device.display_name().as_deref().unwrap_or_default(),
|
||||||
device.is_trusted()
|
device.verified()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@ use ruma::{
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{Device, UserDevices},
|
device::{Device, UserDevices},
|
||||||
|
error::RoomKeyImportError,
|
||||||
verification::{QrVerification, SasVerification, Verification, VerificationRequest},
|
verification::{QrVerification, SasVerification, Verification, VerificationRequest},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -2251,7 +2252,7 @@ impl Client {
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unwrap();
|
/// .unwrap();
|
||||||
///
|
///
|
||||||
/// println!("{:?}", device.is_trusted());
|
/// println!("{:?}", device.verified());
|
||||||
///
|
///
|
||||||
/// let verification = device.start_verification().await.unwrap();
|
/// let verification = device.start_verification().await.unwrap();
|
||||||
/// # });
|
/// # });
|
||||||
|
@ -2491,8 +2492,12 @@ impl Client {
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(all(feature = "encryption", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "encryption", not(target_arch = "wasm32")))]
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(all(encryption, not(target_arch = "wasm32")))))]
|
#[cfg_attr(feature = "docs", doc(cfg(all(encryption, not(target_arch = "wasm32")))))]
|
||||||
pub async fn import_keys(&self, path: PathBuf, passphrase: &str) -> Result<(usize, usize)> {
|
pub async fn import_keys(
|
||||||
let olm = self.base_client.olm_machine().await.ok_or(Error::AuthenticationRequired)?;
|
&self,
|
||||||
|
path: PathBuf,
|
||||||
|
passphrase: &str,
|
||||||
|
) -> StdResult<(usize, usize), RoomKeyImportError> {
|
||||||
|
let olm = self.base_client.olm_machine().await.ok_or(RoomKeyImportError::StoreClosed)?;
|
||||||
let passphrase = Zeroizing::new(passphrase.to_owned());
|
let passphrase = Zeroizing::new(passphrase.to_owned());
|
||||||
|
|
||||||
let decrypt = move || {
|
let decrypt = move || {
|
||||||
|
@ -2501,8 +2506,7 @@ impl Client {
|
||||||
};
|
};
|
||||||
|
|
||||||
let task = tokio::task::spawn_blocking(decrypt);
|
let task = tokio::task::spawn_blocking(decrypt);
|
||||||
// TODO remove this unwrap.
|
let import = task.await.expect("Task join error")?;
|
||||||
let import = task.await.expect("Task join error").unwrap();
|
|
||||||
|
|
||||||
Ok(olm.import_keys(import, |_, _| {}).await?)
|
Ok(olm.import_keys(import, |_, _| {}).await?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,13 @@ use matrix_sdk_base::crypto::{
|
||||||
store::CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice,
|
store::CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice,
|
||||||
UserDevices as BaseUserDevices,
|
UserDevices as BaseUserDevices,
|
||||||
};
|
};
|
||||||
use ruma::{DeviceId, DeviceIdBox};
|
use ruma::{events::key::verification::VerificationMethod, DeviceId, DeviceIdBox};
|
||||||
|
|
||||||
use crate::{error::Result, verification::SasVerification, Client};
|
use crate::{
|
||||||
|
error::Result,
|
||||||
|
verification::{SasVerification, VerificationRequest},
|
||||||
|
Client,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
/// A device represents a E2EE capable client of an user.
|
/// A device represents a E2EE capable client of an user.
|
||||||
|
@ -43,7 +47,10 @@ impl Device {
|
||||||
/// Returns a `Sas` object that represents the interactive verification
|
/// Returns a `Sas` object that represents the interactive verification
|
||||||
/// flow.
|
/// flow.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// This method has been deprecated in the spec and the
|
||||||
|
/// [`request_verification()`] method should be used instead.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use std::convert::TryFrom;
|
/// # use std::convert::TryFrom;
|
||||||
|
@ -62,6 +69,8 @@ impl Device {
|
||||||
/// let verification = device.start_verification().await.unwrap();
|
/// let verification = device.start_verification().await.unwrap();
|
||||||
/// # });
|
/// # });
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`request_verification()`]: #method.request_verification
|
||||||
pub async fn start_verification(&self) -> Result<SasVerification> {
|
pub async fn start_verification(&self) -> Result<SasVerification> {
|
||||||
let (sas, request) = self.inner.start_verification().await?;
|
let (sas, request) = self.inner.start_verification().await?;
|
||||||
self.client.send_to_device(&request).await?;
|
self.client.send_to_device(&request).await?;
|
||||||
|
@ -69,9 +78,97 @@ impl Device {
|
||||||
Ok(SasVerification { inner: sas, client: self.client.clone() })
|
Ok(SasVerification { inner: sas, client: self.client.clone() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the device trusted.
|
/// Request an interacitve verification with this `Device`
|
||||||
pub fn is_trusted(&self) -> bool {
|
///
|
||||||
self.inner.trust_state()
|
/// Returns a `VerificationRequest` object and a to-device request that
|
||||||
|
/// needs to be sent out.
|
||||||
|
///
|
||||||
|
/// The default methods that are supported are `m.sas.v1` and
|
||||||
|
/// `m.qr_code.show.v1`, if this isn't desireable the
|
||||||
|
/// [`request_verification_with_methods()`] method can be used to override
|
||||||
|
/// this.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # use matrix_sdk::{Client, ruma::UserId};
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # let alice = UserId::try_from("@alice:example.org").unwrap();
|
||||||
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||||
|
/// # let client = Client::new(homeserver).unwrap();
|
||||||
|
/// # block_on(async {
|
||||||
|
/// let device = client.get_device(&alice, "DEVICEID".into())
|
||||||
|
/// .await
|
||||||
|
/// .unwrap()
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// let verification = device.request_verification().await.unwrap();
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`request_verification_with_methods()`]:
|
||||||
|
/// #method.request_verification_with_methods
|
||||||
|
pub async fn request_verification(&self) -> Result<VerificationRequest> {
|
||||||
|
let (verification, request) = self.inner.request_verification().await;
|
||||||
|
self.client.send_verification_request(request).await?;
|
||||||
|
|
||||||
|
Ok(VerificationRequest { inner: verification, client: self.client.clone() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request an interacitve verification with this `Device`
|
||||||
|
///
|
||||||
|
/// Returns a `VerificationRequest` object and a to-device request that
|
||||||
|
/// needs to be sent out.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `methods` - The verification methods that we want to support.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
|
/// # use matrix_sdk::{
|
||||||
|
/// # Client,
|
||||||
|
/// # ruma::{
|
||||||
|
/// # UserId,
|
||||||
|
/// # events::key::verification::VerificationMethod,
|
||||||
|
/// # }
|
||||||
|
/// # };
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # let alice = UserId::try_from("@alice:example.org").unwrap();
|
||||||
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||||
|
/// # let client = Client::new(homeserver).unwrap();
|
||||||
|
/// # block_on(async {
|
||||||
|
/// let device = client.get_device(&alice, "DEVICEID".into())
|
||||||
|
/// .await
|
||||||
|
/// .unwrap()
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// // We don't want to support showing a QR code, we only support SAS
|
||||||
|
/// // verification
|
||||||
|
/// let methods = vec![VerificationMethod::SasV1];
|
||||||
|
///
|
||||||
|
/// let verification = device.request_verification_with_methods(methods).await.unwrap();
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
|
pub async fn request_verification_with_methods(
|
||||||
|
&self,
|
||||||
|
methods: Vec<VerificationMethod>,
|
||||||
|
) -> Result<VerificationRequest> {
|
||||||
|
let (verification, request) = self.inner.request_verification_with_methods(methods).await;
|
||||||
|
self.client.send_verification_request(request).await?;
|
||||||
|
|
||||||
|
Ok(VerificationRequest { inner: verification, client: self.client.clone() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the device considered to be verified, either by locally trusting it
|
||||||
|
/// or using cross signing.
|
||||||
|
pub fn verified(&self) -> bool {
|
||||||
|
self.inner.verified()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the local trust state of the device to the given state.
|
/// Set the local trust state of the device to the given state.
|
||||||
|
|
|
@ -18,7 +18,9 @@ use std::io::Error as IoError;
|
||||||
|
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use matrix_sdk_base::crypto::{CryptoStoreError, DecryptorError, MegolmError, OlmError};
|
use matrix_sdk_base::crypto::{
|
||||||
|
CryptoStoreError, DecryptorError, KeyExportError, MegolmError, OlmError,
|
||||||
|
};
|
||||||
use matrix_sdk_base::{Error as SdkBaseError, StoreError};
|
use matrix_sdk_base::{Error as SdkBaseError, StoreError};
|
||||||
use reqwest::Error as ReqwestError;
|
use reqwest::Error as ReqwestError;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
@ -151,6 +153,35 @@ pub enum Error {
|
||||||
Url(#[from] UrlParseError),
|
Url(#[from] UrlParseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error for the room key importing functionality.
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
// This is allowed because key importing isn't enabled under wasm.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum RoomKeyImportError {
|
||||||
|
/// An error de/serializing type for the `StateStore`
|
||||||
|
#[error(transparent)]
|
||||||
|
SerdeJson(#[from] JsonError),
|
||||||
|
|
||||||
|
/// The cryptostore isn't yet open, logging in is required to open the
|
||||||
|
/// cryptostore.
|
||||||
|
#[error("The cryptostore hasn't been yet opened, can't import yet.")]
|
||||||
|
StoreClosed,
|
||||||
|
|
||||||
|
/// An IO error happened.
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] IoError),
|
||||||
|
|
||||||
|
/// An error occurred in the crypto store.
|
||||||
|
#[error(transparent)]
|
||||||
|
CryptoStore(#[from] CryptoStoreError),
|
||||||
|
|
||||||
|
/// An error occurred while importing the key export.
|
||||||
|
#[error(transparent)]
|
||||||
|
Export(#[from] KeyExportError),
|
||||||
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
/// Try to destructure the error into an universal interactive auth info.
|
/// Try to destructure the error into an universal interactive auth info.
|
||||||
///
|
///
|
||||||
|
|
|
@ -33,6 +33,7 @@ mod qrcode;
|
||||||
mod requests;
|
mod requests;
|
||||||
mod sas;
|
mod sas;
|
||||||
|
|
||||||
|
pub use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo};
|
||||||
pub use qrcode::QrVerification;
|
pub use qrcode::QrVerification;
|
||||||
pub use requests::VerificationRequest;
|
pub use requests::VerificationRequest;
|
||||||
pub use sas::SasVerification;
|
pub use sas::SasVerification;
|
||||||
|
@ -81,6 +82,15 @@ impl Verification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get info about the cancellation if the verification flow has been
|
||||||
|
/// cancelled.
|
||||||
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
|
match self {
|
||||||
|
Verification::SasV1(s) => s.cancel_info(),
|
||||||
|
Verification::QrV1(q) => q.cancel_info(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get our own user id.
|
/// Get our own user id.
|
||||||
pub fn own_user_id(&self) -> &ruma::UserId {
|
pub fn own_user_id(&self) -> &ruma::UserId {
|
||||||
match self {
|
match self {
|
||||||
|
@ -105,6 +115,14 @@ impl Verification {
|
||||||
Verification::QrV1(v) => v.is_self_verification(),
|
Verification::QrV1(v) => v.is_self_verification(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Did we initiate the verification flow.
|
||||||
|
pub fn we_started(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Verification::SasV1(s) => s.we_started(),
|
||||||
|
Verification::QrV1(q) => q.we_started(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SasVerification> for Verification {
|
impl From<SasVerification> for Verification {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
use matrix_sdk_base::crypto::{
|
use matrix_sdk_base::crypto::{
|
||||||
matrix_qrcode::{qrcode::QrCode, EncodingError},
|
matrix_qrcode::{qrcode::QrCode, EncodingError},
|
||||||
QrVerification as BaseQrVerification,
|
CancelInfo, QrVerification as BaseQrVerification,
|
||||||
};
|
};
|
||||||
use ruma::UserId;
|
use ruma::UserId;
|
||||||
|
|
||||||
|
@ -43,6 +43,23 @@ impl QrVerification {
|
||||||
self.inner.is_done()
|
self.inner.is_done()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Did we initiate the verification flow.
|
||||||
|
pub fn we_started(&self) -> bool {
|
||||||
|
self.inner.we_started()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get info about the cancellation if the verification flow has been
|
||||||
|
/// cancelled.
|
||||||
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
|
self.inner.cancel_info()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the user id of the other user participating in this verification
|
||||||
|
/// flow.
|
||||||
|
pub fn other_user_id(&self) -> &UserId {
|
||||||
|
self.inner.other_user_id()
|
||||||
|
}
|
||||||
|
|
||||||
/// Has the verification been cancelled.
|
/// Has the verification been cancelled.
|
||||||
pub fn is_cancelled(&self) -> bool {
|
pub fn is_cancelled(&self) -> bool {
|
||||||
self.inner.is_cancelled()
|
self.inner.is_cancelled()
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use matrix_sdk_base::crypto::VerificationRequest as BaseVerificationRequest;
|
use matrix_sdk_base::crypto::{CancelInfo, VerificationRequest as BaseVerificationRequest};
|
||||||
use ruma::events::key::verification::VerificationMethod;
|
use ruma::events::key::verification::VerificationMethod;
|
||||||
|
|
||||||
use super::{QrVerification, SasVerification};
|
use super::{QrVerification, SasVerification};
|
||||||
|
@ -36,6 +36,12 @@ impl VerificationRequest {
|
||||||
self.inner.is_cancelled()
|
self.inner.is_cancelled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get info about the cancellation if the verification request has been
|
||||||
|
/// cancelled.
|
||||||
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
|
self.inner.cancel_info()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get our own user id.
|
/// Get our own user id.
|
||||||
pub fn own_user_id(&self) -> &ruma::UserId {
|
pub fn own_user_id(&self) -> &ruma::UserId {
|
||||||
self.inner.own_user_id()
|
self.inner.own_user_id()
|
||||||
|
@ -51,6 +57,11 @@ impl VerificationRequest {
|
||||||
self.inner.is_ready()
|
self.inner.is_ready()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Did we initiate the verification flow.
|
||||||
|
pub fn we_started(&self) -> bool {
|
||||||
|
self.inner.we_started()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the user id of the other user participating in this verification
|
/// Get the user id of the other user participating in this verification
|
||||||
/// flow.
|
/// flow.
|
||||||
pub fn other_user_id(&self) -> &ruma::UserId {
|
pub fn other_user_id(&self) -> &ruma::UserId {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use matrix_sdk_base::crypto::{AcceptSettings, ReadOnlyDevice, Sas as BaseSas};
|
use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo, ReadOnlyDevice, Sas as BaseSas};
|
||||||
use ruma::UserId;
|
use ruma::UserId;
|
||||||
|
|
||||||
use crate::{error::Result, Client};
|
use crate::{error::Result, Client};
|
||||||
|
@ -121,6 +121,17 @@ impl SasVerification {
|
||||||
self.inner.can_be_presented()
|
self.inner.can_be_presented()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Did we initiate the verification flow.
|
||||||
|
pub fn we_started(&self) -> bool {
|
||||||
|
self.inner.we_started()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get info about the cancellation if the verification flow has been
|
||||||
|
/// cancelled.
|
||||||
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
|
self.inner.cancel_info()
|
||||||
|
}
|
||||||
|
|
||||||
/// Is the verification process canceled.
|
/// Is the verification process canceled.
|
||||||
pub fn is_cancelled(&self) -> bool {
|
pub fn is_cancelled(&self) -> bool {
|
||||||
self.inner.is_cancelled()
|
self.inner.is_cancelled()
|
||||||
|
@ -145,4 +156,10 @@ impl SasVerification {
|
||||||
pub fn own_user_id(&self) -> &UserId {
|
pub fn own_user_id(&self) -> &UserId {
|
||||||
self.inner.user_id()
|
self.inner.user_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the user id of the other user participating in this verification
|
||||||
|
/// flow.
|
||||||
|
pub fn other_user_id(&self) -> &UserId {
|
||||||
|
self.inner.other_user_id()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub struct MemoryStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryStore {
|
impl MemoryStore {
|
||||||
#[cfg(not(feature = "sled_state_store"))]
|
#[allow(dead_code)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
sync_token: Arc::new(RwLock::new(None)),
|
sync_token: Arc::new(RwLock::new(None)),
|
||||||
|
@ -558,7 +558,6 @@ impl StateStore for MemoryStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(not(feature = "sled_state_store"))]
|
|
||||||
mod test {
|
mod test {
|
||||||
use matrix_sdk_test::async_test;
|
use matrix_sdk_test::async_test;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
|
|
@ -28,7 +28,8 @@ use ruma::{
|
||||||
encryption::{DeviceKeys, SignedKey},
|
encryption::{DeviceKeys, SignedKey},
|
||||||
events::{
|
events::{
|
||||||
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
|
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
|
||||||
room::encrypted::EncryptedEventContent, AnyToDeviceEventContent,
|
key::verification::VerificationMethod, room::encrypted::EncryptedEventContent,
|
||||||
|
AnyToDeviceEventContent,
|
||||||
},
|
},
|
||||||
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId,
|
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId,
|
||||||
};
|
};
|
||||||
|
@ -39,11 +40,11 @@ use tracing::warn;
|
||||||
use super::{atomic_bool_deserializer, atomic_bool_serializer};
|
use super::{atomic_bool_deserializer, atomic_bool_serializer};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{EventError, OlmError, OlmResult, SignatureError},
|
error::{EventError, OlmError, OlmResult, SignatureError},
|
||||||
identities::{OwnUserIdentity, UserIdentities},
|
identities::{ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities},
|
||||||
olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session, Utility},
|
olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session, Utility},
|
||||||
store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult},
|
store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult},
|
||||||
verification::VerificationMachine,
|
verification::VerificationMachine,
|
||||||
OutgoingVerificationRequest, Sas, ToDeviceRequest,
|
OutgoingVerificationRequest, Sas, ToDeviceRequest, VerificationRequest,
|
||||||
};
|
};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::{OlmMachine, ReadOnlyAccount};
|
use crate::{OlmMachine, ReadOnlyAccount};
|
||||||
|
@ -104,8 +105,8 @@ pub struct Device {
|
||||||
pub(crate) inner: ReadOnlyDevice,
|
pub(crate) inner: ReadOnlyDevice,
|
||||||
pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
|
pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
|
||||||
pub(crate) verification_machine: VerificationMachine,
|
pub(crate) verification_machine: VerificationMachine,
|
||||||
pub(crate) own_identity: Option<OwnUserIdentity>,
|
pub(crate) own_identity: Option<ReadOnlyOwnUserIdentity>,
|
||||||
pub(crate) device_owner_identity: Option<UserIdentities>,
|
pub(crate) device_owner_identity: Option<ReadOnlyUserIdentities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Device {
|
impl std::fmt::Debug for Device {
|
||||||
|
@ -125,7 +126,13 @@ impl Deref for Device {
|
||||||
impl Device {
|
impl Device {
|
||||||
/// Start a interactive verification with this `Device`
|
/// Start a interactive verification with this `Device`
|
||||||
///
|
///
|
||||||
/// Returns a `Sas` object and to-device request that needs to be sent out.
|
/// Returns a `Sas` object and a to-device request that needs to be sent
|
||||||
|
/// out.
|
||||||
|
///
|
||||||
|
/// This method has been deprecated in the spec and the
|
||||||
|
/// [`request_verification()`] method should be used instead.
|
||||||
|
///
|
||||||
|
/// [`request_verification()`]: #method.request_verification
|
||||||
pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> {
|
pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> {
|
||||||
let (sas, request) = self.verification_machine.start_sas(self.inner.clone()).await?;
|
let (sas, request) = self.verification_machine.start_sas(self.inner.clone()).await?;
|
||||||
|
|
||||||
|
@ -136,6 +143,42 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request an interacitve verification with this `Device`
|
||||||
|
///
|
||||||
|
/// Returns a `VerificationRequest` object and a to-device request that
|
||||||
|
/// needs to be sent out.
|
||||||
|
pub async fn request_verification(&self) -> (VerificationRequest, OutgoingVerificationRequest) {
|
||||||
|
self.request_verification_helper(None).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request an interacitve verification with this `Device`
|
||||||
|
///
|
||||||
|
/// Returns a `VerificationRequest` object and a to-device request that
|
||||||
|
/// needs to be sent out.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `methods` - The verification methods that we want to support.
|
||||||
|
pub async fn request_verification_with_methods(
|
||||||
|
&self,
|
||||||
|
methods: Vec<VerificationMethod>,
|
||||||
|
) -> (VerificationRequest, OutgoingVerificationRequest) {
|
||||||
|
self.request_verification_helper(Some(methods)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request_verification_helper(
|
||||||
|
&self,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
|
) -> (VerificationRequest, OutgoingVerificationRequest) {
|
||||||
|
self.verification_machine
|
||||||
|
.request_to_device_verification(
|
||||||
|
self.user_id(),
|
||||||
|
vec![self.device_id().to_owned()],
|
||||||
|
methods,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the Olm sessions that belong to this device.
|
/// Get the Olm sessions that belong to this device.
|
||||||
pub(crate) async fn get_sessions(&self) -> StoreResult<Option<Arc<Mutex<Vec<Session>>>>> {
|
pub(crate) async fn get_sessions(&self) -> StoreResult<Option<Arc<Mutex<Vec<Session>>>>> {
|
||||||
if let Some(k) = self.get_key(DeviceKeyAlgorithm::Curve25519) {
|
if let Some(k) = self.get_key(DeviceKeyAlgorithm::Curve25519) {
|
||||||
|
@ -145,9 +188,20 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the trust state of the device.
|
/// Is this device considered to be verified.
|
||||||
pub fn trust_state(&self) -> bool {
|
///
|
||||||
self.inner.trust_state(&self.own_identity, &self.device_owner_identity)
|
/// This method returns true if either [`is_locally_trusted()`] returns true
|
||||||
|
/// or if [`is_cross_signing_trusted()`] returns true.
|
||||||
|
///
|
||||||
|
/// [`is_locally_trusted()`]: #method.is_locally_trusted
|
||||||
|
/// [`is_cross_signing_trusted()`]: #method.is_cross_signing_trusted
|
||||||
|
pub fn verified(&self) -> bool {
|
||||||
|
self.inner.verified(&self.own_identity, &self.device_owner_identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this device considered to be verified using cross signing.
|
||||||
|
pub fn is_cross_signing_trusted(&self) -> bool {
|
||||||
|
self.inner.is_cross_signing_trusted(&self.own_identity, &self.device_owner_identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the local trust state of the device to the given state.
|
/// Set the local trust state of the device to the given state.
|
||||||
|
@ -217,8 +271,8 @@ pub struct UserDevices {
|
||||||
pub(crate) inner: HashMap<DeviceIdBox, ReadOnlyDevice>,
|
pub(crate) inner: HashMap<DeviceIdBox, ReadOnlyDevice>,
|
||||||
pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
|
pub(crate) private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
|
||||||
pub(crate) verification_machine: VerificationMachine,
|
pub(crate) verification_machine: VerificationMachine,
|
||||||
pub(crate) own_identity: Option<OwnUserIdentity>,
|
pub(crate) own_identity: Option<ReadOnlyOwnUserIdentity>,
|
||||||
pub(crate) device_owner_identity: Option<UserIdentities>,
|
pub(crate) device_owner_identity: Option<ReadOnlyUserIdentities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserDevices {
|
impl UserDevices {
|
||||||
|
@ -236,7 +290,7 @@ impl UserDevices {
|
||||||
/// Returns true if there is at least one devices of this user that is
|
/// Returns true if there is at least one devices of this user that is
|
||||||
/// considered to be verified, false otherwise.
|
/// considered to be verified, false otherwise.
|
||||||
pub fn is_any_verified(&self) -> bool {
|
pub fn is_any_verified(&self) -> bool {
|
||||||
self.inner.values().any(|d| d.trust_state(&self.own_identity, &self.device_owner_identity))
|
self.inner.values().any(|d| d.verified(&self.own_identity, &self.device_owner_identity))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator over all the device ids of the user devices.
|
/// Iterator over all the device ids of the user devices.
|
||||||
|
@ -340,7 +394,7 @@ impl ReadOnlyDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the device locally marked as trusted.
|
/// Is the device locally marked as trusted.
|
||||||
pub fn is_trusted(&self) -> bool {
|
pub fn is_locally_trusted(&self) -> bool {
|
||||||
self.local_trust_state() == LocalTrust::Verified
|
self.local_trust_state() == LocalTrust::Verified
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,19 +423,19 @@ impl ReadOnlyDevice {
|
||||||
self.deleted.load(Ordering::Relaxed)
|
self.deleted.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn trust_state(
|
pub(crate) fn verified(
|
||||||
&self,
|
&self,
|
||||||
own_identity: &Option<OwnUserIdentity>,
|
own_identity: &Option<ReadOnlyOwnUserIdentity>,
|
||||||
device_owner: &Option<UserIdentities>,
|
device_owner: &Option<ReadOnlyUserIdentities>,
|
||||||
|
) -> bool {
|
||||||
|
self.is_locally_trusted() || self.is_cross_signing_trusted(own_identity, device_owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_cross_signing_trusted(
|
||||||
|
&self,
|
||||||
|
own_identity: &Option<ReadOnlyOwnUserIdentity>,
|
||||||
|
device_owner: &Option<ReadOnlyUserIdentities>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// TODO we want to return an enum mentioning if the trust is local, if
|
|
||||||
// only the identity is trusted, if the identity and the device are
|
|
||||||
// trusted.
|
|
||||||
if self.is_trusted() {
|
|
||||||
// If the device is locally marked as verified just return so, no
|
|
||||||
// need to check signatures.
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
own_identity.as_ref().map_or(false, |own_identity| {
|
own_identity.as_ref().map_or(false, |own_identity| {
|
||||||
// Our own identity needs to be marked as verified.
|
// Our own identity needs to be marked as verified.
|
||||||
own_identity.is_verified()
|
own_identity.is_verified()
|
||||||
|
@ -390,26 +444,21 @@ impl ReadOnlyDevice {
|
||||||
.map(|device_identity| match device_identity {
|
.map(|device_identity| match device_identity {
|
||||||
// If it's one of our own devices, just check that
|
// If it's one of our own devices, just check that
|
||||||
// we signed the device.
|
// we signed the device.
|
||||||
UserIdentities::Own(_) => {
|
ReadOnlyUserIdentities::Own(_) => {
|
||||||
own_identity.is_device_signed(self).map_or(false, |_| true)
|
own_identity.is_device_signed(self).map_or(false, |_| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's a device from someone else, first check
|
// If it's a device from someone else, first check
|
||||||
// that our user has signed the other user and then
|
// that our user has signed the other user and then
|
||||||
// check if the other user has signed this device.
|
// check if the other user has signed this device.
|
||||||
UserIdentities::Other(device_identity) => {
|
ReadOnlyUserIdentities::Other(device_identity) => {
|
||||||
own_identity
|
own_identity.is_identity_signed(device_identity).map_or(false, |_| true)
|
||||||
.is_identity_signed(device_identity)
|
&& device_identity.is_device_signed(self).map_or(false, |_| true)
|
||||||
.map_or(false, |_| true)
|
|
||||||
&& device_identity
|
|
||||||
.is_device_signed(self)
|
|
||||||
.map_or(false, |_| true)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn encrypt(
|
pub(crate) async fn encrypt(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -29,8 +29,8 @@ use tracing::{trace, warn};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::OlmResult,
|
error::OlmResult,
|
||||||
identities::{
|
identities::{
|
||||||
MasterPubkey, OwnUserIdentity, ReadOnlyDevice, SelfSigningPubkey, UserIdentities,
|
MasterPubkey, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities,
|
||||||
UserIdentity, UserSigningPubkey,
|
ReadOnlyUserIdentity, SelfSigningPubkey, UserSigningPubkey,
|
||||||
},
|
},
|
||||||
requests::KeysQueryRequest,
|
requests::KeysQueryRequest,
|
||||||
store::{Changes, DeviceChanges, IdentityChanges, Result as StoreResult, Store},
|
store::{Changes, DeviceChanges, IdentityChanges, Result as StoreResult, Store},
|
||||||
|
@ -246,7 +246,7 @@ impl IdentityManager {
|
||||||
|
|
||||||
let result = if let Some(mut i) = self.store.get_user_identity(user_id).await? {
|
let result = if let Some(mut i) = self.store.get_user_identity(user_id).await? {
|
||||||
match &mut i {
|
match &mut i {
|
||||||
UserIdentities::Own(ref mut identity) => {
|
ReadOnlyUserIdentities::Own(ref mut identity) => {
|
||||||
let user_signing = if let Some(s) = response.user_signing_keys.get(user_id)
|
let user_signing = if let Some(s) = response.user_signing_keys.get(user_id)
|
||||||
{
|
{
|
||||||
UserSigningPubkey::from(s)
|
UserSigningPubkey::from(s)
|
||||||
|
@ -261,7 +261,7 @@ impl IdentityManager {
|
||||||
|
|
||||||
identity.update(master_key, self_signing, user_signing).map(|_| (i, false))
|
identity.update(master_key, self_signing, user_signing).map(|_| (i, false))
|
||||||
}
|
}
|
||||||
UserIdentities::Other(ref mut identity) => {
|
ReadOnlyUserIdentities::Other(ref mut identity) => {
|
||||||
identity.update(master_key, self_signing).map(|_| (i, false))
|
identity.update(master_key, self_signing).map(|_| (i, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,8 +280,8 @@ impl IdentityManager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnUserIdentity::new(master_key, self_signing, user_signing)
|
ReadOnlyOwnUserIdentity::new(master_key, self_signing, user_signing)
|
||||||
.map(|i| (UserIdentities::Own(i), true))
|
.map(|i| (ReadOnlyUserIdentities::Own(i), true))
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"User identity for our own user {} didn't contain a \
|
"User identity for our own user {} didn't contain a \
|
||||||
|
@ -294,8 +294,8 @@ impl IdentityManager {
|
||||||
warn!("User id mismatch in one of the cross signing keys for user {}", user_id);
|
warn!("User id mismatch in one of the cross signing keys for user {}", user_id);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
UserIdentity::new(master_key, self_signing)
|
ReadOnlyUserIdentity::new(master_key, self_signing)
|
||||||
.map(|i| (UserIdentities::Other(i), true))
|
.map(|i| (ReadOnlyUserIdentities::Other(i), true))
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
|
|
@ -53,8 +53,8 @@ pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices};
|
||||||
pub(crate) use manager::IdentityManager;
|
pub(crate) use manager::IdentityManager;
|
||||||
use serde::{Deserialize, Deserializer, Serializer};
|
use serde::{Deserialize, Deserializer, Serializer};
|
||||||
pub use user::{
|
pub use user::{
|
||||||
MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity,
|
MasterPubkey, OwnUserIdentity, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities,
|
||||||
UserSigningPubkey,
|
ReadOnlyUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity, UserSigningPubkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
// These methods are only here because Serialize and Deserialize don't seem to
|
// These methods are only here because Serialize and Deserialize don't seem to
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{btree_map::Iter, BTreeMap},
|
collections::{btree_map::Iter, BTreeMap},
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
|
ops::Deref,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
|
@ -23,7 +24,10 @@ use std::{
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
encryption::{CrossSigningKey, KeyUsage},
|
encryption::{CrossSigningKey, KeyUsage},
|
||||||
DeviceKeyId, UserId,
|
events::{
|
||||||
|
key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
|
||||||
|
},
|
||||||
|
DeviceIdBox, DeviceKeyId, EventId, RoomId, UserId,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
|
@ -31,7 +35,173 @@ use serde_json::to_value;
|
||||||
use super::{atomic_bool_deserializer, atomic_bool_serializer};
|
use super::{atomic_bool_deserializer, atomic_bool_serializer};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::olm::PrivateCrossSigningIdentity;
|
use crate::olm::PrivateCrossSigningIdentity;
|
||||||
use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice};
|
use crate::{
|
||||||
|
error::SignatureError, olm::Utility, verification::VerificationMachine, CryptoStoreError,
|
||||||
|
OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Enum over the different user identity types we can have.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum UserIdentities {
|
||||||
|
/// Our own user identity.
|
||||||
|
Own(OwnUserIdentity),
|
||||||
|
/// An identity belonging to another user.
|
||||||
|
Other(UserIdentity),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserIdentities {
|
||||||
|
/// Destructure the enum into an `OwnUserIdentity` if it's of the correct
|
||||||
|
/// type.
|
||||||
|
pub fn own(self) -> Option<OwnUserIdentity> {
|
||||||
|
match self {
|
||||||
|
Self::Own(i) => Some(i),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destructure the enum into an `UserIdentity` if it's of the correct
|
||||||
|
/// type.
|
||||||
|
pub fn other(self) -> Option<UserIdentity> {
|
||||||
|
match self {
|
||||||
|
Self::Other(i) => Some(i),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// This struct wraps a read-only version of the struct and allows verifications
|
||||||
|
/// to be requested to verify our own device with the user identity.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OwnUserIdentity {
|
||||||
|
pub(crate) inner: ReadOnlyOwnUserIdentity,
|
||||||
|
pub(crate) verification_machine: VerificationMachine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for OwnUserIdentity {
|
||||||
|
type Target = ReadOnlyOwnUserIdentity;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnUserIdentity {
|
||||||
|
/// Send a verification request to our other devices.
|
||||||
|
pub async fn request_verification(
|
||||||
|
&self,
|
||||||
|
) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> {
|
||||||
|
self.request_verification_helper(None).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a verification request to our other devices while specifying our
|
||||||
|
/// supported methods.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `methods` - The verification methods that we're supporting.
|
||||||
|
pub async fn request_verification_with_methods(
|
||||||
|
&self,
|
||||||
|
methods: Vec<VerificationMethod>,
|
||||||
|
) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> {
|
||||||
|
self.request_verification_helper(Some(methods)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request_verification_helper(
|
||||||
|
&self,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
|
) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> {
|
||||||
|
let devices: Vec<DeviceIdBox> = self
|
||||||
|
.verification_machine
|
||||||
|
.store
|
||||||
|
.get_user_devices(self.user_id())
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(d, _)| d)
|
||||||
|
.filter(|d| &**d != self.verification_machine.own_device_id())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(self
|
||||||
|
.verification_machine
|
||||||
|
.request_to_device_verification(self.user_id(), devices, methods)
|
||||||
|
.await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// This struct wraps a read-only version of the struct and allows verifications
|
||||||
|
/// to be requested to verify our own device with the user identity.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct UserIdentity {
|
||||||
|
pub(crate) inner: ReadOnlyUserIdentity,
|
||||||
|
pub(crate) verification_machine: VerificationMachine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for UserIdentity {
|
||||||
|
type Target = ReadOnlyUserIdentity;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserIdentity {
|
||||||
|
/// Create a `VerificationRequest` object after the verification request
|
||||||
|
/// content has been sent out.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a verification request to the given user.
|
||||||
|
///
|
||||||
|
/// The returned content needs to be sent out into a DM room with the given
|
||||||
|
/// user.
|
||||||
|
///
|
||||||
|
/// After the content has been sent out a `VerificationRequest` can be
|
||||||
|
/// started with the [`request_verification`] method.
|
||||||
|
///
|
||||||
|
/// [`request_verification()`]: #method.request_verification
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper for a cross signing key marking it as the master key.
|
/// Wrapper for a cross signing key marking it as the master key.
|
||||||
///
|
///
|
||||||
|
@ -358,47 +528,47 @@ impl<'a> IntoIterator for &'a SelfSigningPubkey {
|
||||||
|
|
||||||
/// Enum over the different user identity types we can have.
|
/// Enum over the different user identity types we can have.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum UserIdentities {
|
pub enum ReadOnlyUserIdentities {
|
||||||
/// Our own user identity.
|
/// Our own user identity.
|
||||||
Own(OwnUserIdentity),
|
Own(ReadOnlyOwnUserIdentity),
|
||||||
/// Identities of other users.
|
/// Identities of other users.
|
||||||
Other(UserIdentity),
|
Other(ReadOnlyUserIdentity),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OwnUserIdentity> for UserIdentities {
|
impl From<ReadOnlyOwnUserIdentity> for ReadOnlyUserIdentities {
|
||||||
fn from(identity: OwnUserIdentity) -> Self {
|
fn from(identity: ReadOnlyOwnUserIdentity) -> Self {
|
||||||
UserIdentities::Own(identity)
|
ReadOnlyUserIdentities::Own(identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UserIdentity> for UserIdentities {
|
impl From<ReadOnlyUserIdentity> for ReadOnlyUserIdentities {
|
||||||
fn from(identity: UserIdentity) -> Self {
|
fn from(identity: ReadOnlyUserIdentity) -> Self {
|
||||||
UserIdentities::Other(identity)
|
ReadOnlyUserIdentities::Other(identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserIdentities {
|
impl ReadOnlyUserIdentities {
|
||||||
/// The unique user id of this identity.
|
/// The unique user id of this identity.
|
||||||
pub fn user_id(&self) -> &UserId {
|
pub fn user_id(&self) -> &UserId {
|
||||||
match self {
|
match self {
|
||||||
UserIdentities::Own(i) => i.user_id(),
|
ReadOnlyUserIdentities::Own(i) => i.user_id(),
|
||||||
UserIdentities::Other(i) => i.user_id(),
|
ReadOnlyUserIdentities::Other(i) => i.user_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the master key of the identity.
|
/// Get the master key of the identity.
|
||||||
pub fn master_key(&self) -> &MasterPubkey {
|
pub fn master_key(&self) -> &MasterPubkey {
|
||||||
match self {
|
match self {
|
||||||
UserIdentities::Own(i) => i.master_key(),
|
ReadOnlyUserIdentities::Own(i) => i.master_key(),
|
||||||
UserIdentities::Other(i) => i.master_key(),
|
ReadOnlyUserIdentities::Other(i) => i.master_key(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the self-signing key of the identity.
|
/// Get the self-signing key of the identity.
|
||||||
pub fn self_signing_key(&self) -> &SelfSigningPubkey {
|
pub fn self_signing_key(&self) -> &SelfSigningPubkey {
|
||||||
match self {
|
match self {
|
||||||
UserIdentities::Own(i) => &i.self_signing_key,
|
ReadOnlyUserIdentities::Own(i) => &i.self_signing_key,
|
||||||
UserIdentities::Other(i) => &i.self_signing_key,
|
ReadOnlyUserIdentities::Other(i) => &i.self_signing_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,32 +576,32 @@ impl UserIdentities {
|
||||||
/// own user identity..
|
/// own user identity..
|
||||||
pub fn user_signing_key(&self) -> Option<&UserSigningPubkey> {
|
pub fn user_signing_key(&self) -> Option<&UserSigningPubkey> {
|
||||||
match self {
|
match self {
|
||||||
UserIdentities::Own(i) => Some(&i.user_signing_key),
|
ReadOnlyUserIdentities::Own(i) => Some(&i.user_signing_key),
|
||||||
UserIdentities::Other(_) => None,
|
ReadOnlyUserIdentities::Other(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destructure the enum into an `OwnUserIdentity` if it's of the correct
|
/// Destructure the enum into an `ReadOnlyOwnUserIdentity` if it's of the
|
||||||
/// type.
|
/// correct type.
|
||||||
pub fn own(&self) -> Option<&OwnUserIdentity> {
|
pub fn own(&self) -> Option<&ReadOnlyOwnUserIdentity> {
|
||||||
match self {
|
match self {
|
||||||
UserIdentities::Own(i) => Some(i),
|
ReadOnlyUserIdentities::Own(i) => Some(i),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destructure the enum into an `UserIdentity` if it's of the correct
|
/// Destructure the enum into an `UserIdentity` if it's of the correct
|
||||||
/// type.
|
/// type.
|
||||||
pub fn other(&self) -> Option<&UserIdentity> {
|
pub fn other(&self) -> Option<&ReadOnlyUserIdentity> {
|
||||||
match self {
|
match self {
|
||||||
UserIdentities::Other(i) => Some(i),
|
ReadOnlyUserIdentities::Other(i) => Some(i),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for UserIdentities {
|
impl PartialEq for ReadOnlyUserIdentities {
|
||||||
fn eq(&self, other: &UserIdentities) -> bool {
|
fn eq(&self, other: &ReadOnlyUserIdentities) -> bool {
|
||||||
self.user_id() == other.user_id()
|
self.user_id() == other.user_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,13 +612,13 @@ impl PartialEq for UserIdentities {
|
||||||
/// only contain a master key and a self signing key, meaning that only device
|
/// only contain a master key and a self signing key, meaning that only device
|
||||||
/// signatures can be checked with this identity.
|
/// signatures can be checked with this identity.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct UserIdentity {
|
pub struct ReadOnlyUserIdentity {
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
pub(crate) master_key: MasterPubkey,
|
pub(crate) master_key: MasterPubkey,
|
||||||
self_signing_key: SelfSigningPubkey,
|
self_signing_key: SelfSigningPubkey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserIdentity {
|
impl ReadOnlyUserIdentity {
|
||||||
/// Create a new user identity with the given master and self signing key.
|
/// Create a new user identity with the given master and self signing key.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -543,7 +713,7 @@ impl UserIdentity {
|
||||||
/// This identity can verify other identities as well as devices belonging to
|
/// This identity can verify other identities as well as devices belonging to
|
||||||
/// the identity.
|
/// the identity.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct OwnUserIdentity {
|
pub struct ReadOnlyOwnUserIdentity {
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
master_key: MasterPubkey,
|
master_key: MasterPubkey,
|
||||||
self_signing_key: SelfSigningPubkey,
|
self_signing_key: SelfSigningPubkey,
|
||||||
|
@ -555,7 +725,7 @@ pub struct OwnUserIdentity {
|
||||||
verified: Arc<AtomicBool>,
|
verified: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OwnUserIdentity {
|
impl ReadOnlyOwnUserIdentity {
|
||||||
/// Create a new own user identity with the given master, self signing, and
|
/// Create a new own user identity with the given master, self signing, and
|
||||||
/// user signing key.
|
/// user signing key.
|
||||||
///
|
///
|
||||||
|
@ -615,7 +785,10 @@ impl OwnUserIdentity {
|
||||||
///
|
///
|
||||||
/// Returns an empty result if the signature check succeeded, otherwise a
|
/// Returns an empty result if the signature check succeeded, otherwise a
|
||||||
/// SignatureError indicating why the check failed.
|
/// SignatureError indicating why the check failed.
|
||||||
pub fn is_identity_signed(&self, identity: &UserIdentity) -> Result<(), SignatureError> {
|
pub fn is_identity_signed(
|
||||||
|
&self,
|
||||||
|
identity: &ReadOnlyUserIdentity,
|
||||||
|
) -> Result<(), SignatureError> {
|
||||||
self.user_signing_key.verify_master_key(&identity.master_key)
|
self.user_signing_key.verify_master_key(&identity.master_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,7 +865,7 @@ pub(crate) mod test {
|
||||||
use matrix_sdk_test::async_test;
|
use matrix_sdk_test::async_test;
|
||||||
use ruma::{api::client::r0::keys::get_keys::Response as KeyQueryResponse, user_id};
|
use ruma::{api::client::r0::keys::get_keys::Response as KeyQueryResponse, user_id};
|
||||||
|
|
||||||
use super::{OwnUserIdentity, UserIdentities, UserIdentity};
|
use super::{ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities, ReadOnlyUserIdentity};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{
|
identities::{
|
||||||
manager::test::{other_key_query, own_key_query},
|
manager::test::{other_key_query, own_key_query},
|
||||||
|
@ -710,28 +883,29 @@ pub(crate) mod test {
|
||||||
(first, second)
|
(first, second)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn own_identity(response: &KeyQueryResponse) -> OwnUserIdentity {
|
fn own_identity(response: &KeyQueryResponse) -> ReadOnlyOwnUserIdentity {
|
||||||
let user_id = user_id!("@example:localhost");
|
let user_id = user_id!("@example:localhost");
|
||||||
|
|
||||||
let master_key = response.master_keys.get(&user_id).unwrap();
|
let master_key = response.master_keys.get(&user_id).unwrap();
|
||||||
let user_signing = response.user_signing_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();
|
let self_signing = response.self_signing_keys.get(&user_id).unwrap();
|
||||||
|
|
||||||
OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap()
|
ReadOnlyOwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into())
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_own_identity() -> OwnUserIdentity {
|
pub(crate) fn get_own_identity() -> ReadOnlyOwnUserIdentity {
|
||||||
own_identity(&own_key_query())
|
own_identity(&own_key_query())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_other_identity() -> UserIdentity {
|
pub(crate) fn get_other_identity() -> ReadOnlyUserIdentity {
|
||||||
let user_id = user_id!("@example2:localhost");
|
let user_id = user_id!("@example2:localhost");
|
||||||
let response = other_key_query();
|
let response = other_key_query();
|
||||||
|
|
||||||
let master_key = response.master_keys.get(&user_id).unwrap();
|
let master_key = response.master_keys.get(&user_id).unwrap();
|
||||||
let self_signing = response.self_signing_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()
|
ReadOnlyUserIdentity::new(master_key.into(), self_signing.into()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -743,7 +917,8 @@ pub(crate) mod test {
|
||||||
let user_signing = response.user_signing_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();
|
let self_signing = response.self_signing_keys.get(&user_id).unwrap();
|
||||||
|
|
||||||
OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap();
|
ReadOnlyOwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -773,7 +948,7 @@ pub(crate) mod test {
|
||||||
verification_machine: verification_machine.clone(),
|
verification_machine: verification_machine.clone(),
|
||||||
private_identity: private_identity.clone(),
|
private_identity: private_identity.clone(),
|
||||||
own_identity: Some(identity.clone()),
|
own_identity: Some(identity.clone()),
|
||||||
device_owner_identity: Some(UserIdentities::Own(identity.clone())),
|
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let second = Device {
|
let second = Device {
|
||||||
|
@ -781,18 +956,18 @@ pub(crate) mod test {
|
||||||
verification_machine,
|
verification_machine,
|
||||||
private_identity,
|
private_identity,
|
||||||
own_identity: Some(identity.clone()),
|
own_identity: Some(identity.clone()),
|
||||||
device_owner_identity: Some(UserIdentities::Own(identity.clone())),
|
device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!second.trust_state());
|
assert!(!second.is_locally_trusted());
|
||||||
assert!(!second.is_trusted());
|
assert!(!second.is_cross_signing_trusted());
|
||||||
|
|
||||||
assert!(!first.trust_state());
|
assert!(!first.is_locally_trusted());
|
||||||
assert!(!first.is_trusted());
|
assert!(!first.is_cross_signing_trusted());
|
||||||
|
|
||||||
identity.mark_as_verified();
|
identity.mark_as_verified();
|
||||||
assert!(second.trust_state());
|
assert!(second.verified());
|
||||||
assert!(!first.trust_state());
|
assert!(!first.verified());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_test]
|
#[async_test]
|
||||||
|
@ -821,12 +996,12 @@ pub(crate) mod test {
|
||||||
device_owner_identity: Some(public_identity.clone().into()),
|
device_owner_identity: Some(public_identity.clone().into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!device.trust_state());
|
assert!(!device.verified());
|
||||||
|
|
||||||
let mut device_keys = device.as_device_keys();
|
let mut device_keys = device.as_device_keys();
|
||||||
|
|
||||||
identity.sign_device_keys(&mut device_keys).await.unwrap();
|
identity.sign_device_keys(&mut device_keys).await.unwrap();
|
||||||
device.inner.signatures = Arc::new(device_keys.signatures);
|
device.inner.signatures = Arc::new(device_keys.signatures);
|
||||||
assert!(device.trust_state());
|
assert!(device.verified());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -479,7 +479,7 @@ impl KeyRequestMachine {
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let own_device_check = || {
|
let own_device_check = || {
|
||||||
if device.trust_state() {
|
if device.verified() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err(KeyshareDecision::UntrustedDevice)
|
Err(KeyshareDecision::UntrustedDevice)
|
||||||
|
|
|
@ -45,7 +45,8 @@ pub use file_encryption::{
|
||||||
DecryptorError, EncryptionInfo, KeyExportError,
|
DecryptorError, EncryptionInfo, KeyExportError,
|
||||||
};
|
};
|
||||||
pub use identities::{
|
pub use identities::{
|
||||||
Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, UserDevices, UserIdentities, UserIdentity,
|
Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, ReadOnlyOwnUserIdentity,
|
||||||
|
ReadOnlyUserIdentities, ReadOnlyUserIdentity, UserDevices, UserIdentities, UserIdentity,
|
||||||
};
|
};
|
||||||
pub use machine::OlmMachine;
|
pub use machine::OlmMachine;
|
||||||
pub use matrix_qrcode;
|
pub use matrix_qrcode;
|
||||||
|
@ -56,4 +57,6 @@ pub use requests::{
|
||||||
OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest,
|
OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
pub use store::CryptoStoreError;
|
pub use store::CryptoStoreError;
|
||||||
pub use verification::{AcceptSettings, QrVerification, Sas, Verification, VerificationRequest};
|
pub use verification::{
|
||||||
|
AcceptSettings, CancelInfo, QrVerification, Sas, Verification, VerificationRequest,
|
||||||
|
};
|
||||||
|
|
|
@ -46,7 +46,7 @@ use tracing::{debug, error, info, trace, warn};
|
||||||
use crate::store::sled::SledStore;
|
use crate::store::sled::SledStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult},
|
error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult},
|
||||||
identities::{Device, IdentityManager, UserDevices},
|
identities::{user::UserIdentities, Device, IdentityManager, UserDevices},
|
||||||
key_request::KeyRequestMachine,
|
key_request::KeyRequestMachine,
|
||||||
olm::{
|
olm::{
|
||||||
Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys,
|
Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys,
|
||||||
|
@ -789,7 +789,7 @@ impl OlmMachine {
|
||||||
one_time_keys_counts: &BTreeMap<DeviceKeyAlgorithm, UInt>,
|
one_time_keys_counts: &BTreeMap<DeviceKeyAlgorithm, UInt>,
|
||||||
) -> OlmResult<ToDevice> {
|
) -> OlmResult<ToDevice> {
|
||||||
// Remove verification objects that have expired or are done.
|
// Remove verification objects that have expired or are done.
|
||||||
self.verification_machine.garbage_collect();
|
let mut events = self.verification_machine.garbage_collect();
|
||||||
|
|
||||||
// Always save the account, a new session might get created which also
|
// Always save the account, a new session might get created which also
|
||||||
// touches the account.
|
// touches the account.
|
||||||
|
@ -804,8 +804,6 @@ impl OlmMachine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut events = Vec::new();
|
|
||||||
|
|
||||||
for mut raw_event in to_device_events.events {
|
for mut raw_event in to_device_events.events {
|
||||||
let event = match raw_event.deserialize() {
|
let event = match raw_event.deserialize() {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
|
@ -928,7 +926,7 @@ impl OlmMachine {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}) {
|
}) {
|
||||||
if (self.user_id() == device.user_id() && self.device_id() == device.device_id())
|
if (self.user_id() == device.user_id() && self.device_id() == device.device_id())
|
||||||
|| device.is_trusted()
|
|| device.verified()
|
||||||
{
|
{
|
||||||
VerificationState::Trusted
|
VerificationState::Trusted
|
||||||
} else {
|
} else {
|
||||||
|
@ -1054,6 +1052,18 @@ impl OlmMachine {
|
||||||
self.store.get_device(user_id, device_id).await
|
self.store.get_device(user_id, device_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the cross signing user identity of a user.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `user_id` - The unique id of the user that the identity belongs to
|
||||||
|
///
|
||||||
|
/// Returns a `UserIdentities` enum if one is found and the crypto store
|
||||||
|
/// didn't throw an error.
|
||||||
|
pub async fn get_identity(&self, user_id: &UserId) -> StoreResult<Option<UserIdentities>> {
|
||||||
|
self.store.get_identity(user_id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a map holding all the devices of an user.
|
/// Get a map holding all the devices of an user.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -1771,7 +1781,7 @@ pub(crate) mod test {
|
||||||
|
|
||||||
let bob_device = alice.get_device(bob.user_id(), bob.device_id()).await.unwrap().unwrap();
|
let bob_device = alice.get_device(bob.user_id(), bob.device_id()).await.unwrap().unwrap();
|
||||||
|
|
||||||
assert!(!bob_device.is_trusted());
|
assert!(!bob_device.verified());
|
||||||
|
|
||||||
let (alice_sas, request) = bob_device.start_verification().await.unwrap();
|
let (alice_sas, request) = bob_device.start_verification().await.unwrap();
|
||||||
|
|
||||||
|
@ -1834,14 +1844,14 @@ pub(crate) mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(alice_sas.is_done());
|
assert!(alice_sas.is_done());
|
||||||
assert!(bob_device.is_trusted());
|
assert!(bob_device.verified());
|
||||||
|
|
||||||
let alice_device =
|
let alice_device =
|
||||||
bob.get_device(alice.user_id(), alice.device_id()).await.unwrap().unwrap();
|
bob.get_device(alice.user_id(), alice.device_id()).await.unwrap().unwrap();
|
||||||
|
|
||||||
assert!(!alice_device.is_trusted());
|
assert!(!alice_device.verified());
|
||||||
bob.handle_verification_event(&event).await;
|
bob.handle_verification_event(&event).await;
|
||||||
assert!(bob_sas.is_done());
|
assert!(bob_sas.is_done());
|
||||||
assert!(alice_device.is_trusted());
|
assert!(alice_device.verified());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ use serde_json::Error as JsonError;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest,
|
error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest,
|
||||||
OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice, UserIdentity,
|
ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentity,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Private cross signing identity.
|
/// Private cross signing identity.
|
||||||
|
@ -117,7 +117,9 @@ impl PrivateCrossSigningIdentity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn to_public_identity(&self) -> Result<OwnUserIdentity, SignatureError> {
|
pub(crate) async fn to_public_identity(
|
||||||
|
&self,
|
||||||
|
) -> Result<ReadOnlyOwnUserIdentity, SignatureError> {
|
||||||
let master = self
|
let master = self
|
||||||
.master_key
|
.master_key
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -142,7 +144,7 @@ impl PrivateCrossSigningIdentity {
|
||||||
.ok_or(SignatureError::MissingSigningKey)?
|
.ok_or(SignatureError::MissingSigningKey)?
|
||||||
.public_key
|
.public_key
|
||||||
.clone();
|
.clone();
|
||||||
let identity = OwnUserIdentity::new(master, self_signing, user_signing)?;
|
let identity = ReadOnlyOwnUserIdentity::new(master, self_signing, user_signing)?;
|
||||||
identity.mark_as_verified();
|
identity.mark_as_verified();
|
||||||
|
|
||||||
Ok(identity)
|
Ok(identity)
|
||||||
|
@ -151,7 +153,7 @@ impl PrivateCrossSigningIdentity {
|
||||||
/// Sign the given public user identity with this private identity.
|
/// Sign the given public user identity with this private identity.
|
||||||
pub(crate) async fn sign_user(
|
pub(crate) async fn sign_user(
|
||||||
&self,
|
&self,
|
||||||
user_identity: &UserIdentity,
|
user_identity: &ReadOnlyUserIdentity,
|
||||||
) -> Result<SignatureUploadRequest, SignatureError> {
|
) -> Result<SignatureUploadRequest, SignatureError> {
|
||||||
let signed_keys = self
|
let signed_keys = self
|
||||||
.user_signing_key
|
.user_signing_key
|
||||||
|
@ -407,7 +409,7 @@ mod test {
|
||||||
|
|
||||||
use super::{PrivateCrossSigningIdentity, Signing};
|
use super::{PrivateCrossSigningIdentity, Signing};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentity},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentity},
|
||||||
olm::ReadOnlyAccount,
|
olm::ReadOnlyAccount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -518,7 +520,7 @@ mod test {
|
||||||
|
|
||||||
let bob_account = ReadOnlyAccount::new(&user_id!("@bob:localhost"), "DEVICEID".into());
|
let bob_account = ReadOnlyAccount::new(&user_id!("@bob:localhost"), "DEVICEID".into());
|
||||||
let (bob_private, _, _) = PrivateCrossSigningIdentity::new_with_account(&bob_account).await;
|
let (bob_private, _, _) = PrivateCrossSigningIdentity::new_with_account(&bob_account).await;
|
||||||
let mut bob_public = UserIdentity::from_private(&bob_private).await;
|
let mut bob_public = ReadOnlyUserIdentity::from_private(&bob_private).await;
|
||||||
|
|
||||||
let user_signing = identity.user_signing_key.lock().await;
|
let user_signing = identity.user_signing_key.lock().await;
|
||||||
let user_signing = user_signing.as_ref().unwrap();
|
let user_signing = user_signing.as_ref().unwrap();
|
||||||
|
|
|
@ -37,7 +37,7 @@ use crate::{
|
||||||
error::SignatureError,
|
error::SignatureError,
|
||||||
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
||||||
utilities::{decode_url_safe as decode, encode_url_safe as encode, DecodeError},
|
utilities::{decode_url_safe as decode, encode_url_safe as encode, DecodeError},
|
||||||
UserIdentity,
|
ReadOnlyUserIdentity,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NONCE_SIZE: usize = 12;
|
const NONCE_SIZE: usize = 12;
|
||||||
|
@ -186,7 +186,7 @@ impl UserSigning {
|
||||||
|
|
||||||
pub async fn sign_user(
|
pub async fn sign_user(
|
||||||
&self,
|
&self,
|
||||||
user: &UserIdentity,
|
user: &ReadOnlyUserIdentity,
|
||||||
) -> Result<BTreeMap<UserId, BTreeMap<String, Value>>, SignatureError> {
|
) -> Result<BTreeMap<UserId, BTreeMap<String, Value>>, SignatureError> {
|
||||||
let user_master: &CrossSigningKey = user.master_key().as_ref();
|
let user_master: &CrossSigningKey = user.master_key().as_ref();
|
||||||
let signature = self.inner.sign_json(serde_json::to_value(user_master)?).await?;
|
let signature = self.inner.sign_json(serde_json::to_value(user_master)?).await?;
|
||||||
|
|
|
@ -78,6 +78,29 @@ impl ToDeviceRequest {
|
||||||
Self::new_with_id(recipient, recipient_device, content, Uuid::new_v4())
|
Self::new_with_id(recipient, recipient_device, content, Uuid::new_v4())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_for_recipients(
|
||||||
|
recipient: &UserId,
|
||||||
|
recipient_devices: Vec<DeviceIdBox>,
|
||||||
|
content: AnyToDeviceEventContent,
|
||||||
|
txn_id: Uuid,
|
||||||
|
) -> Self {
|
||||||
|
let mut messages = BTreeMap::new();
|
||||||
|
let event_type = EventType::from(content.event_type());
|
||||||
|
|
||||||
|
if recipient_devices.is_empty() {
|
||||||
|
Self::new(recipient, DeviceIdOrAllDevices::AllDevices, content)
|
||||||
|
} else {
|
||||||
|
let device_messages = recipient_devices
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| (DeviceIdOrAllDevices::DeviceId(d), Raw::from(content.clone())))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
messages.insert(recipient.clone(), device_messages);
|
||||||
|
|
||||||
|
ToDeviceRequest { event_type, txn_id, messages }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn new_with_id(
|
pub(crate) fn new_with_id(
|
||||||
recipient: &UserId,
|
recipient: &UserId,
|
||||||
recipient_device: impl Into<DeviceIdOrAllDevices>,
|
recipient_device: impl Into<DeviceIdOrAllDevices>,
|
||||||
|
|
|
@ -412,10 +412,7 @@ impl GroupSessionManager {
|
||||||
users: impl Iterator<Item = &UserId>,
|
users: impl Iterator<Item = &UserId>,
|
||||||
encryption_settings: impl Into<EncryptionSettings>,
|
encryption_settings: impl Into<EncryptionSettings>,
|
||||||
) -> OlmResult<Vec<Arc<ToDeviceRequest>>> {
|
) -> OlmResult<Vec<Arc<ToDeviceRequest>>> {
|
||||||
debug!(
|
debug!(room_id = room_id.as_str(), "Checking if a room key needs to be shared",);
|
||||||
room_id = room_id.as_str(),
|
|
||||||
"Checking if a group session needs to be shared for room {}", room_id
|
|
||||||
);
|
|
||||||
|
|
||||||
let encryption_settings = encryption_settings.into();
|
let encryption_settings = encryption_settings.into();
|
||||||
let history_visibility = encryption_settings.history_visibility.clone();
|
let history_visibility = encryption_settings.history_visibility.clone();
|
||||||
|
|
|
@ -26,7 +26,7 @@ use super::{
|
||||||
Changes, CryptoStore, InboundGroupSession, ReadOnlyAccount, Result, Session,
|
Changes, CryptoStore, InboundGroupSession, ReadOnlyAccount, Result, Session,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
key_request::OutgoingKeyRequest,
|
key_request::OutgoingKeyRequest,
|
||||||
olm::{OutboundGroupSession, PrivateCrossSigningIdentity},
|
olm::{OutboundGroupSession, PrivateCrossSigningIdentity},
|
||||||
};
|
};
|
||||||
|
@ -44,7 +44,7 @@ pub struct MemoryStore {
|
||||||
users_for_key_query: Arc<DashSet<UserId>>,
|
users_for_key_query: Arc<DashSet<UserId>>,
|
||||||
olm_hashes: Arc<DashMap<String, DashSet<String>>>,
|
olm_hashes: Arc<DashMap<String, DashSet<String>>>,
|
||||||
devices: DeviceStore,
|
devices: DeviceStore,
|
||||||
identities: Arc<DashMap<UserId, UserIdentities>>,
|
identities: Arc<DashMap<UserId, ReadOnlyUserIdentities>>,
|
||||||
outgoing_key_requests: Arc<DashMap<Uuid, OutgoingKeyRequest>>,
|
outgoing_key_requests: Arc<DashMap<Uuid, OutgoingKeyRequest>>,
|
||||||
key_requests_by_info: Arc<DashMap<String, Uuid>>,
|
key_requests_by_info: Arc<DashMap<String, Uuid>>,
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ impl CryptoStore for MemoryStore {
|
||||||
Ok(self.devices.user_devices(user_id))
|
Ok(self.devices.user_devices(user_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentities>> {
|
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<ReadOnlyUserIdentities>> {
|
||||||
#[allow(clippy::map_clone)]
|
#[allow(clippy::map_clone)]
|
||||||
Ok(self.identities.get(user_id).map(|i| i.clone()))
|
Ok(self.identities.get(user_id).map(|i| i.clone()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,10 @@ use thiserror::Error;
|
||||||
pub use self::sled::SledStore;
|
pub use self::sled::SledStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SessionUnpicklingError,
|
error::SessionUnpicklingError,
|
||||||
identities::{Device, ReadOnlyDevice, UserDevices, UserIdentities},
|
identities::{
|
||||||
|
user::{OwnUserIdentity, UserIdentities, UserIdentity},
|
||||||
|
Device, ReadOnlyDevice, ReadOnlyUserIdentities, UserDevices,
|
||||||
|
},
|
||||||
key_request::OutgoingKeyRequest,
|
key_request::OutgoingKeyRequest,
|
||||||
olm::{
|
olm::{
|
||||||
InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
|
InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
|
||||||
|
@ -109,8 +112,8 @@ pub struct Changes {
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct IdentityChanges {
|
pub struct IdentityChanges {
|
||||||
pub new: Vec<UserIdentities>,
|
pub new: Vec<ReadOnlyUserIdentities>,
|
||||||
pub changed: Vec<UserIdentities>,
|
pub changed: Vec<ReadOnlyUserIdentities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
@ -215,8 +218,8 @@ impl Store {
|
||||||
device_id: &DeviceId,
|
device_id: &DeviceId,
|
||||||
) -> Result<Option<Device>> {
|
) -> Result<Option<Device>> {
|
||||||
let own_identity =
|
let own_identity =
|
||||||
self.get_user_identity(&self.user_id).await?.map(|i| i.own().cloned()).flatten();
|
self.inner.get_user_identity(&self.user_id).await?.map(|i| i.own().cloned()).flatten();
|
||||||
let device_owner_identity = self.get_user_identity(user_id).await?;
|
let device_owner_identity = self.inner.get_user_identity(user_id).await?;
|
||||||
|
|
||||||
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,
|
||||||
|
@ -226,6 +229,20 @@ impl Store {
|
||||||
device_owner_identity,
|
device_owner_identity,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_identity(&self, user_id: &UserId) -> Result<Option<UserIdentities>> {
|
||||||
|
Ok(self.inner.get_user_identity(user_id).await?.map(|i| match i {
|
||||||
|
ReadOnlyUserIdentities::Own(i) => OwnUserIdentity {
|
||||||
|
inner: i,
|
||||||
|
verification_machine: self.verification_machine.clone(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
ReadOnlyUserIdentities::Other(i) => {
|
||||||
|
UserIdentity { inner: i, verification_machine: self.verification_machine.clone() }
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Store {
|
impl Deref for Store {
|
||||||
|
@ -388,7 +405,7 @@ pub trait CryptoStore: AsyncTraitDeps {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `user_id` - The user for which we should get the identity.
|
/// * `user_id` - The user for which we should get the identity.
|
||||||
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentities>>;
|
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<ReadOnlyUserIdentities>>;
|
||||||
|
|
||||||
/// Check if a hash for an Olm message stored in the database.
|
/// Check if a hash for an Olm message stored in the database.
|
||||||
async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool>;
|
async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool>;
|
||||||
|
|
|
@ -35,7 +35,7 @@ use super::{
|
||||||
ReadOnlyAccount, Result, Session,
|
ReadOnlyAccount, Result, Session,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
key_request::OutgoingKeyRequest,
|
key_request::OutgoingKeyRequest,
|
||||||
olm::{OutboundGroupSession, PickledInboundGroupSession, PrivateCrossSigningIdentity},
|
olm::{OutboundGroupSession, PickledInboundGroupSession, PrivateCrossSigningIdentity},
|
||||||
};
|
};
|
||||||
|
@ -669,7 +669,7 @@ impl CryptoStore for SledStore {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentities>> {
|
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<ReadOnlyUserIdentities>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.identities
|
.identities
|
||||||
.get(user_id.encode())?
|
.get(user_id.encode())?
|
||||||
|
|
|
@ -17,9 +17,13 @@ use std::sync::Arc;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use matrix_sdk_common::uuid::Uuid;
|
use matrix_sdk_common::uuid::Uuid;
|
||||||
use ruma::{DeviceId, UserId};
|
use ruma::{DeviceId, UserId};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
use super::{event_enums::OutgoingContent, Sas, Verification};
|
use super::{event_enums::OutgoingContent, Sas, Verification};
|
||||||
use crate::{OutgoingRequest, QrVerification, RoomMessageRequest, ToDeviceRequest};
|
use crate::{
|
||||||
|
OutgoingRequest, OutgoingVerificationRequest, QrVerification, RoomMessageRequest,
|
||||||
|
ToDeviceRequest,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct VerificationCache {
|
pub struct VerificationCache {
|
||||||
|
@ -73,7 +77,7 @@ impl VerificationCache {
|
||||||
self.outgoing_requests.iter().map(|r| (*r).clone()).collect()
|
self.outgoing_requests.iter().map(|r| (*r).clone()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn garbage_collect(&self) -> Vec<OutgoingRequest> {
|
pub fn garbage_collect(&self) -> Vec<OutgoingVerificationRequest> {
|
||||||
for user_verification in self.verification.iter() {
|
for user_verification in self.verification.iter() {
|
||||||
user_verification.retain(|_, s| !(s.is_done() || s.is_cancelled()));
|
user_verification.retain(|_, s| !(s.is_done() || s.is_cancelled()));
|
||||||
}
|
}
|
||||||
|
@ -83,15 +87,12 @@ impl VerificationCache {
|
||||||
self.verification
|
self.verification
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|v| {
|
.flat_map(|v| {
|
||||||
let requests: Vec<OutgoingRequest> = v
|
let requests: Vec<OutgoingVerificationRequest> = v
|
||||||
.value()
|
.value()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|s| {
|
.filter_map(|s| {
|
||||||
if let Verification::SasV1(s) = s.value() {
|
if let Verification::SasV1(s) = s.value() {
|
||||||
s.cancel_if_timed_out().map(|r| OutgoingRequest {
|
s.cancel_if_timed_out()
|
||||||
request_id: r.request_id(),
|
|
||||||
request: Arc::new(r.into()),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -114,9 +115,16 @@ impl VerificationCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_request(&self, request: OutgoingRequest) {
|
pub fn add_request(&self, request: OutgoingRequest) {
|
||||||
|
trace!("Adding an outgoing verification request {:?}", request);
|
||||||
self.outgoing_requests.insert(request.request_id, request);
|
self.outgoing_requests.insert(request.request_id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_verification_request(&self, request: OutgoingVerificationRequest) {
|
||||||
|
let request =
|
||||||
|
OutgoingRequest { request_id: request.request_id(), request: Arc::new(request.into()) };
|
||||||
|
self.add_request(request);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn queue_up_content(
|
pub fn queue_up_content(
|
||||||
&self,
|
&self,
|
||||||
recipient: &UserId,
|
recipient: &UserId,
|
||||||
|
|
|
@ -379,6 +379,33 @@ try_from_outgoing_content!(MacContent, KeyVerificationMac);
|
||||||
try_from_outgoing_content!(CancelContent, KeyVerificationCancel);
|
try_from_outgoing_content!(CancelContent, KeyVerificationCancel);
|
||||||
try_from_outgoing_content!(DoneContent, KeyVerificationDone);
|
try_from_outgoing_content!(DoneContent, KeyVerificationDone);
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a OutgoingContent> for RequestContent<'a> {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: &'a OutgoingContent) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
OutgoingContent::Room(_, c) => {
|
||||||
|
if let AnyMessageEventContent::RoomMessage(m) = c {
|
||||||
|
if let MessageType::VerificationRequest(c) = &m.msgtype {
|
||||||
|
Ok(Self::Room(c))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutgoingContent::ToDevice(c) => {
|
||||||
|
if let AnyToDeviceEventContent::KeyVerificationRequest(c) = c {
|
||||||
|
Ok(Self::ToDevice(c))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StartContent<'a> {
|
pub enum StartContent<'a> {
|
||||||
ToDevice(&'a StartToDeviceEventContent),
|
ToDevice(&'a StartToDeviceEventContent),
|
||||||
|
@ -665,27 +692,25 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest};
|
use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest};
|
||||||
|
|
||||||
#[cfg(test)]
|
impl TryFrom<OutgoingVerificationRequest> for OutgoingContent {
|
||||||
impl From<OutgoingVerificationRequest> for OutgoingContent {
|
type Error = String;
|
||||||
fn from(request: OutgoingVerificationRequest) -> Self {
|
|
||||||
|
fn try_from(request: OutgoingVerificationRequest) -> Result<Self, Self::Error> {
|
||||||
match request {
|
match request {
|
||||||
OutgoingVerificationRequest::ToDevice(r) => Self::try_from(r).unwrap(),
|
OutgoingVerificationRequest::ToDevice(r) => Self::try_from(r),
|
||||||
OutgoingVerificationRequest::InRoom(r) => Self::from(r),
|
OutgoingVerificationRequest::InRoom(r) => Ok(Self::from(r)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl From<RoomMessageRequest> for OutgoingContent {
|
impl From<RoomMessageRequest> for OutgoingContent {
|
||||||
fn from(value: RoomMessageRequest) -> Self {
|
fn from(value: RoomMessageRequest) -> Self {
|
||||||
(value.room_id, value.content).into()
|
(value.room_id, value.content).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl TryFrom<ToDeviceRequest> for OutgoingContent {
|
impl TryFrom<ToDeviceRequest> for OutgoingContent {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
|
@ -736,7 +761,6 @@ impl TryFrom<ToDeviceRequest> for OutgoingContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl TryFrom<OutgoingRequest> for OutgoingContent {
|
impl TryFrom<OutgoingRequest> for OutgoingContent {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,21 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::{
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use matrix_sdk_common::{locks::Mutex, uuid::Uuid};
|
use matrix_sdk_common::{locks::Mutex, uuid::Uuid};
|
||||||
use ruma::{DeviceId, MilliSecondsSinceUnixEpoch, UserId};
|
use ruma::{
|
||||||
|
events::{
|
||||||
|
key::verification::VerificationMethod, AnyToDeviceEvent, AnyToDeviceEventContent,
|
||||||
|
ToDeviceEvent,
|
||||||
|
},
|
||||||
|
serde::Raw,
|
||||||
|
DeviceId, DeviceIdBox, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId,
|
||||||
|
};
|
||||||
use tracing::{info, trace, warn};
|
use tracing::{info, trace, warn};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -30,8 +40,8 @@ use crate::{
|
||||||
olm::PrivateCrossSigningIdentity,
|
olm::PrivateCrossSigningIdentity,
|
||||||
requests::OutgoingRequest,
|
requests::OutgoingRequest,
|
||||||
store::{CryptoStore, CryptoStoreError},
|
store::{CryptoStore, CryptoStoreError},
|
||||||
OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, RoomMessageRequest,
|
OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyUserIdentity,
|
||||||
ToDeviceRequest,
|
RoomMessageRequest, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -58,6 +68,65 @@ impl VerificationMachine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn own_user_id(&self) -> &UserId {
|
||||||
|
self.account.user_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn own_device_id(&self) -> &DeviceId {
|
||||||
|
self.account.device_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn request_to_device_verification(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
recipient_devices: Vec<DeviceIdBox>,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
|
) -> (VerificationRequest, OutgoingVerificationRequest) {
|
||||||
|
let flow_id = FlowId::from(Uuid::new_v4().to_string());
|
||||||
|
|
||||||
|
let verification = VerificationRequest::new(
|
||||||
|
self.verifications.clone(),
|
||||||
|
self.account.clone(),
|
||||||
|
self.private_identity.lock().await.clone(),
|
||||||
|
self.store.clone(),
|
||||||
|
flow_id,
|
||||||
|
user_id,
|
||||||
|
recipient_devices,
|
||||||
|
methods,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.insert_request(verification.clone());
|
||||||
|
|
||||||
|
let request = verification.request_to_device();
|
||||||
|
|
||||||
|
(verification, request.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn request_verification(
|
||||||
|
&self,
|
||||||
|
identity: &ReadOnlyUserIdentity,
|
||||||
|
room_id: &RoomId,
|
||||||
|
request_event_id: &EventId,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
|
) -> VerificationRequest {
|
||||||
|
let flow_id = FlowId::InRoom(room_id.to_owned(), request_event_id.to_owned());
|
||||||
|
|
||||||
|
let request = VerificationRequest::new(
|
||||||
|
self.verifications.clone(),
|
||||||
|
self.account.clone(),
|
||||||
|
self.private_identity.lock().await.clone(),
|
||||||
|
self.store.clone(),
|
||||||
|
flow_id,
|
||||||
|
identity.user_id(),
|
||||||
|
vec![],
|
||||||
|
methods,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.insert_request(request.clone());
|
||||||
|
|
||||||
|
request
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn start_sas(
|
pub async fn start_sas(
|
||||||
&self,
|
&self,
|
||||||
device: ReadOnlyDevice,
|
device: ReadOnlyDevice,
|
||||||
|
@ -72,6 +141,8 @@ impl VerificationMachine {
|
||||||
self.store.clone(),
|
self.store.clone(),
|
||||||
identity,
|
identity,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = match content {
|
let request = match content {
|
||||||
|
@ -164,15 +235,40 @@ impl VerificationMachine {
|
||||||
self.verifications.outgoing_requests()
|
self.verifications.outgoing_requests()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn garbage_collect(&self) {
|
pub fn garbage_collect(&self) -> Vec<Raw<AnyToDeviceEvent>> {
|
||||||
|
let mut events = vec![];
|
||||||
|
|
||||||
for user_verification in self.requests.iter() {
|
for user_verification in self.requests.iter() {
|
||||||
user_verification.retain(|_, v| !(v.is_done() || v.is_cancelled()));
|
user_verification.retain(|_, v| !(v.is_done() || v.is_cancelled()));
|
||||||
}
|
}
|
||||||
self.requests.retain(|_, v| !v.is_empty());
|
self.requests.retain(|_, v| !v.is_empty());
|
||||||
|
|
||||||
for request in self.verifications.garbage_collect() {
|
let mut requests: Vec<OutgoingVerificationRequest> = self
|
||||||
self.verifications.add_request(request)
|
.requests
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| {
|
||||||
|
let requests: Vec<OutgoingVerificationRequest> =
|
||||||
|
v.value().iter().filter_map(|v| v.cancel_if_timed_out()).collect();
|
||||||
|
requests
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
requests.extend(self.verifications.garbage_collect().into_iter());
|
||||||
|
|
||||||
|
for request in requests {
|
||||||
|
if let Ok(OutgoingContent::ToDevice(AnyToDeviceEventContent::KeyVerificationCancel(
|
||||||
|
content,
|
||||||
|
))) = request.clone().try_into()
|
||||||
|
{
|
||||||
|
let event = ToDeviceEvent { content, sender: self.account.user_id().to_owned() };
|
||||||
|
|
||||||
|
events.push(AnyToDeviceEvent::KeyVerificationCancel(event).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.verifications.add_verification_request(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mark_sas_as_done(
|
async fn mark_sas_as_done(
|
||||||
|
@ -286,10 +382,17 @@ impl VerificationMachine {
|
||||||
verification.receive_cancel(event.sender(), c);
|
verification.receive_cancel(event.sender(), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sas) = self.get_sas(event.sender(), flow_id.as_str()) {
|
if let Some(verification) =
|
||||||
|
self.get_verification(event.sender(), flow_id.as_str())
|
||||||
|
{
|
||||||
|
match verification {
|
||||||
|
Verification::SasV1(sas) => {
|
||||||
// This won't produce an outgoing content
|
// This won't produce an outgoing content
|
||||||
let _ = sas.receive_any_event(event.sender(), &content);
|
let _ = sas.receive_any_event(event.sender(), &content);
|
||||||
}
|
}
|
||||||
|
Verification::QrV1(qr) => qr.receive_cancel(event.sender(), c),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AnyVerificationContent::Ready(c) => {
|
AnyVerificationContent::Ready(c) => {
|
||||||
if let Some(request) = self.get_request(event.sender(), flow_id.as_str()) {
|
if let Some(request) = self.get_request(event.sender(), flow_id.as_str()) {
|
||||||
|
@ -324,6 +427,7 @@ impl VerificationMachine {
|
||||||
private_identity,
|
private_identity,
|
||||||
device,
|
device,
|
||||||
identity,
|
identity,
|
||||||
|
None,
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
Ok(sas) => {
|
Ok(sas) => {
|
||||||
|
@ -461,6 +565,8 @@ mod test {
|
||||||
bob_store,
|
bob_store,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
machine
|
machine
|
||||||
|
|
|
@ -44,7 +44,7 @@ use crate::{
|
||||||
error::SignatureError,
|
error::SignatureError,
|
||||||
olm::PrivateCrossSigningIdentity,
|
olm::PrivateCrossSigningIdentity,
|
||||||
store::{Changes, CryptoStore},
|
store::{Changes, CryptoStore},
|
||||||
CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities,
|
CryptoStoreError, LocalTrust, ReadOnlyDevice, ReadOnlyUserIdentities,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An enum over the different verification types the SDK supports.
|
/// An enum over the different verification types the SDK supports.
|
||||||
|
@ -144,7 +144,7 @@ impl From<QrVerification> for Verification {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Done {
|
pub struct Done {
|
||||||
verified_devices: Arc<[ReadOnlyDevice]>,
|
verified_devices: Arc<[ReadOnlyDevice]>,
|
||||||
verified_master_keys: Arc<[UserIdentities]>,
|
verified_master_keys: Arc<[ReadOnlyUserIdentities]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Done {
|
impl Done {
|
||||||
|
@ -165,14 +165,47 @@ impl Done {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about the cancellation of a verification request or verification
|
||||||
|
/// flow.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CancelInfo {
|
||||||
|
cancelled_by_us: bool,
|
||||||
|
cancel_code: CancelCode,
|
||||||
|
reason: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CancelInfo {
|
||||||
|
/// Get the human readable reason of the cancellation.
|
||||||
|
pub fn reason(&self) -> &'static str {
|
||||||
|
&self.reason
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `CancelCode` that cancelled this verification.
|
||||||
|
pub fn cancel_code(&self) -> &CancelCode {
|
||||||
|
&self.cancel_code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Was the verification cancelled by us?
|
||||||
|
pub fn cancelled_by_us(&self) -> bool {
|
||||||
|
self.cancelled_by_us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Cancelled> for CancelInfo {
|
||||||
|
fn from(c: Cancelled) -> Self {
|
||||||
|
Self { cancelled_by_us: c.cancelled_by_us, cancel_code: c.cancel_code, reason: c.reason }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Cancelled {
|
pub struct Cancelled {
|
||||||
|
cancelled_by_us: bool,
|
||||||
cancel_code: CancelCode,
|
cancel_code: CancelCode,
|
||||||
reason: &'static str,
|
reason: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cancelled {
|
impl Cancelled {
|
||||||
fn new(code: CancelCode) -> Self {
|
fn new(cancelled_by_us: bool, code: CancelCode) -> Self {
|
||||||
let reason = match code {
|
let reason = match code {
|
||||||
CancelCode::Accepted => {
|
CancelCode::Accepted => {
|
||||||
"A m.key.verification.request was accepted by a different device."
|
"A m.key.verification.request was accepted by a different device."
|
||||||
|
@ -192,7 +225,7 @@ impl Cancelled {
|
||||||
_ => "Unknown cancel reason",
|
_ => "Unknown cancel reason",
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { cancel_code: code, reason }
|
Self { cancelled_by_us, cancel_code: code, reason }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
|
pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
|
||||||
|
@ -276,7 +309,7 @@ pub struct IdentitiesBeingVerified {
|
||||||
private_identity: PrivateCrossSigningIdentity,
|
private_identity: PrivateCrossSigningIdentity,
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
device_being_verified: ReadOnlyDevice,
|
device_being_verified: ReadOnlyDevice,
|
||||||
identity_being_verified: Option<UserIdentities>,
|
identity_being_verified: Option<ReadOnlyUserIdentities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdentitiesBeingVerified {
|
impl IdentitiesBeingVerified {
|
||||||
|
@ -307,7 +340,7 @@ impl IdentitiesBeingVerified {
|
||||||
pub async fn mark_as_done(
|
pub async fn mark_as_done(
|
||||||
&self,
|
&self,
|
||||||
verified_devices: Option<&[ReadOnlyDevice]>,
|
verified_devices: Option<&[ReadOnlyDevice]>,
|
||||||
verified_identities: Option<&[UserIdentities]>,
|
verified_identities: Option<&[ReadOnlyUserIdentities]>,
|
||||||
) -> Result<VerificationResult, CryptoStoreError> {
|
) -> Result<VerificationResult, CryptoStoreError> {
|
||||||
let device = self.mark_device_as_verified(verified_devices).await?;
|
let device = self.mark_device_as_verified(verified_devices).await?;
|
||||||
let identity = self.mark_identity_as_verified(verified_identities).await?;
|
let identity = self.mark_identity_as_verified(verified_identities).await?;
|
||||||
|
@ -414,8 +447,8 @@ impl IdentitiesBeingVerified {
|
||||||
|
|
||||||
async fn mark_identity_as_verified(
|
async fn mark_identity_as_verified(
|
||||||
&self,
|
&self,
|
||||||
verified_identities: Option<&[UserIdentities]>,
|
verified_identities: Option<&[ReadOnlyUserIdentities]>,
|
||||||
) -> Result<Option<UserIdentities>, CryptoStoreError> {
|
) -> Result<Option<ReadOnlyUserIdentities>, CryptoStoreError> {
|
||||||
// If there wasn't an identity available during the verification flow
|
// If there wasn't an identity available during the verification flow
|
||||||
// return early as there's nothing to do.
|
// return early as there's nothing to do.
|
||||||
if self.identity_being_verified.is_none() {
|
if self.identity_being_verified.is_none() {
|
||||||
|
@ -436,7 +469,7 @@ impl IdentitiesBeingVerified {
|
||||||
"Marking the user identity of as verified."
|
"Marking the user identity of as verified."
|
||||||
);
|
);
|
||||||
|
|
||||||
if let UserIdentities::Own(i) = &identity {
|
if let ReadOnlyUserIdentities::Own(i) = &identity {
|
||||||
i.mark_as_verified();
|
i.mark_as_verified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,6 +557,8 @@ impl IdentitiesBeingVerified {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test {
|
pub(crate) mod test {
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent},
|
events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent},
|
||||||
UserId,
|
UserId,
|
||||||
|
@ -539,7 +574,8 @@ pub(crate) mod test {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
request: &OutgoingVerificationRequest,
|
request: &OutgoingVerificationRequest,
|
||||||
) -> AnyToDeviceEvent {
|
) -> AnyToDeviceEvent {
|
||||||
let content = request.to_owned().into();
|
let content =
|
||||||
|
request.to_owned().try_into().expect("Can't fetch content out of the request");
|
||||||
wrap_any_to_device_content(sender, content)
|
wrap_any_to_device_content(sender, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,19 +33,21 @@ use ruma::{
|
||||||
},
|
},
|
||||||
AnyMessageEventContent, AnyToDeviceEventContent,
|
AnyMessageEventContent, AnyToDeviceEventContent,
|
||||||
},
|
},
|
||||||
DeviceIdBox, DeviceKeyAlgorithm, UserId,
|
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, RoomId, UserId,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
event_enums::{DoneContent, OutgoingContent, OwnedStartContent, StartContent},
|
event_enums::{CancelContent, DoneContent, OutgoingContent, OwnedStartContent, StartContent},
|
||||||
Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult,
|
requests::RequestHandle,
|
||||||
|
CancelInfo, Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
|
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
|
||||||
store::CryptoStore,
|
store::CryptoStore,
|
||||||
CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest,
|
CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, ReadOnlyUserIdentities,
|
||||||
ToDeviceRequest, UserIdentities,
|
RoomMessageRequest, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SECRET_SIZE: usize = 16;
|
const SECRET_SIZE: usize = 16;
|
||||||
|
@ -83,6 +85,8 @@ pub struct QrVerification {
|
||||||
inner: Arc<QrVerificationData>,
|
inner: Arc<QrVerificationData>,
|
||||||
state: Arc<Mutex<InnerState>>,
|
state: Arc<Mutex<InnerState>>,
|
||||||
identities: IdentitiesBeingVerified,
|
identities: IdentitiesBeingVerified,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
|
we_started: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for QrVerification {
|
impl std::fmt::Debug for QrVerification {
|
||||||
|
@ -100,10 +104,15 @@ impl QrVerification {
|
||||||
///
|
///
|
||||||
/// When the verification object is in this state it's required that the
|
/// When the verification object is in this state it's required that the
|
||||||
/// user confirms that the other side has scanned the QR code.
|
/// user confirms that the other side has scanned the QR code.
|
||||||
pub fn is_scanned(&self) -> bool {
|
pub fn has_been_scanned(&self) -> bool {
|
||||||
matches!(&*self.state.lock().unwrap(), InnerState::Scanned(_))
|
matches!(&*self.state.lock().unwrap(), InnerState::Scanned(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Has the scanning of the QR code been confirmed by us.
|
||||||
|
pub fn has_been_confirmed(&self) -> bool {
|
||||||
|
matches!(&*self.state.lock().unwrap(), InnerState::Confirmed(_))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get our own user id.
|
/// Get our own user id.
|
||||||
pub fn user_id(&self) -> &UserId {
|
pub fn user_id(&self) -> &UserId {
|
||||||
self.identities.user_id()
|
self.identities.user_id()
|
||||||
|
@ -115,6 +124,26 @@ impl QrVerification {
|
||||||
self.identities.other_user_id()
|
self.identities.other_user_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the device id of the other side.
|
||||||
|
pub fn other_device_id(&self) -> &DeviceId {
|
||||||
|
self.identities.other_device_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Did we initiate the verification request
|
||||||
|
pub fn we_started(&self) -> bool {
|
||||||
|
self.we_started
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get info about the cancellation if the verification flow has been
|
||||||
|
/// cancelled.
|
||||||
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
|
if let InnerState::Cancelled(c) = &*self.state.lock().unwrap() {
|
||||||
|
Some(c.state.clone().into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Has the verification flow completed.
|
/// Has the verification flow completed.
|
||||||
pub fn is_done(&self) -> bool {
|
pub fn is_done(&self) -> bool {
|
||||||
matches!(&*self.state.lock().unwrap(), InnerState::Done(_))
|
matches!(&*self.state.lock().unwrap(), InnerState::Done(_))
|
||||||
|
@ -130,11 +159,25 @@ impl QrVerification {
|
||||||
self.identities.is_self_verification()
|
self.identities.is_self_verification()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Have we successfully scanned the QR code and are able to send a
|
||||||
|
/// reciprocation event.
|
||||||
|
pub fn reciprocated(&self) -> bool {
|
||||||
|
matches!(&*self.state.lock().unwrap(), InnerState::Reciprocated(_))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the unique ID that identifies this QR code verification flow.
|
/// Get the unique ID that identifies this QR code verification flow.
|
||||||
pub fn flow_id(&self) -> &FlowId {
|
pub fn flow_id(&self) -> &FlowId {
|
||||||
&self.flow_id
|
&self.flow_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the room id if the verification is happening inside a room.
|
||||||
|
pub fn room_id(&self) -> Option<&RoomId> {
|
||||||
|
match self.flow_id() {
|
||||||
|
FlowId::ToDevice(_) => None,
|
||||||
|
FlowId::InRoom(r, _) => Some(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a QR code object that is representing this verification flow.
|
/// Generate a QR code object that is representing this verification flow.
|
||||||
///
|
///
|
||||||
/// The `QrCode` can then be rendered as an image or as an unicode string.
|
/// The `QrCode` can then be rendered as an image or as an unicode string.
|
||||||
|
@ -156,7 +199,43 @@ impl QrVerification {
|
||||||
|
|
||||||
/// Cancel the verification flow.
|
/// Cancel the verification flow.
|
||||||
pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
|
pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
|
||||||
self.cancel_with_code(CancelCode::User).map(|c| self.content_to_request(c))
|
self.cancel_with_code(CancelCode::User)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancel the verification.
|
||||||
|
///
|
||||||
|
/// This cancels the verification with given `CancelCode`.
|
||||||
|
///
|
||||||
|
/// **Note**: This method should generally not be used, the [`cancel()`]
|
||||||
|
/// method should be preferred. The SDK will automatically cancel with the
|
||||||
|
/// approprate cancel code, user initiated cancellations should only cancel
|
||||||
|
/// with the `CancelCode::User`
|
||||||
|
///
|
||||||
|
/// Returns None if the `Sas` object is already in a canceled state,
|
||||||
|
/// otherwise it returns a request that needs to be sent out.
|
||||||
|
///
|
||||||
|
/// [`cancel()`]: #method.cancel
|
||||||
|
pub fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingVerificationRequest> {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(request) = &self.request_handle {
|
||||||
|
request.cancel_with_code(&code);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_state = QrState::<Cancelled>::new(true, code);
|
||||||
|
let content = new_state.as_content(self.flow_id());
|
||||||
|
|
||||||
|
match &*state {
|
||||||
|
InnerState::Confirmed(_)
|
||||||
|
| InnerState::Created(_)
|
||||||
|
| InnerState::Scanned(_)
|
||||||
|
| InnerState::Reciprocated(_)
|
||||||
|
| InnerState::Done(_) => {
|
||||||
|
*state = InnerState::Cancelled(new_state);
|
||||||
|
Some(self.content_to_request(content))
|
||||||
|
}
|
||||||
|
InnerState::Cancelled(_) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notify the other side that we have successfully scanned the QR code and
|
/// Notify the other side that we have successfully scanned the QR code and
|
||||||
|
@ -164,9 +243,11 @@ impl QrVerification {
|
||||||
///
|
///
|
||||||
/// This will return some `OutgoingContent` if the object is in the correct
|
/// This will return some `OutgoingContent` if the object is in the correct
|
||||||
/// state to start the verification flow, otherwise `None`.
|
/// state to start the verification flow, otherwise `None`.
|
||||||
pub fn reciprocate(&self) -> Option<OutgoingContent> {
|
pub fn reciprocate(&self) -> Option<OutgoingVerificationRequest> {
|
||||||
match &*self.state.lock().unwrap() {
|
match &*self.state.lock().unwrap() {
|
||||||
InnerState::Reciprocated(s) => Some(s.as_content(self.flow_id())),
|
InnerState::Reciprocated(s) => {
|
||||||
|
Some(self.content_to_request(s.as_content(self.flow_id())))
|
||||||
|
}
|
||||||
InnerState::Created(_)
|
InnerState::Created(_)
|
||||||
| InnerState::Scanned(_)
|
| InnerState::Scanned(_)
|
||||||
| InnerState::Confirmed(_)
|
| InnerState::Confirmed(_)
|
||||||
|
@ -209,25 +290,6 @@ impl QrVerification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingContent> {
|
|
||||||
let new_state = QrState::<Cancelled>::new(code);
|
|
||||||
let content = new_state.as_content(self.flow_id());
|
|
||||||
|
|
||||||
let mut state = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
match &*state {
|
|
||||||
InnerState::Confirmed(_)
|
|
||||||
| InnerState::Created(_)
|
|
||||||
| InnerState::Scanned(_)
|
|
||||||
| InnerState::Reciprocated(_)
|
|
||||||
| InnerState::Done(_) => {
|
|
||||||
*state = InnerState::Cancelled(new_state);
|
|
||||||
Some(content)
|
|
||||||
}
|
|
||||||
InnerState::Cancelled(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn mark_as_done(
|
async fn mark_as_done(
|
||||||
&self,
|
&self,
|
||||||
new_state: QrState<Done>,
|
new_state: QrState<Done>,
|
||||||
|
@ -243,7 +305,7 @@ impl QrVerification {
|
||||||
match self.identities.mark_as_done(Some(&devices), Some(&identities)).await? {
|
match self.identities.mark_as_done(Some(&devices), Some(&identities)).await? {
|
||||||
VerificationResult::Ok => (None, None),
|
VerificationResult::Ok => (None, None),
|
||||||
VerificationResult::Cancel(c) => {
|
VerificationResult::Cancel(c) => {
|
||||||
let canceled = QrState::<Cancelled>::new(c);
|
let canceled = QrState::<Cancelled>::new(false, c);
|
||||||
let content = canceled.as_content(self.flow_id());
|
let content = canceled.as_content(self.flow_id());
|
||||||
new_state = InnerState::Cancelled(canceled);
|
new_state = InnerState::Cancelled(canceled);
|
||||||
(Some(content), None)
|
(Some(content), None)
|
||||||
|
@ -338,6 +400,28 @@ impl QrVerification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) {
|
||||||
|
if sender == self.other_user_id() {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
|
let new_state = match &*state {
|
||||||
|
InnerState::Created(s) => s.clone().into_cancelled(content),
|
||||||
|
InnerState::Scanned(s) => s.clone().into_cancelled(content),
|
||||||
|
InnerState::Confirmed(s) => s.clone().into_cancelled(content),
|
||||||
|
InnerState::Reciprocated(s) => s.clone().into_cancelled(content),
|
||||||
|
InnerState::Done(_) | InnerState::Cancelled(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
sender = sender.as_str(),
|
||||||
|
code = content.cancel_code().as_str(),
|
||||||
|
"Cancelling a QR verification, other user has cancelled"
|
||||||
|
);
|
||||||
|
|
||||||
|
*state = InnerState::Cancelled(new_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_secret() -> String {
|
fn generate_secret() -> String {
|
||||||
let mut shared_secret = [0u8; SECRET_SIZE];
|
let mut shared_secret = [0u8; SECRET_SIZE];
|
||||||
getrandom::getrandom(&mut shared_secret)
|
getrandom::getrandom(&mut shared_secret)
|
||||||
|
@ -351,6 +435,8 @@ impl QrVerification {
|
||||||
own_master_key: String,
|
own_master_key: String,
|
||||||
other_device_key: String,
|
other_device_key: String,
|
||||||
identities: IdentitiesBeingVerified,
|
identities: IdentitiesBeingVerified,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let secret = Self::generate_secret();
|
let secret = Self::generate_secret();
|
||||||
|
|
||||||
|
@ -362,7 +448,7 @@ impl QrVerification {
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
Self::new_helper(store, flow_id, inner, identities)
|
Self::new_helper(store, flow_id, inner, identities, we_started, request_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_self_no_master(
|
pub(crate) fn new_self_no_master(
|
||||||
|
@ -371,6 +457,8 @@ impl QrVerification {
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
own_master_key: String,
|
own_master_key: String,
|
||||||
identities: IdentitiesBeingVerified,
|
identities: IdentitiesBeingVerified,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> QrVerification {
|
) -> QrVerification {
|
||||||
let secret = Self::generate_secret();
|
let secret = Self::generate_secret();
|
||||||
|
|
||||||
|
@ -382,7 +470,7 @@ impl QrVerification {
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
Self::new_helper(store, flow_id, inner, identities)
|
Self::new_helper(store, flow_id, inner, identities, we_started, request_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_cross(
|
pub(crate) fn new_cross(
|
||||||
|
@ -391,6 +479,8 @@ impl QrVerification {
|
||||||
own_master_key: String,
|
own_master_key: String,
|
||||||
other_master_key: String,
|
other_master_key: String,
|
||||||
identities: IdentitiesBeingVerified,
|
identities: IdentitiesBeingVerified,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let secret = Self::generate_secret();
|
let secret = Self::generate_secret();
|
||||||
|
|
||||||
|
@ -403,9 +493,10 @@ impl QrVerification {
|
||||||
let inner: QrVerificationData =
|
let inner: QrVerificationData =
|
||||||
VerificationData::new(event_id, own_master_key, other_master_key, secret).into();
|
VerificationData::new(event_id, own_master_key, other_master_key, secret).into();
|
||||||
|
|
||||||
Self::new_helper(store, flow_id, inner, identities)
|
Self::new_helper(store, flow_id, inner, identities, we_started, request_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) async fn from_scan(
|
pub(crate) async fn from_scan(
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
own_account: ReadOnlyAccount,
|
own_account: ReadOnlyAccount,
|
||||||
|
@ -414,6 +505,8 @@ impl QrVerification {
|
||||||
other_device_id: DeviceIdBox,
|
other_device_id: DeviceIdBox,
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
qr_code: QrVerificationData,
|
qr_code: QrVerificationData,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> Result<Self, ScanError> {
|
) -> Result<Self, ScanError> {
|
||||||
if flow_id.as_str() != qr_code.flow_id() {
|
if flow_id.as_str() != qr_code.flow_id() {
|
||||||
return Err(ScanError::FlowIdMismatch {
|
return Err(ScanError::FlowIdMismatch {
|
||||||
|
@ -435,7 +528,7 @@ impl QrVerification {
|
||||||
ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone())
|
ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let check_master_key = |key, identity: &UserIdentities| {
|
let check_master_key = |key, identity: &ReadOnlyUserIdentities| {
|
||||||
let master_key = identity.master_key().get_first_key().ok_or_else(|| {
|
let master_key = identity.master_key().get_first_key().ok_or_else(|| {
|
||||||
ScanError::MissingCrossSigningIdentity(identity.user_id().clone())
|
ScanError::MissingCrossSigningIdentity(identity.user_id().clone())
|
||||||
})?;
|
})?;
|
||||||
|
@ -510,6 +603,8 @@ impl QrVerification {
|
||||||
}))
|
}))
|
||||||
.into(),
|
.into(),
|
||||||
identities,
|
identities,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,6 +613,8 @@ impl QrVerification {
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
inner: QrVerificationData,
|
inner: QrVerificationData,
|
||||||
identities: IdentitiesBeingVerified,
|
identities: IdentitiesBeingVerified,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let secret = inner.secret().to_owned();
|
let secret = inner.secret().to_owned();
|
||||||
|
|
||||||
|
@ -527,6 +624,8 @@ impl QrVerification {
|
||||||
inner: inner.into(),
|
inner: inner.into(),
|
||||||
state: Mutex::new(InnerState::Created(QrState { state: Created { secret } })).into(),
|
state: Mutex::new(InnerState::Created(QrState { state: Created { secret } })).into(),
|
||||||
identities,
|
identities,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,6 +645,12 @@ struct QrState<S: Clone> {
|
||||||
state: S,
|
state: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S: Clone> QrState<S> {
|
||||||
|
pub fn into_cancelled(self, content: &CancelContent<'_>) -> QrState<Cancelled> {
|
||||||
|
QrState { state: Cancelled::new(false, content.cancel_code().to_owned()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Created {
|
struct Created {
|
||||||
secret: String,
|
secret: String,
|
||||||
|
@ -594,8 +699,8 @@ impl QrState<Scanned> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QrState<Cancelled> {
|
impl QrState<Cancelled> {
|
||||||
fn new(cancel_code: CancelCode) -> Self {
|
fn new(cancelled_by_us: bool, cancel_code: CancelCode) -> Self {
|
||||||
QrState { state: Cancelled::new(cancel_code) }
|
QrState { state: Cancelled::new(cancelled_by_us, cancel_code) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
|
fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
|
||||||
|
@ -614,10 +719,10 @@ impl QrState<Created> {
|
||||||
if self.state.secret == m.secret {
|
if self.state.secret == m.secret {
|
||||||
Ok(QrState { state: Scanned {} })
|
Ok(QrState { state: Scanned {} })
|
||||||
} else {
|
} else {
|
||||||
Err(QrState::<Cancelled>::new(CancelCode::KeyMismatch))
|
Err(QrState::<Cancelled>::new(false, CancelCode::KeyMismatch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(QrState::<Cancelled>::new(CancelCode::UnknownMethod)),
|
_ => Err(QrState::<Cancelled>::new(false, CancelCode::UnknownMethod)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -627,7 +732,7 @@ impl QrState<Done> {
|
||||||
self.state.as_content(flow_id)
|
self.state.as_content(flow_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verified_identities(&self) -> (Arc<[ReadOnlyDevice]>, Arc<[UserIdentities]>) {
|
fn verified_identities(&self) -> (Arc<[ReadOnlyDevice]>, Arc<[ReadOnlyUserIdentities]>) {
|
||||||
(self.state.verified_devices.clone(), self.state.verified_master_keys.clone())
|
(self.state.verified_devices.clone(), self.state.verified_master_keys.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -637,7 +742,7 @@ impl QrState<Confirmed> {
|
||||||
self,
|
self,
|
||||||
_: &DoneContent,
|
_: &DoneContent,
|
||||||
verified_device: Option<&ReadOnlyDevice>,
|
verified_device: Option<&ReadOnlyDevice>,
|
||||||
verified_identity: Option<&UserIdentities>,
|
verified_identity: Option<&ReadOnlyUserIdentities>,
|
||||||
) -> QrState<Done> {
|
) -> QrState<Done> {
|
||||||
let devices: Vec<_> = verified_device.into_iter().cloned().collect();
|
let devices: Vec<_> = verified_device.into_iter().cloned().collect();
|
||||||
let identities: Vec<_> = verified_identity.into_iter().cloned().collect();
|
let identities: Vec<_> = verified_identity.into_iter().cloned().collect();
|
||||||
|
@ -676,7 +781,7 @@ impl QrState<Reciprocated> {
|
||||||
self,
|
self,
|
||||||
_: &DoneContent,
|
_: &DoneContent,
|
||||||
verified_device: Option<&ReadOnlyDevice>,
|
verified_device: Option<&ReadOnlyDevice>,
|
||||||
verified_identity: Option<&UserIdentities>,
|
verified_identity: Option<&ReadOnlyUserIdentities>,
|
||||||
) -> QrState<Done> {
|
) -> QrState<Done> {
|
||||||
let devices: Vec<_> = verified_device.into_iter().cloned().collect();
|
let devices: Vec<_> = verified_device.into_iter().cloned().collect();
|
||||||
let identities: Vec<_> = verified_identity.into_iter().cloned().collect();
|
let identities: Vec<_> = verified_identity.into_iter().cloned().collect();
|
||||||
|
@ -747,6 +852,8 @@ mod test {
|
||||||
flow_id.clone(),
|
flow_id.clone(),
|
||||||
master_key.clone(),
|
master_key.clone(),
|
||||||
identities.clone(),
|
identities.clone(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(verification.inner.first_key(), &device_key);
|
assert_eq!(verification.inner.first_key(), &device_key);
|
||||||
|
@ -758,6 +865,8 @@ mod test {
|
||||||
master_key.clone(),
|
master_key.clone(),
|
||||||
device_key.clone(),
|
device_key.clone(),
|
||||||
identities.clone(),
|
identities.clone(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(verification.inner.first_key(), &master_key);
|
assert_eq!(verification.inner.first_key(), &master_key);
|
||||||
|
@ -775,6 +884,8 @@ mod test {
|
||||||
master_key.clone(),
|
master_key.clone(),
|
||||||
bob_master_key.clone(),
|
bob_master_key.clone(),
|
||||||
identities,
|
identities,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(verification.inner.first_key(), &master_key);
|
assert_eq!(verification.inner.first_key(), &master_key);
|
||||||
|
@ -818,6 +929,8 @@ mod test {
|
||||||
flow_id.clone(),
|
flow_id.clone(),
|
||||||
master_key.clone(),
|
master_key.clone(),
|
||||||
identities,
|
identities,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let bob_store = memory_store();
|
let bob_store = memory_store();
|
||||||
|
@ -838,24 +951,27 @@ mod test {
|
||||||
alice_account.device_id().to_owned(),
|
alice_account.device_id().to_owned(),
|
||||||
flow_id,
|
flow_id,
|
||||||
qr_code,
|
qr_code,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let content = bob_verification.reciprocate().unwrap();
|
let request = bob_verification.reciprocate().unwrap();
|
||||||
|
let content = OutgoingContent::try_from(request).unwrap();
|
||||||
let content = StartContent::try_from(&content).unwrap();
|
let content = StartContent::try_from(&content).unwrap();
|
||||||
|
|
||||||
alice_verification.receive_reciprocation(&content);
|
alice_verification.receive_reciprocation(&content);
|
||||||
|
|
||||||
let request = alice_verification.confirm_scanning().unwrap();
|
let request = alice_verification.confirm_scanning().unwrap();
|
||||||
let content = OutgoingContent::from(request);
|
let content = OutgoingContent::try_from(request).unwrap();
|
||||||
let content = DoneContent::try_from(&content).unwrap();
|
let content = DoneContent::try_from(&content).unwrap();
|
||||||
|
|
||||||
assert!(!alice_verification.is_done());
|
assert!(!alice_verification.is_done());
|
||||||
assert!(!bob_verification.is_done());
|
assert!(!bob_verification.is_done());
|
||||||
|
|
||||||
let (request, _) = bob_verification.receive_done(&content).await.unwrap();
|
let (request, _) = bob_verification.receive_done(&content).await.unwrap();
|
||||||
let content = OutgoingContent::from(request.unwrap());
|
let content = OutgoingContent::try_from(request.unwrap()).unwrap();
|
||||||
let content = DoneContent::try_from(&content).unwrap();
|
let content = DoneContent::try_from(&content).unwrap();
|
||||||
alice_verification.receive_done(&content).await.unwrap();
|
alice_verification.receive_done(&content).await.unwrap();
|
||||||
|
|
||||||
|
@ -871,8 +987,8 @@ mod test {
|
||||||
|
|
||||||
let identity = identity.own().unwrap();
|
let identity = identity.own().unwrap();
|
||||||
|
|
||||||
assert!(!bob_device.is_trusted());
|
assert!(!bob_device.is_locally_trusted());
|
||||||
assert!(alice_device.is_trusted());
|
assert!(alice_device.is_locally_trusted());
|
||||||
assert!(identity.is_verified());
|
assert!(identity.is_verified());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,13 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
use std::{
|
||||||
|
sync::{Arc, Mutex},
|
||||||
use std::sync::{Arc, Mutex};
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use matrix_qrcode::QrVerificationData;
|
use matrix_qrcode::QrVerificationData;
|
||||||
use matrix_sdk_common::uuid::Uuid;
|
use matrix_sdk_common::{instant::Instant, uuid::Uuid};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{
|
events::{
|
||||||
key::verification::{
|
key::verification::{
|
||||||
|
@ -31,7 +32,7 @@ use ruma::{
|
||||||
AnyMessageEventContent, AnyToDeviceEventContent,
|
AnyMessageEventContent, AnyToDeviceEventContent,
|
||||||
},
|
},
|
||||||
to_device::DeviceIdOrAllDevices,
|
to_device::DeviceIdOrAllDevices,
|
||||||
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId,
|
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, RoomId, UserId,
|
||||||
};
|
};
|
||||||
use tracing::{info, trace, warn};
|
use tracing::{info, trace, warn};
|
||||||
|
|
||||||
|
@ -41,13 +42,13 @@ use super::{
|
||||||
CancelContent, DoneContent, OutgoingContent, ReadyContent, RequestContent, StartContent,
|
CancelContent, DoneContent, OutgoingContent, ReadyContent, RequestContent, StartContent,
|
||||||
},
|
},
|
||||||
qrcode::{QrVerification, ScanError},
|
qrcode::{QrVerification, ScanError},
|
||||||
Cancelled, FlowId, IdentitiesBeingVerified,
|
CancelInfo, Cancelled, FlowId, IdentitiesBeingVerified,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
|
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
|
||||||
store::CryptoStore,
|
store::CryptoStore,
|
||||||
CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, Sas,
|
CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, ReadOnlyUserIdentities,
|
||||||
ToDeviceRequest, UserIdentities,
|
RoomMessageRequest, Sas, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SUPPORTED_METHODS: &[VerificationMethod] = &[
|
const SUPPORTED_METHODS: &[VerificationMethod] = &[
|
||||||
|
@ -56,6 +57,8 @@ const SUPPORTED_METHODS: &[VerificationMethod] = &[
|
||||||
VerificationMethod::ReciprocateV1,
|
VerificationMethod::ReciprocateV1,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const VERIFICATION_TIMEOUT: Duration = Duration::from_secs(60 * 10);
|
||||||
|
|
||||||
/// An object controlling key verification requests.
|
/// An object controlling key verification requests.
|
||||||
///
|
///
|
||||||
/// Interactive verification flows usually start with a verification request,
|
/// Interactive verification flows usually start with a verification request,
|
||||||
|
@ -70,50 +73,48 @@ pub struct VerificationRequest {
|
||||||
flow_id: Arc<FlowId>,
|
flow_id: Arc<FlowId>,
|
||||||
other_user_id: Arc<UserId>,
|
other_user_id: Arc<UserId>,
|
||||||
inner: Arc<Mutex<InnerRequest>>,
|
inner: Arc<Mutex<InnerRequest>>,
|
||||||
|
creation_time: Arc<Instant>,
|
||||||
we_started: bool,
|
we_started: bool,
|
||||||
|
recipient_devices: Arc<Vec<DeviceIdBox>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to a request so child verification flows can cancel the request.
|
||||||
|
///
|
||||||
|
/// A verification flow can branch off into different types of verification
|
||||||
|
/// flows after the initial request handshake is done.
|
||||||
|
///
|
||||||
|
/// Cancelling a QR code verification should also cancel the request. This
|
||||||
|
/// `RequestHandle` allows the QR code verification object to cancel the parent
|
||||||
|
/// `VerificationRequest` object.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct RequestHandle {
|
||||||
|
inner: Arc<Mutex<InnerRequest>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestHandle {
|
||||||
|
pub fn cancel_with_code(&self, cancel_code: &CancelCode) {
|
||||||
|
self.inner.lock().unwrap().cancel(true, cancel_code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Arc<Mutex<InnerRequest>>> for RequestHandle {
|
||||||
|
fn from(inner: Arc<Mutex<InnerRequest>>) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationRequest {
|
impl VerificationRequest {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
cache: VerificationCache,
|
cache: VerificationCache,
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
private_cross_signing_identity: PrivateCrossSigningIdentity,
|
private_cross_signing_identity: PrivateCrossSigningIdentity,
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
room_id: &RoomId,
|
flow_id: FlowId,
|
||||||
event_id: &EventId,
|
|
||||||
other_user: &UserId,
|
other_user: &UserId,
|
||||||
|
recipient_devices: Vec<DeviceIdBox>,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let flow_id = (room_id.to_owned(), event_id.to_owned()).into();
|
|
||||||
|
|
||||||
let inner = Mutex::new(InnerRequest::Created(RequestState::new(
|
|
||||||
account.clone(),
|
|
||||||
private_cross_signing_identity,
|
|
||||||
cache.clone(),
|
|
||||||
store,
|
|
||||||
other_user,
|
|
||||||
&flow_id,
|
|
||||||
)))
|
|
||||||
.into();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
account,
|
|
||||||
verification_cache: cache,
|
|
||||||
flow_id: flow_id.into(),
|
|
||||||
inner,
|
|
||||||
other_user_id: other_user.to_owned().into(),
|
|
||||||
we_started: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new_to_device(
|
|
||||||
cache: VerificationCache,
|
|
||||||
account: ReadOnlyAccount,
|
|
||||||
private_cross_signing_identity: PrivateCrossSigningIdentity,
|
|
||||||
store: Arc<dyn CryptoStore>,
|
|
||||||
other_user: &UserId,
|
|
||||||
) -> Self {
|
|
||||||
let flow_id = Uuid::new_v4().to_string().into();
|
|
||||||
|
|
||||||
let inner = Mutex::new(InnerRequest::Created(RequestState::new(
|
let inner = Mutex::new(InnerRequest::Created(RequestState::new(
|
||||||
account.clone(),
|
account.clone(),
|
||||||
private_cross_signing_identity,
|
private_cross_signing_identity,
|
||||||
|
@ -121,6 +122,7 @@ impl VerificationRequest {
|
||||||
store,
|
store,
|
||||||
other_user,
|
other_user,
|
||||||
&flow_id,
|
&flow_id,
|
||||||
|
methods,
|
||||||
)))
|
)))
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
@ -130,7 +132,9 @@ impl VerificationRequest {
|
||||||
flow_id: flow_id.into(),
|
flow_id: flow_id.into(),
|
||||||
inner,
|
inner,
|
||||||
other_user_id: other_user.to_owned().into(),
|
other_user_id: other_user.to_owned().into(),
|
||||||
|
creation_time: Instant::now().into(),
|
||||||
we_started: true,
|
we_started: true,
|
||||||
|
recipient_devices: recipient_devices.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,12 +142,27 @@ impl VerificationRequest {
|
||||||
/// verification from the other side. This should be used only for
|
/// verification from the other side. This should be used only for
|
||||||
/// self-verifications and it should be sent to the specific device that we
|
/// self-verifications and it should be sent to the specific device that we
|
||||||
/// want to verify.
|
/// want to verify.
|
||||||
pub fn request_to_device(&self) -> RequestToDeviceEventContent {
|
pub(crate) fn request_to_device(&self) -> ToDeviceRequest {
|
||||||
RequestToDeviceEventContent::new(
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
let methods = if let InnerRequest::Created(c) = &*inner {
|
||||||
|
c.state.our_methods.clone()
|
||||||
|
} else {
|
||||||
|
SUPPORTED_METHODS.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
let content = RequestToDeviceEventContent::new(
|
||||||
self.account.device_id().into(),
|
self.account.device_id().into(),
|
||||||
self.flow_id().as_str().to_string(),
|
self.flow_id().as_str().to_string(),
|
||||||
SUPPORTED_METHODS.to_vec(),
|
methods,
|
||||||
MilliSecondsSinceUnixEpoch::now(),
|
MilliSecondsSinceUnixEpoch::now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
ToDeviceRequest::new_for_recipients(
|
||||||
|
self.other_user(),
|
||||||
|
self.recipient_devices.to_vec(),
|
||||||
|
AnyToDeviceEventContent::KeyVerificationRequest(content),
|
||||||
|
Uuid::new_v4(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +174,7 @@ impl VerificationRequest {
|
||||||
own_user_id: &UserId,
|
own_user_id: &UserId,
|
||||||
own_device_id: &DeviceId,
|
own_device_id: &DeviceId,
|
||||||
other_user_id: &UserId,
|
other_user_id: &UserId,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
) -> KeyVerificationRequestEventContent {
|
) -> KeyVerificationRequestEventContent {
|
||||||
KeyVerificationRequestEventContent::new(
|
KeyVerificationRequestEventContent::new(
|
||||||
format!(
|
format!(
|
||||||
|
@ -163,7 +183,7 @@ impl VerificationRequest {
|
||||||
key verification to verify keys.",
|
key verification to verify keys.",
|
||||||
own_user_id
|
own_user_id
|
||||||
),
|
),
|
||||||
SUPPORTED_METHODS.to_vec(),
|
methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec()),
|
||||||
own_device_id.into(),
|
own_device_id.into(),
|
||||||
other_user_id.to_owned(),
|
other_user_id.to_owned(),
|
||||||
)
|
)
|
||||||
|
@ -200,11 +220,13 @@ impl VerificationRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the `CancelCode` that cancelled this verification request.
|
/// Get info about the cancellation if the verification request has been
|
||||||
pub fn cancel_code(&self) -> Option<CancelCode> {
|
/// cancelled.
|
||||||
match &*self.inner.lock().unwrap() {
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
InnerRequest::Cancelled(c) => Some(c.state.cancel_code.to_owned()),
|
if let InnerRequest::Cancelled(c) = &*self.inner.lock().unwrap() {
|
||||||
_ => None,
|
Some(c.state.clone().into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +240,11 @@ impl VerificationRequest {
|
||||||
matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_))
|
matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Has the verification flow timed out.
|
||||||
|
pub fn timed_out(&self) -> bool {
|
||||||
|
self.creation_time.elapsed() > VERIFICATION_TIMEOUT
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the supported verification methods of the other side.
|
/// Get the supported verification methods of the other side.
|
||||||
///
|
///
|
||||||
/// Will be present only if the other side requested the verification or if
|
/// Will be present only if the other side requested the verification or if
|
||||||
|
@ -277,7 +304,11 @@ impl VerificationRequest {
|
||||||
/// Generate a QR code that can be used by another client to start a QR code
|
/// Generate a QR code that can be used by another client to start a QR code
|
||||||
/// based verification.
|
/// based verification.
|
||||||
pub async fn generate_qr_code(&self) -> Result<Option<QrVerification>, CryptoStoreError> {
|
pub async fn generate_qr_code(&self) -> Result<Option<QrVerification>, CryptoStoreError> {
|
||||||
self.inner.lock().unwrap().generate_qr_code().await
|
self.inner
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.generate_qr_code(self.we_started, self.inner.clone().into())
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a QR code verification by providing a scanned QR code for this
|
/// Start a QR code verification by providing a scanned QR code for this
|
||||||
|
@ -294,8 +325,7 @@ impl VerificationRequest {
|
||||||
let state = self.inner.lock().unwrap();
|
let state = self.inner.lock().unwrap();
|
||||||
|
|
||||||
if let InnerRequest::Ready(r) = &*state {
|
if let InnerRequest::Ready(r) = &*state {
|
||||||
Ok(Some(
|
let qr_verification = QrVerification::from_scan(
|
||||||
QrVerification::from_scan(
|
|
||||||
r.store.clone(),
|
r.store.clone(),
|
||||||
r.account.clone(),
|
r.account.clone(),
|
||||||
r.private_cross_signing_identity.clone(),
|
r.private_cross_signing_identity.clone(),
|
||||||
|
@ -303,9 +333,14 @@ impl VerificationRequest {
|
||||||
r.state.other_device_id.clone(),
|
r.state.other_device_id.clone(),
|
||||||
r.flow_id.as_ref().to_owned(),
|
r.flow_id.as_ref().to_owned(),
|
||||||
data,
|
data,
|
||||||
|
self.we_started,
|
||||||
|
Some(self.inner.clone().into()),
|
||||||
)
|
)
|
||||||
.await?,
|
.await?;
|
||||||
))
|
|
||||||
|
self.verification_cache.insert_qr(qr_verification.clone());
|
||||||
|
|
||||||
|
Ok(Some(qr_verification))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -335,6 +370,8 @@ impl VerificationRequest {
|
||||||
other_user_id: sender.to_owned().into(),
|
other_user_id: sender.to_owned().into(),
|
||||||
flow_id: flow_id.into(),
|
flow_id: flow_id.into(),
|
||||||
we_started: false,
|
we_started: false,
|
||||||
|
creation_time: Instant::now().into(),
|
||||||
|
recipient_devices: vec![].into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,8 +412,16 @@ impl VerificationRequest {
|
||||||
|
|
||||||
/// Cancel the verification request
|
/// Cancel the verification request
|
||||||
pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
|
pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
|
||||||
|
self.cancel_with_code(CancelCode::User)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel_with_code(&self, cancel_code: CancelCode) -> Option<OutgoingVerificationRequest> {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
inner.cancel(&CancelCode::User);
|
|
||||||
|
let send_to_everyone = self.we_started() && matches!(&*inner, InnerRequest::Created(_));
|
||||||
|
let other_device = inner.other_device_id();
|
||||||
|
|
||||||
|
inner.cancel(true, &cancel_code);
|
||||||
|
|
||||||
let content = if let InnerRequest::Cancelled(c) = &*inner {
|
let content = if let InnerRequest::Cancelled(c) = &*inner {
|
||||||
Some(c.state.as_content(self.flow_id()))
|
Some(c.state.as_content(self.flow_id()))
|
||||||
|
@ -384,26 +429,126 @@ impl VerificationRequest {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
content.map(|c| match c {
|
let request = content.map(|c| match c {
|
||||||
OutgoingContent::ToDevice(content) => {
|
OutgoingContent::ToDevice(content) => {
|
||||||
ToDeviceRequest::new(&self.other_user(), inner.other_device_id(), content).into()
|
if send_to_everyone {
|
||||||
|
ToDeviceRequest::new_for_recipients(
|
||||||
|
&self.other_user(),
|
||||||
|
self.recipient_devices.to_vec(),
|
||||||
|
content,
|
||||||
|
Uuid::new_v4(),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
ToDeviceRequest::new(&self.other_user(), other_device, content).into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OutgoingContent::Room(room_id, content) => {
|
OutgoingContent::Room(room_id, content) => {
|
||||||
RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into()
|
RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into()
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
drop(inner);
|
||||||
|
|
||||||
|
if let Some(verification) =
|
||||||
|
self.verification_cache.get(self.other_user(), self.flow_id().as_str())
|
||||||
|
{
|
||||||
|
match verification {
|
||||||
|
crate::Verification::SasV1(s) => s.cancel_with_code(cancel_code),
|
||||||
|
crate::Verification::QrV1(q) => q.cancel_with_code(cancel_code),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
request
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cancel_if_timed_out(&self) -> Option<OutgoingVerificationRequest> {
|
||||||
|
if self.is_cancelled() || self.is_done() {
|
||||||
|
None
|
||||||
|
} else if self.timed_out() {
|
||||||
|
let request = self.cancel_with_code(CancelCode::Timeout);
|
||||||
|
|
||||||
|
if self.is_passive() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
request
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a key verification cancellation for devices that received the
|
||||||
|
/// request but either shouldn't continue in the verification or didn't get
|
||||||
|
/// notified that the other side cancelled.
|
||||||
|
///
|
||||||
|
/// The spec states the following[1]:
|
||||||
|
/// When Bob accepts or declines the verification on one of his devices
|
||||||
|
/// (sending either an m.key.verification.ready or m.key.verification.cancel
|
||||||
|
/// event), Alice will send an m.key.verification.cancel event to Bob’s
|
||||||
|
/// other devices with a code of m.accepted in the case where Bob accepted
|
||||||
|
/// the verification, or m.user in the case where Bob rejected the
|
||||||
|
/// verification.
|
||||||
|
///
|
||||||
|
/// Realistically sending the cancellation to Bob's other devices is only
|
||||||
|
/// possible if Bob accepted the verification since we don't know the device
|
||||||
|
/// id of Bob's device that rejected the verification.
|
||||||
|
///
|
||||||
|
/// Thus, we're sending the cancellation to all devices that received the
|
||||||
|
/// request in the rejection case.
|
||||||
|
///
|
||||||
|
/// [1]: https://spec.matrix.org/unstable/client-server-api/#key-verification-framework
|
||||||
|
pub(crate) fn cancel_for_other_devices(
|
||||||
|
&self,
|
||||||
|
code: CancelCode,
|
||||||
|
filter_device: Option<&DeviceId>,
|
||||||
|
) -> Option<ToDeviceRequest> {
|
||||||
|
let cancelled = Cancelled::new(true, code);
|
||||||
|
let cancel_content = cancelled.as_content(self.flow_id());
|
||||||
|
|
||||||
|
if let OutgoingContent::ToDevice(c) = cancel_content {
|
||||||
|
let recipients: Vec<DeviceIdBox> = self
|
||||||
|
.recipient_devices
|
||||||
|
.to_vec()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|d| if let Some(device) = filter_device { &**d != device } else { true })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(ToDeviceRequest::new_for_recipients(
|
||||||
|
self.other_user(),
|
||||||
|
recipients,
|
||||||
|
c,
|
||||||
|
Uuid::new_v4(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) {
|
pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
if let InnerRequest::Created(s) = &*inner {
|
match &*inner {
|
||||||
if sender == self.own_user_id() && content.from_device() == self.account.device_id() {
|
InnerRequest::Created(s) => {
|
||||||
*inner = InnerRequest::Passive(s.clone().into_passive(content))
|
|
||||||
} else {
|
|
||||||
*inner = InnerRequest::Ready(s.clone().into_ready(sender, content));
|
*inner = InnerRequest::Ready(s.clone().into_ready(sender, content));
|
||||||
|
|
||||||
|
if let Some(request) =
|
||||||
|
self.cancel_for_other_devices(CancelCode::Accepted, Some(content.from_device()))
|
||||||
|
{
|
||||||
|
self.verification_cache.add_verification_request(request.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InnerRequest::Requested(s) => {
|
||||||
|
if sender == self.own_user_id() && content.from_device() != self.account.device_id()
|
||||||
|
{
|
||||||
|
*inner = InnerRequest::Passive(s.clone().into_passive(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InnerRequest::Ready(_)
|
||||||
|
| InnerRequest::Passive(_)
|
||||||
|
| InnerRequest::Done(_)
|
||||||
|
| InnerRequest::Cancelled(_) => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn receive_start(
|
pub(crate) async fn receive_start(
|
||||||
|
@ -414,7 +559,7 @@ impl VerificationRequest {
|
||||||
let inner = self.inner.lock().unwrap().clone();
|
let inner = self.inner.lock().unwrap().clone();
|
||||||
|
|
||||||
if let InnerRequest::Ready(s) = inner {
|
if let InnerRequest::Ready(s) = inner {
|
||||||
s.receive_start(sender, content).await?;
|
s.receive_start(sender, content, self.we_started, self.inner.clone().into()).await?;
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
sender = sender.as_str(),
|
sender = sender.as_str(),
|
||||||
|
@ -435,8 +580,21 @@ impl VerificationRequest {
|
||||||
|
|
||||||
pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) {
|
pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) {
|
||||||
if sender == self.other_user() {
|
if sender == self.other_user() {
|
||||||
let mut inner = self.inner.lock().unwrap().clone();
|
trace!(
|
||||||
inner.cancel(content.cancel_code());
|
sender = sender.as_str(),
|
||||||
|
code = content.cancel_code().as_str(),
|
||||||
|
"Cancelling a verification request, other user has cancelled"
|
||||||
|
);
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.cancel(false, content.cancel_code());
|
||||||
|
|
||||||
|
if self.we_started() {
|
||||||
|
if let Some(request) =
|
||||||
|
self.cancel_for_other_devices(content.cancel_code().to_owned(), None)
|
||||||
|
{
|
||||||
|
self.verification_cache.add_verification_request(request.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,6 +612,8 @@ impl VerificationRequest {
|
||||||
s.store.clone(),
|
s.store.clone(),
|
||||||
s.account.clone(),
|
s.account.clone(),
|
||||||
s.private_cross_signing_identity.clone(),
|
s.private_cross_signing_identity.clone(),
|
||||||
|
self.we_started,
|
||||||
|
self.inner.clone().into(),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
@ -505,17 +665,6 @@ impl InnerRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn other_user_id(&self) -> &UserId {
|
|
||||||
match self {
|
|
||||||
InnerRequest::Created(s) => &s.other_user_id,
|
|
||||||
InnerRequest::Requested(s) => &s.other_user_id,
|
|
||||||
InnerRequest::Ready(s) => &s.other_user_id,
|
|
||||||
InnerRequest::Passive(s) => &s.other_user_id,
|
|
||||||
InnerRequest::Done(s) => &s.other_user_id,
|
|
||||||
InnerRequest::Cancelled(s) => &s.other_user_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accept(&mut self, methods: Vec<VerificationMethod>) -> Option<OutgoingContent> {
|
fn accept(&mut self, methods: Vec<VerificationMethod>) -> Option<OutgoingContent> {
|
||||||
if let InnerRequest::Requested(s) = self {
|
if let InnerRequest::Requested(s) = self {
|
||||||
let (state, content) = s.clone().accept(methods);
|
let (state, content) = s.clone().accept(methods);
|
||||||
|
@ -529,49 +678,44 @@ impl InnerRequest {
|
||||||
|
|
||||||
fn receive_done(&mut self, content: &DoneContent) {
|
fn receive_done(&mut self, content: &DoneContent) {
|
||||||
*self = InnerRequest::Done(match self {
|
*self = InnerRequest::Done(match self {
|
||||||
InnerRequest::Created(s) => s.clone().into_done(content),
|
|
||||||
InnerRequest::Requested(s) => s.clone().into_done(content),
|
|
||||||
InnerRequest::Ready(s) => s.clone().into_done(content),
|
InnerRequest::Ready(s) => s.clone().into_done(content),
|
||||||
InnerRequest::Passive(s) => s.clone().into_done(content),
|
InnerRequest::Passive(s) => s.clone().into_done(content),
|
||||||
InnerRequest::Done(s) => s.clone().into_done(content),
|
InnerRequest::Done(_)
|
||||||
InnerRequest::Cancelled(_) => return,
|
| InnerRequest::Created(_)
|
||||||
|
| InnerRequest::Requested(_)
|
||||||
|
| InnerRequest::Cancelled(_) => return,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel(&mut self, cancel_code: &CancelCode) {
|
fn cancel(&mut self, cancelled_by_us: bool, cancel_code: &CancelCode) {
|
||||||
|
trace!(
|
||||||
|
cancelled_by_us = cancelled_by_us,
|
||||||
|
code = cancel_code.as_str(),
|
||||||
|
"Verification request going into the cancelled state"
|
||||||
|
);
|
||||||
|
|
||||||
*self = InnerRequest::Cancelled(match self {
|
*self = InnerRequest::Cancelled(match self {
|
||||||
InnerRequest::Created(s) => s.clone().into_canceled(cancel_code),
|
InnerRequest::Created(s) => s.clone().into_canceled(cancelled_by_us, cancel_code),
|
||||||
InnerRequest::Requested(s) => s.clone().into_canceled(cancel_code),
|
InnerRequest::Requested(s) => s.clone().into_canceled(cancelled_by_us, cancel_code),
|
||||||
InnerRequest::Ready(s) => s.clone().into_canceled(cancel_code),
|
InnerRequest::Ready(s) => s.clone().into_canceled(cancelled_by_us, cancel_code),
|
||||||
InnerRequest::Passive(s) => s.clone().into_canceled(cancel_code),
|
InnerRequest::Passive(_) | InnerRequest::Done(_) | InnerRequest::Cancelled(_) => return,
|
||||||
InnerRequest::Done(_) => return,
|
});
|
||||||
InnerRequest::Cancelled(_) => return,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn generate_qr_code(&self) -> Result<Option<QrVerification>, CryptoStoreError> {
|
async fn generate_qr_code(
|
||||||
|
&self,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: RequestHandle,
|
||||||
|
) -> Result<Option<QrVerification>, CryptoStoreError> {
|
||||||
match self {
|
match self {
|
||||||
InnerRequest::Created(_) => Ok(None),
|
InnerRequest::Created(_) => Ok(None),
|
||||||
InnerRequest::Requested(_) => Ok(None),
|
InnerRequest::Requested(_) => Ok(None),
|
||||||
InnerRequest::Ready(s) => s.generate_qr_code().await,
|
InnerRequest::Ready(s) => s.generate_qr_code(we_started, request_handle).await,
|
||||||
InnerRequest::Passive(_) => Ok(None),
|
InnerRequest::Passive(_) => Ok(None),
|
||||||
InnerRequest::Done(_) => Ok(None),
|
InnerRequest::Done(_) => Ok(None),
|
||||||
InnerRequest::Cancelled(_) => Ok(None),
|
InnerRequest::Cancelled(_) => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_started_sas(
|
|
||||||
&self,
|
|
||||||
content: &StartContent,
|
|
||||||
other_device: ReadOnlyDevice,
|
|
||||||
other_identity: Option<UserIdentities>,
|
|
||||||
) -> Result<Option<Sas>, OutgoingContent> {
|
|
||||||
if let InnerRequest::Ready(s) = self {
|
|
||||||
Ok(Some(s.to_started_sas(content, other_device, other_identity)?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -602,7 +746,11 @@ impl<S: Clone> RequestState<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_canceled(self, cancel_code: &CancelCode) -> RequestState<Cancelled> {
|
fn into_canceled(
|
||||||
|
self,
|
||||||
|
cancelled_by_us: bool,
|
||||||
|
cancel_code: &CancelCode,
|
||||||
|
) -> RequestState<Cancelled> {
|
||||||
RequestState::<Cancelled> {
|
RequestState::<Cancelled> {
|
||||||
account: self.account,
|
account: self.account,
|
||||||
private_cross_signing_identity: self.private_cross_signing_identity,
|
private_cross_signing_identity: self.private_cross_signing_identity,
|
||||||
|
@ -610,7 +758,7 @@ impl<S: Clone> RequestState<S> {
|
||||||
store: self.store,
|
store: self.store,
|
||||||
flow_id: self.flow_id,
|
flow_id: self.flow_id,
|
||||||
other_user_id: self.other_user_id,
|
other_user_id: self.other_user_id,
|
||||||
state: Cancelled::new(cancel_code.clone()),
|
state: Cancelled::new(cancelled_by_us, cancel_code.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,30 +771,21 @@ impl RequestState<Created> {
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
other_user_id: &UserId,
|
other_user_id: &UserId,
|
||||||
flow_id: &FlowId,
|
flow_id: &FlowId,
|
||||||
|
methods: Option<Vec<VerificationMethod>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let our_methods = methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
account,
|
account,
|
||||||
other_user_id: other_user_id.to_owned(),
|
other_user_id: other_user_id.to_owned(),
|
||||||
private_cross_signing_identity: private_identity,
|
private_cross_signing_identity: private_identity,
|
||||||
state: Created { our_methods: SUPPORTED_METHODS.to_vec() },
|
state: Created { our_methods },
|
||||||
verification_cache: cache,
|
verification_cache: cache,
|
||||||
store,
|
store,
|
||||||
flow_id: flow_id.to_owned().into(),
|
flow_id: flow_id.to_owned().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_passive(self, content: &ReadyContent) -> RequestState<Passive> {
|
|
||||||
RequestState {
|
|
||||||
account: self.account,
|
|
||||||
flow_id: self.flow_id,
|
|
||||||
verification_cache: self.verification_cache,
|
|
||||||
private_cross_signing_identity: self.private_cross_signing_identity,
|
|
||||||
store: self.store,
|
|
||||||
other_user_id: self.other_user_id,
|
|
||||||
state: Passive { other_device_id: content.from_device().to_owned() },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_ready(self, _sender: &UserId, content: &ReadyContent) -> RequestState<Ready> {
|
fn into_ready(self, _sender: &UserId, content: &ReadyContent) -> RequestState<Ready> {
|
||||||
// TODO check the flow id, and that the methods match what we suggested.
|
// TODO check the flow id, and that the methods match what we suggested.
|
||||||
RequestState {
|
RequestState {
|
||||||
|
@ -705,6 +844,18 @@ impl RequestState<Requested> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_passive(self, content: &ReadyContent) -> RequestState<Passive> {
|
||||||
|
RequestState {
|
||||||
|
account: self.account,
|
||||||
|
flow_id: self.flow_id,
|
||||||
|
verification_cache: self.verification_cache,
|
||||||
|
private_cross_signing_identity: self.private_cross_signing_identity,
|
||||||
|
store: self.store,
|
||||||
|
other_user_id: self.other_user_id,
|
||||||
|
state: Passive { other_device_id: content.from_device().to_owned() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn accept(self, methods: Vec<VerificationMethod>) -> (RequestState<Ready>, OutgoingContent) {
|
fn accept(self, methods: Vec<VerificationMethod>) -> (RequestState<Ready>, OutgoingContent) {
|
||||||
let state = RequestState {
|
let state = RequestState {
|
||||||
account: self.account.clone(),
|
account: self.account.clone(),
|
||||||
|
@ -761,7 +912,9 @@ impl RequestState<Ready> {
|
||||||
&self,
|
&self,
|
||||||
content: &StartContent<'a>,
|
content: &StartContent<'a>,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: RequestHandle,
|
||||||
) -> Result<Sas, OutgoingContent> {
|
) -> Result<Sas, OutgoingContent> {
|
||||||
Sas::from_start_event(
|
Sas::from_start_event(
|
||||||
(&*self.flow_id).to_owned(),
|
(&*self.flow_id).to_owned(),
|
||||||
|
@ -771,13 +924,16 @@ impl RequestState<Ready> {
|
||||||
self.private_cross_signing_identity.clone(),
|
self.private_cross_signing_identity.clone(),
|
||||||
other_device,
|
other_device,
|
||||||
other_identity,
|
other_identity,
|
||||||
true,
|
Some(request_handle),
|
||||||
|
we_started,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn generate_qr_code(&self) -> Result<Option<QrVerification>, CryptoStoreError> {
|
async fn generate_qr_code(
|
||||||
// TODO return an error explaining why we can't generate a QR code?
|
&self,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: RequestHandle,
|
||||||
|
) -> Result<Option<QrVerification>, CryptoStoreError> {
|
||||||
// If we didn't state that we support showing QR codes or if the other
|
// If we didn't state that we support showing QR codes or if the other
|
||||||
// side doesn't support scanning QR codes bail early.
|
// side doesn't support scanning QR codes bail early.
|
||||||
if !self.state.our_methods.contains(&VerificationMethod::QrCodeShowV1)
|
if !self.state.our_methods.contains(&VerificationMethod::QrCodeShowV1)
|
||||||
|
@ -809,42 +965,90 @@ impl RequestState<Ready> {
|
||||||
|
|
||||||
let verification = if let Some(identity) = &identites.identity_being_verified {
|
let verification = if let Some(identity) = &identites.identity_being_verified {
|
||||||
match &identity {
|
match &identity {
|
||||||
UserIdentities::Own(i) => {
|
ReadOnlyUserIdentities::Own(i) => {
|
||||||
|
if let Some(master_key) = i.master_key().get_first_key() {
|
||||||
if identites.can_sign_devices().await {
|
if identites.can_sign_devices().await {
|
||||||
|
if let Some(device_key) =
|
||||||
|
identites.other_device().get_key(DeviceKeyAlgorithm::Ed25519)
|
||||||
|
{
|
||||||
Some(QrVerification::new_self(
|
Some(QrVerification::new_self(
|
||||||
self.store.clone(),
|
self.store.clone(),
|
||||||
self.flow_id.as_ref().to_owned(),
|
self.flow_id.as_ref().to_owned(),
|
||||||
i.master_key().get_first_key().unwrap().to_owned(),
|
master_key.to_owned(),
|
||||||
identites
|
device_key.to_owned(),
|
||||||
.other_device()
|
|
||||||
.get_key(DeviceKeyAlgorithm::Ed25519)
|
|
||||||
.unwrap()
|
|
||||||
.to_owned(),
|
|
||||||
identites,
|
identites,
|
||||||
|
we_started,
|
||||||
|
Some(request_handle),
|
||||||
))
|
))
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
user_id = self.other_user_id.as_str(),
|
||||||
|
device_id = self.state.other_device_id.as_str(),
|
||||||
|
"Can't create a QR code, the other device \
|
||||||
|
doesn't have a valid device key"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(QrVerification::new_self_no_master(
|
Some(QrVerification::new_self_no_master(
|
||||||
self.account.clone(),
|
self.account.clone(),
|
||||||
self.store.clone(),
|
self.store.clone(),
|
||||||
self.flow_id.as_ref().to_owned(),
|
self.flow_id.as_ref().to_owned(),
|
||||||
i.master_key().get_first_key().unwrap().to_owned(),
|
master_key.to_owned(),
|
||||||
identites,
|
identites,
|
||||||
|
we_started,
|
||||||
|
Some(request_handle),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
user_id = self.other_user_id.as_str(),
|
||||||
|
device_id = self.state.other_device_id.as_str(),
|
||||||
|
"Can't create a QR code, our cross signing identity \
|
||||||
|
doesn't contain a valid master key"
|
||||||
|
);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
UserIdentities::Other(i) => Some(QrVerification::new_cross(
|
}
|
||||||
self.store.clone(),
|
ReadOnlyUserIdentities::Other(i) => {
|
||||||
self.flow_id.as_ref().to_owned(),
|
if let Some(other_master) = i.master_key().get_first_key() {
|
||||||
self.private_cross_signing_identity
|
// TODO we can get the master key from the public
|
||||||
|
// identity if we don't have the private one and we
|
||||||
|
// trust the public one.
|
||||||
|
if let Some(own_master) = self
|
||||||
|
.private_cross_signing_identity
|
||||||
.master_public_key()
|
.master_public_key()
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.and_then(|m| m.get_first_key().map(|m| m.to_owned()))
|
||||||
.get_first_key()
|
{
|
||||||
.unwrap()
|
Some(QrVerification::new_cross(
|
||||||
.to_owned(),
|
self.store.clone(),
|
||||||
i.master_key().get_first_key().unwrap().to_owned(),
|
self.flow_id.as_ref().to_owned(),
|
||||||
|
own_master,
|
||||||
|
other_master.to_owned(),
|
||||||
identites,
|
identites,
|
||||||
)),
|
we_started,
|
||||||
|
Some(request_handle),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
user_id = self.other_user_id.as_str(),
|
||||||
|
device_id = self.state.other_device_id.as_str(),
|
||||||
|
"Can't create a QR code, we don't trust our own \
|
||||||
|
master key"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
user_id = self.other_user_id.as_str(),
|
||||||
|
device_id = self.state.other_device_id.as_str(),
|
||||||
|
"Can't create a QR code, the user's identity \
|
||||||
|
doesn't have a valid master key"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -868,6 +1072,8 @@ impl RequestState<Ready> {
|
||||||
&self,
|
&self,
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &StartContent<'_>,
|
content: &StartContent<'_>,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: RequestHandle,
|
||||||
) -> Result<(), CryptoStoreError> {
|
) -> Result<(), CryptoStoreError> {
|
||||||
info!(
|
info!(
|
||||||
sender = sender.as_str(),
|
sender = sender.as_str(),
|
||||||
|
@ -890,7 +1096,14 @@ impl RequestState<Ready> {
|
||||||
let identity = self.store.get_user_identity(sender).await?;
|
let identity = self.store.get_user_identity(sender).await?;
|
||||||
|
|
||||||
match content.method() {
|
match content.method() {
|
||||||
StartMethod::SasV1(_) => match self.to_started_sas(content, device.clone(), identity) {
|
StartMethod::SasV1(_) => {
|
||||||
|
match self.to_started_sas(
|
||||||
|
content,
|
||||||
|
device.clone(),
|
||||||
|
identity,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
|
) {
|
||||||
// TODO check if there is already a SAS verification, i.e. we
|
// TODO check if there is already a SAS verification, i.e. we
|
||||||
// already started one before the other side tried to do the
|
// already started one before the other side tried to do the
|
||||||
// same; ignore it if we did and we're the lexicographically
|
// same; ignore it if we did and we're the lexicographically
|
||||||
|
@ -912,7 +1125,8 @@ impl RequestState<Ready> {
|
||||||
c,
|
c,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
StartMethod::ReciprocateV1(_) => {
|
StartMethod::ReciprocateV1(_) => {
|
||||||
if let Some(qr_verification) =
|
if let Some(qr_verification) =
|
||||||
self.verification_cache.get_qr(sender, content.flow_id())
|
self.verification_cache.get_qr(sender, content.flow_id())
|
||||||
|
@ -941,6 +1155,8 @@ impl RequestState<Ready> {
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
private_identity: PrivateCrossSigningIdentity,
|
private_identity: PrivateCrossSigningIdentity,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: RequestHandle,
|
||||||
) -> Result<Option<(Sas, OutgoingContent)>, CryptoStoreError> {
|
) -> Result<Option<(Sas, OutgoingContent)>, CryptoStoreError> {
|
||||||
if !self.state.their_methods.contains(&VerificationMethod::SasV1) {
|
if !self.state.their_methods.contains(&VerificationMethod::SasV1) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -972,6 +1188,8 @@ impl RequestState<Ready> {
|
||||||
store,
|
store,
|
||||||
other_identity,
|
other_identity,
|
||||||
Some(t.to_owned()),
|
Some(t.to_owned()),
|
||||||
|
we_started,
|
||||||
|
Some(request_handle),
|
||||||
);
|
);
|
||||||
(sas, content)
|
(sas, content)
|
||||||
}
|
}
|
||||||
|
@ -984,6 +1202,8 @@ impl RequestState<Ready> {
|
||||||
device,
|
device,
|
||||||
store,
|
store,
|
||||||
other_identity,
|
other_identity,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
);
|
);
|
||||||
(sas, content)
|
(sas, content)
|
||||||
}
|
}
|
||||||
|
@ -1002,7 +1222,7 @@ struct Done {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::convert::TryFrom;
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
use matrix_sdk_test::async_test;
|
use matrix_sdk_test::async_test;
|
||||||
use ruma::{event_id, room_id, DeviceIdBox, UserId};
|
use ruma::{event_id, room_id, DeviceIdBox, UserId};
|
||||||
|
@ -1013,7 +1233,7 @@ mod test {
|
||||||
store::{Changes, CryptoStore, MemoryStore},
|
store::{Changes, CryptoStore, MemoryStore},
|
||||||
verification::{
|
verification::{
|
||||||
cache::VerificationCache,
|
cache::VerificationCache,
|
||||||
event_enums::{OutgoingContent, ReadyContent, StartContent},
|
event_enums::{OutgoingContent, ReadyContent, RequestContent, StartContent},
|
||||||
FlowId,
|
FlowId,
|
||||||
},
|
},
|
||||||
ReadOnlyDevice,
|
ReadOnlyDevice,
|
||||||
|
@ -1048,20 +1268,22 @@ mod test {
|
||||||
let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
|
let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
|
||||||
let bob_identity = PrivateCrossSigningIdentity::empty(alice_id());
|
let bob_identity = PrivateCrossSigningIdentity::empty(alice_id());
|
||||||
|
|
||||||
let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id());
|
let content =
|
||||||
|
VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id(), None);
|
||||||
|
|
||||||
|
let flow_id = FlowId::InRoom(room_id, event_id);
|
||||||
|
|
||||||
let bob_request = VerificationRequest::new(
|
let bob_request = VerificationRequest::new(
|
||||||
VerificationCache::new(),
|
VerificationCache::new(),
|
||||||
bob,
|
bob,
|
||||||
bob_identity,
|
bob_identity,
|
||||||
bob_store.into(),
|
bob_store.into(),
|
||||||
&room_id,
|
flow_id.clone(),
|
||||||
&event_id,
|
|
||||||
&alice_id(),
|
&alice_id(),
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let flow_id = FlowId::from((room_id, event_id));
|
|
||||||
|
|
||||||
let alice_request = VerificationRequest::from_request(
|
let alice_request = VerificationRequest::from_request(
|
||||||
VerificationCache::new(),
|
VerificationCache::new(),
|
||||||
alice,
|
alice,
|
||||||
|
@ -1072,7 +1294,7 @@ mod test {
|
||||||
&(&content).into(),
|
&(&content).into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let content: OutgoingContent = alice_request.accept().unwrap().into();
|
let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap();
|
||||||
let content = ReadyContent::try_from(&content).unwrap();
|
let content = ReadyContent::try_from(&content).unwrap();
|
||||||
|
|
||||||
bob_request.receive_ready(&alice_id(), &content);
|
bob_request.receive_ready(&alice_id(), &content);
|
||||||
|
@ -1105,20 +1327,21 @@ mod test {
|
||||||
changes.devices.new.push(alice_device.clone());
|
changes.devices.new.push(alice_device.clone());
|
||||||
bob_store.save_changes(changes).await.unwrap();
|
bob_store.save_changes(changes).await.unwrap();
|
||||||
|
|
||||||
let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id());
|
let content =
|
||||||
|
VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id(), None);
|
||||||
|
let flow_id = FlowId::from((room_id, event_id));
|
||||||
|
|
||||||
let bob_request = VerificationRequest::new(
|
let bob_request = VerificationRequest::new(
|
||||||
VerificationCache::new(),
|
VerificationCache::new(),
|
||||||
bob,
|
bob,
|
||||||
bob_identity,
|
bob_identity,
|
||||||
bob_store.into(),
|
bob_store.into(),
|
||||||
&room_id,
|
flow_id.clone(),
|
||||||
&event_id,
|
|
||||||
&alice_id(),
|
&alice_id(),
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let flow_id = FlowId::from((room_id, event_id));
|
|
||||||
|
|
||||||
let alice_request = VerificationRequest::from_request(
|
let alice_request = VerificationRequest::from_request(
|
||||||
VerificationCache::new(),
|
VerificationCache::new(),
|
||||||
alice,
|
alice,
|
||||||
|
@ -1129,7 +1352,7 @@ mod test {
|
||||||
&(&content).into(),
|
&(&content).into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let content: OutgoingContent = alice_request.accept().unwrap().into();
|
let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap();
|
||||||
let content = ReadyContent::try_from(&content).unwrap();
|
let content = ReadyContent::try_from(&content).unwrap();
|
||||||
|
|
||||||
bob_request.receive_ready(&alice_id(), &content);
|
bob_request.receive_ready(&alice_id(), &content);
|
||||||
|
@ -1139,7 +1362,7 @@ mod test {
|
||||||
|
|
||||||
let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
|
let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
|
||||||
|
|
||||||
let content: OutgoingContent = request.into();
|
let content: OutgoingContent = request.try_into().unwrap();
|
||||||
let content = StartContent::try_from(&content).unwrap();
|
let content = StartContent::try_from(&content).unwrap();
|
||||||
let flow_id = content.flow_id().to_owned();
|
let flow_id = content.flow_id().to_owned();
|
||||||
alice_request.receive_start(bob_device.user_id(), &content).await.unwrap();
|
alice_request.receive_start(bob_device.user_id(), &content).await.unwrap();
|
||||||
|
@ -1171,15 +1394,22 @@ mod test {
|
||||||
changes.devices.new.push(alice_device.clone());
|
changes.devices.new.push(alice_device.clone());
|
||||||
bob_store.save_changes(changes).await.unwrap();
|
bob_store.save_changes(changes).await.unwrap();
|
||||||
|
|
||||||
let bob_request = VerificationRequest::new_to_device(
|
let flow_id = FlowId::from("TEST_FLOW_ID".to_owned());
|
||||||
|
|
||||||
|
let bob_request = VerificationRequest::new(
|
||||||
VerificationCache::new(),
|
VerificationCache::new(),
|
||||||
bob,
|
bob,
|
||||||
bob_identity,
|
bob_identity,
|
||||||
bob_store.into(),
|
bob_store.into(),
|
||||||
|
flow_id,
|
||||||
&alice_id(),
|
&alice_id(),
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let content = bob_request.request_to_device();
|
let request = bob_request.request_to_device();
|
||||||
|
let content: OutgoingContent = request.try_into().unwrap();
|
||||||
|
let content = RequestContent::try_from(&content).unwrap();
|
||||||
let flow_id = bob_request.flow_id().to_owned();
|
let flow_id = bob_request.flow_id().to_owned();
|
||||||
|
|
||||||
let alice_request = VerificationRequest::from_request(
|
let alice_request = VerificationRequest::from_request(
|
||||||
|
@ -1189,10 +1419,10 @@ mod test {
|
||||||
alice_store.into(),
|
alice_store.into(),
|
||||||
&bob_id(),
|
&bob_id(),
|
||||||
flow_id,
|
flow_id,
|
||||||
&(&content).into(),
|
&content,
|
||||||
);
|
);
|
||||||
|
|
||||||
let content: OutgoingContent = alice_request.accept().unwrap().into();
|
let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap();
|
||||||
let content = ReadyContent::try_from(&content).unwrap();
|
let content = ReadyContent::try_from(&content).unwrap();
|
||||||
|
|
||||||
bob_request.receive_ready(&alice_id(), &content);
|
bob_request.receive_ready(&alice_id(), &content);
|
||||||
|
@ -1202,7 +1432,7 @@ mod test {
|
||||||
|
|
||||||
let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
|
let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
|
||||||
|
|
||||||
let content: OutgoingContent = request.into();
|
let content: OutgoingContent = request.try_into().unwrap();
|
||||||
let content = StartContent::try_from(&content).unwrap();
|
let content = StartContent::try_from(&content).unwrap();
|
||||||
let flow_id = content.flow_id().to_owned();
|
let flow_id = content.flow_id().to_owned();
|
||||||
alice_request.receive_start(bob_device.user_id(), &content).await.unwrap();
|
alice_request.receive_start(bob_device.user_id(), &content).await.unwrap();
|
||||||
|
|
|
@ -31,7 +31,7 @@ use tracing::{trace, warn};
|
||||||
|
|
||||||
use super::{FlowId, OutgoingContent};
|
use super::{FlowId, OutgoingContent};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
utilities::encode,
|
utilities::encode,
|
||||||
verification::event_enums::{MacContent, StartContent},
|
verification::event_enums::{MacContent, StartContent},
|
||||||
ReadOnlyAccount,
|
ReadOnlyAccount,
|
||||||
|
@ -41,7 +41,7 @@ use crate::{
|
||||||
pub struct SasIds {
|
pub struct SasIds {
|
||||||
pub account: ReadOnlyAccount,
|
pub account: ReadOnlyAccount,
|
||||||
pub other_device: ReadOnlyDevice,
|
pub other_device: ReadOnlyDevice,
|
||||||
pub other_identity: Option<UserIdentities>,
|
pub other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the commitment for a accept event from the public key and the
|
/// Calculate the commitment for a accept event from the public key and the
|
||||||
|
@ -182,7 +182,7 @@ pub fn receive_mac_event(
|
||||||
flow_id: &str,
|
flow_id: &str,
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &MacContent,
|
content: &MacContent,
|
||||||
) -> Result<(Vec<ReadOnlyDevice>, Vec<UserIdentities>), CancelCode> {
|
) -> Result<(Vec<ReadOnlyDevice>, Vec<ReadOnlyUserIdentities>), CancelCode> {
|
||||||
let mut verified_devices = Vec::new();
|
let mut verified_devices = Vec::new();
|
||||||
let mut verified_identities = Vec::new();
|
let mut verified_identities = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,12 @@ use ruma::{
|
||||||
use super::{
|
use super::{
|
||||||
sas_state::{
|
sas_state::{
|
||||||
Accepted, Confirmed, Created, KeyReceived, MacReceived, SasState, Started, WaitingForDone,
|
Accepted, Confirmed, Created, KeyReceived, MacReceived, SasState, Started, WaitingForDone,
|
||||||
|
WeAccepted,
|
||||||
},
|
},
|
||||||
FlowId,
|
FlowId,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
verification::{
|
verification::{
|
||||||
event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent},
|
event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent},
|
||||||
Cancelled, Done,
|
Cancelled, Done,
|
||||||
|
@ -41,6 +42,7 @@ pub enum InnerSas {
|
||||||
Created(SasState<Created>),
|
Created(SasState<Created>),
|
||||||
Started(SasState<Started>),
|
Started(SasState<Started>),
|
||||||
Accepted(SasState<Accepted>),
|
Accepted(SasState<Accepted>),
|
||||||
|
WeAccepted(SasState<WeAccepted>),
|
||||||
KeyReceived(SasState<KeyReceived>),
|
KeyReceived(SasState<KeyReceived>),
|
||||||
Confirmed(SasState<Confirmed>),
|
Confirmed(SasState<Confirmed>),
|
||||||
MacReceived(SasState<MacReceived>),
|
MacReceived(SasState<MacReceived>),
|
||||||
|
@ -53,7 +55,7 @@ impl InnerSas {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
transaction_id: Option<String>,
|
transaction_id: Option<String>,
|
||||||
) -> (InnerSas, OutgoingContent) {
|
) -> (InnerSas, OutgoingContent) {
|
||||||
let sas = SasState::<Created>::new(account, other_device, other_identity, transaction_id);
|
let sas = SasState::<Created>::new(account, other_device, other_identity, transaction_id);
|
||||||
|
@ -65,6 +67,7 @@ impl InnerSas {
|
||||||
match self {
|
match self {
|
||||||
InnerSas::Created(s) => s.started_from_request,
|
InnerSas::Created(s) => s.started_from_request,
|
||||||
InnerSas::Started(s) => s.started_from_request,
|
InnerSas::Started(s) => s.started_from_request,
|
||||||
|
InnerSas::WeAccepted(s) => s.started_from_request,
|
||||||
InnerSas::Accepted(s) => s.started_from_request,
|
InnerSas::Accepted(s) => s.started_from_request,
|
||||||
InnerSas::KeyReceived(s) => s.started_from_request,
|
InnerSas::KeyReceived(s) => s.started_from_request,
|
||||||
InnerSas::Confirmed(s) => s.started_from_request,
|
InnerSas::Confirmed(s) => s.started_from_request,
|
||||||
|
@ -75,6 +78,19 @@ impl InnerSas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_been_accepted(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
InnerSas::Created(_) | InnerSas::Started(_) | InnerSas::Cancelled(_) => false,
|
||||||
|
InnerSas::Accepted(_)
|
||||||
|
| InnerSas::WeAccepted(_)
|
||||||
|
| InnerSas::KeyReceived(_)
|
||||||
|
| InnerSas::Confirmed(_)
|
||||||
|
| InnerSas::MacReceived(_)
|
||||||
|
| InnerSas::WaitingForDone(_)
|
||||||
|
| InnerSas::Done(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn supports_emoji(&self) -> bool {
|
pub fn supports_emoji(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
InnerSas::Created(_) => false,
|
InnerSas::Created(_) => false,
|
||||||
|
@ -83,6 +99,11 @@ impl InnerSas {
|
||||||
.accepted_protocols
|
.accepted_protocols
|
||||||
.short_auth_string
|
.short_auth_string
|
||||||
.contains(&ShortAuthenticationString::Emoji),
|
.contains(&ShortAuthenticationString::Emoji),
|
||||||
|
InnerSas::WeAccepted(s) => s
|
||||||
|
.state
|
||||||
|
.accepted_protocols
|
||||||
|
.short_auth_string
|
||||||
|
.contains(&ShortAuthenticationString::Emoji),
|
||||||
InnerSas::Accepted(s) => s
|
InnerSas::Accepted(s) => s
|
||||||
.state
|
.state
|
||||||
.accepted_protocols
|
.accepted_protocols
|
||||||
|
@ -110,7 +131,7 @@ impl InnerSas {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
) -> (InnerSas, OutgoingContent) {
|
) -> (InnerSas, OutgoingContent) {
|
||||||
let sas = SasState::<Created>::new_in_room(
|
let sas = SasState::<Created>::new_in_room(
|
||||||
room_id,
|
room_id,
|
||||||
|
@ -128,7 +149,7 @@ impl InnerSas {
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
content: &StartContent,
|
content: &StartContent,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
started_from_request: bool,
|
started_from_request: bool,
|
||||||
) -> Result<InnerSas, OutgoingContent> {
|
) -> Result<InnerSas, OutgoingContent> {
|
||||||
match SasState::<Started>::from_start_event(
|
match SasState::<Started>::from_start_event(
|
||||||
|
@ -144,9 +165,14 @@ impl InnerSas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accept(&self) -> Option<OwnedAcceptContent> {
|
pub fn accept(
|
||||||
|
self,
|
||||||
|
methods: Vec<ShortAuthenticationString>,
|
||||||
|
) -> Option<(InnerSas, OwnedAcceptContent)> {
|
||||||
if let InnerSas::Started(s) = self {
|
if let InnerSas::Started(s) = self {
|
||||||
Some(s.as_content())
|
let sas = s.into_accepted(methods);
|
||||||
|
let content = sas.as_content();
|
||||||
|
Some((InnerSas::WeAccepted(sas), content))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -165,17 +191,25 @@ impl InnerSas {
|
||||||
InnerSas::MacReceived(s) => s.set_creation_time(time),
|
InnerSas::MacReceived(s) => s.set_creation_time(time),
|
||||||
InnerSas::Done(s) => s.set_creation_time(time),
|
InnerSas::Done(s) => s.set_creation_time(time),
|
||||||
InnerSas::WaitingForDone(s) => s.set_creation_time(time),
|
InnerSas::WaitingForDone(s) => s.set_creation_time(time),
|
||||||
|
InnerSas::WeAccepted(s) => s.set_creation_time(time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel(self, code: CancelCode) -> (InnerSas, Option<OutgoingContent>) {
|
pub fn cancel(
|
||||||
|
self,
|
||||||
|
cancelled_by_us: bool,
|
||||||
|
code: CancelCode,
|
||||||
|
) -> (InnerSas, Option<OutgoingContent>) {
|
||||||
let sas = match self {
|
let sas = match self {
|
||||||
InnerSas::Created(s) => s.cancel(code),
|
InnerSas::Created(s) => s.cancel(cancelled_by_us, code),
|
||||||
InnerSas::Started(s) => s.cancel(code),
|
InnerSas::Started(s) => s.cancel(cancelled_by_us, code),
|
||||||
InnerSas::Accepted(s) => s.cancel(code),
|
InnerSas::Accepted(s) => s.cancel(cancelled_by_us, code),
|
||||||
InnerSas::KeyReceived(s) => s.cancel(code),
|
InnerSas::WeAccepted(s) => s.cancel(cancelled_by_us, code),
|
||||||
InnerSas::MacReceived(s) => s.cancel(code),
|
InnerSas::KeyReceived(s) => s.cancel(cancelled_by_us, code),
|
||||||
_ => return (self, None),
|
InnerSas::MacReceived(s) => s.cancel(cancelled_by_us, code),
|
||||||
|
InnerSas::Confirmed(s) => s.cancel(cancelled_by_us, code),
|
||||||
|
InnerSas::WaitingForDone(s) => s.cancel(cancelled_by_us, code),
|
||||||
|
InnerSas::Done(_) | InnerSas::Cancelled(_) => return (self, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let content = sas.as_content();
|
let content = sas.as_content();
|
||||||
|
@ -230,7 +264,7 @@ impl InnerSas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnyVerificationContent::Cancel(c) => {
|
AnyVerificationContent::Cancel(c) => {
|
||||||
let (sas, _) = self.cancel(c.cancel_code().to_owned());
|
let (sas, _) = self.cancel(false, c.cancel_code().to_owned());
|
||||||
(sas, None)
|
(sas, None)
|
||||||
}
|
}
|
||||||
AnyVerificationContent::Key(c) => match self {
|
AnyVerificationContent::Key(c) => match self {
|
||||||
|
@ -241,7 +275,7 @@ impl InnerSas {
|
||||||
(InnerSas::Cancelled(s), Some(content))
|
(InnerSas::Cancelled(s), Some(content))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InnerSas::Started(s) => match s.into_key_received(sender, c) {
|
InnerSas::WeAccepted(s) => match s.into_key_received(sender, c) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let content = s.as_content();
|
let content = s.as_content();
|
||||||
(InnerSas::KeyReceived(s), Some(content))
|
(InnerSas::KeyReceived(s), Some(content))
|
||||||
|
@ -312,6 +346,10 @@ impl InnerSas {
|
||||||
matches!(self, InnerSas::Cancelled(_))
|
matches!(self, InnerSas::Cancelled(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn have_we_confirmed(&self) -> bool {
|
||||||
|
matches!(self, InnerSas::Confirmed(_) | InnerSas::WaitingForDone(_) | InnerSas::Done(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn timed_out(&self) -> bool {
|
pub fn timed_out(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
InnerSas::Created(s) => s.timed_out(),
|
InnerSas::Created(s) => s.timed_out(),
|
||||||
|
@ -323,6 +361,7 @@ impl InnerSas {
|
||||||
InnerSas::MacReceived(s) => s.timed_out(),
|
InnerSas::MacReceived(s) => s.timed_out(),
|
||||||
InnerSas::WaitingForDone(s) => s.timed_out(),
|
InnerSas::WaitingForDone(s) => s.timed_out(),
|
||||||
InnerSas::Done(s) => s.timed_out(),
|
InnerSas::Done(s) => s.timed_out(),
|
||||||
|
InnerSas::WeAccepted(s) => s.timed_out(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,6 +376,7 @@ impl InnerSas {
|
||||||
InnerSas::MacReceived(s) => s.verification_flow_id.clone(),
|
InnerSas::MacReceived(s) => s.verification_flow_id.clone(),
|
||||||
InnerSas::WaitingForDone(s) => s.verification_flow_id.clone(),
|
InnerSas::WaitingForDone(s) => s.verification_flow_id.clone(),
|
||||||
InnerSas::Done(s) => s.verification_flow_id.clone(),
|
InnerSas::Done(s) => s.verification_flow_id.clone(),
|
||||||
|
InnerSas::WeAccepted(s) => s.verification_flow_id.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +412,7 @@ impl InnerSas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verified_identities(&self) -> Option<Arc<[UserIdentities]>> {
|
pub fn verified_identities(&self) -> Option<Arc<[ReadOnlyUserIdentities]>> {
|
||||||
if let InnerSas::Done(s) = self {
|
if let InnerSas::Done(s) = self {
|
||||||
Some(s.verified_identities())
|
Some(s.verified_identities())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -25,11 +25,7 @@ use matrix_sdk_common::uuid::Uuid;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest,
|
api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest,
|
||||||
events::{
|
events::{
|
||||||
key::verification::{
|
key::verification::{cancel::CancelCode, ShortAuthenticationString},
|
||||||
accept::{AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent},
|
|
||||||
cancel::CancelCode,
|
|
||||||
ShortAuthenticationString,
|
|
||||||
},
|
|
||||||
AnyMessageEventContent, AnyToDeviceEventContent,
|
AnyMessageEventContent, AnyToDeviceEventContent,
|
||||||
},
|
},
|
||||||
DeviceId, EventId, RoomId, UserId,
|
DeviceId, EventId, RoomId, UserId,
|
||||||
|
@ -38,10 +34,11 @@ use tracing::trace;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent},
|
event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent},
|
||||||
FlowId, IdentitiesBeingVerified, VerificationResult,
|
requests::RequestHandle,
|
||||||
|
CancelInfo, FlowId, IdentitiesBeingVerified, VerificationResult,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
olm::PrivateCrossSigningIdentity,
|
olm::PrivateCrossSigningIdentity,
|
||||||
requests::{OutgoingVerificationRequest, RoomMessageRequest},
|
requests::{OutgoingVerificationRequest, RoomMessageRequest},
|
||||||
store::{CryptoStore, CryptoStoreError},
|
store::{CryptoStore, CryptoStoreError},
|
||||||
|
@ -55,6 +52,8 @@ pub struct Sas {
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
identities_being_verified: IdentitiesBeingVerified,
|
identities_being_verified: IdentitiesBeingVerified,
|
||||||
flow_id: Arc<FlowId>,
|
flow_id: Arc<FlowId>,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sas {
|
impl Sas {
|
||||||
|
@ -88,6 +87,15 @@ impl Sas {
|
||||||
&self.flow_id
|
&self.flow_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the room id if the verification is happening inside a room.
|
||||||
|
pub fn room_id(&self) -> Option<&RoomId> {
|
||||||
|
if let FlowId::InRoom(r, _) = self.flow_id() {
|
||||||
|
Some(r)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Does this verification flow support displaying emoji for the short
|
/// Does this verification flow support displaying emoji for the short
|
||||||
/// authentication string.
|
/// authentication string.
|
||||||
pub fn supports_emoji(&self) -> bool {
|
pub fn supports_emoji(&self) -> bool {
|
||||||
|
@ -104,19 +112,47 @@ impl Sas {
|
||||||
self.identities_being_verified.is_self_verification()
|
self.identities_being_verified.is_self_verification()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Have we confirmed that the short auth string matches.
|
||||||
|
pub fn have_we_confirmed(&self) -> bool {
|
||||||
|
self.inner.lock().unwrap().have_we_confirmed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Has the verification been accepted by both parties.
|
||||||
|
pub fn has_been_accepted(&self) -> bool {
|
||||||
|
self.inner.lock().unwrap().has_been_accepted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get info about the cancellation if the verification flow has been
|
||||||
|
/// cancelled.
|
||||||
|
pub fn cancel_info(&self) -> Option<CancelInfo> {
|
||||||
|
if let InnerSas::Cancelled(c) = &*self.inner.lock().unwrap() {
|
||||||
|
Some(c.state.as_ref().clone().into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Did we initiate the verification flow.
|
||||||
|
pub fn we_started(&self) -> bool {
|
||||||
|
self.we_started
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn set_creation_time(&self, time: Instant) {
|
pub(crate) fn set_creation_time(&self, time: Instant) {
|
||||||
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,
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
private_identity: PrivateCrossSigningIdentity,
|
private_identity: PrivateCrossSigningIdentity,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> Sas {
|
) -> Sas {
|
||||||
let flow_id = inner_sas.verification_flow_id();
|
let flow_id = inner_sas.verification_flow_id();
|
||||||
|
|
||||||
|
@ -132,6 +168,8 @@ impl Sas {
|
||||||
account,
|
account,
|
||||||
identities_being_verified: identities,
|
identities_being_verified: identities,
|
||||||
flow_id,
|
flow_id,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,13 +183,16 @@ 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(
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
private_identity: PrivateCrossSigningIdentity,
|
private_identity: PrivateCrossSigningIdentity,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
transaction_id: Option<String>,
|
transaction_id: Option<String>,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: Option<RequestHandle>,
|
||||||
) -> (Sas, OutgoingContent) {
|
) -> (Sas, OutgoingContent) {
|
||||||
let (inner, content) = InnerSas::start(
|
let (inner, content) = InnerSas::start(
|
||||||
account.clone(),
|
account.clone(),
|
||||||
|
@ -168,6 +209,8 @@ impl Sas {
|
||||||
other_device,
|
other_device,
|
||||||
store,
|
store,
|
||||||
other_identity,
|
other_identity,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
),
|
),
|
||||||
content,
|
content,
|
||||||
)
|
)
|
||||||
|
@ -183,6 +226,7 @@ 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_in_room(
|
pub(crate) fn start_in_room(
|
||||||
flow_id: EventId,
|
flow_id: EventId,
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
|
@ -190,7 +234,9 @@ impl Sas {
|
||||||
private_identity: PrivateCrossSigningIdentity,
|
private_identity: PrivateCrossSigningIdentity,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
store: Arc<dyn CryptoStore>,
|
store: Arc<dyn CryptoStore>,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
|
we_started: bool,
|
||||||
|
request_handle: RequestHandle,
|
||||||
) -> (Sas, OutgoingContent) {
|
) -> (Sas, OutgoingContent) {
|
||||||
let (inner, content) = InnerSas::start_in_room(
|
let (inner, content) = InnerSas::start_in_room(
|
||||||
flow_id,
|
flow_id,
|
||||||
|
@ -208,6 +254,8 @@ impl Sas {
|
||||||
other_device,
|
other_device,
|
||||||
store,
|
store,
|
||||||
other_identity,
|
other_identity,
|
||||||
|
we_started,
|
||||||
|
Some(request_handle),
|
||||||
),
|
),
|
||||||
content,
|
content,
|
||||||
)
|
)
|
||||||
|
@ -231,8 +279,9 @@ impl Sas {
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
private_identity: PrivateCrossSigningIdentity,
|
private_identity: PrivateCrossSigningIdentity,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
started_from_request: bool,
|
request_handle: Option<RequestHandle>,
|
||||||
|
we_started: bool,
|
||||||
) -> Result<Sas, OutgoingContent> {
|
) -> Result<Sas, OutgoingContent> {
|
||||||
let inner = InnerSas::from_start_event(
|
let inner = InnerSas::from_start_event(
|
||||||
account.clone(),
|
account.clone(),
|
||||||
|
@ -240,7 +289,7 @@ impl Sas {
|
||||||
flow_id,
|
flow_id,
|
||||||
content,
|
content,
|
||||||
other_identity.clone(),
|
other_identity.clone(),
|
||||||
started_from_request,
|
request_handle.is_some(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Self::start_helper(
|
Ok(Self::start_helper(
|
||||||
|
@ -250,6 +299,8 @@ impl Sas {
|
||||||
other_device,
|
other_device,
|
||||||
store,
|
store,
|
||||||
other_identity,
|
other_identity,
|
||||||
|
we_started,
|
||||||
|
request_handle,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +322,14 @@ impl Sas {
|
||||||
&self,
|
&self,
|
||||||
settings: AcceptSettings,
|
settings: AcceptSettings,
|
||||||
) -> Option<OutgoingVerificationRequest> {
|
) -> Option<OutgoingVerificationRequest> {
|
||||||
self.inner.lock().unwrap().accept().map(|c| match settings.apply(c) {
|
let mut guard = self.inner.lock().unwrap();
|
||||||
|
let sas: InnerSas = (*guard).clone();
|
||||||
|
let methods = settings.allowed_methods;
|
||||||
|
|
||||||
|
if let Some((sas, content)) = sas.accept(methods) {
|
||||||
|
*guard = sas;
|
||||||
|
|
||||||
|
Some(match content {
|
||||||
OwnedAcceptContent::ToDevice(c) => {
|
OwnedAcceptContent::ToDevice(c) => {
|
||||||
let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
|
let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
|
||||||
self.content_to_request(content).into()
|
self.content_to_request(content).into()
|
||||||
|
@ -283,6 +341,9 @@ impl Sas {
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Confirm the Sas verification.
|
/// Confirm the Sas verification.
|
||||||
|
@ -349,10 +410,28 @@ impl Sas {
|
||||||
self.cancel_with_code(CancelCode::User)
|
self.cancel_with_code(CancelCode::User)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingVerificationRequest> {
|
/// Cancel the verification.
|
||||||
|
///
|
||||||
|
/// This cancels the verification with given `CancelCode`.
|
||||||
|
///
|
||||||
|
/// **Note**: This method should generally not be used, the [`cancel()`]
|
||||||
|
/// method should be preferred. The SDK will automatically cancel with the
|
||||||
|
/// approprate cancel code, user initiated cancellations should only cancel
|
||||||
|
/// with the `CancelCode::User`
|
||||||
|
///
|
||||||
|
/// Returns None if the `Sas` object is already in a canceled state,
|
||||||
|
/// otherwise it returns a request that needs to be sent out.
|
||||||
|
///
|
||||||
|
/// [`cancel()`]: #method.cancel
|
||||||
|
pub fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingVerificationRequest> {
|
||||||
let mut guard = self.inner.lock().unwrap();
|
let mut guard = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(request) = &self.request_handle {
|
||||||
|
request.cancel_with_code(&code)
|
||||||
|
}
|
||||||
|
|
||||||
let sas: InnerSas = (*guard).clone();
|
let sas: InnerSas = (*guard).clone();
|
||||||
let (sas, content) = sas.cancel(code);
|
let (sas, content) = sas.cancel(true, code);
|
||||||
*guard = sas;
|
*guard = sas;
|
||||||
content.map(|c| match c {
|
content.map(|c| match c {
|
||||||
OutgoingContent::Room(room_id, content) => {
|
OutgoingContent::Room(room_id, content) => {
|
||||||
|
@ -436,7 +515,7 @@ impl Sas {
|
||||||
self.inner.lock().unwrap().verified_devices()
|
self.inner.lock().unwrap().verified_devices()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verified_identities(&self) -> Option<Arc<[UserIdentities]>> {
|
pub(crate) fn verified_identities(&self) -> Option<Arc<[ReadOnlyUserIdentities]>> {
|
||||||
self.inner.lock().unwrap().verified_identities()
|
self.inner.lock().unwrap().verified_identities()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,23 +551,6 @@ impl AcceptSettings {
|
||||||
pub fn with_allowed_methods(methods: Vec<ShortAuthenticationString>) -> Self {
|
pub fn with_allowed_methods(methods: Vec<ShortAuthenticationString>) -> Self {
|
||||||
Self { allowed_methods: methods }
|
Self { allowed_methods: methods }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, mut content: OwnedAcceptContent) -> OwnedAcceptContent {
|
|
||||||
match &mut content {
|
|
||||||
OwnedAcceptContent::ToDevice(AcceptToDeviceEventContent {
|
|
||||||
method: AcceptMethod::SasV1(c),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| OwnedAcceptContent::Room(
|
|
||||||
_,
|
|
||||||
AcceptEventContent { method: AcceptMethod::SasV1(c), .. },
|
|
||||||
) => {
|
|
||||||
c.short_authentication_string.retain(|sas| self.allowed_methods.contains(sas));
|
|
||||||
content
|
|
||||||
}
|
|
||||||
_ => content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -545,6 +607,8 @@ mod test {
|
||||||
alice_store,
|
alice_store,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let flow_id = alice.flow_id().to_owned();
|
let flow_id = alice.flow_id().to_owned();
|
||||||
|
@ -558,6 +622,7 @@ mod test {
|
||||||
PrivateCrossSigningIdentity::empty(bob_id()),
|
PrivateCrossSigningIdentity::empty(bob_id()),
|
||||||
alice_device,
|
alice_device,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -52,7 +52,7 @@ use super::{
|
||||||
OutgoingContent,
|
OutgoingContent,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
verification::{
|
verification::{
|
||||||
event_enums::{
|
event_enums::{
|
||||||
AcceptContent, DoneContent, KeyContent, MacContent, OwnedAcceptContent,
|
AcceptContent, DoneContent, KeyContent, MacContent, OwnedAcceptContent,
|
||||||
|
@ -241,6 +241,15 @@ pub struct Accepted {
|
||||||
commitment: String,
|
commitment: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The SAS state we're going to be in after we accepted our
|
||||||
|
/// verification start event.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct WeAccepted {
|
||||||
|
we_started: bool,
|
||||||
|
pub accepted_protocols: Arc<AcceptedProtocols>,
|
||||||
|
commitment: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// The SAS state we're going to be in after we received the public key of the
|
/// The SAS state we're going to be in after we received the public key of the
|
||||||
/// other participant.
|
/// other participant.
|
||||||
///
|
///
|
||||||
|
@ -268,7 +277,7 @@ pub struct MacReceived {
|
||||||
we_started: bool,
|
we_started: bool,
|
||||||
their_pubkey: String,
|
their_pubkey: String,
|
||||||
verified_devices: Arc<[ReadOnlyDevice]>,
|
verified_devices: Arc<[ReadOnlyDevice]>,
|
||||||
verified_master_keys: Arc<[UserIdentities]>,
|
verified_master_keys: Arc<[ReadOnlyUserIdentities]>,
|
||||||
pub accepted_protocols: Arc<AcceptedProtocols>,
|
pub accepted_protocols: Arc<AcceptedProtocols>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +287,7 @@ pub struct MacReceived {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct WaitingForDone {
|
pub struct WaitingForDone {
|
||||||
verified_devices: Arc<[ReadOnlyDevice]>,
|
verified_devices: Arc<[ReadOnlyDevice]>,
|
||||||
verified_master_keys: Arc<[UserIdentities]>,
|
verified_master_keys: Arc<[ReadOnlyUserIdentities]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Clone> SasState<S> {
|
impl<S: Clone> SasState<S> {
|
||||||
|
@ -298,14 +307,14 @@ impl<S: Clone> SasState<S> {
|
||||||
self.ids.other_device.clone()
|
self.ids.other_device.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel(self, cancel_code: CancelCode) -> SasState<Cancelled> {
|
pub fn cancel(self, cancelled_by_us: bool, cancel_code: CancelCode) -> SasState<Cancelled> {
|
||||||
SasState {
|
SasState {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: self.last_event_time,
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
state: Arc::new(Cancelled::new(cancel_code)),
|
state: Arc::new(Cancelled::new(cancelled_by_us, cancel_code)),
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,7 +362,7 @@ impl SasState<Created> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
transaction_id: Option<String>,
|
transaction_id: Option<String>,
|
||||||
) -> SasState<Created> {
|
) -> SasState<Created> {
|
||||||
let started_from_request = transaction_id.is_some();
|
let started_from_request = transaction_id.is_some();
|
||||||
|
@ -379,7 +388,7 @@ impl SasState<Created> {
|
||||||
event_id: EventId,
|
event_id: EventId,
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
) -> SasState<Created> {
|
) -> SasState<Created> {
|
||||||
let flow_id = FlowId::InRoom(room_id, event_id);
|
let flow_id = FlowId::InRoom(room_id, event_id);
|
||||||
Self::new_helper(flow_id, account, other_device, other_identity, false)
|
Self::new_helper(flow_id, account, other_device, other_identity, false)
|
||||||
|
@ -389,7 +398,7 @@ impl SasState<Created> {
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
started_from_request: bool,
|
started_from_request: bool,
|
||||||
) -> SasState<Created> {
|
) -> SasState<Created> {
|
||||||
SasState {
|
SasState {
|
||||||
|
@ -444,11 +453,11 @@ impl SasState<Created> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &AcceptContent,
|
content: &AcceptContent,
|
||||||
) -> Result<SasState<Accepted>, SasState<Cancelled>> {
|
) -> Result<SasState<Accepted>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
if let AcceptMethod::SasV1(content) = content.method() {
|
if let AcceptMethod::SasV1(content) = content.method() {
|
||||||
let accepted_protocols =
|
let accepted_protocols = AcceptedProtocols::try_from(content.clone())
|
||||||
AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?;
|
.map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
let start_content = self.as_content().into();
|
let start_content = self.as_content().into();
|
||||||
|
|
||||||
|
@ -457,7 +466,7 @@ impl SasState<Created> {
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
state: Arc::new(Accepted {
|
state: Arc::new(Accepted {
|
||||||
start_content,
|
start_content,
|
||||||
|
@ -466,7 +475,7 @@ impl SasState<Created> {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(self.cancel(CancelCode::UnknownMethod))
|
Err(self.cancel(true, CancelCode::UnknownMethod))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -488,7 +497,7 @@ impl SasState<Started> {
|
||||||
pub fn from_start_event(
|
pub fn from_start_event(
|
||||||
account: ReadOnlyAccount,
|
account: ReadOnlyAccount,
|
||||||
other_device: ReadOnlyDevice,
|
other_device: ReadOnlyDevice,
|
||||||
other_identity: Option<UserIdentities>,
|
other_identity: Option<ReadOnlyUserIdentities>,
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
content: &StartContent,
|
content: &StartContent,
|
||||||
started_from_request: bool,
|
started_from_request: bool,
|
||||||
|
@ -509,7 +518,7 @@ impl SasState<Started> {
|
||||||
},
|
},
|
||||||
|
|
||||||
verification_flow_id: flow_id.clone(),
|
verification_flow_id: flow_id.clone(),
|
||||||
state: Arc::new(Cancelled::new(CancelCode::UnknownMethod)),
|
state: Arc::new(Cancelled::new(true, CancelCode::UnknownMethod)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let StartMethod::SasV1(method_content) = content.method() {
|
if let StartMethod::SasV1(method_content) = content.method() {
|
||||||
|
@ -548,6 +557,32 @@ impl SasState<Started> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_accepted(self, methods: Vec<ShortAuthenticationString>) -> SasState<WeAccepted> {
|
||||||
|
let mut accepted_protocols = self.state.accepted_protocols.as_ref().to_owned();
|
||||||
|
accepted_protocols.short_auth_string = methods;
|
||||||
|
|
||||||
|
// Decimal is required per spec.
|
||||||
|
if !accepted_protocols.short_auth_string.contains(&ShortAuthenticationString::Decimal) {
|
||||||
|
accepted_protocols.short_auth_string.push(ShortAuthenticationString::Decimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
SasState {
|
||||||
|
inner: self.inner,
|
||||||
|
ids: self.ids,
|
||||||
|
verification_flow_id: self.verification_flow_id,
|
||||||
|
creation_time: self.creation_time,
|
||||||
|
last_event_time: self.last_event_time,
|
||||||
|
started_from_request: self.started_from_request,
|
||||||
|
state: Arc::new(WeAccepted {
|
||||||
|
we_started: false,
|
||||||
|
accepted_protocols: accepted_protocols.into(),
|
||||||
|
commitment: self.state.commitment.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SasState<WeAccepted> {
|
||||||
/// Get the content for the accept event.
|
/// Get the content for the accept event.
|
||||||
///
|
///
|
||||||
/// The content needs to be sent to the other device.
|
/// The content needs to be sent to the other device.
|
||||||
|
@ -600,7 +635,7 @@ impl SasState<Started> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &KeyContent,
|
content: &KeyContent,
|
||||||
) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
|
) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
let their_pubkey = content.public_key().to_owned();
|
let their_pubkey = content.public_key().to_owned();
|
||||||
|
|
||||||
|
@ -615,7 +650,7 @@ impl SasState<Started> {
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
state: Arc::new(KeyReceived {
|
state: Arc::new(KeyReceived {
|
||||||
we_started: false,
|
we_started: false,
|
||||||
|
@ -640,7 +675,7 @@ impl SasState<Accepted> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &KeyContent,
|
content: &KeyContent,
|
||||||
) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
|
) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
let commitment = calculate_commitment(
|
let commitment = calculate_commitment(
|
||||||
content.public_key(),
|
content.public_key(),
|
||||||
|
@ -648,7 +683,7 @@ impl SasState<Accepted> {
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.state.commitment != commitment {
|
if self.state.commitment != commitment {
|
||||||
Err(self.cancel(CancelCode::InvalidMessage))
|
Err(self.cancel(true, CancelCode::InvalidMessage))
|
||||||
} else {
|
} else {
|
||||||
let their_pubkey = content.public_key().to_owned();
|
let their_pubkey = content.public_key().to_owned();
|
||||||
|
|
||||||
|
@ -663,7 +698,7 @@ impl SasState<Accepted> {
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
state: Arc::new(KeyReceived {
|
state: Arc::new(KeyReceived {
|
||||||
their_pubkey,
|
their_pubkey,
|
||||||
|
@ -777,7 +812,7 @@ impl SasState<KeyReceived> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &MacContent,
|
content: &MacContent,
|
||||||
) -> Result<SasState<MacReceived>, SasState<Cancelled>> {
|
) -> Result<SasState<MacReceived>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
let (devices, master_keys) = receive_mac_event(
|
let (devices, master_keys) = receive_mac_event(
|
||||||
&self.inner.lock().unwrap(),
|
&self.inner.lock().unwrap(),
|
||||||
|
@ -786,13 +821,13 @@ impl SasState<KeyReceived> {
|
||||||
sender,
|
sender,
|
||||||
content,
|
content,
|
||||||
)
|
)
|
||||||
.map_err(|c| self.clone().cancel(c))?;
|
.map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
Ok(SasState {
|
Ok(SasState {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
state: Arc::new(MacReceived {
|
state: Arc::new(MacReceived {
|
||||||
|
@ -837,7 +872,7 @@ impl SasState<Confirmed> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &MacContent,
|
content: &MacContent,
|
||||||
) -> Result<SasState<Done>, SasState<Cancelled>> {
|
) -> Result<SasState<Done>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
let (devices, master_keys) = receive_mac_event(
|
let (devices, master_keys) = receive_mac_event(
|
||||||
&self.inner.lock().unwrap(),
|
&self.inner.lock().unwrap(),
|
||||||
|
@ -846,12 +881,12 @@ impl SasState<Confirmed> {
|
||||||
sender,
|
sender,
|
||||||
content,
|
content,
|
||||||
)
|
)
|
||||||
.map_err(|c| self.clone().cancel(c))?;
|
.map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
Ok(SasState {
|
Ok(SasState {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
|
@ -877,7 +912,7 @@ impl SasState<Confirmed> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &MacContent,
|
content: &MacContent,
|
||||||
) -> Result<SasState<WaitingForDone>, SasState<Cancelled>> {
|
) -> Result<SasState<WaitingForDone>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
let (devices, master_keys) = receive_mac_event(
|
let (devices, master_keys) = receive_mac_event(
|
||||||
&self.inner.lock().unwrap(),
|
&self.inner.lock().unwrap(),
|
||||||
|
@ -886,12 +921,12 @@ impl SasState<Confirmed> {
|
||||||
sender,
|
sender,
|
||||||
content,
|
content,
|
||||||
)
|
)
|
||||||
.map_err(|c| self.clone().cancel(c))?;
|
.map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
Ok(SasState {
|
Ok(SasState {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
|
@ -1032,12 +1067,12 @@ impl SasState<WaitingForDone> {
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
content: &DoneContent,
|
content: &DoneContent,
|
||||||
) -> Result<SasState<Done>, SasState<Cancelled>> {
|
) -> Result<SasState<Done>, SasState<Cancelled>> {
|
||||||
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
|
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
|
||||||
|
|
||||||
Ok(SasState {
|
Ok(SasState {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: Instant::now().into(),
|
||||||
verification_flow_id: self.verification_flow_id,
|
verification_flow_id: self.verification_flow_id,
|
||||||
started_from_request: self.started_from_request,
|
started_from_request: self.started_from_request,
|
||||||
ids: self.ids,
|
ids: self.ids,
|
||||||
|
@ -1069,7 +1104,7 @@ impl SasState<Done> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the list of verified identities.
|
/// Get the list of verified identities.
|
||||||
pub fn verified_identities(&self) -> Arc<[UserIdentities]> {
|
pub fn verified_identities(&self) -> Arc<[ReadOnlyUserIdentities]> {
|
||||||
self.state.verified_master_keys.clone()
|
self.state.verified_master_keys.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1088,12 +1123,13 @@ mod test {
|
||||||
events::key::verification::{
|
events::key::verification::{
|
||||||
accept::{AcceptMethod, AcceptToDeviceEventContent},
|
accept::{AcceptMethod, AcceptToDeviceEventContent},
|
||||||
start::{StartMethod, StartToDeviceEventContent},
|
start::{StartMethod, StartToDeviceEventContent},
|
||||||
|
ShortAuthenticationString,
|
||||||
},
|
},
|
||||||
DeviceId, UserId,
|
DeviceId, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use super::{Accepted, Created, SasState, Started};
|
use super::{Accepted, Created, SasState, Started, WeAccepted};
|
||||||
use crate::{
|
use crate::{
|
||||||
verification::event_enums::{AcceptContent, KeyContent, MacContent, StartContent},
|
verification::event_enums::{AcceptContent, KeyContent, MacContent, StartContent},
|
||||||
ReadOnlyAccount, ReadOnlyDevice,
|
ReadOnlyAccount, ReadOnlyDevice,
|
||||||
|
@ -1115,7 +1151,7 @@ mod test {
|
||||||
"BOBDEVCIE".into()
|
"BOBDEVCIE".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_sas_pair() -> (SasState<Created>, SasState<Started>) {
|
async fn get_sas_pair() -> (SasState<Created>, SasState<WeAccepted>) {
|
||||||
let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id());
|
let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id());
|
||||||
let alice_device = ReadOnlyDevice::from_account(&alice).await;
|
let alice_device = ReadOnlyDevice::from_account(&alice).await;
|
||||||
|
|
||||||
|
@ -1135,8 +1171,9 @@ mod test {
|
||||||
&start_content.as_start_content(),
|
&start_content.as_start_content(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
let bob_sas = bob_sas.unwrap().into_accepted(vec![ShortAuthenticationString::Emoji]);
|
||||||
|
|
||||||
(alice_sas, bob_sas.unwrap())
|
(alice_sas, bob_sas)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
Loading…
Reference in New Issue