Commit f8c02496 authored by Johannes Hayeß's avatar Johannes Hayeß
Browse files

Initial commit

parents
/target
**/*.rs.bk
Cargo.lock
[package]
name = "metaolm"
version = "0.1.0"
authors = ["Johannes Hayeß <jhaye@gmx.de>"]
[dependencies]
olm-rs = { git = "https://gitlab.gnome.org/jhaye/olm-rs.git", branch = "group_session" }
serde_json = "1.0"
This diff is collapsed.
// Metaolm - a stand-alone module for E2EE in the Matrix protocol.
// Copyright (C) 2018 Johannes Hayeß
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Object used for the client to communicate with
/// the Metaolm module, but now the other way around -
/// that's what M2CCommObject is for.
pub struct C2MCommObject {
pub header: C2MCommType,
pub body: String,
pub blocker_id: Option<u32>,
}
/// Object used for the Metaolm module to communicate with
/// the client, but now the other way around -
/// that's what M2CCommObject is for.
pub struct M2CCommObject {
pub header: M2CCommType,
pub details: String,
pub body: String,
pub blocker_id: Option<u32>,
}
pub enum C2MCommType {
StorageRsp,
}
pub enum M2CCommType {
HTTPPost,
Load,
Store,
}
// Metaolm - a stand-alone module for E2EE in the Matrix protocol.
// Copyright (C) 2018 Johannes Hayeß
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
extern crate olm_rs;
#[macro_use]
extern crate serde_json;
pub mod commobject;
pub mod metaolm;
// Metaolm - a stand-alone module for E2EE in the Matrix protocol.
// Copyright (C) 2018 Johannes Hayeß
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// 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 commobject::{C2MCommObject, M2CCommObject, M2CCommType};
use olm_rs::account::OlmAccount;
use olm_rs::errors::OlmAccountError;
use std::collections::BTreeMap;
use std::sync::mpsc::{Receiver, Sender};
use std::{thread, time};
/// Monolithically encapsulates all functionallity of the Metaolm module.
pub struct Metaolm {
device_id: String,
user_id: String,
olm_account: OlmAccount,
next_blocker_id: u32,
blocked_items: BTreeMap<u32, M2CCommObject>,
sender: Sender<M2CCommObject>,
receiver: Receiver<C2MCommObject>,
}
/// Used to build a Metaolm object.
pub struct MetaolmBuilder {
device_id: Option<String>,
user_id: Option<String>,
olm_account: OlmAccount,
next_blocker_id: u32,
blocked_items: BTreeMap<u32, M2CCommObject>,
sender: Option<Sender<M2CCommObject>>,
receiver: Option<Receiver<C2MCommObject>>,
}
impl Metaolm {
/// Returns the next blocker ID to be used for communication objects
/// that are currently blocked.
///
/// All blocker IDs that originate from the module are even.
pub fn next_blocker_id(&mut self) -> u32 {
let return_id = self.next_blocker_id;
self.next_blocker_id += 2;
return_id
}
/// Initiates the main loop.
pub fn loop_start(&mut self) {
let ten_millis = time::Duration::from_millis(10);
loop {
let iter = self.receiver.try_iter();
for comm_object in iter {
if comm_object.blocker_id.is_some() {
let blocker_id = comm_object.blocker_id.unwrap();
let blocked_item = self.blocked_items.remove(&blocker_id);
match blocked_item {
Some(x) => {
self.sender.send(x).unwrap();
}
None => {}
}
}
}
thread::sleep(ten_millis);
}
}
}
impl MetaolmBuilder {
/// Creates a new instance of MetaolmBuilder and initialises
/// all necessary values.
pub fn new() -> Self {
MetaolmBuilder {
device_id: None,
user_id: None,
olm_account: OlmAccount::new(),
next_blocker_id: 0,
blocked_items: BTreeMap::new(),
sender: None,
receiver: None,
}
}
/// Sets the device ID for the module. Is required.
pub fn with_device_id(mut self, device_id: &str) -> MetaolmBuilder {
self.device_id = Some(String::from(device_id));
self
}
/// Sets the user ID for the module. Is required.
pub fn with_user_id(mut self, user_id: &str) -> MetaolmBuilder {
self.user_id = Some(String::from(user_id));
self
}
/// Sets the communication channels for communication with the client.
///
/// # Parameters
/// * sender: Sender used for sending data to the client
/// * receiver: Receiver used for receiving data sent from the client
pub fn connected(
mut self,
sender: Sender<M2CCommObject>,
receiver: Receiver<C2MCommObject>,
) -> MetaolmBuilder {
self.sender = Some(sender);
self.receiver = Some(receiver);
self
}
/// Finishes the building process for an instance of Metaolm.
/// Requires that all required functions have been called.
pub fn finish(self) -> Result<Metaolm, &'static str> {
let mut finish_blocker = None;
if self.device_id.is_none() {
finish_blocker = Some("device_id");
} else if self.user_id.is_none() {
finish_blocker = Some("user_id");
} else if self.sender.is_none() {
finish_blocker = Some("sender");
} else if self.receiver.is_none() {
finish_blocker = Some("receiver");
}
match finish_blocker {
// FIXME: don't panic here
Some(x) => panic!(
"{} has to be set for Metaolm, before finishing the building stage!",
x
),
None => {}
}
let mut metaolm = Metaolm {
device_id: self.device_id.unwrap(),
user_id: self.user_id.unwrap(),
olm_account: self.olm_account,
next_blocker_id: self.next_blocker_id,
blocked_items: self.blocked_items,
sender: self.sender.unwrap(),
receiver: self.receiver.unwrap(),
};
let device_req_id = metaolm.next_blocker_id();
// request for getting potentailly stored OlmAccount
let device_req = M2CCommObject {
header: M2CCommType::Load,
details: String::from(format!("account/{}", &metaolm.device_id)),
body: String::new(),
blocker_id: Some(device_req_id),
};
metaolm.sender.send(device_req).unwrap();
// waiting for client response
let mut answer;
loop {
answer = metaolm.receiver.recv().unwrap();
match answer.blocker_id {
Some(id) => {
if id == device_req_id {
break;
}
}
_ => {}
}
}
// OlmAccount for the specified device ID is stored and
// is contained in the response
if answer.body != String::new() {
let stored_account = OlmAccount::unpickle(answer.body, &[]);
match stored_account {
Ok(account) => {
metaolm.olm_account = account;
}
Err(OlmAccountError::InvalidBase64) => {
return Err("The given account isn't valid base64 coding!");
}
Err(OlmAccountError::BadAccountKey) => {
return Err("The given account was encrypted, which is not implemented");
}
Err(_) => unreachable!(),
}
}
// empty response body means that no OlmAccount for this device id
// is stored, so we have to tell the client to store the one
// we already created
else {
let store_request_blocker = metaolm.next_blocker_id();
let register_request_blocker = metaolm.next_blocker_id();
// build a store request for the client
let store_request = M2CCommObject {
header: M2CCommType::Store,
details: String::from(format!("account/{}", &metaolm.device_id)),
body: metaolm.olm_account.pickle(&[]),
blocker_id: Some(store_request_blocker),
};
// and send it
metaolm.sender.send(store_request).unwrap();
// build the register request for the Matrix server
let mut register_json = json!(
{
"algorithms": ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"device_id": metaolm.device_id,
"keys": {
format!("curve25519:{}", &metaolm.device_id): "<curve25519_key>",
format!("ed25519:{}", &metaolm.device_id): "<ed25519_key>"
},
"user_id": metaolm.user_id
}
);
// FIXME: use proper Canonical JSON
let register_json_signature = metaolm
.olm_account
.sign_utf8_msg(&register_json.to_string());
register_json["signatures"][&metaolm.user_id]
[format!("ed25519:{}", metaolm.device_id)] = json!(register_json_signature);
// build the register request for the client that is to be
// forwarded to the Matrix server
let register_request = M2CCommObject {
header: M2CCommType::HTTPPost,
details: String::from("/_matrix/client/unstable/keys/upload"),
body: register_json.to_string(),
blocker_id: Some(register_request_blocker),
};
// the register request is currently blocked, as the OlmAccount
// has to be stored before we announce our keys to the Matrix server
metaolm
.blocked_items
.insert(register_request_blocker, register_request);
}
Ok(metaolm)
}
}
// Metaolm - a stand-alone module for E2EE in the Matrix protocol.
// Copyright (C) 2018 Johannes Hayeß
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
extern crate metaolm;
use metaolm::commobject::{C2MCommObject, C2MCommType};
use metaolm::metaolm::MetaolmBuilder;
use std::sync::mpsc::channel;
use std::thread;
#[test]
fn empty_init() {
// Channel initialisation
// Client <== Module
let (csender, creceiver) = channel();
// Client ==> Module
let (msender, mreceiver) = channel();
thread::spawn(move || {
let metaolm_builder = MetaolmBuilder::new();
metaolm_builder
.with_device_id("some_id")
.with_user_id("some_user_id")
.connected(csender, mreceiver)
.finish()
.unwrap();
});
let initial_message = creceiver.recv().unwrap();
let response = C2MCommObject {
header: C2MCommType::StorageRsp,
body: String::new(),
blocker_id: initial_message.blocker_id,
};
msender.send(response).unwrap();
let store_req = creceiver.recv().unwrap();
assert_eq!(String::from("account/some_id"), store_req.details);
println!("Body:\n{}", store_req.body);
}
#[test]
fn known_account_init() {
// Channel initialisation
// Client <== Module
let (csender, creceiver) = channel();
// Client ==> Module
let (msender, mreceiver) = channel();
thread::spawn(move || {
let metaolm_builder = MetaolmBuilder::new();
metaolm_builder
.with_device_id("some_id")
.with_user_id("some_user_id")
.connected(csender, mreceiver)
.finish()
.unwrap();
});
let initial_message = creceiver.recv().unwrap();
let response = C2MCommObject {
header: C2MCommType::StorageRsp,
body: String::from("9Mfk9/6oShETGfYBzdnbzGdsiVjqlQwBU0aFJa6AUYgnizgAGJAzstDHpcGA3NXuXHPnKIQN0Kq9itC6P7xrXJ0i6VevDwxQBNgAC61of2CdIl/ghI5Dua5uWsO25BMY1mVXiTA8jnINmQsKzba8gb9kw/yd/zM3BxVaZ0BgRyHoSWc3SXVlnol0isUcB+OtjznpgmSikCf3+17cK4+Eyke230L0cEjseCp0EaMUNW7Gn+rovmQYGQ"),
blocker_id: initial_message.blocker_id,
};
msender.send(response).unwrap();
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment