crypto: Add the users for key claiming to the outgoing requests

This makes sure that we immediately claim one-time keys after we
receive the sync changes instead of waiting for a room message to be
sent by the user.

Users may not send a message in a long time which would mean that we'll
likely never share secrets or forward room keys if a Olm session was
missing with the requester.
This commit is contained in:
Damir Jelić 2021-08-10 15:56:43 +02:00
parent da82fbab4f
commit 315e77ebf2
5 changed files with 69 additions and 6 deletions

View file

@ -2120,6 +2120,14 @@ impl Client {
.unwrap(); .unwrap();
} }
} }
OutgoingRequests::KeysClaim(request) => {
if let Ok(resp) = self.send(request.clone(), None).await {
self.base_client
.mark_request_as_sent(r.request_id(), &resp)
.await
.unwrap();
}
}
} }
} }
} }

View file

@ -20,11 +20,12 @@
// If we don't trust the device store an object that remembers the request and // If we don't trust the device store an object that remembers the request and
// let the users introspect that object. // let the users introspect that object.
use std::sync::Arc; use std::{collections::BTreeMap, sync::Arc};
use dashmap::{mapref::entry::Entry, DashMap, DashSet}; use dashmap::{mapref::entry::Entry, DashMap, DashSet};
use matrix_sdk_common::uuid::Uuid; use matrix_sdk_common::uuid::Uuid;
use ruma::{ use ruma::{
api::client::r0::keys::claim_keys::Request as KeysClaimRequest,
events::{ events::{
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
room_key_request::{Action, RequestedKeyInfo, RoomKeyRequestToDeviceEventContent}, room_key_request::{Action, RequestedKeyInfo, RoomKeyRequestToDeviceEventContent},
@ -36,7 +37,7 @@ use ruma::{
}, },
AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
}, },
DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId, DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId,
}; };
use tracing::{debug, info, trace, warn}; use tracing::{debug, info, trace, warn};
@ -112,6 +113,28 @@ impl GossipMachine {
self.outgoing_requests.iter().map(|i| i.value().clone()).collect(); self.outgoing_requests.iter().map(|i| i.value().clone()).collect();
key_requests.extend(key_forwards); key_requests.extend(key_forwards);
let users_for_key_claim: BTreeMap<_, _> = self
.users_for_key_claim
.iter()
.map(|i| {
let device_map = i
.value()
.iter()
.map(|d| (d.key().to_owned(), DeviceKeyAlgorithm::SignedCurve25519))
.collect();
(i.key().to_owned(), device_map)
})
.collect();
if !users_for_key_claim.is_empty() {
let key_claim_request = KeysClaimRequest::new(users_for_key_claim);
key_requests.push(OutgoingRequest {
request_id: Uuid::new_v4(),
request: Arc::new(key_claim_request.into()),
});
}
Ok(key_requests) Ok(key_requests)
} }
@ -866,6 +889,7 @@ mod test {
use std::{convert::TryInto, sync::Arc}; use std::{convert::TryInto, sync::Arc};
use dashmap::DashMap; use dashmap::DashMap;
use matches::assert_matches;
use matrix_sdk_common::locks::Mutex; use matrix_sdk_common::locks::Mutex;
use matrix_sdk_test::async_test; use matrix_sdk_test::async_test;
use ruma::{ use ruma::{
@ -888,6 +912,7 @@ mod test {
session_manager::GroupSessionCache, session_manager::GroupSessionCache,
store::{Changes, CryptoStore, MemoryStore, Store}, store::{Changes, CryptoStore, MemoryStore, Store},
verification::VerificationMachine, verification::VerificationMachine,
OutgoingRequests,
}; };
fn alice_id() -> UserId { fn alice_id() -> UserId {
@ -1507,8 +1532,12 @@ mod test {
// Receive the room key request from alice. // Receive the room key request from alice.
bob_machine.receive_incoming_key_request(&event); bob_machine.receive_incoming_key_request(&event);
bob_machine.collect_incoming_key_requests().await.unwrap(); bob_machine.collect_incoming_key_requests().await.unwrap();
// Bob doesn't have an outgoing requests since we're lacking a session. // Bob only has a keys claim request, since we're lacking a session
assert!(bob_machine.outgoing_to_device_requests().await.unwrap().is_empty()); assert_eq!(bob_machine.outgoing_to_device_requests().await.unwrap().len(), 1);
assert_matches!(
bob_machine.outgoing_to_device_requests().await.unwrap().first().unwrap().request(),
OutgoingRequests::KeysClaim(_)
);
assert!(!bob_machine.users_for_key_claim.is_empty()); assert!(!bob_machine.users_for_key_claim.is_empty());
assert!(!bob_machine.wait_queue.is_empty()); assert!(!bob_machine.wait_queue.is_empty());

View file

@ -18,7 +18,7 @@ use matrix_sdk_common::uuid::Uuid;
use ruma::{ use ruma::{
api::client::r0::{ api::client::r0::{
keys::{ keys::{
claim_keys::Response as KeysClaimResponse, claim_keys::{Request as KeysClaimRequest, Response as KeysClaimResponse},
get_keys::Response as KeysQueryResponse, get_keys::Response as KeysQueryResponse,
upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse}, upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse},
upload_signatures::{ upload_signatures::{
@ -183,6 +183,10 @@ pub enum OutgoingRequests {
/// The keys query request, fetching the device and cross singing keys of /// The keys query request, fetching the device and cross singing keys of
/// other users. /// other users.
KeysQuery(KeysQueryRequest), KeysQuery(KeysQueryRequest),
/// The request to claim one-time keys for a user/device pair from the
/// server, after the response is received an 1-to-1 Olm session will be
/// established with the user/device pair.
KeysClaim(KeysClaimRequest),
/// The to-device requests, this request is used for a couple of different /// The to-device requests, this request is used for a couple of different
/// things, the main use is key requests/forwards and interactive device /// things, the main use is key requests/forwards and interactive device
/// verification. /// verification.
@ -211,6 +215,12 @@ impl From<KeysQueryRequest> for OutgoingRequests {
} }
} }
impl From<KeysClaimRequest> for OutgoingRequests {
fn from(r: KeysClaimRequest) -> Self {
Self::KeysClaim(r)
}
}
impl From<KeysUploadRequest> for OutgoingRequests { impl From<KeysUploadRequest> for OutgoingRequests {
fn from(request: KeysUploadRequest) -> Self { fn from(request: KeysUploadRequest) -> Self {
OutgoingRequests::KeysUpload(request) OutgoingRequests::KeysUpload(request)

View file

@ -279,7 +279,22 @@ impl SessionManager {
} }
} }
Ok(self.store.save_changes(changes).await?) // TODO turn this into a single save_changes() call.
self.store.save_changes(changes).await?;
match self.key_request_machine.collect_incoming_key_requests().await {
Ok(sessions) => {
let changes = Changes { sessions, ..Default::default() };
self.store.save_changes(changes).await?
}
// We don't propagate the error here since the next sync will retry
// this.
Err(e) => {
warn!(error =? e, "Error while trying to collect the incoming secret requests")
}
}
Ok(())
} }
} }

View file

@ -770,6 +770,7 @@ impl TryFrom<OutgoingRequest> for OutgoingContent {
crate::OutgoingRequests::KeysQuery(_) => Err("Invalid request type".to_owned()), crate::OutgoingRequests::KeysQuery(_) => Err("Invalid request type".to_owned()),
crate::OutgoingRequests::ToDeviceRequest(r) => Self::try_from(r.clone()), crate::OutgoingRequests::ToDeviceRequest(r) => Self::try_from(r.clone()),
crate::OutgoingRequests::SignatureUpload(_) => Err("Invalid request type".to_owned()), crate::OutgoingRequests::SignatureUpload(_) => Err("Invalid request type".to_owned()),
crate::OutgoingRequests::KeysClaim(_) => Err("Invalid request type".to_owned()),
crate::OutgoingRequests::RoomMessage(r) => Ok(Self::from(r.clone())), crate::OutgoingRequests::RoomMessage(r) => Ok(Self::from(r.clone())),
} }
} }