crypto: Time out verification requests as well

master
Damir Jelić 2021-07-09 17:01:35 +02:00
parent cca73b2622
commit 71c89c2670
6 changed files with 84 additions and 35 deletions

View File

@ -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,

View File

@ -665,16 +665,15 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent {
} }
} }
#[cfg(test)] use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest};
use crate::OutgoingVerificationRequest;
use crate::{OutgoingRequest, 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)),
} }
} }
} }

View File

@ -247,7 +247,19 @@ impl VerificationMachine {
} }
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
.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( if let Ok(OutgoingContent::ToDevice(AnyToDeviceEventContent::KeyVerificationCancel(
content, content,
))) = request.clone().try_into() ))) = request.clone().try_into()
@ -257,7 +269,7 @@ impl VerificationMachine {
events.push(AnyToDeviceEvent::KeyVerificationCancel(event).into()); events.push(AnyToDeviceEvent::KeyVerificationCancel(event).into());
} }
self.verifications.add_request(request) self.verifications.add_verification_request(request)
} }
events events

View File

@ -525,6 +525,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,
@ -540,7 +542,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)
} }

View File

@ -207,7 +207,7 @@ 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. /// Cancel the verification.
@ -223,7 +223,7 @@ impl QrVerification {
/// otherwise it returns a request that needs to be sent out. /// otherwise it returns a request that needs to be sent out.
/// ///
/// [`cancel()`]: #method.cancel /// [`cancel()`]: #method.cancel
pub fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingContent> { pub fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingVerificationRequest> {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
if let Some(request) = &self.request_handle { if let Some(request) = &self.request_handle {
@ -240,7 +240,7 @@ impl QrVerification {
| InnerState::Reciprocated(_) | InnerState::Reciprocated(_)
| InnerState::Done(_) => { | InnerState::Done(_) => {
*state = InnerState::Cancelled(new_state); *state = InnerState::Cancelled(new_state);
Some(content) Some(self.content_to_request(content))
} }
InnerState::Cancelled(_) => None, InnerState::Cancelled(_) => None,
} }
@ -966,20 +966,20 @@ mod test {
.unwrap(); .unwrap();
let request = bob_verification.reciprocate().unwrap(); let request = bob_verification.reciprocate().unwrap();
let content = OutgoingContent::from(request); 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();

View File

@ -12,10 +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.
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::{
@ -54,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,
@ -68,6 +73,7 @@ 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,
} }
@ -123,6 +129,7 @@ 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,
} }
} }
@ -220,6 +227,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
@ -345,6 +357,7 @@ 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(),
} }
} }
@ -385,8 +398,12 @@ 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(true, &CancelCode::User); 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()))
@ -409,14 +426,24 @@ impl VerificationRequest {
self.verification_cache.get(self.other_user(), self.flow_id().as_str()) self.verification_cache.get(self.other_user(), self.flow_id().as_str())
{ {
match verification { match verification {
crate::Verification::SasV1(s) => s.cancel(), crate::Verification::SasV1(s) => s.cancel_with_code(cancel_code),
crate::Verification::QrV1(q) => q.cancel(), crate::Verification::QrV1(q) => q.cancel_with_code(cancel_code),
}; };
} }
request request
} }
pub(crate) fn cancel_if_timed_out(&self) -> Option<OutgoingVerificationRequest> {
if self.is_cancelled() || self.is_done() {
None
} else if self.timed_out() {
self.cancel_with_code(CancelCode::Timeout)
} 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();
@ -1095,7 +1122,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};
@ -1166,7 +1193,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);
@ -1223,7 +1250,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);
@ -1233,7 +1260,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();
@ -1290,7 +1317,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);
@ -1300,7 +1327,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();