crypto: WIP more work on in-room verifications now up to accepting them.

master
Damir Jelić 2020-12-15 16:35:54 +01:00
parent 5105629c08
commit b6e28e2280
13 changed files with 320 additions and 117 deletions

View File

@ -45,7 +45,7 @@ use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_base::crypto::{ use matrix_sdk_base::crypto::{
decrypt_key_export, encrypt_key_export, olm::InboundGroupSession, store::CryptoStoreError, decrypt_key_export, encrypt_key_export, olm::InboundGroupSession, store::CryptoStoreError,
AttachmentEncryptor, OutgoingRequests, ToDeviceRequest, AttachmentEncryptor, OutgoingRequests, RoomMessageRequest, ToDeviceRequest,
}; };
/// Enum controlling if a loop running callbacks should continue or abort. /// Enum controlling if a loop running callbacks should continue or abort.
@ -1160,6 +1160,17 @@ impl Client {
Ok(()) Ok(())
} }
async fn room_send_helper(
&self,
request: &RoomMessageRequest,
) -> Result<send_message_event::Response> {
let content = request.content.clone();
let txn_id = request.txn_id.into();
let room_id = &request.room_id;
self.room_send(&room_id, content, Some(txn_id)).await
}
/// Send a room message to the homeserver. /// Send a room message to the homeserver.
/// ///
/// Returns the parsed response from the server. /// Returns the parsed response from the server.
@ -1458,7 +1469,10 @@ impl Client {
} }
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
async fn send_to_device(&self, request: &ToDeviceRequest) -> Result<ToDeviceResponse> { pub(crate) async fn send_to_device(
&self,
request: &ToDeviceRequest,
) -> Result<ToDeviceResponse> {
let txn_id_string = request.txn_id_string(); let txn_id_string = request.txn_id_string();
let request = RumaToDeviceRequest::new( let request = RumaToDeviceRequest::new(
request.event_type.clone(), request.event_type.clone(),
@ -1737,6 +1751,14 @@ impl Client {
.unwrap(); .unwrap();
} }
} }
OutgoingRequests::RoomMessage(request) => {
if let Ok(resp) = self.room_send_helper(request).await {
self.base_client
.mark_request_as_sent(&r.request_id(), &resp)
.await
.unwrap();
}
}
} }
} }
} }
@ -1891,7 +1913,7 @@ impl Client {
.await .await
.map(|sas| Sas { .map(|sas| Sas {
inner: sas, inner: sas,
http_client: self.http_client.clone(), client: self.clone(),
}) })
} }
@ -1953,7 +1975,7 @@ impl Client {
Ok(device.map(|d| Device { Ok(device.map(|d| Device {
inner: d, inner: d,
http_client: self.http_client.clone(), client: self.clone(),
})) }))
} }
@ -2070,7 +2092,7 @@ impl Client {
Ok(UserDevices { Ok(UserDevices {
inner: devices, inner: devices,
http_client: self.http_client.clone(), client: self.clone(),
}) })
} }

View File

