2020-09-17 14:09:08 +00:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
//
|
|
|
|
// Incoming key requests:
|
|
|
|
// First handle the easy case, if we trust the device and have a session, queue
|
|
|
|
// up a to-device request.
|
|
|
|
//
|
|
|
|
// If we don't have a session, queue up a key claim request, once we get a
|
|
|
|
// session send out the key if we trust the device.
|
|
|
|
//
|
|
|
|
// If we don't trust the device store an object that remembers the request and
|
|
|
|
// let the users introspect that object.
|
2020-09-28 07:27:16 +00:00
|
|
|
|
|
|
|
#![allow(dead_code)]
|
|
|
|
|
2020-09-17 12:16:43 +00:00
|
|
|
use dashmap::DashMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2020-09-28 07:27:16 +00:00
|
|
|
use serde_json::{value::to_raw_value, Value};
|
|
|
|
use std::{collections::BTreeMap, convert::TryInto, sync::Arc};
|
2020-09-18 11:45:00 +00:00
|
|
|
use tracing::{info, trace};
|
2020-09-17 12:16:43 +00:00
|
|
|
|
|
|
|
use matrix_sdk_common::{
|
|
|
|
api::r0::to_device::DeviceIdOrAllDevices,
|
|
|
|
events::{
|
|
|
|
forwarded_room_key::ForwardedRoomKeyEventContent,
|
2020-09-28 07:27:16 +00:00
|
|
|
room::encrypted::EncryptedEventContent,
|
2020-09-17 12:16:43 +00:00
|
|
|
room_key_request::{Action, RequestedKeyInfo, RoomKeyRequestEventContent},
|
2020-09-24 09:18:01 +00:00
|
|
|
AnyToDeviceEvent, EventType, ToDeviceEvent,
|
2020-09-17 12:16:43 +00:00
|
|
|
},
|
|
|
|
identifiers::{DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId},
|
|
|
|
uuid::Uuid,
|
2020-09-24 09:18:01 +00:00
|
|
|
Raw,
|
2020-09-17 12:16:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
2020-09-28 07:27:16 +00:00
|
|
|
error::OlmResult,
|
|
|
|
identities::{OwnUserIdentity, ReadOnlyDevice, UserIdentities},
|
2020-09-17 14:09:08 +00:00
|
|
|
olm::InboundGroupSession,
|
2020-09-17 12:16:43 +00:00
|
|
|
requests::{OutgoingRequest, ToDeviceRequest},
|
2020-09-17 14:09:08 +00:00
|
|
|
store::{CryptoStoreError, Store},
|
2020-09-17 12:16:43 +00:00
|
|
|
};
|
|
|
|
|
2020-09-28 07:27:16 +00:00
|
|
|
struct Device {
|
|
|
|
inner: ReadOnlyDevice,
|
|
|
|
store: Store,
|
|
|
|
own_identity: Option<OwnUserIdentity>,
|
|
|
|
device_owner_identity: Option<UserIdentities>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Device {
|
|
|
|
fn trust_state(&self) -> bool {
|
|
|
|
self.inner
|
|
|
|
.trust_state(&self.own_identity, &self.device_owner_identity)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn encrypt(
|
|
|
|
&self,
|
|
|
|
event_type: EventType,
|
|
|
|
content: Value,
|
|
|
|
) -> OlmResult<EncryptedEventContent> {
|
|
|
|
self.inner
|
|
|
|
.encrypt(self.store.clone(), event_type, content)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-17 12:16:43 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2020-09-18 11:45:00 +00:00
|
|
|
pub(crate) struct KeyRequestMachine {
|
2020-09-17 12:16:43 +00:00
|
|
|
user_id: Arc<UserId>,
|
|
|
|
device_id: Arc<DeviceIdBox>,
|
|
|
|
store: Store,
|
|
|
|
outgoing_to_device_requests: Arc<DashMap<Uuid, OutgoingRequest>>,
|
2020-09-28 07:27:16 +00:00
|
|
|
incoming_key_requests:
|
|
|
|
Arc<DashMap<(UserId, DeviceIdBox, String), ToDeviceEvent<RoomKeyRequestEventContent>>>,
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
struct OugoingKeyInfo {
|
|
|
|
request_id: Uuid,
|
|
|
|
info: RequestedKeyInfo,
|
|
|
|
sent_out: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
trait Encode {
|
|
|
|
fn encode(&self) -> String;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Encode for RequestedKeyInfo {
|
|
|
|
fn encode(&self) -> String {
|
|
|
|
format!(
|
|
|
|
"{}|{}|{}|{}",
|
|
|
|
self.sender_key, self.room_id, self.session_id, self.algorithm
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Encode for ForwardedRoomKeyEventContent {
|
|
|
|
fn encode(&self) -> String {
|
|
|
|
format!(
|
|
|
|
"{}|{}|{}|{}",
|
|
|
|
self.sender_key, self.room_id, self.session_id, self.algorithm
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
fn wrap_key_request_content(
|
|
|
|
recipient: UserId,
|
|
|
|
id: Uuid,
|
|
|
|
content: &RoomKeyRequestEventContent,
|
|
|
|
) -> Result<OutgoingRequest, serde_json::Error> {
|
|
|
|
let mut messages = BTreeMap::new();
|
|
|
|
|
|
|
|
messages
|
|
|
|
.entry(recipient)
|
|
|
|
.or_insert_with(BTreeMap::new)
|
|
|
|
.insert(DeviceIdOrAllDevices::AllDevices, to_raw_value(content)?);
|
|
|
|
|
|
|
|
Ok(OutgoingRequest {
|
|
|
|
request_id: id,
|
|
|
|
request: Arc::new(
|
|
|
|
ToDeviceRequest {
|
|
|
|
event_type: EventType::RoomKeyRequest,
|
|
|
|
txn_id: id,
|
|
|
|
messages,
|
|
|
|
}
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-17 12:16:43 +00:00
|
|
|
impl KeyRequestMachine {
|
2020-09-17 14:09:08 +00:00
|
|
|
pub fn new(user_id: Arc<UserId>, device_id: Arc<DeviceIdBox>, store: Store) -> Self {
|
|
|
|
Self {
|
|
|
|
user_id,
|
|
|
|
device_id,
|
|
|
|
store,
|
|
|
|
outgoing_to_device_requests: Arc::new(DashMap::new()),
|
2020-09-28 07:27:16 +00:00
|
|
|
incoming_key_requests: Arc::new(DashMap::new()),
|
2020-09-17 14:09:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
fn user_id(&self) -> &UserId {
|
|
|
|
&self.user_id
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn outgoing_to_device_requests(&self) -> Vec<OutgoingRequest> {
|
|
|
|
#[allow(clippy::map_clone)]
|
|
|
|
self.outgoing_to_device_requests
|
|
|
|
.iter()
|
|
|
|
.map(|r| (*r).clone())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2020-09-28 07:27:16 +00:00
|
|
|
/// Receive a room key request event.
|
|
|
|
pub fn receive_incoming_key_request(&self, event: &ToDeviceEvent<RoomKeyRequestEventContent>) {
|
|
|
|
let sender = event.sender.clone();
|
|
|
|
let device_id = event.content.requesting_device_id.clone();
|
|
|
|
let request_id = event.content.request_id.clone();
|
|
|
|
self.incoming_key_requests
|
|
|
|
.insert((sender, device_id, request_id), event.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn collect_incoming_key_requests(&self) -> Result<(), CryptoStoreError> {
|
|
|
|
for item in self.incoming_key_requests.iter() {
|
|
|
|
let event = item.value();
|
|
|
|
|
|
|
|
// TODO move this into the handle key request method.
|
|
|
|
match event.content.action {
|
|
|
|
Action::Request => {
|
|
|
|
self.handle_key_request(event).await?;
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("HELLO {:?}", event);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.incoming_key_requests.clear();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn handle_key_request(
|
|
|
|
&self,
|
|
|
|
event: &ToDeviceEvent<RoomKeyRequestEventContent>,
|
|
|
|
) -> Result<(), CryptoStoreError> {
|
|
|
|
let key_info = event.content.body.as_ref().unwrap();
|
|
|
|
let session = self
|
|
|
|
.store
|
|
|
|
.get_inbound_group_session(
|
|
|
|
&key_info.room_id,
|
|
|
|
&key_info.sender_key,
|
|
|
|
&key_info.session_id,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let session = if let Some(s) = session {
|
|
|
|
s
|
|
|
|
} else {
|
|
|
|
info!("Received a key request for an unknown inbound group session.");
|
|
|
|
return Ok(());
|
|
|
|
};
|
|
|
|
|
|
|
|
let device = self
|
|
|
|
.store
|
|
|
|
.get_device_and_users(&event.sender, &event.content.requesting_device_id)
|
|
|
|
.await?
|
|
|
|
.map(|(d, o, u)| Device {
|
|
|
|
inner: d,
|
|
|
|
store: self.store.clone(),
|
|
|
|
own_identity: o,
|
|
|
|
device_owner_identity: u,
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Some(device) = device {
|
|
|
|
if self.user_id() == &event.sender {
|
|
|
|
self.handle_key_request_from_own_user(event, session, device)
|
|
|
|
.await?;
|
|
|
|
} else {
|
|
|
|
self.handle_key_request_from_others(event, session, device)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
info!("Received a key request from an unknown device.");
|
|
|
|
self.store.update_tracked_user(&event.sender, true).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn handle_key_request_from_own_user(
|
|
|
|
&self,
|
|
|
|
event: &ToDeviceEvent<RoomKeyRequestEventContent>,
|
|
|
|
session: InboundGroupSession,
|
|
|
|
device: Device,
|
|
|
|
) -> Result<(), CryptoStoreError> {
|
|
|
|
// TODO should we create yet another Device type that holds a store
|
|
|
|
// but not a verification machine?
|
|
|
|
if device.trust_state() {
|
|
|
|
let export = session.export().await;
|
|
|
|
let content: ForwardedRoomKeyEventContent = export.try_into().unwrap();
|
|
|
|
|
|
|
|
todo!("Queue up a key to be shared");
|
|
|
|
} else {
|
|
|
|
info!("Received a key request from an untrusted device.");
|
|
|
|
}
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn handle_key_request_from_others(
|
|
|
|
&self,
|
|
|
|
event: &ToDeviceEvent<RoomKeyRequestEventContent>,
|
|
|
|
session: InboundGroupSession,
|
|
|
|
device: Device,
|
|
|
|
) -> Result<(), CryptoStoreError> {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
/// Create a new outgoing key request for the key with the given session id.
|
|
|
|
///
|
|
|
|
/// This will queue up a new to-device request and store the key info so
|
|
|
|
/// once we receive a forwarded room key we can check that it matches the
|
|
|
|
/// key we requested.
|
|
|
|
///
|
|
|
|
/// This does nothing if a request for this key has already been sent out.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `room_id` - The id of the room where the key is used in.
|
|
|
|
///
|
|
|
|
/// * `sender_key` - The curve25519 key of the sender that owns the key.
|
|
|
|
///
|
|
|
|
/// * `session_id` - The id that uniquely identifies the session.
|
2020-09-18 11:45:00 +00:00
|
|
|
pub async fn create_outgoing_key_request(
|
2020-09-17 12:16:43 +00:00
|
|
|
&self,
|
|
|
|
room_id: &RoomId,
|
|
|
|
sender_key: &str,
|
|
|
|
session_id: &str,
|
2020-09-17 14:09:08 +00:00
|
|
|
) -> Result<(), CryptoStoreError> {
|
2020-09-17 12:16:43 +00:00
|
|
|
let key_info = RequestedKeyInfo {
|
|
|
|
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
|
|
|
|
room_id: room_id.to_owned(),
|
|
|
|
sender_key: sender_key.to_owned(),
|
|
|
|
session_id: session_id.to_owned(),
|
|
|
|
};
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
let id: Option<String> = self.store.get_object(&key_info.encode()).await?;
|
2020-09-17 12:16:43 +00:00
|
|
|
|
|
|
|
if id.is_some() {
|
|
|
|
// We already sent out a request for this key, nothing to do.
|
2020-09-17 14:09:08 +00:00
|
|
|
return Ok(());
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
info!("Creating new outgoing room key request {:#?}", key_info);
|
|
|
|
|
|
|
|
let id = Uuid::new_v4();
|
|
|
|
|
|
|
|
let content = RoomKeyRequestEventContent {
|
|
|
|
action: Action::Request,
|
|
|
|
request_id: id.to_string(),
|
|
|
|
requesting_device_id: (&*self.device_id).clone(),
|
|
|
|
body: Some(key_info),
|
|
|
|
};
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
let request = wrap_key_request_content(self.user_id().clone(), id, &content)?;
|
2020-09-17 12:16:43 +00:00
|
|
|
|
|
|
|
let info = OugoingKeyInfo {
|
|
|
|
request_id: id,
|
|
|
|
info: content.body.unwrap(),
|
|
|
|
sent_out: false,
|
|
|
|
};
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
self.save_outgoing_key_info(id, info).await?;
|
2020-09-17 12:16:43 +00:00
|
|
|
self.outgoing_to_device_requests.insert(id, request);
|
2020-09-17 14:09:08 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
/// Save an outgoing key info.
|
|
|
|
async fn save_outgoing_key_info(
|
|
|
|
&self,
|
|
|
|
id: Uuid,
|
|
|
|
info: OugoingKeyInfo,
|
|
|
|
) -> Result<(), CryptoStoreError> {
|
|
|
|
// TODO we'll want to use a transaction to store those atomically.
|
|
|
|
// To allow this we'll need to rework our cryptostore trait to return
|
|
|
|
// a transaction trait and the transaction trait will have the save_X
|
|
|
|
// methods.
|
2020-09-17 12:16:43 +00:00
|
|
|
let id_string = id.to_string();
|
2020-09-17 14:09:08 +00:00
|
|
|
self.store.save_object(&id_string, &info).await?;
|
|
|
|
self.store.save_object(&info.info.encode(), &id).await?;
|
|
|
|
|
|
|
|
Ok(())
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
/// Get an outgoing key info that matches the forwarded room key content.
|
|
|
|
async fn get_key_info(
|
|
|
|
&self,
|
|
|
|
content: &ForwardedRoomKeyEventContent,
|
|
|
|
) -> Result<Option<OugoingKeyInfo>, CryptoStoreError> {
|
|
|
|
let id: Option<Uuid> = self.store.get_object(&content.encode()).await?;
|
2020-09-17 12:16:43 +00:00
|
|
|
|
|
|
|
if let Some(id) = id {
|
2020-09-17 14:09:08 +00:00
|
|
|
self.store.get_object(&id.to_string()).await
|
2020-09-17 12:16:43 +00:00
|
|
|
} else {
|
2020-09-17 14:09:08 +00:00
|
|
|
Ok(None)
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
/// Delete the given outgoing key info.
|
2020-09-18 11:45:00 +00:00
|
|
|
async fn delete_key_info(&self, info: &OugoingKeyInfo) -> Result<(), CryptoStoreError> {
|
2020-09-17 12:16:43 +00:00
|
|
|
self.store
|
|
|
|
.delete_object(&info.request_id.to_string())
|
2020-09-17 14:09:08 +00:00
|
|
|
.await?;
|
|
|
|
self.store.delete_object(&info.info.encode()).await?;
|
|
|
|
|
|
|
|
Ok(())
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
/// Mark the outgoing request as sent.
|
2020-09-18 11:45:00 +00:00
|
|
|
pub async fn mark_outgoing_request_as_sent(&self, id: &Uuid) -> Result<(), CryptoStoreError> {
|
|
|
|
self.outgoing_to_device_requests.remove(id);
|
2020-09-17 14:09:08 +00:00
|
|
|
let info: Option<OugoingKeyInfo> = self.store.get_object(&id.to_string()).await?;
|
2020-09-17 12:16:43 +00:00
|
|
|
|
|
|
|
if let Some(mut info) = info {
|
|
|
|
trace!("Marking outgoing key request as sent {:#?}", info);
|
|
|
|
info.sent_out = true;
|
2020-09-18 11:45:00 +00:00
|
|
|
self.save_outgoing_key_info(*id, info).await?;
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
2020-09-17 14:09:08 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
/// Save an inbound group session we received using a key forward.
|
|
|
|
///
|
|
|
|
/// At the same time delete the key info since we received the wanted key.
|
|
|
|
async fn save_session(
|
|
|
|
&self,
|
|
|
|
key_info: OugoingKeyInfo,
|
|
|
|
session: InboundGroupSession,
|
|
|
|
) -> Result<(), CryptoStoreError> {
|
|
|
|
// TODO perhaps only remove the key info if the first known index is 0.
|
2020-09-18 11:45:00 +00:00
|
|
|
trace!(
|
|
|
|
"Successfully received a forwarded room key for {:#?}",
|
|
|
|
key_info
|
|
|
|
);
|
|
|
|
|
2020-09-17 14:09:08 +00:00
|
|
|
self.store.save_inbound_group_sessions(&[session]).await?;
|
|
|
|
self.outgoing_to_device_requests
|
|
|
|
.remove(&key_info.request_id);
|
2020-09-18 11:45:00 +00:00
|
|
|
self.delete_key_info(&key_info).await?;
|
|
|
|
|
|
|
|
let content = RoomKeyRequestEventContent {
|
|
|
|
action: Action::CancelRequest,
|
|
|
|
request_id: key_info.request_id.to_string(),
|
|
|
|
requesting_device_id: (&*self.device_id).clone(),
|
|
|
|
body: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let id = Uuid::new_v4();
|
|
|
|
|
|
|
|
let request = wrap_key_request_content(self.user_id().clone(), id, &content)?;
|
|
|
|
|
|
|
|
self.outgoing_to_device_requests.insert(id, request);
|
|
|
|
|
|
|
|
Ok(())
|
2020-09-17 14:09:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Receive a forwarded room key event.
|
2020-09-18 11:45:00 +00:00
|
|
|
pub async fn receive_forwarded_room_key(
|
2020-09-17 12:16:43 +00:00
|
|
|
&self,
|
2020-09-17 14:09:08 +00:00
|
|
|
sender_key: &str,
|
2020-09-17 12:16:43 +00:00
|
|
|
event: &mut ToDeviceEvent<ForwardedRoomKeyEventContent>,
|
2020-09-24 09:18:01 +00:00
|
|
|
) -> Result<Option<Raw<AnyToDeviceEvent>>, CryptoStoreError> {
|
2020-09-17 14:09:08 +00:00
|
|
|
let key_info = self.get_key_info(&event.content).await?;
|
2020-09-17 12:16:43 +00:00
|
|
|
|
|
|
|
if let Some(info) = key_info {
|
2020-09-24 09:18:01 +00:00
|
|
|
let session = InboundGroupSession::from_forwarded_key(sender_key, &mut event.content)?;
|
2020-09-17 14:09:08 +00:00
|
|
|
|
|
|
|
let old_session = self
|
|
|
|
.store
|
|
|
|
.get_inbound_group_session(
|
|
|
|
session.room_id(),
|
|
|
|
&session.sender_key,
|
|
|
|
session.session_id(),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
// If we have a previous session, check if we have a better version
|
|
|
|
// and store the new one if so.
|
|
|
|
if let Some(old_session) = old_session {
|
|
|
|
let first_old_index = old_session.first_known_index().await;
|
|
|
|
let first_index = session.first_known_index().await;
|
|
|
|
|
|
|
|
if first_old_index > first_index {
|
|
|
|
self.save_session(info, session).await?;
|
|
|
|
}
|
|
|
|
// If we didn't have a previous session, store it.
|
|
|
|
} else {
|
|
|
|
self.save_session(info, session).await?;
|
|
|
|
}
|
2020-09-24 09:18:01 +00:00
|
|
|
|
|
|
|
Ok(Some(Raw::from(AnyToDeviceEvent::ForwardedRoomKey(
|
|
|
|
event.clone(),
|
|
|
|
))))
|
2020-09-17 12:16:43 +00:00
|
|
|
} else {
|
|
|
|
info!(
|
|
|
|
"Received a forwarded room key from {}, but no key info was found.",
|
|
|
|
event.sender,
|
|
|
|
);
|
2020-09-24 09:18:01 +00:00
|
|
|
Ok(None)
|
2020-09-17 12:16:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-17 14:55:33 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2020-09-17 15:13:42 +00:00
|
|
|
use matrix_sdk_common::{
|
|
|
|
events::{forwarded_room_key::ForwardedRoomKeyEventContent, ToDeviceEvent},
|
|
|
|
identifiers::{room_id, user_id, DeviceIdBox, RoomId, UserId},
|
|
|
|
};
|
2020-09-17 14:55:33 +00:00
|
|
|
use matrix_sdk_test::async_test;
|
2020-09-17 15:13:42 +00:00
|
|
|
use std::{convert::TryInto, sync::Arc};
|
2020-09-17 14:55:33 +00:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
olm::Account,
|
|
|
|
store::{MemoryStore, Store},
|
|
|
|
};
|
|
|
|
|
|
|
|
use super::KeyRequestMachine;
|
|
|
|
|
|
|
|
fn alice_id() -> UserId {
|
|
|
|
user_id!("@alice:example.org")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn alice_device_id() -> DeviceIdBox {
|
|
|
|
"JLAFKJWSCS".into()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn room_id() -> RoomId {
|
|
|
|
room_id!("!test:example.org")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn account() -> Account {
|
|
|
|
Account::new(&alice_id(), &alice_device_id())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_machine() -> KeyRequestMachine {
|
2020-09-28 07:27:16 +00:00
|
|
|
let user_id = Arc::new(alice_id());
|
|
|
|
let store = Store::new(user_id.clone(), Box::new(MemoryStore::new()));
|
2020-09-17 14:55:33 +00:00
|
|
|
|
2020-09-28 07:27:16 +00:00
|
|
|
KeyRequestMachine::new(user_id, Arc::new(alice_device_id()), store)
|
2020-09-17 14:55:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn create_machine() {
|
|
|
|
let machine = get_machine();
|
|
|
|
|
2020-09-24 10:00:22 +00:00
|
|
|
assert!(machine.outgoing_to_device_requests().is_empty());
|
2020-09-17 14:55:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_test]
|
|
|
|
async fn create_key_request() {
|
|
|
|
let machine = get_machine();
|
|
|
|
let account = account();
|
|
|
|
|
|
|
|
let (_, session) = account
|
2020-09-18 16:55:17 +00:00
|
|
|
.create_group_session_pair_with_defaults(&room_id())
|
2020-09-17 14:55:33 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2020-09-24 10:00:22 +00:00
|
|
|
assert!(machine.outgoing_to_device_requests().is_empty());
|
2020-09-17 14:55:33 +00:00
|
|
|
machine
|
|
|
|
.create_outgoing_key_request(
|
|
|
|
session.room_id(),
|
|
|
|
&session.sender_key,
|
|
|
|
session.session_id(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2020-09-24 10:00:22 +00:00
|
|
|
assert!(!machine.outgoing_to_device_requests().is_empty());
|
|
|
|
assert_eq!(machine.outgoing_to_device_requests().len(), 1);
|
2020-09-17 14:55:33 +00:00
|
|
|
|
|
|
|
machine
|
|
|
|
.create_outgoing_key_request(
|
|
|
|
session.room_id(),
|
|
|
|
&session.sender_key,
|
|
|
|
session.session_id(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(machine.outgoing_to_device_requests.len(), 1);
|
|
|
|
|
|
|
|
let request = machine.outgoing_to_device_requests.iter().next().unwrap();
|
|
|
|
|
|
|
|
let id = request.request_id;
|
|
|
|
drop(request);
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
machine.mark_outgoing_request_as_sent(&id).await.unwrap();
|
2020-09-17 14:55:33 +00:00
|
|
|
assert!(machine.outgoing_to_device_requests.is_empty());
|
|
|
|
}
|
2020-09-17 15:13:42 +00:00
|
|
|
|
|
|
|
#[async_test]
|
|
|
|
async fn receive_forwarded_key() {
|
|
|
|
let machine = get_machine();
|
|
|
|
let account = account();
|
|
|
|
|
|
|
|
let (_, session) = account
|
2020-09-18 16:55:17 +00:00
|
|
|
.create_group_session_pair_with_defaults(&room_id())
|
2020-09-17 15:13:42 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
machine
|
|
|
|
.create_outgoing_key_request(
|
|
|
|
session.room_id(),
|
|
|
|
&session.sender_key,
|
|
|
|
session.session_id(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let request = machine.outgoing_to_device_requests.iter().next().unwrap();
|
|
|
|
let id = request.request_id;
|
|
|
|
drop(request);
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
machine.mark_outgoing_request_as_sent(&id).await.unwrap();
|
2020-09-17 15:13:42 +00:00
|
|
|
|
|
|
|
let export = session.export_at_index(10).await.unwrap();
|
|
|
|
|
|
|
|
let content: ForwardedRoomKeyEventContent = export.try_into().unwrap();
|
|
|
|
|
|
|
|
let mut event = ToDeviceEvent {
|
|
|
|
sender: alice_id(),
|
|
|
|
content,
|
|
|
|
};
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
machine
|
|
|
|
.store
|
|
|
|
.get_inbound_group_session(
|
|
|
|
session.room_id(),
|
|
|
|
&session.sender_key,
|
|
|
|
session.session_id(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.is_none()
|
|
|
|
);
|
|
|
|
|
|
|
|
machine
|
|
|
|
.receive_forwarded_room_key(&session.sender_key, &mut event)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let first_session = machine
|
|
|
|
.store
|
|
|
|
.get_inbound_group_session(session.room_id(), &session.sender_key, session.session_id())
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(first_session.first_known_index().await, 10);
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
// Get the cancel request.
|
|
|
|
let request = machine.outgoing_to_device_requests.iter().next().unwrap();
|
|
|
|
let id = request.request_id;
|
|
|
|
drop(request);
|
|
|
|
machine.mark_outgoing_request_as_sent(&id).await.unwrap();
|
|
|
|
|
2020-09-17 15:13:42 +00:00
|
|
|
machine
|
|
|
|
.create_outgoing_key_request(
|
|
|
|
session.room_id(),
|
|
|
|
&session.sender_key,
|
|
|
|
session.session_id(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let request = machine.outgoing_to_device_requests.iter().next().unwrap();
|
|
|
|
let id = request.request_id;
|
|
|
|
drop(request);
|
|
|
|
|
2020-09-18 11:45:00 +00:00
|
|
|
machine.mark_outgoing_request_as_sent(&id).await.unwrap();
|
2020-09-17 15:13:42 +00:00
|
|
|
|
|
|
|
let export = session.export_at_index(15).await.unwrap();
|
|
|
|
|
|
|
|
let content: ForwardedRoomKeyEventContent = export.try_into().unwrap();
|
|
|
|
|
|
|
|
let mut event = ToDeviceEvent {
|
|
|
|
sender: alice_id(),
|
|
|
|
content,
|
|
|
|
};
|
|
|
|
|
|
|
|
machine
|
|
|
|
.receive_forwarded_room_key(&session.sender_key, &mut event)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let second_session = machine
|
|
|
|
.store
|
|
|
|
.get_inbound_group_session(session.room_id(), &session.sender_key, session.session_id())
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(second_session.first_known_index().await, 10);
|
|
|
|
|
|
|
|
let export = session.export_at_index(0).await.unwrap();
|
|
|
|
|
|
|
|
let content: ForwardedRoomKeyEventContent = export.try_into().unwrap();
|
|
|
|
|
|
|
|
let mut event = ToDeviceEvent {
|
|
|
|
sender: alice_id(),
|
|
|
|
content,
|
|
|
|
};
|
|
|
|
|
|
|
|
machine
|
|
|
|
.receive_forwarded_room_key(&session.sender_key, &mut event)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let second_session = machine
|
|
|
|
.store
|
|
|
|
.get_inbound_group_session(session.room_id(), &session.sender_key, session.session_id())
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(second_session.first_known_index().await, 0);
|
|
|
|
}
|
2020-09-17 14:55:33 +00:00
|
|
|
}
|