crypto: Refactor and split out the gossiping logic
This commit is contained in:
parent
adca302dfe
commit
2cf6ad21d3
9 changed files with 362 additions and 331 deletions
|
@ -36,13 +36,11 @@ use ruma::{
|
||||||
},
|
},
|
||||||
AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
|
AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
|
||||||
},
|
},
|
||||||
to_device::DeviceIdOrAllDevices,
|
|
||||||
DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId,
|
DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use tracing::{debug, info, trace, warn};
|
||||||
use thiserror::Error;
|
|
||||||
use tracing::{debug, error, info, trace, warn};
|
|
||||||
|
|
||||||
|
use super::{GossipRequest, KeyforwardDecision, RequestEvent, RequestInfo, SecretInfo, WaitQueue};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{OlmError, OlmResult},
|
error::{OlmError, OlmResult},
|
||||||
olm::{InboundGroupSession, Session, ShareState},
|
olm::{InboundGroupSession, Session, ShareState},
|
||||||
|
@ -52,139 +50,8 @@ use crate::{
|
||||||
Device,
|
Device,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An error describing why a key share request won't be honored.
|
|
||||||
#[derive(Debug, Clone, Error, PartialEq)]
|
|
||||||
pub enum KeyshareDecision {
|
|
||||||
/// The key request is from a device that we don't own, we're only sharing
|
|
||||||
/// sessions that we know the requesting device already was supposed to get.
|
|
||||||
#[error("can't find an active outbound group session")]
|
|
||||||
MissingOutboundSession,
|
|
||||||
/// The key request is from a device that we don't own and the device wasn't
|
|
||||||
/// meant to receive the session in the original key share.
|
|
||||||
#[error("outbound session wasn't shared with the requesting device")]
|
|
||||||
OutboundSessionNotShared,
|
|
||||||
/// The key request is from a device we own, yet we don't trust it.
|
|
||||||
#[error("requesting device isn't trusted")]
|
|
||||||
UntrustedDevice,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum RequestEvent {
|
pub(crate) struct GossipMachine {
|
||||||
KeyShare(ToDeviceEvent<RoomKeyRequestToDeviceEventContent>),
|
|
||||||
Secret(ToDeviceEvent<SecretRequestEventContent>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ToDeviceEvent<SecretRequestEventContent>> for RequestEvent {
|
|
||||||
fn from(e: ToDeviceEvent<SecretRequestEventContent>) -> Self {
|
|
||||||
Self::Secret(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ToDeviceEvent<RoomKeyRequestToDeviceEventContent>> for RequestEvent {
|
|
||||||
fn from(e: ToDeviceEvent<RoomKeyRequestToDeviceEventContent>) -> Self {
|
|
||||||
Self::KeyShare(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestEvent {
|
|
||||||
fn to_request_info(&self) -> RequestInfo {
|
|
||||||
RequestInfo::new(
|
|
||||||
self.sender().to_owned(),
|
|
||||||
self.requesting_device_id().into(),
|
|
||||||
self.request_id().to_owned(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sender(&self) -> &UserId {
|
|
||||||
match self {
|
|
||||||
RequestEvent::KeyShare(e) => &e.sender,
|
|
||||||
RequestEvent::Secret(e) => &e.sender,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn requesting_device_id(&self) -> &DeviceId {
|
|
||||||
match self {
|
|
||||||
RequestEvent::KeyShare(e) => &e.content.requesting_device_id,
|
|
||||||
RequestEvent::Secret(e) => &e.content.requesting_device_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn request_id(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
RequestEvent::KeyShare(e) => &e.content.request_id,
|
|
||||||
RequestEvent::Secret(e) => &e.content.request_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
struct RequestInfo {
|
|
||||||
sender: UserId,
|
|
||||||
requesting_device_id: DeviceIdBox,
|
|
||||||
request_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestInfo {
|
|
||||||
fn new(sender: UserId, requesting_device_id: DeviceIdBox, request_id: String) -> Self {
|
|
||||||
Self { sender, requesting_device_id, request_id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A queue where we store room key requests that we want to serve but the
|
|
||||||
/// device that requested the key doesn't share an Olm session with us.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct WaitQueue {
|
|
||||||
requests_waiting_for_session: Arc<DashMap<RequestInfo, RequestEvent>>,
|
|
||||||
requests_ids_waiting: Arc<DashMap<(UserId, DeviceIdBox), DashSet<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WaitQueue {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
requests_waiting_for_session: Arc::new(DashMap::new()),
|
|
||||||
requests_ids_waiting: Arc::new(DashMap::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.requests_ids_waiting.is_empty() && self.requests_waiting_for_session.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert(&self, device: &Device, event: &ToDeviceEvent<RoomKeyRequestToDeviceEventContent>) {
|
|
||||||
let key = RequestInfo::new(
|
|
||||||
device.user_id().to_owned(),
|
|
||||||
device.device_id().into(),
|
|
||||||
event.content.request_id.to_owned(),
|
|
||||||
);
|
|
||||||
self.requests_waiting_for_session.insert(key, event.clone().into());
|
|
||||||
|
|
||||||
let key = (device.user_id().to_owned(), device.device_id().into());
|
|
||||||
self.requests_ids_waiting
|
|
||||||
.entry(key)
|
|
||||||
.or_insert_with(DashSet::new)
|
|
||||||
.insert(event.content.request_id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove(&self, user_id: &UserId, device_id: &DeviceId) -> Vec<(RequestInfo, RequestEvent)> {
|
|
||||||
self.requests_ids_waiting
|
|
||||||
.remove(&(user_id.to_owned(), device_id.into()))
|
|
||||||
.map(|(_, request_ids)| {
|
|
||||||
request_ids
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| {
|
|
||||||
let key =
|
|
||||||
RequestInfo::new(user_id.to_owned(), device_id.into(), id.to_owned());
|
|
||||||
self.requests_waiting_for_session.remove(&key)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct KeyRequestMachine {
|
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
device_id: Arc<DeviceId>,
|
device_id: Arc<DeviceId>,
|
||||||
store: Store,
|
store: Store,
|
||||||
|
@ -195,136 +62,7 @@ pub(crate) struct KeyRequestMachine {
|
||||||
users_for_key_claim: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
users_for_key_claim: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct describing an outgoing key request.
|
impl GossipMachine {
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct OutgoingKeyRequest {
|
|
||||||
/// The user we requested the key from
|
|
||||||
pub request_recipient: UserId,
|
|
||||||
/// The unique id of the key request.
|
|
||||||
pub request_id: Uuid,
|
|
||||||
/// The info of the requested key.
|
|
||||||
pub info: SecretInfo,
|
|
||||||
/// Has the request been sent out.
|
|
||||||
pub sent_out: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An enum over the various secret request types we can have.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum SecretInfo {
|
|
||||||
// Info for the `m.room_key_request` variant
|
|
||||||
KeyRequest(RequestedKeyInfo),
|
|
||||||
// Info for the `m.secret.request` variant
|
|
||||||
SecretRequest(SecretName),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RequestedKeyInfo> for SecretInfo {
|
|
||||||
fn from(i: RequestedKeyInfo) -> Self {
|
|
||||||
Self::KeyRequest(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SecretName> for SecretInfo {
|
|
||||||
fn from(i: SecretName) -> Self {
|
|
||||||
Self::SecretRequest(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutgoingKeyRequest {
|
|
||||||
/// Create an ougoing secret request for the given secret.
|
|
||||||
pub(crate) fn from_secret_name(own_user_id: UserId, secret_name: SecretName) -> Self {
|
|
||||||
Self {
|
|
||||||
request_recipient: own_user_id,
|
|
||||||
request_id: Uuid::new_v4(),
|
|
||||||
info: secret_name.into(),
|
|
||||||
sent_out: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn request_type(&self) -> &str {
|
|
||||||
match &self.info {
|
|
||||||
SecretInfo::KeyRequest(_) => "m.room_key_request",
|
|
||||||
SecretInfo::SecretRequest(s) => s.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_request(&self, own_device_id: &DeviceId) -> OutgoingRequest {
|
|
||||||
let content = match &self.info {
|
|
||||||
SecretInfo::KeyRequest(r) => {
|
|
||||||
AnyToDeviceEventContent::RoomKeyRequest(RoomKeyRequestToDeviceEventContent::new(
|
|
||||||
Action::Request,
|
|
||||||
Some(r.clone()),
|
|
||||||
own_device_id.to_owned(),
|
|
||||||
self.request_id.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
SecretInfo::SecretRequest(s) => {
|
|
||||||
AnyToDeviceEventContent::SecretRequest(SecretRequestEventContent::new(
|
|
||||||
RequestAction::Request(s.clone()),
|
|
||||||
own_device_id.to_owned(),
|
|
||||||
self.request_id.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = ToDeviceRequest::new_with_id(
|
|
||||||
&self.request_recipient,
|
|
||||||
DeviceIdOrAllDevices::AllDevices,
|
|
||||||
content,
|
|
||||||
self.request_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_cancellation(&self, own_device_id: &DeviceId) -> OutgoingRequest {
|
|
||||||
let content = match self.info {
|
|
||||||
SecretInfo::KeyRequest(_) => {
|
|
||||||
AnyToDeviceEventContent::RoomKeyRequest(RoomKeyRequestToDeviceEventContent::new(
|
|
||||||
Action::CancelRequest,
|
|
||||||
None,
|
|
||||||
own_device_id.to_owned(),
|
|
||||||
self.request_id.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
SecretInfo::SecretRequest(_) => {
|
|
||||||
AnyToDeviceEventContent::SecretRequest(SecretRequestEventContent::new(
|
|
||||||
RequestAction::RequestCancellation,
|
|
||||||
own_device_id.to_owned(),
|
|
||||||
self.request_id.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = ToDeviceRequest::new(
|
|
||||||
&self.request_recipient,
|
|
||||||
DeviceIdOrAllDevices::AllDevices,
|
|
||||||
content,
|
|
||||||
);
|
|
||||||
|
|
||||||
OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for OutgoingKeyRequest {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
let is_info_equal = match (&self.info, &other.info) {
|
|
||||||
(SecretInfo::KeyRequest(first), SecretInfo::KeyRequest(second)) => {
|
|
||||||
first.algorithm == second.algorithm
|
|
||||||
&& first.room_id == second.room_id
|
|
||||||
&& first.session_id == second.session_id
|
|
||||||
}
|
|
||||||
(SecretInfo::SecretRequest(first), SecretInfo::SecretRequest(second)) => {
|
|
||||||
first == second
|
|
||||||
}
|
|
||||||
(SecretInfo::KeyRequest(_), SecretInfo::SecretRequest(_))
|
|
||||||
| (SecretInfo::SecretRequest(_), SecretInfo::KeyRequest(_)) => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.request_id == other.request_id && is_info_equal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyRequestMachine {
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
device_id: Arc<DeviceId>,
|
device_id: Arc<DeviceId>,
|
||||||
|
@ -711,7 +449,7 @@ impl KeyRequestMachine {
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
session: &InboundGroupSession,
|
session: &InboundGroupSession,
|
||||||
) -> Result<Option<u32>, KeyshareDecision> {
|
) -> Result<Option<u32>, KeyforwardDecision> {
|
||||||
let outbound_session = self
|
let outbound_session = self
|
||||||
.outbound_group_sessions
|
.outbound_group_sessions
|
||||||
.get_with_id(session.room_id(), session.session_id())
|
.get_with_id(session.room_id(), session.session_id())
|
||||||
|
@ -723,7 +461,7 @@ impl KeyRequestMachine {
|
||||||
if device.verified() {
|
if device.verified() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err(KeyshareDecision::UntrustedDevice)
|
Err(KeyforwardDecision::UntrustedDevice)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -738,7 +476,7 @@ impl KeyRequestMachine {
|
||||||
} else if device.user_id() == self.user_id() {
|
} else if device.user_id() == self.user_id() {
|
||||||
own_device_check()
|
own_device_check()
|
||||||
} else {
|
} else {
|
||||||
Err(KeyshareDecision::OutboundSessionNotShared)
|
Err(KeyforwardDecision::OutboundSessionNotShared)
|
||||||
}
|
}
|
||||||
// Else just check if it's one of our own devices that requested the key
|
// Else just check if it's one of our own devices that requested the key
|
||||||
// and check if the device is trusted.
|
// and check if the device is trusted.
|
||||||
|
@ -747,7 +485,7 @@ impl KeyRequestMachine {
|
||||||
// Otherwise, there's not enough info to decide if we can safely share
|
// Otherwise, there's not enough info to decide if we can safely share
|
||||||
// the session.
|
// the session.
|
||||||
} else {
|
} else {
|
||||||
Err(KeyshareDecision::MissingOutboundSession)
|
Err(KeyforwardDecision::MissingOutboundSession)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,13 +567,13 @@ impl KeyRequestMachine {
|
||||||
pub fn request_missing_secrets(
|
pub fn request_missing_secrets(
|
||||||
own_user_id: &UserId,
|
own_user_id: &UserId,
|
||||||
secret_names: Vec<SecretName>,
|
secret_names: Vec<SecretName>,
|
||||||
) -> Vec<OutgoingKeyRequest> {
|
) -> Vec<GossipRequest> {
|
||||||
if !secret_names.is_empty() {
|
if !secret_names.is_empty() {
|
||||||
info!(secret_names =? secret_names, "Creating new outgoing secret requests");
|
info!(secret_names =? secret_names, "Creating new outgoing secret requests");
|
||||||
|
|
||||||
secret_names
|
secret_names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|n| OutgoingKeyRequest::from_secret_name(own_user_id.to_owned(), n))
|
.map(|n| GossipRequest::from_secret_name(own_user_id.to_owned(), n))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
trace!("No secrets are missing from our store, not requesting them");
|
trace!("No secrets are missing from our store, not requesting them");
|
||||||
|
@ -847,7 +585,7 @@ impl KeyRequestMachine {
|
||||||
&self,
|
&self,
|
||||||
key_info: SecretInfo,
|
key_info: SecretInfo,
|
||||||
) -> Result<OutgoingRequest, CryptoStoreError> {
|
) -> Result<OutgoingRequest, CryptoStoreError> {
|
||||||
let request = OutgoingKeyRequest {
|
let request = GossipRequest {
|
||||||
request_recipient: self.user_id().to_owned(),
|
request_recipient: self.user_id().to_owned(),
|
||||||
request_id: Uuid::new_v4(),
|
request_id: Uuid::new_v4(),
|
||||||
info: key_info,
|
info: key_info,
|
||||||
|
@ -896,10 +634,7 @@ impl KeyRequestMachine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save an outgoing key info.
|
/// Save an outgoing key info.
|
||||||
async fn save_outgoing_key_info(
|
async fn save_outgoing_key_info(&self, info: GossipRequest) -> Result<(), CryptoStoreError> {
|
||||||
&self,
|
|
||||||
info: OutgoingKeyRequest,
|
|
||||||
) -> Result<(), CryptoStoreError> {
|
|
||||||
let mut changes = Changes::default();
|
let mut changes = Changes::default();
|
||||||
changes.key_requests.push(info);
|
changes.key_requests.push(info);
|
||||||
self.store.save_changes(changes).await?;
|
self.store.save_changes(changes).await?;
|
||||||
|
@ -911,7 +646,7 @@ impl KeyRequestMachine {
|
||||||
async fn get_key_info(
|
async fn get_key_info(
|
||||||
&self,
|
&self,
|
||||||
content: &ForwardedRoomKeyToDeviceEventContent,
|
content: &ForwardedRoomKeyToDeviceEventContent,
|
||||||
) -> Result<Option<OutgoingKeyRequest>, CryptoStoreError> {
|
) -> Result<Option<GossipRequest>, CryptoStoreError> {
|
||||||
let info = RequestedKeyInfo::new(
|
let info = RequestedKeyInfo::new(
|
||||||
content.algorithm.clone(),
|
content.algorithm.clone(),
|
||||||
content.room_id.clone(),
|
content.room_id.clone(),
|
||||||
|
@ -924,7 +659,7 @@ impl KeyRequestMachine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete the given outgoing key info.
|
/// Delete the given outgoing key info.
|
||||||
async fn delete_key_info(&self, info: &OutgoingKeyRequest) -> Result<(), CryptoStoreError> {
|
async fn delete_key_info(&self, info: &GossipRequest) -> Result<(), CryptoStoreError> {
|
||||||
self.store.delete_outgoing_secret_requests(info.request_id).await
|
self.store.delete_outgoing_secret_requests(info.request_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,7 +686,7 @@ impl KeyRequestMachine {
|
||||||
/// Mark the given outgoing key info as done.
|
/// Mark the given outgoing key info as done.
|
||||||
///
|
///
|
||||||
/// This will queue up a request cancellation.
|
/// This will queue up a request cancellation.
|
||||||
async fn mark_as_done(&self, key_info: OutgoingKeyRequest) -> Result<(), CryptoStoreError> {
|
async fn mark_as_done(&self, key_info: GossipRequest) -> Result<(), CryptoStoreError> {
|
||||||
trace!(
|
trace!(
|
||||||
recipient = key_info.request_recipient.as_str(),
|
recipient = key_info.request_recipient.as_str(),
|
||||||
request_type = key_info.request_type(),
|
request_type = key_info.request_type(),
|
||||||
|
@ -1127,15 +862,16 @@ mod test {
|
||||||
events::{
|
events::{
|
||||||
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
|
forwarded_room_key::ForwardedRoomKeyToDeviceEventContent,
|
||||||
room::encrypted::EncryptedToDeviceEventContent,
|
room::encrypted::EncryptedToDeviceEventContent,
|
||||||
room_key_request::RoomKeyRequestToDeviceEventContent, AnyToDeviceEvent, ToDeviceEvent,
|
room_key_request::RoomKeyRequestToDeviceEventContent,
|
||||||
secret::request::{RequestAction, RequestToDeviceEventContent, SecretName},
|
secret::request::{RequestAction, RequestToDeviceEventContent, SecretName},
|
||||||
|
AnyToDeviceEvent, ToDeviceEvent,
|
||||||
},
|
},
|
||||||
room_id,
|
room_id,
|
||||||
to_device::DeviceIdOrAllDevices,
|
to_device::DeviceIdOrAllDevices,
|
||||||
user_id, DeviceIdBox, RoomId, UserId,
|
user_id, DeviceIdBox, RoomId, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{KeyRequestMachine, KeyshareDecision};
|
use super::{GossipMachine, KeyforwardDecision};
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{LocalTrust, ReadOnlyDevice},
|
identities::{LocalTrust, ReadOnlyDevice},
|
||||||
olm::{Account, PrivateCrossSigningIdentity, ReadOnlyAccount},
|
olm::{Account, PrivateCrossSigningIdentity, ReadOnlyAccount},
|
||||||
|
@ -1180,7 +916,7 @@ mod test {
|
||||||
ReadOnlyAccount::new(&alice_id(), &alice2_device_id())
|
ReadOnlyAccount::new(&alice_id(), &alice2_device_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bob_machine() -> KeyRequestMachine {
|
fn bob_machine() -> GossipMachine {
|
||||||
let user_id = Arc::new(bob_id());
|
let user_id = Arc::new(bob_id());
|
||||||
let account = ReadOnlyAccount::new(&user_id, &alice_device_id());
|
let account = ReadOnlyAccount::new(&user_id, &alice_device_id());
|
||||||
let store: Arc<dyn CryptoStore> = Arc::new(MemoryStore::new());
|
let store: Arc<dyn CryptoStore> = Arc::new(MemoryStore::new());
|
||||||
|
@ -1189,7 +925,7 @@ mod test {
|
||||||
let store = Store::new(user_id.clone(), identity, store, verification);
|
let store = Store::new(user_id.clone(), identity, store, verification);
|
||||||
let session_cache = GroupSessionCache::new(store.clone());
|
let session_cache = GroupSessionCache::new(store.clone());
|
||||||
|
|
||||||
KeyRequestMachine::new(
|
GossipMachine::new(
|
||||||
user_id,
|
user_id,
|
||||||
bob_device_id().into(),
|
bob_device_id().into(),
|
||||||
store,
|
store,
|
||||||
|
@ -1198,7 +934,7 @@ mod test {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_machine() -> KeyRequestMachine {
|
async fn get_machine() -> GossipMachine {
|
||||||
let user_id: Arc<UserId> = alice_id().into();
|
let user_id: Arc<UserId> = alice_id().into();
|
||||||
let account = ReadOnlyAccount::new(&user_id, &alice_device_id());
|
let account = ReadOnlyAccount::new(&user_id, &alice_device_id());
|
||||||
let device = ReadOnlyDevice::from_account(&account).await;
|
let device = ReadOnlyDevice::from_account(&account).await;
|
||||||
|
@ -1209,7 +945,7 @@ mod test {
|
||||||
store.save_devices(&[device]).await.unwrap();
|
store.save_devices(&[device]).await.unwrap();
|
||||||
let session_cache = GroupSessionCache::new(store.clone());
|
let session_cache = GroupSessionCache::new(store.clone());
|
||||||
|
|
||||||
KeyRequestMachine::new(
|
GossipMachine::new(
|
||||||
user_id,
|
user_id,
|
||||||
alice_device_id().into(),
|
alice_device_id().into(),
|
||||||
store,
|
store,
|
||||||
|
@ -1411,7 +1147,7 @@ mod test {
|
||||||
.should_share_key(&own_device, &inbound)
|
.should_share_key(&own_device, &inbound)
|
||||||
.await
|
.await
|
||||||
.expect_err("Should not share with untrusted"),
|
.expect_err("Should not share with untrusted"),
|
||||||
KeyshareDecision::UntrustedDevice
|
KeyforwardDecision::UntrustedDevice
|
||||||
);
|
);
|
||||||
own_device.set_trust_state(LocalTrust::Verified);
|
own_device.set_trust_state(LocalTrust::Verified);
|
||||||
// Now we do want to share the keys.
|
// Now we do want to share the keys.
|
||||||
|
@ -1430,7 +1166,7 @@ mod test {
|
||||||
.should_share_key(&bob_device, &inbound)
|
.should_share_key(&bob_device, &inbound)
|
||||||
.await
|
.await
|
||||||
.expect_err("Should not share with other."),
|
.expect_err("Should not share with other."),
|
||||||
KeyshareDecision::MissingOutboundSession
|
KeyforwardDecision::MissingOutboundSession
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut changes = Changes::default();
|
let mut changes = Changes::default();
|
||||||
|
@ -1447,7 +1183,7 @@ mod test {
|
||||||
.should_share_key(&bob_device, &inbound)
|
.should_share_key(&bob_device, &inbound)
|
||||||
.await
|
.await
|
||||||
.expect_err("Should not share with other unless shared."),
|
.expect_err("Should not share with other unless shared."),
|
||||||
KeyshareDecision::OutboundSessionNotShared
|
KeyforwardDecision::OutboundSessionNotShared
|
||||||
);
|
);
|
||||||
|
|
||||||
bob_device.set_trust_state(LocalTrust::Verified);
|
bob_device.set_trust_state(LocalTrust::Verified);
|
||||||
|
@ -1459,7 +1195,7 @@ mod test {
|
||||||
.should_share_key(&bob_device, &inbound)
|
.should_share_key(&bob_device, &inbound)
|
||||||
.await
|
.await
|
||||||
.expect_err("Should not share with other unless shared."),
|
.expect_err("Should not share with other unless shared."),
|
||||||
KeyshareDecision::OutboundSessionNotShared
|
KeyforwardDecision::OutboundSessionNotShared
|
||||||
);
|
);
|
||||||
|
|
||||||
// We now share the session, since it was shared before.
|
// We now share the session, since it was shared before.
|
||||||
|
@ -1476,7 +1212,7 @@ mod test {
|
||||||
.should_share_key(&bob_device, &other_inbound)
|
.should_share_key(&bob_device, &other_inbound)
|
||||||
.await
|
.await
|
||||||
.expect_err("Should not share with other unless shared."),
|
.expect_err("Should not share with other unless shared."),
|
||||||
KeyshareDecision::MissingOutboundSession
|
KeyforwardDecision::MissingOutboundSession
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
300
matrix_sdk_crypto/src/gossiping/mod.rs
Normal file
300
matrix_sdk_crypto/src/gossiping/mod.rs
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
mod machine;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use dashmap::{DashMap, DashSet};
|
||||||
|
pub(crate) use machine::GossipMachine;
|
||||||
|
use matrix_sdk_common::uuid::Uuid;
|
||||||
|
use ruma::{
|
||||||
|
events::{
|
||||||
|
room_key_request::{Action, RequestedKeyInfo, RoomKeyRequestToDeviceEventContent},
|
||||||
|
secret::request::{
|
||||||
|
RequestAction, RequestToDeviceEventContent as SecretRequestEventContent, SecretName,
|
||||||
|
},
|
||||||
|
AnyToDeviceEventContent, ToDeviceEvent,
|
||||||
|
},
|
||||||
|
to_device::DeviceIdOrAllDevices,
|
||||||
|
DeviceId, DeviceIdBox, UserId,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
requests::{OutgoingRequest, ToDeviceRequest},
|
||||||
|
Device,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An error describing why a key share request won't be honored.
|
||||||
|
#[derive(Debug, Clone, Error, PartialEq)]
|
||||||
|
pub enum KeyforwardDecision {
|
||||||
|
/// The key request is from a device that we don't own, we're only sharing
|
||||||
|
/// sessions that we know the requesting device already was supposed to get.
|
||||||
|
#[error("can't find an active outbound group session")]
|
||||||
|
MissingOutboundSession,
|
||||||
|
/// The key request is from a device that we don't own and the device wasn't
|
||||||
|
/// meant to receive the session in the original key share.
|
||||||
|
#[error("outbound session wasn't shared with the requesting device")]
|
||||||
|
OutboundSessionNotShared,
|
||||||
|
/// The key request is from a device we own, yet we don't trust it.
|
||||||
|
#[error("requesting device isn't trusted")]
|
||||||
|
UntrustedDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A struct describing an outgoing key request.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct GossipRequest {
|
||||||
|
/// The user we requested the secret from
|
||||||
|
pub request_recipient: UserId,
|
||||||
|
/// The unique id of the secret request.
|
||||||
|
pub request_id: Uuid,
|
||||||
|
/// The info of the requested secret.
|
||||||
|
pub info: SecretInfo,
|
||||||
|
/// Has the request been sent out.
|
||||||
|
pub sent_out: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An enum over the various secret request types we can have.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum SecretInfo {
|
||||||
|
// Info for the `m.room_key_request` variant
|
||||||
|
KeyRequest(RequestedKeyInfo),
|
||||||
|
// Info for the `m.secret.request` variant
|
||||||
|
SecretRequest(SecretName),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RequestedKeyInfo> for SecretInfo {
|
||||||
|
fn from(i: RequestedKeyInfo) -> Self {
|
||||||
|
Self::KeyRequest(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SecretName> for SecretInfo {
|
||||||
|
fn from(i: SecretName) -> Self {
|
||||||
|
Self::SecretRequest(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GossipRequest {
|
||||||
|
/// Create an ougoing secret request for the given secret.
|
||||||
|
pub(crate) fn from_secret_name(own_user_id: UserId, secret_name: SecretName) -> Self {
|
||||||
|
Self {
|
||||||
|
request_recipient: own_user_id,
|
||||||
|
request_id: Uuid::new_v4(),
|
||||||
|
info: secret_name.into(),
|
||||||
|
sent_out: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_type(&self) -> &str {
|
||||||
|
match &self.info {
|
||||||
|
SecretInfo::KeyRequest(_) => "m.room_key_request",
|
||||||
|
SecretInfo::SecretRequest(s) => s.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_request(&self, own_device_id: &DeviceId) -> OutgoingRequest {
|
||||||
|
let content = match &self.info {
|
||||||
|
SecretInfo::KeyRequest(r) => {
|
||||||
|
AnyToDeviceEventContent::RoomKeyRequest(RoomKeyRequestToDeviceEventContent::new(
|
||||||
|
Action::Request,
|
||||||
|
Some(r.clone()),
|
||||||
|
own_device_id.to_owned(),
|
||||||
|
self.request_id.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
SecretInfo::SecretRequest(s) => {
|
||||||
|
AnyToDeviceEventContent::SecretRequest(SecretRequestEventContent::new(
|
||||||
|
RequestAction::Request(s.clone()),
|
||||||
|
own_device_id.to_owned(),
|
||||||
|
self.request_id.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let request = ToDeviceRequest::new_with_id(
|
||||||
|
&self.request_recipient,
|
||||||
|
DeviceIdOrAllDevices::AllDevices,
|
||||||
|
content,
|
||||||
|
self.request_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_cancellation(&self, own_device_id: &DeviceId) -> OutgoingRequest {
|
||||||
|
let content = match self.info {
|
||||||
|
SecretInfo::KeyRequest(_) => {
|
||||||
|
AnyToDeviceEventContent::RoomKeyRequest(RoomKeyRequestToDeviceEventContent::new(
|
||||||
|
Action::CancelRequest,
|
||||||
|
None,
|
||||||
|
own_device_id.to_owned(),
|
||||||
|
self.request_id.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
SecretInfo::SecretRequest(_) => {
|
||||||
|
AnyToDeviceEventContent::SecretRequest(SecretRequestEventContent::new(
|
||||||
|
RequestAction::RequestCancellation,
|
||||||
|
own_device_id.to_owned(),
|
||||||
|
self.request_id.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let request = ToDeviceRequest::new(
|
||||||
|
&self.request_recipient,
|
||||||
|
DeviceIdOrAllDevices::AllDevices,
|
||||||
|
content,
|
||||||
|
);
|
||||||
|
|
||||||
|
OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for GossipRequest {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let is_info_equal = match (&self.info, &other.info) {
|
||||||
|
(SecretInfo::KeyRequest(first), SecretInfo::KeyRequest(second)) => {
|
||||||
|
first.algorithm == second.algorithm
|
||||||
|
&& first.room_id == second.room_id
|
||||||
|
&& first.session_id == second.session_id
|
||||||
|
}
|
||||||
|
(SecretInfo::SecretRequest(first), SecretInfo::SecretRequest(second)) => {
|
||||||
|
first == second
|
||||||
|
}
|
||||||
|
(SecretInfo::KeyRequest(_), SecretInfo::SecretRequest(_))
|
||||||
|
| (SecretInfo::SecretRequest(_), SecretInfo::KeyRequest(_)) => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.request_id == other.request_id && is_info_equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum RequestEvent {
|
||||||
|
KeyShare(ToDeviceEvent<RoomKeyRequestToDeviceEventContent>),
|
||||||
|
Secret(ToDeviceEvent<SecretRequestEventContent>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ToDeviceEvent<SecretRequestEventContent>> for RequestEvent {
|
||||||
|
fn from(e: ToDeviceEvent<SecretRequestEventContent>) -> Self {
|
||||||
|
Self::Secret(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ToDeviceEvent<RoomKeyRequestToDeviceEventContent>> for RequestEvent {
|
||||||
|
fn from(e: ToDeviceEvent<RoomKeyRequestToDeviceEventContent>) -> Self {
|
||||||
|
Self::KeyShare(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestEvent {
|
||||||
|
fn to_request_info(&self) -> RequestInfo {
|
||||||
|
RequestInfo::new(
|
||||||
|
self.sender().to_owned(),
|
||||||
|
self.requesting_device_id().into(),
|
||||||
|
self.request_id().to_owned(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sender(&self) -> &UserId {
|
||||||
|
match self {
|
||||||
|
RequestEvent::KeyShare(e) => &e.sender,
|
||||||
|
RequestEvent::Secret(e) => &e.sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requesting_device_id(&self) -> &DeviceId {
|
||||||
|
match self {
|
||||||
|
RequestEvent::KeyShare(e) => &e.content.requesting_device_id,
|
||||||
|
RequestEvent::Secret(e) => &e.content.requesting_device_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_id(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
RequestEvent::KeyShare(e) => &e.content.request_id,
|
||||||
|
RequestEvent::Secret(e) => &e.content.request_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
struct RequestInfo {
|
||||||
|
sender: UserId,
|
||||||
|
requesting_device_id: DeviceIdBox,
|
||||||
|
request_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestInfo {
|
||||||
|
fn new(sender: UserId, requesting_device_id: DeviceIdBox, request_id: String) -> Self {
|
||||||
|
Self { sender, requesting_device_id, request_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A queue where we store room key requests that we want to serve but the
|
||||||
|
/// device that requested the key doesn't share an Olm session with us.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct WaitQueue {
|
||||||
|
requests_waiting_for_session: Arc<DashMap<RequestInfo, RequestEvent>>,
|
||||||
|
requests_ids_waiting: Arc<DashMap<(UserId, DeviceIdBox), DashSet<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WaitQueue {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
requests_waiting_for_session: Arc::new(DashMap::new()),
|
||||||
|
requests_ids_waiting: Arc::new(DashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.requests_ids_waiting.is_empty() && self.requests_waiting_for_session.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&self, device: &Device, event: &ToDeviceEvent<RoomKeyRequestToDeviceEventContent>) {
|
||||||
|
let key = RequestInfo::new(
|
||||||
|
device.user_id().to_owned(),
|
||||||
|
device.device_id().into(),
|
||||||
|
event.content.request_id.to_owned(),
|
||||||
|
);
|
||||||
|
self.requests_waiting_for_session.insert(key, event.clone().into());
|
||||||
|
|
||||||
|
let key = (device.user_id().to_owned(), device.device_id().into());
|
||||||
|
self.requests_ids_waiting
|
||||||
|
.entry(key)
|
||||||
|
.or_insert_with(DashSet::new)
|
||||||
|
.insert(event.content.request_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&self, user_id: &UserId, device_id: &DeviceId) -> Vec<(RequestInfo, RequestEvent)> {
|
||||||
|
self.requests_ids_waiting
|
||||||
|
.remove(&(user_id.to_owned(), device_id.into()))
|
||||||
|
.map(|(_, request_ids)| {
|
||||||
|
request_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| {
|
||||||
|
let key =
|
||||||
|
RequestInfo::new(user_id.to_owned(), device_id.into(), id.to_owned());
|
||||||
|
self.requests_waiting_for_session.remove(&key)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,8 +29,8 @@
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod file_encryption;
|
mod file_encryption;
|
||||||
|
mod gossiping;
|
||||||
mod identities;
|
mod identities;
|
||||||
mod key_request;
|
|
||||||
mod machine;
|
mod machine;
|
||||||
pub mod olm;
|
pub mod olm;
|
||||||
mod requests;
|
mod requests;
|
||||||
|
|
|
@ -48,8 +48,8 @@ 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},
|
||||||
|
gossiping::GossipMachine,
|
||||||
identities::{user::UserIdentities, Device, IdentityManager, UserDevices},
|
identities::{user::UserIdentities, Device, IdentityManager, UserDevices},
|
||||||
key_request::KeyRequestMachine,
|
|
||||||
olm::{
|
olm::{
|
||||||
Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys,
|
Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys,
|
||||||
InboundGroupSession, OlmDecryptionInfo, PrivateCrossSigningIdentity, ReadOnlyAccount,
|
InboundGroupSession, OlmDecryptionInfo, PrivateCrossSigningIdentity, ReadOnlyAccount,
|
||||||
|
@ -93,7 +93,7 @@ pub struct OlmMachine {
|
||||||
verification_machine: VerificationMachine,
|
verification_machine: VerificationMachine,
|
||||||
/// The state machine that is responsible to handle outgoing and incoming
|
/// The state machine that is responsible to handle outgoing and incoming
|
||||||
/// key requests.
|
/// key requests.
|
||||||
key_request_machine: KeyRequestMachine,
|
key_request_machine: GossipMachine,
|
||||||
/// State machine handling public user identities and devices, keeping track
|
/// State machine handling public user identities and devices, keeping track
|
||||||
/// of when a key query needs to be done and handling one.
|
/// of when a key query needs to be done and handling one.
|
||||||
identity_manager: IdentityManager,
|
identity_manager: IdentityManager,
|
||||||
|
@ -157,7 +157,7 @@ impl OlmMachine {
|
||||||
|
|
||||||
let group_session_manager = GroupSessionManager::new(account.clone(), store.clone());
|
let group_session_manager = GroupSessionManager::new(account.clone(), store.clone());
|
||||||
|
|
||||||
let key_request_machine = KeyRequestMachine::new(
|
let key_request_machine = GossipMachine::new(
|
||||||
user_id.clone(),
|
user_id.clone(),
|
||||||
device_id.clone(),
|
device_id.clone(),
|
||||||
store.clone(),
|
store.clone(),
|
||||||
|
|
|
@ -28,7 +28,7 @@ use tracing::{error, info, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::OlmResult,
|
error::OlmResult,
|
||||||
key_request::KeyRequestMachine,
|
gossiping::GossipMachine,
|
||||||
olm::Account,
|
olm::Account,
|
||||||
requests::{OutgoingRequest, ToDeviceRequest},
|
requests::{OutgoingRequest, ToDeviceRequest},
|
||||||
store::{Changes, Result as StoreResult, Store},
|
store::{Changes, Result as StoreResult, Store},
|
||||||
|
@ -45,7 +45,7 @@ pub(crate) struct SessionManager {
|
||||||
/// [`get_missing_sessions`](#method.get_missing_sessions) is called.
|
/// [`get_missing_sessions`](#method.get_missing_sessions) is called.
|
||||||
users_for_key_claim: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
users_for_key_claim: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
||||||
wedged_devices: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
wedged_devices: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
||||||
key_request_machine: KeyRequestMachine,
|
key_request_machine: GossipMachine,
|
||||||
outgoing_to_device_requests: Arc<DashMap<Uuid, OutgoingRequest>>,
|
outgoing_to_device_requests: Arc<DashMap<Uuid, OutgoingRequest>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ impl SessionManager {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
account: Account,
|
account: Account,
|
||||||
users_for_key_claim: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
users_for_key_claim: Arc<DashMap<UserId, DashSet<DeviceIdBox>>>,
|
||||||
key_request_machine: KeyRequestMachine,
|
key_request_machine: GossipMachine,
|
||||||
store: Store,
|
store: Store,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -297,8 +297,8 @@ mod test {
|
||||||
|
|
||||||
use super::SessionManager;
|
use super::SessionManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
gossiping::GossipMachine,
|
||||||
identities::ReadOnlyDevice,
|
identities::ReadOnlyDevice,
|
||||||
key_request::KeyRequestMachine,
|
|
||||||
olm::{Account, PrivateCrossSigningIdentity, ReadOnlyAccount},
|
olm::{Account, PrivateCrossSigningIdentity, ReadOnlyAccount},
|
||||||
session_manager::GroupSessionCache,
|
session_manager::GroupSessionCache,
|
||||||
store::{CryptoStore, MemoryStore, Store},
|
store::{CryptoStore, MemoryStore, Store},
|
||||||
|
@ -338,7 +338,7 @@ mod test {
|
||||||
|
|
||||||
let session_cache = GroupSessionCache::new(store.clone());
|
let session_cache = GroupSessionCache::new(store.clone());
|
||||||
|
|
||||||
let key_request = KeyRequestMachine::new(
|
let key_request = GossipMachine::new(
|
||||||
user_id,
|
user_id,
|
||||||
device_id,
|
device_id,
|
||||||
store.clone(),
|
store.clone(),
|
||||||
|
|
|
@ -26,8 +26,8 @@ use super::{
|
||||||
Changes, CryptoStore, InboundGroupSession, ReadOnlyAccount, Result, Session,
|
Changes, CryptoStore, InboundGroupSession, ReadOnlyAccount, Result, Session,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
gossiping::{GossipRequest, SecretInfo},
|
||||||
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
key_request::{OutgoingKeyRequest, SecretInfo},
|
|
||||||
olm::{OutboundGroupSession, PrivateCrossSigningIdentity},
|
olm::{OutboundGroupSession, PrivateCrossSigningIdentity},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ pub struct MemoryStore {
|
||||||
olm_hashes: Arc<DashMap<String, DashSet<String>>>,
|
olm_hashes: Arc<DashMap<String, DashSet<String>>>,
|
||||||
devices: DeviceStore,
|
devices: DeviceStore,
|
||||||
identities: Arc<DashMap<UserId, ReadOnlyUserIdentities>>,
|
identities: Arc<DashMap<UserId, ReadOnlyUserIdentities>>,
|
||||||
outgoing_key_requests: Arc<DashMap<Uuid, OutgoingKeyRequest>>,
|
outgoing_key_requests: Arc<DashMap<Uuid, GossipRequest>>,
|
||||||
key_requests_by_info: Arc<DashMap<String, Uuid>>,
|
key_requests_by_info: Arc<DashMap<String, Uuid>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,14 +236,14 @@ impl CryptoStore for MemoryStore {
|
||||||
async fn get_outgoing_secret_requests(
|
async fn get_outgoing_secret_requests(
|
||||||
&self,
|
&self,
|
||||||
request_id: Uuid,
|
request_id: Uuid,
|
||||||
) -> Result<Option<OutgoingKeyRequest>> {
|
) -> Result<Option<GossipRequest>> {
|
||||||
Ok(self.outgoing_key_requests.get(&request_id).map(|r| r.clone()))
|
Ok(self.outgoing_key_requests.get(&request_id).map(|r| r.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_secret_request_by_info(
|
async fn get_secret_request_by_info(
|
||||||
&self,
|
&self,
|
||||||
key_info: &SecretInfo,
|
key_info: &SecretInfo,
|
||||||
) -> Result<Option<OutgoingKeyRequest>> {
|
) -> Result<Option<GossipRequest>> {
|
||||||
let key_info_string = encode_key_info(key_info);
|
let key_info_string = encode_key_info(key_info);
|
||||||
|
|
||||||
Ok(self
|
Ok(self
|
||||||
|
@ -252,7 +252,7 @@ impl CryptoStore for MemoryStore {
|
||||||
.and_then(|i| self.outgoing_key_requests.get(&i).map(|r| r.clone())))
|
.and_then(|i| self.outgoing_key_requests.get(&i).map(|r| r.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_unsent_secret_requests(&self) -> Result<Vec<OutgoingKeyRequest>> {
|
async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.outgoing_key_requests
|
.outgoing_key_requests
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -68,11 +68,11 @@ use tracing::{info, warn};
|
||||||
pub use self::sled::SledStore;
|
pub use self::sled::SledStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SessionUnpicklingError,
|
error::SessionUnpicklingError,
|
||||||
|
gossiping::{GossipRequest, SecretInfo},
|
||||||
identities::{
|
identities::{
|
||||||
user::{OwnUserIdentity, UserIdentities, UserIdentity},
|
user::{OwnUserIdentity, UserIdentities, UserIdentity},
|
||||||
Device, ReadOnlyDevice, ReadOnlyUserIdentities, UserDevices,
|
Device, ReadOnlyDevice, ReadOnlyUserIdentities, UserDevices,
|
||||||
},
|
},
|
||||||
key_request::{OutgoingKeyRequest, SecretInfo},
|
|
||||||
olm::{
|
olm::{
|
||||||
InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
|
InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
|
||||||
ReadOnlyAccount, Session,
|
ReadOnlyAccount, Session,
|
||||||
|
@ -107,7 +107,7 @@ pub struct Changes {
|
||||||
pub inbound_group_sessions: Vec<InboundGroupSession>,
|
pub inbound_group_sessions: Vec<InboundGroupSession>,
|
||||||
pub outbound_group_sessions: Vec<OutboundGroupSession>,
|
pub outbound_group_sessions: Vec<OutboundGroupSession>,
|
||||||
pub identities: IdentityChanges,
|
pub identities: IdentityChanges,
|
||||||
pub key_requests: Vec<OutgoingKeyRequest>,
|
pub key_requests: Vec<GossipRequest>,
|
||||||
pub devices: DeviceChanges,
|
pub devices: DeviceChanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,10 +499,8 @@ pub trait CryptoStore: AsyncTraitDeps {
|
||||||
///
|
///
|
||||||
/// * `request_id` - The unique request id that identifies this outgoing
|
/// * `request_id` - The unique request id that identifies this outgoing
|
||||||
/// secret request.
|
/// secret request.
|
||||||
async fn get_outgoing_secret_requests(
|
async fn get_outgoing_secret_requests(&self, request_id: Uuid)
|
||||||
&self,
|
-> Result<Option<GossipRequest>>;
|
||||||
request_id: Uuid,
|
|
||||||
) -> Result<Option<OutgoingKeyRequest>>;
|
|
||||||
|
|
||||||
/// Get an outgoing key request that we created that matches the given
|
/// Get an outgoing key request that we created that matches the given
|
||||||
/// requested key info.
|
/// requested key info.
|
||||||
|
@ -513,10 +511,10 @@ pub trait CryptoStore: AsyncTraitDeps {
|
||||||
async fn get_secret_request_by_info(
|
async fn get_secret_request_by_info(
|
||||||
&self,
|
&self,
|
||||||
secret_info: &SecretInfo,
|
secret_info: &SecretInfo,
|
||||||
) -> Result<Option<OutgoingKeyRequest>>;
|
) -> Result<Option<GossipRequest>>;
|
||||||
|
|
||||||
/// Get all outgoing secret requests that we have in the store.
|
/// Get all outgoing secret requests that we have in the store.
|
||||||
async fn get_unsent_secret_requests(&self) -> Result<Vec<OutgoingKeyRequest>>;
|
async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>>;
|
||||||
|
|
||||||
/// Delete an outgoing key request that we created that matches the given
|
/// Delete an outgoing key request that we created that matches the given
|
||||||
/// request id.
|
/// request id.
|
||||||
|
|
|
@ -38,8 +38,8 @@ use super::{
|
||||||
ReadOnlyAccount, Result, Session,
|
ReadOnlyAccount, Result, Session,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
gossiping::{GossipRequest, SecretInfo},
|
||||||
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
|
||||||
key_request::{OutgoingKeyRequest, SecretInfo},
|
|
||||||
olm::{OutboundGroupSession, PickledInboundGroupSession, PrivateCrossSigningIdentity},
|
olm::{OutboundGroupSession, PickledInboundGroupSession, PrivateCrossSigningIdentity},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -498,10 +498,7 @@ impl SledStore {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_outgoing_key_request_helper(
|
async fn get_outgoing_key_request_helper(&self, id: &[u8]) -> Result<Option<GossipRequest>> {
|
||||||
&self,
|
|
||||||
id: &[u8],
|
|
||||||
) -> Result<Option<OutgoingKeyRequest>> {
|
|
||||||
let request = self
|
let request = self
|
||||||
.outgoing_secret_requests
|
.outgoing_secret_requests
|
||||||
.get(id)?
|
.get(id)?
|
||||||
|
@ -705,7 +702,7 @@ impl CryptoStore for SledStore {
|
||||||
async fn get_outgoing_secret_requests(
|
async fn get_outgoing_secret_requests(
|
||||||
&self,
|
&self,
|
||||||
request_id: Uuid,
|
request_id: Uuid,
|
||||||
) -> Result<Option<OutgoingKeyRequest>> {
|
) -> Result<Option<GossipRequest>> {
|
||||||
let request_id = request_id.encode();
|
let request_id = request_id.encode();
|
||||||
|
|
||||||
self.get_outgoing_key_request_helper(&request_id).await
|
self.get_outgoing_key_request_helper(&request_id).await
|
||||||
|
@ -714,7 +711,7 @@ impl CryptoStore for SledStore {
|
||||||
async fn get_secret_request_by_info(
|
async fn get_secret_request_by_info(
|
||||||
&self,
|
&self,
|
||||||
key_info: &SecretInfo,
|
key_info: &SecretInfo,
|
||||||
) -> Result<Option<OutgoingKeyRequest>> {
|
) -> Result<Option<GossipRequest>> {
|
||||||
let id = self.secret_requests_by_info.get(key_info.encode())?;
|
let id = self.secret_requests_by_info.get(key_info.encode())?;
|
||||||
|
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
|
@ -724,8 +721,8 @@ impl CryptoStore for SledStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_unsent_secret_requests(&self) -> Result<Vec<OutgoingKeyRequest>> {
|
async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
|
||||||
let requests: Result<Vec<OutgoingKeyRequest>> = self
|
let requests: Result<Vec<GossipRequest>> = self
|
||||||
.unsent_secret_requests
|
.unsent_secret_requests
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| serde_json::from_slice(&i?.1).map_err(CryptoStoreError::from))
|
.map(|i| serde_json::from_slice(&i?.1).map_err(CryptoStoreError::from))
|
||||||
|
@ -742,13 +739,13 @@ impl CryptoStore for SledStore {
|
||||||
)
|
)
|
||||||
.transaction(
|
.transaction(
|
||||||
|(outgoing_key_requests, unsent_key_requests, key_requests_by_info)| {
|
|(outgoing_key_requests, unsent_key_requests, key_requests_by_info)| {
|
||||||
let sent_request: Option<OutgoingKeyRequest> = outgoing_key_requests
|
let sent_request: Option<GossipRequest> = outgoing_key_requests
|
||||||
.remove(request_id.encode())?
|
.remove(request_id.encode())?
|
||||||
.map(|r| serde_json::from_slice(&r))
|
.map(|r| serde_json::from_slice(&r))
|
||||||
.transpose()
|
.transpose()
|
||||||
.map_err(ConflictableTransactionError::Abort)?;
|
.map_err(ConflictableTransactionError::Abort)?;
|
||||||
|
|
||||||
let unsent_request: Option<OutgoingKeyRequest> = unsent_key_requests
|
let unsent_request: Option<GossipRequest> = unsent_key_requests
|
||||||
.remove(request_id.encode())?
|
.remove(request_id.encode())?
|
||||||
.map(|r| serde_json::from_slice(&r))
|
.map(|r| serde_json::from_slice(&r))
|
||||||
.transpose()
|
.transpose()
|
||||||
|
@ -786,13 +783,13 @@ mod test {
|
||||||
};
|
};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use super::{CryptoStore, OutgoingKeyRequest, SledStore};
|
use super::{CryptoStore, GossipRequest, SledStore};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
gossiping::SecretInfo,
|
||||||
identities::{
|
identities::{
|
||||||
device::test::get_device,
|
device::test::get_device,
|
||||||
user::test::{get_other_identity, get_own_identity},
|
user::test::{get_other_identity, get_own_identity},
|
||||||
},
|
},
|
||||||
key_request::SecretInfo,
|
|
||||||
olm::{
|
olm::{
|
||||||
GroupSessionKey, InboundGroupSession, OlmMessageHash, PrivateCrossSigningIdentity,
|
GroupSessionKey, InboundGroupSession, OlmMessageHash, PrivateCrossSigningIdentity,
|
||||||
ReadOnlyAccount, Session,
|
ReadOnlyAccount, Session,
|
||||||
|
@ -1232,7 +1229,7 @@ mod test {
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let request = OutgoingKeyRequest {
|
let request = GossipRequest {
|
||||||
request_recipient: account.user_id().to_owned(),
|
request_recipient: account.user_id().to_owned(),
|
||||||
request_id: id,
|
request_id: id,
|
||||||
info: info.clone(),
|
info: info.clone(),
|
||||||
|
@ -1254,7 +1251,7 @@ mod test {
|
||||||
assert_eq!(request, stored_request);
|
assert_eq!(request, stored_request);
|
||||||
assert!(!store.get_unsent_secret_requests().await.unwrap().is_empty());
|
assert!(!store.get_unsent_secret_requests().await.unwrap().is_empty());
|
||||||
|
|
||||||
let request = OutgoingKeyRequest {
|
let request = GossipRequest {
|
||||||
request_recipient: account.user_id().to_owned(),
|
request_recipient: account.user_id().to_owned(),
|
||||||
request_id: id,
|
request_id: id,
|
||||||
info: info.clone(),
|
info: info.clone(),
|
||||||
|
|
|
@ -42,7 +42,7 @@ use tracing::{error, info, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SignatureError,
|
error::SignatureError,
|
||||||
key_request::{KeyRequestMachine, OutgoingKeyRequest},
|
gossiping::{GossipMachine, GossipRequest},
|
||||||
olm::PrivateCrossSigningIdentity,
|
olm::PrivateCrossSigningIdentity,
|
||||||
store::{Changes, CryptoStore},
|
store::{Changes, CryptoStore},
|
||||||
CryptoStoreError, LocalTrust, ReadOnlyDevice, ReadOnlyUserIdentities,
|
CryptoStoreError, LocalTrust, ReadOnlyDevice, ReadOnlyUserIdentities,
|
||||||
|
@ -455,9 +455,9 @@ impl IdentitiesBeingVerified {
|
||||||
.unwrap_or(VerificationResult::Ok))
|
.unwrap_or(VerificationResult::Ok))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn request_missing_secrets(&self) -> Vec<OutgoingKeyRequest> {
|
async fn request_missing_secrets(&self) -> Vec<GossipRequest> {
|
||||||
let secrets = self.private_identity.get_missing_secrets().await;
|
let secrets = self.private_identity.get_missing_secrets().await;
|
||||||
KeyRequestMachine::request_missing_secrets(self.user_id(), secrets)
|
GossipMachine::request_missing_secrets(self.user_id(), secrets)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mark_identity_as_verified(
|
async fn mark_identity_as_verified(
|
||||||
|
|
Loading…
Reference in a new issue