@ -18,18 +18,15 @@ 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 matrix_sdk_common::{ use matrix_sdk_common::identifiers::{DeviceId, DeviceIdBox};
api::r0::to_device::send_event_to_device::Request as ToDeviceRequest,
identifiers::{DeviceId, DeviceIdBox},
};
use crate::{error::Result, http_client::HttpClient, Sas}; use crate::{error::Result, Client, Sas};
#[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.
pub struct Device { pub struct Device {
pub(crate) inner: BaseDevice, pub(crate) inner: BaseDevice,
pub(crate) http_client: HttpClient, pub(crate) client: Client,
} }
impl Deref for Device { impl Deref for Device {
@ -66,14 +63,11 @@ impl Device {
/// ``` /// ```
pub async fn start_verification(&self) -> Result<Sas> { pub async fn start_verification(&self) -> Result<Sas> {
let (sas, request) = self.inner.start_verification().await?; let (sas, request) = self.inner.start_verification().await?;
let txn_id_string = request.txn_id_string(); self.client.send_to_device(&request).await?;
let request = ToDeviceRequest::new(request.event_type, &txn_id_string, request.messages);
self.http_client.send(request).await?;
Ok(Sas { Ok(Sas {
inner: sas, inner: sas,
http_client: self.http_client.clone(), client: self.client.clone(),
}) })
} }
@ -102,7 +96,7 @@ impl Device {
#[derive(Debug)] #[derive(Debug)]
pub struct UserDevices { pub struct UserDevices {
pub(crate) inner: BaseUserDevices, pub(crate) inner: BaseUserDevices,
pub(crate) http_client: HttpClient, pub(crate) client: Client,
} }
impl UserDevices { impl UserDevices {
@ -110,7 +104,7 @@ impl UserDevices {
pub fn get(&self, device_id: &DeviceId) -> Option<Device> { pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
self.inner.get(device_id).map(|d| Device { self.inner.get(device_id).map(|d| Device {
inner: d, inner: d,
http_client: self.http_client.clone(), client: self.client.clone(),
}) })
} }
@ -121,11 +115,11 @@ impl UserDevices {
/// Iterator over all the devices of the user devices. /// Iterator over all the devices of the user devices.
pub fn devices(&self) -> impl Iterator<Item = Device> + '_ { pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
let client = self.http_client.clone(); let client = self.client.clone();
self.inner.devices().map(move |d| Device { self.inner.devices().map(move |d| Device {
inner: d, inner: d,
http_client: client.clone(), client: client.clone(),
}) })
} }
} }

View File

@ -12,26 +12,27 @@
// 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::{ReadOnlyDevice, Sas as BaseSas}; use matrix_sdk_base::crypto::{OutgoingVerificationRequest, ReadOnlyDevice, Sas as BaseSas};
use matrix_sdk_common::api::r0::to_device::send_event_to_device::Request as ToDeviceRequest;
use crate::{error::Result, http_client::HttpClient}; use crate::{error::Result, Client};
/// An object controling the interactive verification flow. /// An object controling the interactive verification flow.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Sas { pub struct Sas {
pub(crate) inner: BaseSas, pub(crate) inner: BaseSas,
pub(crate) http_client: HttpClient, pub(crate) client: Client,
} }
impl Sas { impl Sas {
/// Accept the interactive verification flow. /// Accept the interactive verification flow.
pub async fn accept(&self) -> Result<()> { pub async fn accept(&self) -> Result<()> {
if let Some(req) = self.inner.accept() { if let Some(req) = self.inner.accept() {
let txn_id_string = req.txn_id_string(); match req {
let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages); OutgoingVerificationRequest::ToDevice(r) => {
self.client.send_to_device(&r).await?;
self.http_client.send(request).await?; }
OutgoingVerificationRequest::InRoom(_) => todo!(),
}
} }
Ok(()) Ok(())
} }
@ -40,15 +41,12 @@ impl Sas {
pub async fn confirm(&self) -> Result<()> { pub async fn confirm(&self) -> Result<()> {
let (to_device, signature) = self.inner.confirm().await?; let (to_device, signature) = self.inner.confirm().await?;
if let Some(req) = to_device { if let Some(request) = to_device {
let txn_id_string = req.txn_id_string(); self.client.send_to_device(&request).await?;
let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages);
self.http_client.send(request).await?;
} }
if let Some(s) = signature { if let Some(s) = signature {
self.http_client.send(s).await?; self.client.send(s).await?;
} }
Ok(()) Ok(())
@ -56,12 +54,10 @@ impl Sas {
/// Cancel the interactive verification flow. /// Cancel the interactive verification flow.
pub async fn cancel(&self) -> Result<()> { pub async fn cancel(&self) -> Result<()> {
if let Some(req) = self.inner.cancel() { if let Some(request) = self.inner.cancel() {
let txn_id_string = req.txn_id_string(); self.client.send_to_device(&request).await?;
let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages);
self.http_client.send(request).await?;
} }
Ok(()) Ok(())
} }

View File

@ -42,7 +42,7 @@ use tracing::warn;
use crate::{ use crate::{
olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session}, olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session},
store::{Changes, DeviceChanges}, store::{Changes, DeviceChanges},
OutgoingRequest, OutgoingRequests, OutgoingRequest, OutgoingRequests, OutgoingVerificationRequest,
}; };
#[cfg(test)] #[cfg(test)]
use crate::{OlmMachine, ReadOnlyAccount}; use crate::{OlmMachine, ReadOnlyAccount};
@ -97,7 +97,7 @@ impl Device {
.start_sas(self.inner.clone()) .start_sas(self.inner.clone())
.await?; .await?;
if let OutgoingRequests::ToDeviceRequest(r) = request { if let OutgoingVerificationRequest::ToDevice(r) = request {
Ok((sas, r)) Ok((sas, r))
} else { } else {
panic!("Invalid verification request type"); panic!("Invalid verification request type");

View File

@ -51,6 +51,7 @@ pub use machine::OlmMachine;
pub use olm::EncryptionSettings; pub use olm::EncryptionSettings;
pub(crate) use olm::ReadOnlyAccount; pub(crate) use olm::ReadOnlyAccount;
pub use requests::{ pub use requests::{
IncomingResponse, KeysQueryRequest, OutgoingRequest, OutgoingRequests, ToDeviceRequest, IncomingResponse, KeysQueryRequest, OutgoingRequest, OutgoingRequests,
OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest,
}; };
pub use verification::{Sas, VerificationRequest}; pub use verification::{Sas, VerificationRequest};

View File

@ -322,6 +322,7 @@ impl OlmMachine {
} }
requests.append(&mut self.outgoing_to_device_requests()); requests.append(&mut self.outgoing_to_device_requests());
requests.append(&mut self.verification_machine.outgoing_room_message_requests());
requests.append(&mut self.key_request_machine.outgoing_to_device_requests()); requests.append(&mut self.key_request_machine.outgoing_to_device_requests());
requests requests
@ -360,6 +361,9 @@ impl OlmMachine {
IncomingResponse::SignatureUpload(_) => { IncomingResponse::SignatureUpload(_) => {
self.verification_machine.mark_request_as_sent(request_id); self.verification_machine.mark_request_as_sent(request_id);
} }
IncomingResponse::RoomMessage(_) => {
self.verification_machine.mark_request_as_sent(request_id);
}
}; };
Ok(()) Ok(())

View File

@ -12,6 +12,8 @@
// 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(missing_docs)]
use std::{collections::BTreeMap, sync::Arc, time::Duration}; use std::{collections::BTreeMap, sync::Arc, time::Duration};
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -26,10 +28,11 @@ use matrix_sdk_common::{
upload_signing_keys::Response as SigningKeysUploadResponse, upload_signing_keys::Response as SigningKeysUploadResponse,
CrossSigningKey, CrossSigningKey,
}, },
message::send_message_event::Response as RoomMessageResponse,
to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices}, to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices},
}, },
events::EventType, events::{AnyMessageEventContent, EventType},
identifiers::{DeviceIdBox, UserId}, identifiers::{DeviceIdBox, RoomId, UserId},
uuid::Uuid, uuid::Uuid,
}; };
@ -120,6 +123,7 @@ pub enum OutgoingRequests {
/// Signature upload request, this request is used after a successful device /// Signature upload request, this request is used after a successful device
/// or user verification is done. /// or user verification is done.
SignatureUpload(SignatureUploadRequest), SignatureUpload(SignatureUploadRequest),
RoomMessage(RoomMessageRequest),
} }
#[cfg(test)] #[cfg(test)]
@ -150,6 +154,12 @@ impl From<ToDeviceRequest> for OutgoingRequests {
} }
} }
impl From<RoomMessageRequest> for OutgoingRequests {
fn from(request: RoomMessageRequest) -> Self {
OutgoingRequests::RoomMessage(request)
}
}
impl From<SignatureUploadRequest> for OutgoingRequests { impl From<SignatureUploadRequest> for OutgoingRequests {
fn from(request: SignatureUploadRequest) -> Self { fn from(request: SignatureUploadRequest) -> Self {
OutgoingRequests::SignatureUpload(request) OutgoingRequests::SignatureUpload(request)
@ -176,6 +186,7 @@ pub enum IncomingResponse<'a> {
/// The cross signing keys upload response, marking our private cross /// The cross signing keys upload response, marking our private cross
/// signing identity as shared. /// signing identity as shared.
SignatureUpload(&'a SignatureUploadResponse), SignatureUpload(&'a SignatureUploadResponse),
RoomMessage(&'a RoomMessageResponse),
} }
impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> { impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> {
@ -196,6 +207,12 @@ impl<'a> From<&'a ToDeviceResponse> for IncomingResponse<'a> {
} }
} }
impl<'a> From<&'a RoomMessageResponse> for IncomingResponse<'a> {
fn from(response: &'a RoomMessageResponse) -> Self {
IncomingResponse::RoomMessage(response)
}
}
impl<'a> From<&'a KeysClaimResponse> for IncomingResponse<'a> { impl<'a> From<&'a KeysClaimResponse> for IncomingResponse<'a> {
fn from(response: &'a KeysClaimResponse) -> Self { fn from(response: &'a KeysClaimResponse) -> Self {
IncomingResponse::KeysClaim(response) IncomingResponse::KeysClaim(response)
@ -230,3 +247,55 @@ impl OutgoingRequest {
&self.request &self.request
} }
} }
#[derive(Clone, Debug)]
pub struct RoomMessageRequest {
/// The room to send the event to.
pub room_id: RoomId,
/// The transaction ID for this event.
///
/// Clients should generate an ID unique across requests with the
/// same access token; it will be used by the server to ensure
/// idempotency of requests.
pub txn_id: Uuid,
/// The event content to send.
pub content: AnyMessageEventContent,
}
#[derive(Clone, Debug)]
pub enum OutgoingVerificationRequest {
ToDevice(ToDeviceRequest),
InRoom(RoomMessageRequest),
}
impl OutgoingVerificationRequest {
pub fn request_id(&self) -> Uuid {
match self {
OutgoingVerificationRequest::ToDevice(t) => t.txn_id,
OutgoingVerificationRequest::InRoom(r) => r.txn_id,
}
}
}
impl From<ToDeviceRequest> for OutgoingVerificationRequest {
fn from(r: ToDeviceRequest) -> Self {
OutgoingVerificationRequest::ToDevice(r)
}
}
impl From<RoomMessageRequest> for OutgoingVerificationRequest {
fn from(r: RoomMessageRequest) -> Self {
OutgoingVerificationRequest::InRoom(r)
}
}
impl From<OutgoingVerificationRequest> for OutgoingRequests {
fn from(request: OutgoingVerificationRequest) -> Self {
match request {
OutgoingVerificationRequest::ToDevice(r) => OutgoingRequests::ToDeviceRequest(r),
OutgoingVerificationRequest::InRoom(r) => OutgoingRequests::RoomMessage(r),
}
}
}

