Commit ba2c1d43 authored by Johannes Hayeß's avatar Johannes Hayeß 🖤
Browse files

Merge branch 'self_info_rework' into 'master'

Rework SelfInfo packet solution with enums and some other misc. fixes

Closes #27

See merge request !19
parents 72f38852 31e3ab16
Pipeline #116187 passed with stages
in 7 minutes and 12 seconds
......@@ -32,9 +32,9 @@ pub struct MetaolmBuilder {
user_id: Option<String>,
olm_account: OlmAccount,
blocker_manager: BlockerManager,
blocked_items: BTreeMap<u32, M2CPacket>,
sender: Option<Sender<M2CPacket>>,
receiver: Option<Receiver<C2MPacket>>,
blocked_items: BTreeMap<u32, Blocker>,
sender: Option<Sender<OutPacket>>,
receiver: Option<Receiver<InPacket>>,
}
impl MetaolmBuilder {
......@@ -75,8 +75,8 @@ impl MetaolmBuilder {
/// * receiver: Receiver used for receiving data sent from the client
pub fn connected(
mut self,
sender: Sender<M2CPacket>,
receiver: Receiver<C2MPacket>,
sender: Sender<OutPacket>,
receiver: Receiver<InPacket>,
) -> MetaolmBuilder {
self.sender = Some(sender);
self.receiver = Some(receiver);
......@@ -121,8 +121,8 @@ impl MetaolmBuilder {
let device_req_id = metaolm.blocker_manager.next_blocker_id();
// request for getting potentailly stored OlmAccount
let device_req = M2CPacket {
header: M2CPacketType::Load(StorageDesc::Account {
let device_req = OutPacket {
header: OutPacketType::Load(StorageDesc::Account {
device_id: metaolm.device_id.clone(),
}),
body: String::new(),
......@@ -169,8 +169,8 @@ impl MetaolmBuilder {
let register_request_blocker = metaolm.blocker_manager.next_blocker_id();
// build a store request for the client
let store_request = M2CPacket {
header: M2CPacketType::Store(StorageDesc::Account {
let store_request = OutPacket {
header: OutPacketType::Store(StorageDesc::Account {
device_id: metaolm.device_id.clone(),
}),
body: metaolm.olm_account.pickle(PicklingMode::Unencrypted),
......@@ -195,8 +195,8 @@ impl MetaolmBuilder {
// build the register request for the client that is to be
// forwarded to the Matrix server
let register_request = M2CPacket {
header: M2CPacketType::HTTPReq(
let register_request = OutPacket {
header: OutPacketType::HTTPReq(
HTTPReqType::Post,
endpoints::UPLOAD_KEYS.to_string(),
),
......
......@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use olm_rs::PicklingMode;
use std::collections::BTreeMap;
use std::sync::mpsc::Sender;
use std::time::SystemTime;
......@@ -24,6 +23,7 @@ use olm_rs::{
inbound_group_session::OlmInboundGroupSession,
outbound_group_session::OlmOutboundGroupSession,
session::{OlmMessageType, OlmSession},
PicklingMode,
};
use ring::rand::{SecureRandom, SystemRandom};
......@@ -43,7 +43,7 @@ use crate::json_objects::{
transport::C2MEncryptEventBody,
};
use crate::packet::{
ActionType, HTTPReqType, M2CPacket, M2CPacketType, ModuleInfoReq, SelfInfoType, StorageDesc,
ActionType, Blocker, HTTPReqType, ModuleInfoReq, OutPacket, OutPacketType, StorageDesc,
};
use crate::session_cache::OutboundGroupSessionWithInfo;
use crate::session_cache::{InboundMegolmSessionIdentifier, SessionCache};
......@@ -195,20 +195,19 @@ pub fn olm_decrypt(
/// Handles an event that was encrypted using the `m.olm.v1.curve25519-aes-sha2`
/// algorithm.
///
/// Instead of directly sending the `M2CPacket`s that are generated by this routine,
/// Instead of directly sending the `OutPacket`s that are generated by this routine,
/// they are returned and have to be sent manually afterwards.
pub fn handle_olm_event(
event: &OlmEncrypted,
packet: &str,
pub(crate) fn handle_olm_event(
event: OlmEncrypted,
packet_blocker_id: Option<u32>,
run_type: OlmHandleType,
account: &OlmAccount,
session_cache: &mut SessionCache,
blocker_manager: &mut BlockerManager,
blocked_items: &mut BTreeMap<u32, M2CPacket>,
blocked_items: &mut BTreeMap<u32, Blocker>,
user_id: &str,
device_id: &str,
) -> Vec<M2CPacket> {
) -> Vec<OutPacket> {
let sender_key = &event.content.sender_key;
// The list of packets we are going to return for sending to the client.
......@@ -221,8 +220,8 @@ pub fn handle_olm_event(
// If we don't, we have to ask the client
// about the potentially exported session data.
let load_blocker_id = blocker_manager.next_blocker_id();
packet_list.push(M2CPacket {
header: M2CPacketType::Load(StorageDesc::OlmSession {
packet_list.push(OutPacket {
header: OutPacketType::Load(StorageDesc::OlmSession {
device_id: device_id.to_string(),
sender_key: sender_key.to_string(),
}),
......@@ -230,14 +229,17 @@ pub fn handle_olm_event(
blocker_id: Some(load_blocker_id),
});
let mut self_info_decrypt =
M2CPacket::new_self_info_with_body(SelfInfoType::OlmDecrypt, packet.to_string());
self_info_decrypt.blocker_id = packet_blocker_id;
let self_info_decrypt = Blocker::OlmDecrypt(event, packet_blocker_id.unwrap());
blocked_items.insert(load_blocker_id, self_info_decrypt);
} else {
// If we do, we can immediately proceed
// towards decrypting the received event.
match olm_decrypt(&packet, account, session_cache, user_id) {
match olm_decrypt(
&serde_json::to_string(&event).unwrap(),
account,
session_cache,
user_id,
) {
Ok(OlmDecryptResult::Plaintext(plaintext)) => {
// check for m.room_key event
if check_for_room_key_event_and_handle(
......@@ -253,15 +255,15 @@ pub fn handle_olm_event(
// The client isn't interested in key data, it also
// would be additional information that would need
// cleaning up - so we just send an empty packet.
packet_list.push(M2CPacket {
header: M2CPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
packet_list.push(OutPacket {
header: OutPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
body: String::new(),
blocker_id: packet_blocker_id,
});
} else {
// send plaintext
packet_list.push(M2CPacket {
header: M2CPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
packet_list.push(OutPacket {
header: OutPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
body: serde_json::to_string(&plaintext).unwrap(),
blocker_id: packet_blocker_id,
});
......@@ -274,8 +276,8 @@ pub fn handle_olm_event(
// First we chache the newly created session.
session_cache.cache_olm_session(sender_key.clone(), session);
packet_list.push(M2CPacket {
header: M2CPacketType::Store(StorageDesc::OlmSession {
packet_list.push(OutPacket {
header: OutPacketType::Store(StorageDesc::OlmSession {
device_id: device_id.to_string(),
sender_key: sender_key.to_string(),
}),
......@@ -287,15 +289,20 @@ pub fn handle_olm_event(
// olm account removed the corresponding OTK.
// We now have to store this new state of the
// account.
packet_list.push(M2CPacket {
header: M2CPacketType::Store(StorageDesc::Account {
packet_list.push(OutPacket {
header: OutPacketType::Store(StorageDesc::Account {
device_id: device_id.to_string(),
}),
body: account.pickle(PicklingMode::Unencrypted),
blocker_id: None,
});
match olm_decrypt(&packet, account, session_cache, user_id) {
match olm_decrypt(
&serde_json::to_string(&event).unwrap(),
account,
session_cache,
user_id,
) {
Ok(OlmDecryptResult::Plaintext(plaintext)) => {
// check for m.room_key event
if check_for_room_key_event_and_handle(
......@@ -311,14 +318,14 @@ pub fn handle_olm_event(
// The client isn't interested in key data, it also
// would be additional information that would need
// cleaning up - so we just send an empty packet.
packet_list.push(M2CPacket {
header: M2CPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
packet_list.push(OutPacket {
header: OutPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
body: String::new(),
blocker_id: packet_blocker_id,
});
} else {
packet_list.push(M2CPacket {
header: M2CPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
packet_list.push(OutPacket {
header: OutPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
body: serde_json::to_string(&plaintext).unwrap(),
blocker_id: packet_blocker_id,
});
......@@ -343,8 +350,8 @@ fn check_for_room_key_event_and_handle(
ed25519: &str,
session_cache: &mut SessionCache,
blocker_manager: &mut BlockerManager,
blocked_items: &mut BTreeMap<u32, M2CPacket>,
packet_list: &mut Vec<M2CPacket>,
blocked_items: &mut BTreeMap<u32, Blocker>,
packet_list: &mut Vec<OutPacket>,
) -> bool {
if let Ok(room_key_event) = serde_json::from_str(plain_content) as Result<RoomKey, _> {
let identifier = InboundMegolmSessionIdentifier {
......@@ -373,13 +380,13 @@ fn check_for_room_key_event_and_handle(
}
}
pub fn handle_megolm_event(
pub(crate) fn handle_megolm_event(
event: &MegolmEncrypted,
blocker_id: Option<u32>,
session_cache: &mut SessionCache,
blocker_manager: &mut BlockerManager,
blocked_items: &mut BTreeMap<u32, M2CPacket>,
) -> Vec<M2CPacket> {
blocked_items: &mut BTreeMap<u32, Blocker>,
) -> Vec<OutPacket> {
let mut return_packets = Vec::new();
let identifier = InboundMegolmSessionIdentifier {
room_id: event.room_id.clone(),
......@@ -400,8 +407,8 @@ pub fn handle_megolm_event(
.unwrap();
// send back the plaintext
return_packets.push(M2CPacket {
header: M2CPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
return_packets.push(OutPacket {
header: OutPacketType::ActionRsp(ActionType::ForwardEncryptedEvent),
body: serde_json::to_string(&MegolmPlaintextPacketBody::from_megolm_encrypted(
event, &plaintext,
))
......@@ -415,16 +422,13 @@ pub fn handle_megolm_event(
event: event.clone(),
blocker_id: blocker_id.unwrap(),
};
let load_session_blocked_event = M2CPacket::new_self_info_with_body(
SelfInfoType::MegolmDecrypt,
serde_json::to_string(&blocked_event_body).unwrap(),
);
let load_session_blocked_event = Blocker::MegolmDecrypt(blocked_event_body);
blocked_items.insert(load_session_bid, load_session_blocked_event);
// send load request to module
let session_load_req = M2CPacket {
header: M2CPacketType::Load(StorageDesc::InboundMegolmSession {
let session_load_req = OutPacket {
header: OutPacketType::Load(StorageDesc::InboundMegolmSession {
room_id: identifier.room_id,
sender_key: identifier.sender_key,
session_id: identifier.session_id,
......@@ -439,16 +443,16 @@ pub fn handle_megolm_event(
return_packets
}
pub fn create_new_outbound_megolm_session(
event_to_encrypt: &C2MEncryptEventBody,
pub(crate) fn create_new_outbound_megolm_session(
event_to_encrypt: C2MEncryptEventBody,
blocker_id: Option<u32>,
rotation_period_ms: u64,
rotation_period_msgs: u32,
blocker_manager: &mut BlockerManager,
blocked_items: &mut BTreeMap<u32, M2CPacket>,
blocked_items: &mut BTreeMap<u32, Blocker>,
session_cache: &mut SessionCache,
account: &OlmAccount,
sender: Sender<M2CPacket>,
sender: Sender<OutPacket>,
) {
let session_new = OlmOutboundGroupSession::new();
// create new session info
......@@ -467,8 +471,8 @@ pub fn create_new_outbound_megolm_session(
// store the session for later
sender
.send(M2CPacket {
header: M2CPacketType::Store(StorageDesc::OutboundMegolmSession {
.send(OutPacket {
header: OutPacketType::Store(StorageDesc::OutboundMegolmSession {
room_id: event_to_encrypt.room_id.clone(),
}),
body: serde_json::to_string(&OutboundGroupSessionWithInfoForStorage::from_active(
......@@ -497,8 +501,8 @@ pub fn create_new_outbound_megolm_session(
};
// send the inbound Megolm session to be stored
sender
.send(M2CPacket {
header: M2CPacketType::Store(StorageDesc::InboundMegolmSession {
.send(OutPacket {
header: OutPacketType::Store(StorageDesc::InboundMegolmSession {
room_id: inbound_megolm_identifier.room_id.clone(),
sender_key: inbound_megolm_identifier.sender_key.clone(),
session_id: inbound_megolm_identifier.session_id.clone(),
......@@ -514,8 +518,8 @@ pub fn create_new_outbound_megolm_session(
// send the inbound Megolm session to be stored
sender
.send(M2CPacket {
header: M2CPacketType::Store(StorageDesc::InboundMegolmSession {
.send(OutPacket {
header: OutPacketType::Store(StorageDesc::InboundMegolmSession {
room_id: inbound_megolm_identifier.room_id.clone(),
sender_key: inbound_megolm_identifier.sender_key.clone(),
session_id: inbound_megolm_identifier.session_id.clone(),
......@@ -543,27 +547,20 @@ pub fn create_new_outbound_megolm_session(
// SelfInfo for sending the device request after having received the list
// of users in the room.
let blocked_user_list_self_info = M2CPacket {
header: M2CPacketType::SelfInfo(SelfInfoType::RequestedUsersForRoom),
body: String::new(),
blocker_id: Some(get_devices_bid),
};
let blocked_user_list_self_info = Blocker::RequestedUsersForRoom(get_devices_bid);
// Once we have received the device list, we start the encryption process again,
// which means we'll need the event information again.
// We store it here as a string in the body.
let blocked_device_list_self_info = M2CPacket {
header: M2CPacketType::SelfInfo(SelfInfoType::RequestedDeviceKeys),
body: serde_json::to_string(event_to_encrypt).unwrap(),
blocker_id,
};
let blocked_device_list_self_info =
Blocker::RequestedDeviceKeys(event_to_encrypt.clone(), blocker_id.unwrap());
blocked_items.insert(get_users_list_bid, blocked_user_list_self_info);
blocked_items.insert(get_devices_bid, blocked_device_list_self_info);
sender
.send(M2CPacket {
header: M2CPacketType::RequestInfo(ModuleInfoReq::UsersForRoom {
.send(OutPacket {
header: OutPacketType::RequestInfo(ModuleInfoReq::UsersForRoom {
room_id: event_to_encrypt.room_id.clone(),
}),
body: String::new(),
......@@ -572,15 +569,15 @@ pub fn create_new_outbound_megolm_session(
.unwrap();
}
pub fn try_encrypt_room_message(
event_to_encrypt: &C2MEncryptEventBody,
pub(crate) fn try_encrypt_room_message(
event_to_encrypt: C2MEncryptEventBody,
blocker_id: Option<u32>,
blocker_manager: &mut BlockerManager,
blocked_items: &mut BTreeMap<u32, M2CPacket>,
blocked_items: &mut BTreeMap<u32, Blocker>,
session_cache: &mut SessionCache,
account: &OlmAccount,
device_id: String,
sender: Sender<M2CPacket>,
sender: Sender<OutPacket>,
) {
// check if we have a session for the room the event is supposed to be sent to
if let Some(ref mut session_info) =
......@@ -633,8 +630,8 @@ pub fn try_encrypt_room_message(
// store session with updated info
sender
.send(M2CPacket {
header: M2CPacketType::Store(StorageDesc::OutboundMegolmSession {
.send(OutPacket {
header: OutPacketType::Store(StorageDesc::OutboundMegolmSession {
room_id: event_to_encrypt.room_id.clone(),
}),
body: serde_json::to_string(
......@@ -646,8 +643,8 @@ pub fn try_encrypt_room_message(
.unwrap();
sender
.send(M2CPacket {
header: M2CPacketType::ActionRsp(ActionType::EncryptEvent),
.send(OutPacket {
header: OutPacketType::ActionRsp(ActionType::EncryptEvent),
body: serde_json::to_string(&room_encrypted_event).unwrap(),
blocker_id,
})
......@@ -659,16 +656,12 @@ pub fn try_encrypt_room_message(
blocked_items.insert(
load_outbound_megolm_session_bid,
M2CPacket {
header: M2CPacketType::SelfInfo(SelfInfoType::MegolmEncrypt),
body: serde_json::to_string(event_to_encrypt).unwrap(),
blocker_id,
},
Blocker::MegolmEncrypt(event_to_encrypt.clone(), blocker_id.unwrap()),
);
sender
.send(M2CPacket {
header: M2CPacketType::Load(StorageDesc::OutboundMegolmSession {
.send(OutPacket {
header: OutPacketType::Load(StorageDesc::OutboundMegolmSession {
room_id: event_to_encrypt.room_id.clone(),
}),
body: String::new(),
......@@ -698,14 +691,14 @@ pub fn outbound_megolm_session_needs_rotation(session: &OutboundGroupSessionWith
///
/// We potentially return a Load packet for determining if
/// a session for the provided identifier is already stored.
pub fn handle_room_key(
pub(crate) fn handle_room_key(
identifier: InboundMegolmSessionIdentifier,
session_key: String,
ed25519: String,
session_cache: &mut SessionCache,
blocker_manager: &mut BlockerManager,
blocked_items: &mut BTreeMap<u32, M2CPacket>,
) -> Option<M2CPacket> {
blocked_items: &mut BTreeMap<u32, Blocker>,
) -> Option<OutPacket> {
if !session_cache.inbound_megolm_session_cached(&identifier) {
// There is no session with the corresponding identifier in our cache,
// so we have to look at our stored data for a potentially already
......@@ -717,19 +710,15 @@ pub fn handle_room_key(
// identifier maps to a new session.
blocked_items.insert(
load_session_blocker_id,
M2CPacket::new_self_info_with_body(
SelfInfoType::RoomKey,
serde_json::to_string(&RoomKeyBody {
identifier: identifier.clone(),
session_key,
ed25519,
})
.unwrap(),
),
Blocker::RoomKey(RoomKeyBody {
identifier: identifier.clone(),
session_key,
ed25519,
}),
);
Some(M2CPacket {
header: M2CPacketType::Load(StorageDesc::InboundMegolmSession {
Some(OutPacket {
header: OutPacketType::Load(StorageDesc::InboundMegolmSession {
room_id: identifier.room_id,
sender_key: identifier.sender_key,
session_id: identifier.session_id,
......@@ -755,7 +744,7 @@ pub fn megolm_key_encrypt_and_send(
recipient_ed25519: String,
sender_key: String,
room_id: String,
sender: Sender<M2CPacket>,
sender: Sender<OutPacket>,
) {
let encrypt_message_type = session.encrypt_message_type();
......@@ -803,8 +792,8 @@ pub fn megolm_key_encrypt_and_send(
}
sender
.send(M2CPacket {
header: M2CPacketType::HTTPReq(
.send(OutPacket {
header: OutPacketType::HTTPReq(
HTTPReqType::Put,
format!(
"/_matrix/client/r0/sendToDevice/m.room.encrypted/{}",
......
This diff is collapsed.
......@@ -14,18 +14,25 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::endpoints;
use crate::json_objects::otk::{AccountOneTimeKeys, OneTimeKeys, SignedOneTimeKey};
use crate::packet::{HTTPReqType, M2CPacket, M2CPacketType};
use std::time::{Duration, Instant};
use crate::{
endpoints,
json_objects::otk::{AccountOneTimeKeys, OneTimeKeys, SignedOneTimeKey},
packet::{HTTPReqType, OutPacket, OutPacketType},
};
use olm_rs::account::OlmAccount;
use serde_json;
use std::time::{Duration, Instant};
/// An instance of OTKReplenisher will keeps track of when
/// you sent the last request for replenishing one time keys
/// you sent the last request for replenishing one-time-keys
/// on the Matrix server and can tell you when another one
/// should be sent.
pub struct OTKReplenisher {
pub(crate) struct OTKReplenisher {
/// The duration after which the Matrix server should be asked
/// if new one-time-keys need to be published.
/// The Matrix documentation recommends 10 minutes.
replenish_cycle: Duration,
last_checked: Instant,
replenish_goal: usize,
......@@ -49,17 +56,18 @@ impl OTKReplenisher {
}
/// Tells you if a refresh on checking on the amount of
/// one time keys on the server is due.
/// one-time-keys on the server is due.
/// A cycle is roughly 10 minutes as of now.
pub fn replenish_due(&self) -> bool {
self.last_checked.elapsed() > self.replenish_cycle
}
/// Creates the communication object for uploading new
/// one time keys if necessary.
/// one time keys if necessary. Necessity is determined by checking
/// if `otk_count` exceeds the set `replenish_goal`.
///
/// # Parameters
/// * `otk_count` amount of one time keys currently stored
/// * `otk_count` amount of one-time-keys currently stored
/// on the Matrix server
pub fn replenish_for(
&mut self,
......@@ -68,7 +76,7 @@ impl OTKReplenisher {
device_id: &str,
user_id: &str,
blocker_id: u32,
) -> Option<M2CPacket> {
) -> Option<OutPacket> {
if otk_count >= self.replenish_goal {
None
} else {
......@@ -92,8 +100,8 @@ impl OTKReplenisher {
signed_otks.add_key(index, key);
}
Some(M2CPacket {
header: M2CPacketType::HTTPReq(
Some(OutPacket {
header: OutPacketType::HTTPReq(
HTTPReqType::Post,
endpoints::UPLOAD_KEYS.to_string(),
),
......@@ -103,3 +111,54 @@ impl OTKReplenisher {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::json_objects::olm::OlmIdentityKeys;
use olm_rs::utility::OlmUtility;
#[test]
fn replenish_signal_valid() {
let account = OlmAccount::new();
let replenish_goal = account.max_number_of_one_time_keys() / 2;
let mut replenisher = OTKReplenisher::new(replenish_goal);
// replenisher was just initialised
assert!(!replenisher.replenish_due());
let otk_count = replenish_goal - 3;
let replenish_signal = replenisher
.replenish_for(otk_count, &account, "device_id", "user@example.org", 0)
.unwrap();
let replenish_signal_json: serde_json::Value =
serde_json::from_str(&replenish_signal.body).unwrap();
let replenish_signal_map = replenish_signal_json.as_object().unwrap();
assert_eq!(1, replenish_signal_map.len());
let one_time_keys = replenish_signal_map.get("one_time_keys").unwrap();
let one_time_keys_map = one_time_keys.as_object().unwrap();
assert_eq!(3, one_time_keys_map.len());
let public_key_pair: OlmIdentityKeys =
serde_json::from_str(&account.identity_keys()).unwrap();
let sign_key = public_key_pair.ed25519;
let utility = OlmUtility::new();
// test signature for each OTK
for key_json in one_time_keys_map.values() {
let key: SignedOneTimeKey = serde_json::from_str(&key_json.to_string()).unwrap();
let signature = key.signatures.values().next().unwrap();
let mut signature_str = signature.signatures.values().next().unwrap().clone();
assert!(utility
.ed25519_verify(
&sign_key,
&serde_json::to_string(&key.get_otk_only()).unwrap(),
&mut signature_str
)
.unwrap());
}
}
}
......@@ -14,28 +14,68 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::{
endpoints,
json_objects::{
event::OlmEncrypted,
self_info::{MegolmEncryptClaimOTKBody, MegolmEncryptedBody, RoomKeyBody},
transport::C2MEncryptEventBody,
},
};
/// Object used for the client to communicate with
/// the Metaolm module, but not the other way around -
/// that's what `M2CCommObject` is for.
use crate::endpoints;