2020-02-25 13:24:18 +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.
|
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
use std::collections::HashMap;
|
2020-02-25 13:24:18 +00:00
|
|
|
use std::convert::TryInto;
|
2020-03-18 13:15:56 +00:00
|
|
|
use std::result::Result as StdResult;
|
2020-02-25 13:24:18 +00:00
|
|
|
|
2020-03-18 13:15:56 +00:00
|
|
|
use super::error::{Result, SignatureError, VerificationResult};
|
2020-02-25 13:24:18 +00:00
|
|
|
use super::olm::Account;
|
|
|
|
use crate::api;
|
|
|
|
|
|
|
|
use api::r0::keys;
|
|
|
|
|
2020-02-25 16:36:11 +00:00
|
|
|
use cjson;
|
|
|
|
use olm_rs::utility::OlmUtility;
|
|
|
|
use serde_json::json;
|
2020-02-29 10:13:57 +00:00
|
|
|
use serde_json::Value;
|
2020-02-25 16:36:11 +00:00
|
|
|
|
2020-03-18 13:15:56 +00:00
|
|
|
use super::store::CryptoStoreError;
|
2020-03-10 12:41:14 +00:00
|
|
|
use ruma_client_api::r0::keys::{
|
|
|
|
AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm, OneTimeKey, SignedKey,
|
|
|
|
};
|
2020-03-12 14:41:11 +00:00
|
|
|
use ruma_client_api::r0::sync::sync_events::IncomingResponse as SyncResponse;
|
|
|
|
use ruma_events::{
|
2020-03-13 15:58:59 +00:00
|
|
|
to_device::{AnyToDeviceEvent as ToDeviceEvent, ToDeviceEncrypted, ToDeviceRoomKeyRequest},
|
2020-03-12 14:41:11 +00:00
|
|
|
Algorithm, EventResult,
|
|
|
|
};
|
2020-03-10 12:02:14 +00:00
|
|
|
use ruma_identifiers::{DeviceId, UserId};
|
|
|
|
|
2020-03-10 13:06:30 +00:00
|
|
|
pub type OneTimeKeys = HashMap<AlgorithmAndDeviceId, OneTimeKey>;
|
2020-03-10 12:41:14 +00:00
|
|
|
|
2020-03-18 13:15:56 +00:00
|
|
|
#[cfg(feature = "sqlite-cryptostore")]
|
|
|
|
use super::store::sqlite::SqliteStore;
|
|
|
|
|
2020-03-11 09:04:04 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct OlmMachine {
|
2020-02-25 13:24:18 +00:00
|
|
|
/// The unique user id that owns this account.
|
2020-03-10 12:02:14 +00:00
|
|
|
user_id: UserId,
|
2020-02-25 13:24:18 +00:00
|
|
|
/// The unique device id of the device that holds this account.
|
2020-03-10 12:02:14 +00:00
|
|
|
device_id: DeviceId,
|
2020-02-25 13:24:18 +00:00
|
|
|
/// Our underlying Olm Account holding our identity keys.
|
|
|
|
account: Account,
|
2020-02-26 08:36:52 +00:00
|
|
|
/// The number of signed one-time keys we have uploaded to the server. If
|
|
|
|
/// this is None, no action will be taken. After a sync request the client
|
|
|
|
/// needs to set this for us, depending on the count we will suggest the
|
|
|
|
/// client to upload new keys.
|
|
|
|
uploaded_signed_key_count: Option<u64>,
|
2020-02-25 13:24:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl OlmMachine {
|
2020-03-10 12:02:14 +00:00
|
|
|
const ALGORITHMS: &'static [&'static ruma_events::Algorithm] = &[
|
|
|
|
&Algorithm::OlmV1Curve25519AesSha2,
|
|
|
|
&Algorithm::MegolmV1AesSha2,
|
2020-02-25 16:36:11 +00:00
|
|
|
];
|
|
|
|
|
2020-02-25 13:24:18 +00:00
|
|
|
/// Create a new account.
|
2020-03-18 13:15:56 +00:00
|
|
|
pub fn new(user_id: &UserId, device_id: &str) -> Result<Self> {
|
|
|
|
Ok(OlmMachine {
|
2020-03-11 09:04:04 +00:00
|
|
|
user_id: user_id.clone(),
|
2020-02-25 13:24:18 +00:00
|
|
|
device_id: device_id.to_owned(),
|
|
|
|
account: Account::new(),
|
2020-02-26 08:36:52 +00:00
|
|
|
uploaded_signed_key_count: None,
|
2020-03-18 13:15:56 +00:00
|
|
|
})
|
2020-02-25 13:24:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Should account or one-time keys be uploaded to the server.
|
|
|
|
pub fn should_upload_keys(&self) -> bool {
|
|
|
|
if !self.account.shared() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have a known key count, check that we have more than
|
|
|
|
// max_one_time_Keys() / 2, otherwise tell the client to upload more.
|
2020-02-26 08:36:52 +00:00
|
|
|
match self.uploaded_signed_key_count {
|
2020-02-25 13:24:18 +00:00
|
|
|
Some(count) => {
|
|
|
|
let max_keys = self.account.max_one_time_keys() as u64;
|
|
|
|
let key_count = (max_keys / 2) - count;
|
|
|
|
key_count > 0
|
|
|
|
}
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 16:36:11 +00:00
|
|
|
/// Receive a successful keys upload response.
|
2020-02-25 13:24:18 +00:00
|
|
|
///
|
2020-02-25 16:36:11 +00:00
|
|
|
/// # Arguments
|
2020-02-25 13:24:18 +00:00
|
|
|
///
|
2020-03-02 10:31:03 +00:00
|
|
|
/// * `response` - The keys upload response of the request that the client
|
2020-03-11 09:04:04 +00:00
|
|
|
/// performed.
|
2020-02-25 13:24:18 +00:00
|
|
|
pub async fn receive_keys_upload_response(&mut self, response: &keys::upload_keys::Response) {
|
|
|
|
self.account.shared = true;
|
2020-03-11 09:04:04 +00:00
|
|
|
|
2020-02-25 13:24:18 +00:00
|
|
|
let one_time_key_count = response
|
|
|
|
.one_time_key_counts
|
|
|
|
.get(&keys::KeyAlgorithm::SignedCurve25519);
|
|
|
|
|
2020-03-11 09:09:00 +00:00
|
|
|
let count: u64 = one_time_key_count.map_or(0, |c| (*c).into());
|
|
|
|
self.uploaded_signed_key_count = Some(count);
|
2020-02-25 13:24:18 +00:00
|
|
|
|
|
|
|
self.account.mark_keys_as_published();
|
|
|
|
// TODO save the account here.
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generate new one-time keys.
|
|
|
|
///
|
|
|
|
/// Returns the number of newly generated one-time keys. If no keys can be
|
|
|
|
/// generated returns an empty error.
|
2020-03-18 13:15:56 +00:00
|
|
|
fn generate_one_time_keys(&self) -> StdResult<u64, ()> {
|
2020-02-26 08:36:52 +00:00
|
|
|
match self.uploaded_signed_key_count {
|
2020-02-25 13:24:18 +00:00
|
|
|
Some(count) => {
|
|
|
|
let max_keys = self.account.max_one_time_keys() as u64;
|
2020-02-26 08:18:10 +00:00
|
|
|
let max_on_server = max_keys / 2;
|
2020-02-25 13:24:18 +00:00
|
|
|
|
2020-02-26 08:18:10 +00:00
|
|
|
if count >= (max_on_server) {
|
2020-02-25 13:36:09 +00:00
|
|
|
return Err(());
|
2020-02-25 13:24:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-26 08:18:10 +00:00
|
|
|
let key_count = (max_on_server) - count;
|
|
|
|
|
2020-02-25 13:36:09 +00:00
|
|
|
let key_count: usize = key_count
|
|
|
|
.try_into()
|
|
|
|
.unwrap_or_else(|_| self.account.max_one_time_keys());
|
2020-02-25 13:24:18 +00:00
|
|
|
|
|
|
|
self.account.generate_one_time_keys(key_count);
|
|
|
|
Ok(key_count as u64)
|
2020-02-25 13:36:09 +00:00
|
|
|
}
|
|
|
|
None => Err(()),
|
2020-02-25 13:24:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-25 13:36:09 +00:00
|
|
|
|
2020-02-25 16:36:11 +00:00
|
|
|
/// Sign the device keys and return a JSON Value to upload them.
|
2020-03-10 12:02:14 +00:00
|
|
|
fn device_keys(&self) -> DeviceKeys {
|
2020-02-25 16:36:11 +00:00
|
|
|
let identity_keys = self.account.identity_keys();
|
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
let mut keys = HashMap::new();
|
|
|
|
|
|
|
|
keys.insert(
|
|
|
|
AlgorithmAndDeviceId(KeyAlgorithm::Curve25519, self.device_id.clone()),
|
|
|
|
identity_keys.curve25519().to_owned(),
|
|
|
|
);
|
|
|
|
keys.insert(
|
|
|
|
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
|
|
|
identity_keys.ed25519().to_owned(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let device_keys = json!({
|
2020-02-25 16:36:11 +00:00
|
|
|
"user_id": self.user_id,
|
|
|
|
"device_id": self.device_id,
|
|
|
|
"algorithms": OlmMachine::ALGORITHMS,
|
2020-03-10 12:02:14 +00:00
|
|
|
"keys": keys,
|
2020-02-25 16:36:11 +00:00
|
|
|
});
|
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
let mut signatures = HashMap::new();
|
2020-02-25 16:36:11 +00:00
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
let mut signature = HashMap::new();
|
|
|
|
signature.insert(
|
|
|
|
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
|
|
|
self.sign_json(&device_keys),
|
|
|
|
);
|
|
|
|
signatures.insert(self.user_id.clone(), signature);
|
|
|
|
|
|
|
|
DeviceKeys {
|
|
|
|
user_id: self.user_id.clone(),
|
|
|
|
device_id: self.device_id.clone(),
|
|
|
|
algorithms: vec![
|
|
|
|
Algorithm::OlmV1Curve25519AesSha2,
|
|
|
|
Algorithm::MegolmV1AesSha2,
|
|
|
|
],
|
|
|
|
keys,
|
|
|
|
signatures,
|
|
|
|
unsigned: None,
|
|
|
|
}
|
2020-02-25 16:36:11 +00:00
|
|
|
}
|
|
|
|
|
2020-02-29 10:13:57 +00:00
|
|
|
/// Generate, sign and prepare one-time keys to be uploaded.
|
|
|
|
///
|
|
|
|
/// If no one-time keys need to be uploaded returns an empty error.
|
2020-03-18 13:15:56 +00:00
|
|
|
fn signed_one_time_keys(&self) -> StdResult<OneTimeKeys, ()> {
|
2020-02-29 10:13:57 +00:00
|
|
|
let _ = self.generate_one_time_keys()?;
|
|
|
|
|
|
|
|
let one_time_keys = self.account.one_time_keys();
|
2020-03-10 12:41:14 +00:00
|
|
|
let mut one_time_key_map = HashMap::new();
|
2020-02-29 10:13:57 +00:00
|
|
|
|
|
|
|
for (key_id, key) in one_time_keys.curve25519().iter() {
|
|
|
|
let key_json = json!({
|
|
|
|
"key": key,
|
|
|
|
});
|
|
|
|
|
|
|
|
let signature = self.sign_json(&key_json);
|
|
|
|
|
2020-03-10 12:41:14 +00:00
|
|
|
let mut signature_map = HashMap::new();
|
|
|
|
|
|
|
|
signature_map.insert(
|
|
|
|
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
|
|
|
signature,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut signatures = HashMap::new();
|
|
|
|
signatures.insert(self.user_id.clone(), signature_map);
|
2020-02-29 10:13:57 +00:00
|
|
|
|
2020-03-10 12:41:14 +00:00
|
|
|
let signed_key = SignedKey {
|
|
|
|
key: key.to_owned(),
|
|
|
|
signatures,
|
|
|
|
};
|
|
|
|
|
|
|
|
one_time_key_map.insert(
|
|
|
|
AlgorithmAndDeviceId(KeyAlgorithm::SignedCurve25519, key_id.to_owned()),
|
|
|
|
OneTimeKey::SignedKey(signed_key),
|
|
|
|
);
|
2020-02-29 10:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(one_time_key_map)
|
|
|
|
}
|
|
|
|
|
2020-03-02 10:31:03 +00:00
|
|
|
/// Convert a JSON value to the canonical representation and sign the JSON
|
|
|
|
/// string.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `json` - The value that should be converted into a canonical JSON
|
|
|
|
/// string.
|
2020-02-25 16:36:11 +00:00
|
|
|
fn sign_json(&self, json: &Value) -> String {
|
2020-02-26 08:18:10 +00:00
|
|
|
let canonical_json = cjson::to_string(json)
|
|
|
|
.unwrap_or_else(|_| panic!(format!("Can't serialize {} to canonical JSON", json)));
|
2020-02-25 16:36:11 +00:00
|
|
|
self.account.sign(&canonical_json)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Verify a signed JSON object.
|
|
|
|
///
|
|
|
|
/// The object must have a signatures key associated with an object of the
|
|
|
|
/// form `user_id: {key_id: signature}`.
|
|
|
|
///
|
2020-03-02 10:31:03 +00:00
|
|
|
/// Returns Ok if the signature was successfully verified, otherwise an
|
|
|
|
/// SignatureError.
|
|
|
|
///
|
2020-02-25 16:36:11 +00:00
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `user_id` - The user who signed the JSON object.
|
2020-03-02 10:31:03 +00:00
|
|
|
///
|
2020-02-25 16:36:11 +00:00
|
|
|
/// * `device_id` - The device that signed the JSON object.
|
2020-03-02 10:31:03 +00:00
|
|
|
///
|
2020-02-25 16:36:11 +00:00
|
|
|
/// * `user_key` - The public ed25519 key which was used to sign the JSON
|
|
|
|
/// object.
|
|
|
|
///
|
2020-03-02 10:31:03 +00:00
|
|
|
/// * `json` - The JSON object that should be verified.
|
2020-02-25 16:36:11 +00:00
|
|
|
fn verify_json(
|
|
|
|
&self,
|
2020-03-10 12:02:14 +00:00
|
|
|
user_id: &UserId,
|
2020-02-25 16:36:11 +00:00
|
|
|
device_id: &str,
|
|
|
|
user_key: &str,
|
|
|
|
json: &mut Value,
|
2020-03-18 13:15:56 +00:00
|
|
|
) -> VerificationResult<()> {
|
2020-02-25 16:36:11 +00:00
|
|
|
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
|
|
|
let unsigned = json_object.remove("unsigned");
|
|
|
|
let signatures = json_object.remove("signatures");
|
|
|
|
|
|
|
|
let canonical_json = cjson::to_string(json_object)?;
|
|
|
|
|
|
|
|
if let Some(u) = unsigned {
|
|
|
|
json_object.insert("unsigned".to_string(), u);
|
|
|
|
}
|
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
// TODO this should be part of ruma-client-api.
|
|
|
|
let key_id_string = format!("{}:{}", KeyAlgorithm::Ed25519, device_id);
|
2020-02-25 16:36:11 +00:00
|
|
|
|
|
|
|
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
|
|
|
|
let signature_object = signatures
|
|
|
|
.as_object()
|
|
|
|
.ok_or(SignatureError::NoSignatureFound)?;
|
|
|
|
let signature = signature_object
|
2020-03-10 12:02:14 +00:00
|
|
|
.get(&user_id.to_string())
|
2020-02-25 16:36:11 +00:00
|
|
|
.ok_or(SignatureError::NoSignatureFound)?;
|
|
|
|
let signature = signature
|
2020-03-10 12:02:14 +00:00
|
|
|
.get(key_id_string)
|
2020-02-25 16:36:11 +00:00
|
|
|
.ok_or(SignatureError::NoSignatureFound)?;
|
|
|
|
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
|
|
|
|
|
|
|
|
let utility = OlmUtility::new();
|
|
|
|
|
|
|
|
let ret = if utility
|
|
|
|
.ed25519_verify(&user_key, &canonical_json, signature)
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(SignatureError::VerificationError)
|
|
|
|
};
|
|
|
|
|
|
|
|
json_object.insert("signatures".to_string(), signatures);
|
|
|
|
|
|
|
|
ret
|
|
|
|
}
|
2020-03-10 13:06:30 +00:00
|
|
|
|
|
|
|
/// Get a tuple of device and one-time keys that need to be uploaded.
|
|
|
|
///
|
|
|
|
/// Returns an empty error if no keys need to be uploaded.
|
2020-03-18 13:15:56 +00:00
|
|
|
pub fn keys_for_upload(&self) -> StdResult<(Option<DeviceKeys>, Option<OneTimeKeys>), ()> {
|
2020-03-10 13:06:30 +00:00
|
|
|
if !self.should_upload_keys() {
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let device_keys = if !self.account.shared() {
|
|
|
|
Some(self.device_keys())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let one_time_keys: Option<OneTimeKeys> = self.signed_one_time_keys().ok();
|
|
|
|
|
|
|
|
Ok((device_keys, one_time_keys))
|
|
|
|
}
|
2020-03-12 14:41:11 +00:00
|
|
|
|
|
|
|
/// Decrypt a to-device event.
|
|
|
|
///
|
|
|
|
/// Returns a decrypted `ToDeviceEvent` if the decryption was successful,
|
|
|
|
/// an error indicating why decryption failed otherwise.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `event` - The to-device event that should be decrypted.
|
2020-03-18 13:15:56 +00:00
|
|
|
fn decrypt_to_device_event(&self, _: &ToDeviceEncrypted) -> StdResult<ToDeviceEvent, ()> {
|
2020-03-12 14:41:11 +00:00
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_room_key_request(&self, _: &ToDeviceRoomKeyRequest) {
|
|
|
|
// TODO handle room key requests here.
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_verification_event(&self, _: &ToDeviceEvent) {
|
|
|
|
// TODO handle to-device verification events here.
|
|
|
|
}
|
|
|
|
|
2020-03-12 15:14:43 +00:00
|
|
|
pub fn receive_sync_response(&mut self, response: &mut SyncResponse) {
|
|
|
|
let one_time_key_count = response
|
|
|
|
.device_one_time_keys_count
|
|
|
|
.get(&keys::KeyAlgorithm::SignedCurve25519);
|
|
|
|
|
|
|
|
let count: u64 = one_time_key_count.map_or(0, |c| (*c).into());
|
|
|
|
self.uploaded_signed_key_count = Some(count);
|
|
|
|
|
2020-03-12 14:41:11 +00:00
|
|
|
for event in response.to_device.events.iter() {
|
|
|
|
let event = if let EventResult::Ok(e) = event {
|
|
|
|
e
|
|
|
|
} else {
|
|
|
|
// Skip invalid events.
|
|
|
|
// TODO log here
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
match event {
|
|
|
|
ToDeviceEvent::RoomEncrypted(e) => {
|
|
|
|
// TODO put the decrypted event into a vec so we can replace
|
|
|
|
// them in the sync response.
|
|
|
|
let _ = self.decrypt_to_device_event(e);
|
|
|
|
}
|
|
|
|
ToDeviceEvent::RoomKeyRequest(e) => self.handle_room_key_request(e),
|
|
|
|
ToDeviceEvent::KeyVerificationAccept(..)
|
|
|
|
| ToDeviceEvent::KeyVerificationCancel(..)
|
|
|
|
| ToDeviceEvent::KeyVerificationKey(..)
|
|
|
|
| ToDeviceEvent::KeyVerificationMac(..)
|
|
|
|
| ToDeviceEvent::KeyVerificationRequest(..)
|
|
|
|
| ToDeviceEvent::KeyVerificationStart(..) => self.handle_verification_event(event),
|
|
|
|
_ => continue,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-25 13:24:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2020-03-10 12:02:14 +00:00
|
|
|
static USER_ID: &str = "@test:example.org";
|
2020-02-25 13:24:18 +00:00
|
|
|
const DEVICE_ID: &str = "DEVICEID";
|
|
|
|
|
2020-02-25 13:36:09 +00:00
|
|
|
use js_int::UInt;
|
2020-02-25 13:24:18 +00:00
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::prelude::*;
|
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
use ruma_identifiers::UserId;
|
|
|
|
use serde_json::json;
|
|
|
|
|
2020-02-25 13:24:18 +00:00
|
|
|
use crate::api::r0::keys;
|
|
|
|
use crate::crypto::machine::OlmMachine;
|
|
|
|
use http::Response;
|
|
|
|
|
2020-03-10 12:02:14 +00:00
|
|
|
fn user_id() -> UserId {
|
|
|
|
UserId::try_from(USER_ID).unwrap()
|
|
|
|
}
|
|
|
|
|
2020-02-25 13:24:18 +00:00
|
|
|
fn response_from_file(path: &str) -> Response<Vec<u8>> {
|
2020-02-26 08:36:52 +00:00
|
|
|
let mut file = File::open(path)
|
|
|
|
.unwrap_or_else(|_| panic!(format!("No such data file found {}", path)));
|
2020-02-25 13:24:18 +00:00
|
|
|
let mut contents = Vec::new();
|
|
|
|
file.read_to_end(&mut contents)
|
2020-02-26 08:36:52 +00:00
|
|
|
.unwrap_or_else(|_| panic!(format!("Can't read data file {}", path)));
|
2020-02-25 13:24:18 +00:00
|
|
|
|
|
|
|
Response::builder().status(200).body(contents).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn keys_upload_response() -> keys::upload_keys::Response {
|
|
|
|
let data = response_from_file("tests/data/keys_upload.json");
|
|
|
|
keys::upload_keys::Response::try_from(data).expect("Can't parse the keys upload response")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn create_olm_machine() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-02-25 13:24:18 +00:00
|
|
|
assert!(machine.should_upload_keys());
|
|
|
|
}
|
|
|
|
|
2020-03-16 12:24:54 +00:00
|
|
|
#[tokio::test]
|
2020-02-25 13:24:18 +00:00
|
|
|
async fn receive_keys_upload_response() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let mut machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-02-25 13:24:18 +00:00
|
|
|
let mut response = keys_upload_response();
|
|
|
|
|
2020-02-25 13:36:09 +00:00
|
|
|
response
|
|
|
|
.one_time_key_counts
|
|
|
|
.remove(&keys::KeyAlgorithm::SignedCurve25519)
|
|
|
|
.unwrap();
|
2020-02-25 13:24:18 +00:00
|
|
|
|
|
|
|
assert!(machine.should_upload_keys());
|
|
|
|
machine.receive_keys_upload_response(&response).await;
|
2020-03-11 09:09:00 +00:00
|
|
|
assert!(machine.should_upload_keys());
|
2020-02-25 13:24:18 +00:00
|
|
|
|
2020-02-25 13:36:09 +00:00
|
|
|
response.one_time_key_counts.insert(
|
|
|
|
keys::KeyAlgorithm::SignedCurve25519,
|
|
|
|
UInt::try_from(10).unwrap(),
|
|
|
|
);
|
2020-02-25 13:24:18 +00:00
|
|
|
machine.receive_keys_upload_response(&response).await;
|
|
|
|
assert!(machine.should_upload_keys());
|
|
|
|
|
2020-02-25 13:36:09 +00:00
|
|
|
response.one_time_key_counts.insert(
|
|
|
|
keys::KeyAlgorithm::SignedCurve25519,
|
|
|
|
UInt::try_from(50).unwrap(),
|
|
|
|
);
|
2020-02-25 13:24:18 +00:00
|
|
|
machine.receive_keys_upload_response(&response).await;
|
|
|
|
assert!(!machine.should_upload_keys());
|
|
|
|
}
|
|
|
|
|
2020-03-16 12:24:54 +00:00
|
|
|
#[tokio::test]
|
2020-02-25 13:24:18 +00:00
|
|
|
async fn generate_one_time_keys() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let mut machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-02-25 13:24:18 +00:00
|
|
|
|
|
|
|
let mut response = keys_upload_response();
|
|
|
|
|
|
|
|
assert!(machine.should_upload_keys());
|
|
|
|
assert!(machine.generate_one_time_keys().is_err());
|
|
|
|
|
|
|
|
machine.receive_keys_upload_response(&response).await;
|
|
|
|
assert!(machine.should_upload_keys());
|
|
|
|
assert!(machine.generate_one_time_keys().is_ok());
|
|
|
|
|
2020-02-25 13:36:09 +00:00
|
|
|
response.one_time_key_counts.insert(
|
|
|
|
keys::KeyAlgorithm::SignedCurve25519,
|
|
|
|
UInt::try_from(50).unwrap(),
|
|
|
|
);
|
2020-02-25 13:24:18 +00:00
|
|
|
machine.receive_keys_upload_response(&response).await;
|
|
|
|
assert!(machine.generate_one_time_keys().is_err());
|
|
|
|
}
|
2020-02-25 16:36:11 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_device_key_signing() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-02-25 16:36:11 +00:00
|
|
|
|
|
|
|
let mut device_keys = machine.device_keys();
|
|
|
|
let identity_keys = machine.account.identity_keys();
|
|
|
|
let ed25519_key = identity_keys.ed25519();
|
|
|
|
|
|
|
|
let ret = machine.verify_json(
|
|
|
|
&machine.user_id,
|
|
|
|
&machine.device_id,
|
|
|
|
ed25519_key,
|
2020-03-10 12:02:14 +00:00
|
|
|
&mut json!(&mut device_keys),
|
2020-02-25 16:36:11 +00:00
|
|
|
);
|
|
|
|
assert!(ret.is_ok());
|
|
|
|
}
|
2020-02-25 16:49:43 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_invalid_signature() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-02-25 16:49:43 +00:00
|
|
|
|
|
|
|
let mut device_keys = machine.device_keys();
|
|
|
|
|
|
|
|
let ret = machine.verify_json(
|
|
|
|
&machine.user_id,
|
|
|
|
&machine.device_id,
|
|
|
|
"fake_key",
|
2020-03-10 12:02:14 +00:00
|
|
|
&mut json!(&mut device_keys),
|
2020-02-25 16:49:43 +00:00
|
|
|
);
|
|
|
|
assert!(ret.is_err());
|
|
|
|
}
|
2020-02-29 10:13:57 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_one_time_key_signing() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let mut machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-02-29 10:13:57 +00:00
|
|
|
machine.uploaded_signed_key_count = Some(49);
|
|
|
|
|
|
|
|
let mut one_time_keys = machine.signed_one_time_keys().unwrap();
|
|
|
|
let identity_keys = machine.account.identity_keys();
|
|
|
|
let ed25519_key = identity_keys.ed25519();
|
|
|
|
|
2020-03-10 12:41:14 +00:00
|
|
|
let mut one_time_key = one_time_keys.values_mut().nth(0).unwrap();
|
2020-02-29 10:13:57 +00:00
|
|
|
|
|
|
|
let ret = machine.verify_json(
|
|
|
|
&machine.user_id,
|
|
|
|
&machine.device_id,
|
|
|
|
ed25519_key,
|
2020-03-10 12:41:14 +00:00
|
|
|
&mut json!(&mut one_time_key),
|
2020-02-29 10:13:57 +00:00
|
|
|
);
|
|
|
|
assert!(ret.is_ok());
|
|
|
|
}
|
2020-03-10 13:06:30 +00:00
|
|
|
|
2020-03-16 12:24:54 +00:00
|
|
|
#[tokio::test]
|
2020-03-10 13:06:30 +00:00
|
|
|
async fn test_keys_for_upload() {
|
2020-03-18 13:15:56 +00:00
|
|
|
let mut machine = OlmMachine::new(&user_id(), DEVICE_ID).unwrap();
|
2020-03-10 13:06:30 +00:00
|
|
|
machine.uploaded_signed_key_count = Some(0);
|
|
|
|
|
|
|
|
let identity_keys = machine.account.identity_keys();
|
|
|
|
let ed25519_key = identity_keys.ed25519();
|
|
|
|
|
|
|
|
let (device_keys, mut one_time_keys) = machine
|
|
|
|
.keys_for_upload()
|
|
|
|
.expect("Can't prepare initial key upload");
|
|
|
|
|
|
|
|
let ret = machine.verify_json(
|
|
|
|
&machine.user_id,
|
|
|
|
&machine.device_id,
|
|
|
|
ed25519_key,
|
|
|
|
&mut json!(&mut one_time_keys.as_mut().unwrap().values_mut().nth(0)),
|
|
|
|
);
|
|
|
|
assert!(ret.is_ok());
|
|
|
|
|
|
|
|
let ret = machine.verify_json(
|
|
|
|
&machine.user_id,
|
|
|
|
&machine.device_id,
|
|
|
|
ed25519_key,
|
|
|
|
&mut json!(&mut device_keys.unwrap()),
|
|
|
|
);
|
|
|
|
assert!(ret.is_ok());
|
|
|
|
|
|
|
|
let mut response = keys_upload_response();
|
|
|
|
response.one_time_key_counts.insert(
|
|
|
|
keys::KeyAlgorithm::SignedCurve25519,
|
|
|
|
UInt::new_wrapping(one_time_keys.unwrap().len() as u64),
|
|
|
|
);
|
|
|
|
|
|
|
|
machine.receive_keys_upload_response(&response).await;
|
|
|
|
|
|
|
|
let ret = machine.keys_for_upload();
|
|
|
|
assert!(ret.is_err());
|
|
|
|
}
|
2020-02-25 13:24:18 +00:00
|
|
|
}
|