View File

@ -37,7 +37,8 @@ use crate::{
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
requests::{OutgoingRequest, ToDeviceRequest}, requests::{OutgoingRequest, ToDeviceRequest},
store::{CryptoStore, CryptoStoreError}, store::{CryptoStore, CryptoStoreError},
OutgoingRequests, ReadOnlyAccount, ReadOnlyDevice, OutgoingRequests, OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice,
RoomMessageRequest,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -49,6 +50,7 @@ pub struct VerificationMachine {
room_verifications: Arc<DashMap<EventId, Sas>>, room_verifications: Arc<DashMap<EventId, Sas>>,
requests: Arc<DashMap<EventId, VerificationRequest>>, requests: Arc<DashMap<EventId, VerificationRequest>>,
outgoing_to_device_messages: Arc<DashMap<Uuid, OutgoingRequest>>, outgoing_to_device_messages: Arc<DashMap<Uuid, OutgoingRequest>>,
outgoing_room_messages: Arc<DashMap<Uuid, OutgoingRequest>>,
} }
impl VerificationMachine { impl VerificationMachine {
@ -65,13 +67,14 @@ impl VerificationMachine {
requests: DashMap::new().into(), requests: DashMap::new().into(),
outgoing_to_device_messages: DashMap::new().into(), outgoing_to_device_messages: DashMap::new().into(),
room_verifications: DashMap::new().into(), room_verifications: DashMap::new().into(),
outgoing_room_messages: DashMap::new().into(),
} }
} }
pub async fn start_sas( pub async fn start_sas(
&self, &self,
device: ReadOnlyDevice, device: ReadOnlyDevice,
) -> Result<(Sas, OutgoingRequests), CryptoStoreError> { ) -> Result<(Sas, OutgoingVerificationRequest), CryptoStoreError> {
let identity = self.store.get_user_identity(device.user_id()).await?; let identity = self.store.get_user_identity(device.user_id()).await?;
let private_identity = self.private_identity.lock().await.clone(); let private_identity = self.private_identity.lock().await.clone();
@ -83,8 +86,13 @@ impl VerificationMachine {
identity, identity,
); );
let request: OutgoingRequests = match content { let request = match content {
OutgoingContent::Room(c) => todo!(), OutgoingContent::Room(r, c) => RoomMessageRequest {
room_id: r,
txn_id: Uuid::new_v4(),
content: c,
}
.into(),
OutgoingContent::ToDevice(c) => { OutgoingContent::ToDevice(c) => {
let request = content_to_request(device.user_id(), device.device_id(), c); let request = content_to_request(device.user_id(), device.device_id(), c);
@ -127,7 +135,23 @@ impl VerificationMachine {
self.outgoing_to_device_messages.insert(request_id, request); self.outgoing_to_device_messages.insert(request_id, request);
} }
OutgoingContent::Room(c) => todo!(), OutgoingContent::Room(r, c) => {
let request_id = Uuid::new_v4();
let request = OutgoingRequest {
request: Arc::new(
RoomMessageRequest {
room_id: r,
txn_id: request_id.clone(),
content: c,
}
.into(),
),
request_id,
};
self.outgoing_room_messages.insert(request_id, request);
}
} }
} }
@ -138,9 +162,17 @@ impl VerificationMachine {
} }
pub fn mark_request_as_sent(&self, uuid: &Uuid) { pub fn mark_request_as_sent(&self, uuid: &Uuid) {
self.outgoing_room_messages.remove(uuid);
self.outgoing_to_device_messages.remove(uuid); self.outgoing_to_device_messages.remove(uuid);
} }
pub fn outgoing_room_message_requests(&self) -> Vec<OutgoingRequest> {
self.outgoing_room_messages
.iter()
.map(|r| (*r).clone())
.collect()
}
pub fn outgoing_to_device_requests(&self) -> Vec<OutgoingRequest> { pub fn outgoing_to_device_requests(&self) -> Vec<OutgoingRequest> {
#[allow(clippy::map_clone)] #[allow(clippy::map_clone)]
self.outgoing_to_device_messages self.outgoing_to_device_messages
@ -215,9 +247,23 @@ impl VerificationMachine {
Ok(s) => { Ok(s) => {
// TODO we need to queue up the accept event // TODO we need to queue up the accept event
// here. // here.
let accept_event = s.accept(); info!(
"Started a new SAS verification, \
automatically accepting because of in-room"
);
let accept_request = s.accept().unwrap();
self.room_verifications self.room_verifications
.insert(e.content.relation.event_id.clone(), s); .insert(e.content.relation.event_id.clone(), s);
self.outgoing_room_messages.insert(
accept_request.request_id(),
OutgoingRequest {
request_id: accept_request.request_id(),
request: Arc::new(accept_request.into()),
},
);
} }
Err(c) => { Err(c) => {
warn!( warn!(

View File

@ -93,13 +93,15 @@ impl VerificationRequest {
) -> Result<Sas, OutgoingContent> { ) -> Result<Sas, OutgoingContent> {
match &*self.inner.lock().unwrap() { match &*self.inner.lock().unwrap() {
InnerRequest::Ready(s) => s.into_started_sas( InnerRequest::Ready(s) => s.into_started_sas(
event, &event.clone().into_full_event(self.room_id().clone()),
self.store.clone(), self.store.clone(),
self.account.clone(), self.account.clone(),
self.private_cross_signing_identity.clone(), self.private_cross_signing_identity.clone(),
device, device,
user_identity, user_identity,
), ),
// TODO cancel here since we got a missmatched message or do
// nothing?
_ => todo!(), _ => todo!(),
} }
} }
@ -128,7 +130,7 @@ impl InnerRequest {
fn into_started_sas( fn into_started_sas(
&mut self, &mut self,
event: &SyncMessageEvent<StartEventContent>, event: &MessageEvent<StartEventContent>,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_identity: PrivateCrossSigningIdentity, private_identity: PrivateCrossSigningIdentity,
@ -299,7 +301,7 @@ struct Ready {
impl RequestState<Ready> { impl RequestState<Ready> {
fn into_started_sas( fn into_started_sas(
&self, &self,
event: &SyncMessageEvent<StartEventContent>, event: &MessageEvent<StartEventContent>,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_identity: PrivateCrossSigningIdentity, private_identity: PrivateCrossSigningIdentity,
@ -312,7 +314,7 @@ impl RequestState<Ready> {
other_device, other_device,
store, store,
&event.sender, &event.sender,
event.content.clone(), (event.room_id.clone(), event.content.clone()),
other_identity, other_identity,
) )
} }
@ -325,13 +327,14 @@ impl RequestState<Ready> {
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> (Sas, OutgoingContent) { ) -> (Sas, OutgoingContent) {
Sas::start( todo!()
account, // Sas::start_in_room(
private_identity, // account,
other_device, // private_identity,
store, // other_device,
other_identity, // store,
) // other_identity,
// )
} }
} }

View File

@ -19,11 +19,13 @@ use std::convert::TryInto;
use matrix_sdk_common::{ use matrix_sdk_common::{
events::{ events::{
key::verification::{ key::verification::{
accept::{AcceptEventContent, AcceptToDeviceEventContent},
start::{StartEventContent, StartMethod, StartToDeviceEventContent}, start::{StartEventContent, StartMethod, StartToDeviceEventContent},
KeyAgreementProtocol, KeyAgreementProtocol,
}, },
AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent,
}, },
identifiers::RoomId,
CanonicalJsonValue, CanonicalJsonValue,
}; };
@ -32,28 +34,28 @@ use super::FlowId;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum StartContent { pub enum StartContent {
ToDevice(StartToDeviceEventContent), ToDevice(StartToDeviceEventContent),
Room(StartEventContent), Room(RoomId, StartEventContent),
} }
impl StartContent { impl StartContent {
pub fn method(&self) -> &StartMethod { pub fn method(&self) -> &StartMethod {
match self { match self {
StartContent::ToDevice(c) => &c.method, StartContent::ToDevice(c) => &c.method,
StartContent::Room(c) => &c.method, StartContent::Room(_, c) => &c.method,
} }
} }
pub fn flow_id(&self) -> FlowId { pub fn flow_id(&self) -> FlowId {
match self { match self {
StartContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), StartContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()),
StartContent::Room(c) => FlowId::InRoom(c.relation.event_id.clone()), StartContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()),
} }
} }
pub fn to_canonical_json(self) -> CanonicalJsonValue { pub fn to_canonical_json(self) -> CanonicalJsonValue {
let content = match self { let content = match self {
StartContent::Room(c) => serde_json::to_value(c),
StartContent::ToDevice(c) => serde_json::to_value(c), StartContent::ToDevice(c) => serde_json::to_value(c),
StartContent::Room(_, c) => serde_json::to_value(c),
}; };
content content
@ -63,9 +65,9 @@ impl StartContent {
} }
} }
impl From<StartEventContent> for StartContent { impl From<(RoomId, StartEventContent)> for StartContent {
fn from(content: StartEventContent) -> Self { fn from(tuple: (RoomId, StartEventContent)) -> Self {
StartContent::Room(content) StartContent::Room(tuple.0, tuple.1)
} }
} }
@ -75,16 +77,34 @@ impl From<StartToDeviceEventContent> for StartContent {
} }
} }
#[derive(Clone, Debug)]
pub enum AcceptContent {
ToDevice(AcceptToDeviceEventContent),
Room(RoomId, AcceptEventContent),
}
impl From<AcceptToDeviceEventContent> for AcceptContent {
fn from(content: AcceptToDeviceEventContent) -> Self {
AcceptContent::ToDevice(content)
}
}
impl From<(RoomId, AcceptEventContent)> for AcceptContent {
fn from(content: (RoomId, AcceptEventContent)) -> Self {
AcceptContent::Room(content.0, content.1)
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum OutgoingContent { pub enum OutgoingContent {
Room(AnyMessageEventContent), Room(RoomId, AnyMessageEventContent),
ToDevice(AnyToDeviceEventContent), ToDevice(AnyToDeviceEventContent),
} }
impl From<StartContent> for OutgoingContent { impl From<StartContent> for OutgoingContent {
fn from(content: StartContent) -> Self { fn from(content: StartContent) -> Self {
match content { match content {
StartContent::Room(c) => AnyMessageEventContent::KeyVerificationStart(c).into(), StartContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationStart(c)).into(),
StartContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationStart(c).into(), StartContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationStart(c).into(),
} }
} }
@ -96,8 +116,8 @@ impl From<AnyToDeviceEventContent> for OutgoingContent {
} }
} }
impl From<AnyMessageEventContent> for OutgoingContent { impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent {
fn from(content: AnyMessageEventContent) -> Self { fn from(content: (RoomId, AnyMessageEventContent)) -> Self {
OutgoingContent::Room(content) OutgoingContent::Room(content.0, content.1)
} }
} }

View File

@ -27,7 +27,7 @@ use matrix_sdk_common::{
}, },
AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent,
}, },
identifiers::{EventId, UserId}, identifiers::{EventId, RoomId, UserId},
}; };
use crate::{ use crate::{
@ -36,7 +36,7 @@ use crate::{
}; };
use super::{ use super::{
event_enums::OutgoingContent, event_enums::{AcceptContent, OutgoingContent},
sas_state::{ sas_state::{
Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState,
Started, Started,
@ -69,11 +69,18 @@ impl InnerSas {
pub fn start_in_room( pub fn start_in_room(
event_id: EventId, event_id: EventId,
room_id: RoomId,
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> (InnerSas, OutgoingContent) { ) -> (InnerSas, OutgoingContent) {
let sas = SasState::<Created>::new_in_room(event_id, account, other_device, other_identity); let sas = SasState::<Created>::new_in_room(
room_id,
event_id,
account,
other_device,
other_identity,
);
let content = sas.as_content(); let content = sas.as_content();
(InnerSas::Created(sas), content) (InnerSas::Created(sas), content)
} }
@ -97,7 +104,7 @@ impl InnerSas {
} }
} }
pub fn accept(&self) -> Option<AcceptToDeviceEventContent> { pub fn accept(&self) -> Option<AcceptContent> {
if let InnerSas::Started(s) = self { if let InnerSas::Started(s) = self {
Some(s.as_content()) Some(s.as_content())
} else { } else {

View File

@ -20,6 +20,7 @@ mod sas_state;
#[cfg(test)] #[cfg(test)]
use std::time::Instant; use std::time::Instant;
use event_enums::AcceptContent;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tracing::{error, info, trace, warn}; use tracing::{error, info, trace, warn};
@ -30,15 +31,18 @@ use matrix_sdk_common::{
cancel::CancelCode, cancel::CancelCode,
start::{StartEventContent, StartToDeviceEventContent}, start::{StartEventContent, StartToDeviceEventContent},
}, },
AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, AnyMessageEventContent, AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent,
ToDeviceEvent,
}, },
identifiers::{DeviceId, EventId, RoomId, UserId}, identifiers::{DeviceId, EventId, RoomId, UserId},
uuid::Uuid,
}; };
use crate::{ use crate::{
error::SignatureError, error::SignatureError,
identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, identities::{LocalTrust, ReadOnlyDevice, UserIdentities},
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
requests::{OutgoingVerificationRequest, RoomMessageRequest},
store::{Changes, CryptoStore, CryptoStoreError, DeviceChanges}, store::{Changes, CryptoStore, CryptoStoreError, DeviceChanges},
ReadOnlyAccount, ToDeviceRequest, ReadOnlyAccount, ToDeviceRequest,
}; };
@ -189,6 +193,7 @@ impl Sas {
) -> (Sas, OutgoingContent) { ) -> (Sas, OutgoingContent) {
let (inner, content) = InnerSas::start_in_room( let (inner, content) = InnerSas::start_in_room(
flow_id, flow_id,
room_id,
account.clone(), account.clone(),
other_device.clone(), other_device.clone(),
other_identity.clone(), other_identity.clone(),
@ -249,10 +254,18 @@ impl Sas {
/// ///
/// This does nothing if the verification was already accepted, otherwise it /// This does nothing if the verification was already accepted, otherwise it
/// returns an `AcceptEventContent` that needs to be sent out. /// returns an `AcceptEventContent` that needs to be sent out.
pub fn accept(&self) -> Option<ToDeviceRequest> { pub fn accept(&self) -> Option<OutgoingVerificationRequest> {
self.inner.lock().unwrap().accept().map(|c| { self.inner.lock().unwrap().accept().map(|c| match c {
let content = AnyToDeviceEventContent::KeyVerificationAccept(c); AcceptContent::ToDevice(c) => {
self.content_to_request(content) let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
self.content_to_request(content).into()
}
AcceptContent::Room(room_id, content) => RoomMessageRequest {
room_id,
txn_id: Uuid::new_v4(),
content: AnyMessageEventContent::KeyVerificationAccept(content),
}
.into(),
}) })
} }

View File

@ -14,6 +14,7 @@
use std::{ use std::{
convert::TryFrom, convert::TryFrom,
matches,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@ -24,8 +25,8 @@ use matrix_sdk_common::{
events::{ events::{
key::verification::{ key::verification::{
accept::{ accept::{
AcceptMethod, AcceptToDeviceEventContent, MSasV1Content as AcceptV1Content, AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent,
MSasV1ContentInit as AcceptV1ContentInit, MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit,
}, },
cancel::{CancelCode, CancelToDeviceEventContent}, cancel::{CancelCode, CancelToDeviceEventContent},
key::KeyToDeviceEventContent, key::KeyToDeviceEventContent,
@ -39,13 +40,13 @@ use matrix_sdk_common::{
}, },
AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent,
}, },
identifiers::{DeviceId, EventId, UserId}, identifiers::{DeviceId, EventId, RoomId, UserId},
uuid::Uuid, uuid::Uuid,
}; };
use tracing::error; use tracing::error;
use super::{ use super::{
event_enums::{OutgoingContent, StartContent}, event_enums::{AcceptContent, OutgoingContent, StartContent},
helpers::{ helpers::{
calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds,
}, },
@ -74,20 +75,28 @@ const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum FlowId { pub enum FlowId {
ToDevice(String), ToDevice(String),
InRoom(EventId), InRoom(RoomId, EventId),
} }
impl FlowId { impl FlowId {
pub fn room_id(&self) -> Option<&RoomId> {
if let FlowId::InRoom(r, _) = &self {
Some(r)
} else {
None
}
}
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
match self { match self {
FlowId::InRoom(r) => r.to_string(), FlowId::InRoom(_, r) => r.to_string(),
FlowId::ToDevice(t) => t.to_string(), FlowId::ToDevice(t) => t.to_string(),
} }
} }
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
match self { match self {
FlowId::InRoom(r) => r.as_str(), FlowId::InRoom(_, r) => r.as_str(),
FlowId::ToDevice(t) => t.as_str(), FlowId::ToDevice(t) => t.as_str(),
} }
} }
@ -340,12 +349,13 @@ impl SasState<Created> {
/// ///
/// * `other_identity` - The identity of the other user if one exists. /// * `other_identity` - The identity of the other user if one exists.
pub fn new_in_room( pub fn new_in_room(
room_id: RoomId,
event_id: EventId, event_id: EventId,
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> SasState<Created> { ) -> SasState<Created> {
let flow_id = FlowId::InRoom(event_id); let flow_id = FlowId::InRoom(room_id, event_id);
Self::new_helper(flow_id, account, other_device, other_identity) Self::new_helper(flow_id, account, other_device, other_identity)
} }
@ -380,7 +390,7 @@ impl SasState<Created> {
pub fn as_start_content(&self) -> StartContent { pub fn as_start_content(&self) -> StartContent {
match self.verification_flow_id.as_ref() { match self.verification_flow_id.as_ref() {
FlowId::ToDevice(s) => StartContent::ToDevice(StartToDeviceEventContent { FlowId::ToDevice(_) => StartContent::ToDevice(StartToDeviceEventContent {
transaction_id: self.verification_flow_id.to_string(), transaction_id: self.verification_flow_id.to_string(),
from_device: self.device_id().into(), from_device: self.device_id().into(),
method: StartMethod::MSasV1( method: StartMethod::MSasV1(
@ -388,16 +398,19 @@ impl SasState<Created> {
.expect("Invalid initial protocol definitions."), .expect("Invalid initial protocol definitions."),
), ),
}), }),
FlowId::InRoom(e) => StartContent::Room(StartEventContent { FlowId::InRoom(r, e) => StartContent::Room(
from_device: self.device_id().into(), r.clone(),
method: StartMethod::MSasV1( StartEventContent {
MSasV1Content::new(self.state.protocol_definitions.clone()) from_device: self.device_id().into(),
.expect("Invalid initial protocol definitions."), method: StartMethod::MSasV1(
), MSasV1Content::new(self.state.protocol_definitions.clone())
relation: Relation { .expect("Invalid initial protocol definitions."),
event_id: e.clone(), ),
relation: Relation {
event_id: e.clone(),
},
}, },
}), ),
} }
} }
@ -558,25 +571,40 @@ impl SasState<Started> {
/// This should be sent out automatically if the SAS verification flow has /// This should be sent out automatically if the SAS verification flow has
/// been started because of a /// been started because of a
/// m.key.verification.request -> m.key.verification.ready flow. /// m.key.verification.request -> m.key.verification.ready flow.
pub fn as_content(&self) -> AcceptToDeviceEventContent { pub fn as_content(&self) -> AcceptContent {
let accepted_protocols = AcceptedProtocols::default(); let accepted_protocols = AcceptedProtocols::default();
AcceptToDeviceEventContent { let method = AcceptMethod::MSasV1(
transaction_id: self.verification_flow_id.to_string(), AcceptV1ContentInit {
method: AcceptMethod::MSasV1( commitment: self.state.commitment.clone(),
AcceptV1ContentInit { hash: accepted_protocols.hash,
commitment: self.state.commitment.clone(), key_agreement_protocol: accepted_protocols.key_agreement_protocol,
hash: accepted_protocols.hash, message_authentication_code: accepted_protocols.message_auth_code,
key_agreement_protocol: accepted_protocols.key_agreement_protocol, short_authentication_string: self
message_authentication_code: accepted_protocols.message_auth_code, .state
short_authentication_string: self .protocol_definitions
.state .short_authentication_string
.protocol_definitions .clone(),
.short_authentication_string }
.clone(), .into(),
} );
match self.verification_flow_id.as_ref() {
FlowId::ToDevice(_) => AcceptToDeviceEventContent {
transaction_id: self.verification_flow_id.to_string(),
method,
}
.into(),
FlowId::InRoom(r, e) => (
r.clone(),
AcceptEventContent {
method,
relation: Relation {
event_id: e.clone(),
},
},
)
.into(), .into(),
),
} }
} }
@ -676,7 +704,7 @@ impl SasState<Accepted> {
transaction_id: s.to_string(), transaction_id: s.to_string(),
key: self.inner.lock().unwrap().public_key(), key: self.inner.lock().unwrap().public_key(),
}, },
FlowId::InRoom(r) => { FlowId::InRoom(_, r) => {
todo!("In-room verifications aren't implemented") todo!("In-room verifications aren't implemented")
} }
} }