Merge branch 'master' into crypto-improvements

master
Damir Jelić 2020-12-22 15:53:08 +01:00
commit 007e452d39
9 changed files with 270 additions and 20 deletions

View File

@ -1498,6 +1498,18 @@ impl BaseClient {
.on_custom_event(room, &CustomEvent::Message(e)) .on_custom_event(room, &CustomEvent::Message(e))
.await .await
} }
AnySyncMessageEvent::CallInvite(e) => {
event_emitter.on_room_call_invite(room, e).await
}
AnySyncMessageEvent::CallAnswer(e) => {
event_emitter.on_room_call_answer(room, e).await
}
AnySyncMessageEvent::CallCandidates(e) => {
event_emitter.on_room_call_candidates(room, e).await
}
AnySyncMessageEvent::CallHangup(e) => {
event_emitter.on_room_call_hangup(room, e).await
}
_ => {} _ => {}
}, },
AnySyncRoomEvent::RedactedState(_event) => {} AnySyncRoomEvent::RedactedState(_event) => {}

View File

@ -19,6 +19,10 @@ use serde_json::value::RawValue as RawJsonValue;
use crate::{ use crate::{
events::{ events::{
call::{
answer::AnswerEventContent, candidates::CandidatesEventContent,
hangup::HangupEventContent, invite::InviteEventContent,
},
custom::CustomEventContent, custom::CustomEventContent,
fully_read::FullyReadEventContent, fully_read::FullyReadEventContent,
ignored_user_list::IgnoredUserListEventContent, ignored_user_list::IgnoredUserListEventContent,
@ -135,6 +139,19 @@ pub trait EventEmitter: Send + Sync {
_: &SyncMessageEvent<FeedbackEventContent>, _: &SyncMessageEvent<FeedbackEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `RoomEvent::CallInvite` event
async fn on_room_call_invite(&self, _: SyncRoom, _: &SyncMessageEvent<InviteEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::CallAnswer` event
async fn on_room_call_answer(&self, _: SyncRoom, _: &SyncMessageEvent<AnswerEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::CallCandidates` event
async fn on_room_call_candidates(
&self,
_: SyncRoom,
_: &SyncMessageEvent<CandidatesEventContent>,
) {
}
/// Fires when `Client` receives a `RoomEvent::CallHangup` event
async fn on_room_call_hangup(&self, _: SyncRoom, _: &SyncMessageEvent<HangupEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::RoomRedaction` event. /// Fires when `Client` receives a `RoomEvent::RoomRedaction` event.
async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {} async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {}
/// Fires when `Client` receives a `RoomEvent::RoomPowerLevels` event. /// Fires when `Client` receives a `RoomEvent::RoomPowerLevels` event.
@ -317,6 +334,22 @@ mod test {
) { ) {
self.0.lock().await.push("feedback".to_string()) self.0.lock().await.push("feedback".to_string())
} }
async fn on_room_call_invite(&self, _: SyncRoom, _: &SyncMessageEvent<InviteEventContent>) {
self.0.lock().await.push("call invite".to_string())
}
async fn on_room_call_answer(&self, _: SyncRoom, _: &SyncMessageEvent<AnswerEventContent>) {
self.0.lock().await.push("call answer".to_string())
}
async fn on_room_call_candidates(
&self,
_: SyncRoom,
_: &SyncMessageEvent<CandidatesEventContent>,
) {
self.0.lock().await.push("call candidates".to_string())
}
async fn on_room_call_hangup(&self, _: SyncRoom, _: &SyncMessageEvent<HangupEventContent>) {
self.0.lock().await.push("call hangup".to_string())
}
async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) { async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {
self.0.lock().await.push("redaction".to_string()) self.0.lock().await.push("redaction".to_string())
} }
@ -601,4 +634,28 @@ mod test {
], ],
) )
} }
#[async_test]
async fn event_emitter_voip() {
let vec = Arc::new(Mutex::new(Vec::new()));
let test_vec = Arc::clone(&vec);
let emitter = Box::new(EvEmitterTest(vec));
let client = get_client().await;
client.add_event_emitter(emitter).await;
let mut response = sync_response(SyncResponseFile::Voip);
client.receive_sync_response(&mut response).await.unwrap();
let v = test_vec.lock().await;
assert_eq!(
v.as_slice(),
[
"call invite",
"call answer",
"call candidates",
"call hangup",
],
)
}
} }

View File

@ -43,7 +43,7 @@ use matrix_sdk_common::{
instant::Instant, instant::Instant,
js_int::UInt, js_int::UInt,
locks::Mutex, locks::Mutex,
Raw, CanonicalJsonValue, Raw,
}; };
use olm_rs::{ use olm_rs::{
account::{IdentityKeys, OlmAccount, OneTimeKeys}, account::{IdentityKeys, OlmAccount, OneTimeKeys},
@ -743,7 +743,7 @@ impl ReadOnlyAccount {
.or_insert_with(BTreeMap::new) .or_insert_with(BTreeMap::new)
.insert( .insert(
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id), DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
self.sign_json(&json_device_keys).await, self.sign_json(json_device_keys).await,
); );
device_keys device_keys
@ -770,8 +770,10 @@ impl ReadOnlyAccount {
/// # Panic /// # Panic
/// ///
/// Panics if the json value can't be serialized. /// Panics if the json value can't be serialized.
pub async fn sign_json(&self, json: &Value) -> String { pub async fn sign_json(&self, json: Value) -> String {
self.sign(&json.to_string()).await let canonical_json: CanonicalJsonValue =
json.try_into().expect("Can't canonicalize the json value");
self.sign(&canonical_json.to_string()).await
} }
pub(crate) async fn signed_one_time_keys_helper( pub(crate) async fn signed_one_time_keys_helper(
@ -785,7 +787,7 @@ impl ReadOnlyAccount {
"key": key, "key": key,
}); });
let signature = self.sign_json(&key_json).await; let signature = self.sign_json(key_json).await;
let mut signature_map = BTreeMap::new(); let mut signature_map = BTreeMap::new();

View File

@ -214,7 +214,7 @@ impl PrivateCrossSigningIdentity {
master.cross_signing_key(account.user_id().to_owned(), KeyUsage::Master); master.cross_signing_key(account.user_id().to_owned(), KeyUsage::Master);
let signature = account let signature = account
.sign_json( .sign_json(
&serde_json::to_value(&public_key) serde_json::to_value(&public_key)
.expect("Can't convert own public master key to json"), .expect("Can't convert own public master key to json"),
) )
.await; .await;

View File

@ -23,7 +23,7 @@ use matrix_sdk_common::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{json, Error as JsonError, Value}; use serde_json::{json, Error as JsonError, Value};
use std::{collections::BTreeMap, sync::Arc}; use std::{collections::BTreeMap, convert::TryInto, sync::Arc};
use thiserror::Error; use thiserror::Error;
use zeroize::Zeroizing; use zeroize::Zeroizing;
@ -36,6 +36,7 @@ use matrix_sdk_common::{
api::r0::keys::{CrossSigningKey, KeyUsage}, api::r0::keys::{CrossSigningKey, KeyUsage},
identifiers::UserId, identifiers::UserId,
locks::Mutex, locks::Mutex,
CanonicalJsonValue,
}; };
use crate::{ use crate::{
@ -404,8 +405,9 @@ impl Signing {
pub async fn sign_json(&self, mut json: Value) -> Result<Signature, SignatureError> { pub async fn sign_json(&self, mut json: Value) -> Result<Signature, SignatureError> {
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?; let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
let _ = json_object.remove("signatures"); let _ = json_object.remove("signatures");
let canonical_json = serde_json::to_string(json_object)?; let canonical_json: CanonicalJsonValue =
Ok(self.sign(&canonical_json).await) json.try_into().expect("Can't canonicalize the json value");
Ok(self.sign(&canonical_json.to_string()).await)
} }
pub async fn sign(&self, message: &str) -> Signature { pub async fn sign(&self, message: &str) -> Signature {

View File

@ -14,8 +14,12 @@
use olm_rs::utility::OlmUtility; use olm_rs::utility::OlmUtility;
use serde_json::Value; use serde_json::Value;
use std::convert::TryInto;
use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}; use matrix_sdk_common::{
identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId},
CanonicalJsonValue,
};
use crate::error::SignatureError; use crate::error::SignatureError;
@ -63,11 +67,12 @@ impl Utility {
let unsigned = json_object.remove("unsigned"); let unsigned = json_object.remove("unsigned");
let signatures = json_object.remove("signatures"); let signatures = json_object.remove("signatures");
let canonical_json = serde_json::to_string(json_object)?; let canonical_json: CanonicalJsonValue = json
.clone()
.try_into()
.map_err(|_| SignatureError::NotAnObject)?;
if let Some(u) = unsigned { let canonical_json: String = canonical_json.to_string();
json_object.insert("unsigned".to_string(), u);
}
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?; let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
let signature_object = signatures let signature_object = signatures
@ -81,18 +86,66 @@ impl Utility {
.ok_or(SignatureError::NoSignatureFound)?; .ok_or(SignatureError::NoSignatureFound)?;
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?; let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
let ret = if self let ret = match self
.inner .inner
.ed25519_verify(signing_key, &canonical_json, signature) .ed25519_verify(signing_key, &canonical_json, signature)
.is_ok()
{ {
Ok(()) Ok(_) => Ok(()),
} else { Err(_) => Err(SignatureError::VerificationError),
Err(SignatureError::VerificationError)
}; };
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
if let Some(u) = unsigned {
json_object.insert("unsigned".to_string(), u);
}
json_object.insert("signatures".to_string(), signatures); json_object.insert("signatures".to_string(), signatures);
ret ret
} }
} }
#[cfg(test)]
mod test {
use super::Utility;
use matrix_sdk_common::identifiers::{user_id, DeviceKeyAlgorithm, DeviceKeyId};
use serde_json::json;
#[test]
fn signature_test() {
let mut device_keys = json!({
"device_id": "GBEWHQOYGS",
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"keys": {
"curve25519:GBEWHQOYGS": "F8QhZ0Z1rjtWrQOblMDgZtEX5x1UrG7sZ2Kk3xliNAU",
"ed25519:GBEWHQOYGS": "n469gw7zm+KW+JsFIJKnFVvCKU14HwQyocggcCIQgZY"
},
"signatures": {
"@example:localhost": {
"ed25519:GBEWHQOYGS": "OlF2REsqjYdAfr04ONx8VS/5cB7KjrWYRlLF4eUm2foAiQL/RAfsjsa2JXZeoOHh6vEualZHbWlod49OewVqBg"
}
},
"unsigned": {
"device_display_name": "Weechat-Matrix-rs"
},
"user_id": "@example:localhost"
});
let signing_key = "n469gw7zm+KW+JsFIJKnFVvCKU14HwQyocggcCIQgZY";
let utility = Utility::new();
utility
.verify_json(
&user_id!("@example:localhost"),
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, "GBEWHQOYGS".into()),
&signing_key,
&mut device_keys,
)
.expect("Can't verify device keys");
}
}

View File

@ -355,6 +355,7 @@ pub enum SyncResponseFile {
DefaultWithSummary, DefaultWithSummary,
Invite, Invite,
Leave, Leave,
Voip,
} }
/// Get specific API responses for testing /// Get specific API responses for testing
@ -365,6 +366,7 @@ pub fn sync_response(kind: SyncResponseFile) -> SyncResponse {
SyncResponseFile::DefaultWithSummary => &test_json::DEFAULT_SYNC_SUMMARY, SyncResponseFile::DefaultWithSummary => &test_json::DEFAULT_SYNC_SUMMARY,
SyncResponseFile::Invite => &test_json::INVITE_SYNC, SyncResponseFile::Invite => &test_json::INVITE_SYNC,
SyncResponseFile::Leave => &test_json::LEAVE_SYNC, SyncResponseFile::Leave => &test_json::LEAVE_SYNC,
SyncResponseFile::Voip => &test_json::VOIP_SYNC,
}; };
let response = Response::builder() let response = Response::builder()

View File

@ -16,7 +16,9 @@ pub use events::{
REACTION, REDACTED, REDACTED_INVALID, REDACTED_STATE, REDACTION, REGISTRATION_RESPONSE_ERR, REACTION, REDACTED, REDACTED_INVALID, REDACTED_STATE, REDACTION, REGISTRATION_RESPONSE_ERR,
ROOM_ID, ROOM_MESSAGES, TYPING, ROOM_ID, ROOM_MESSAGES, TYPING,
}; };
pub use sync::{DEFAULT_SYNC_SUMMARY, INVITE_SYNC, LEAVE_SYNC, LEAVE_SYNC_EVENT, MORE_SYNC, SYNC}; pub use sync::{
DEFAULT_SYNC_SUMMARY, INVITE_SYNC, LEAVE_SYNC, LEAVE_SYNC_EVENT, MORE_SYNC, SYNC, VOIP_SYNC,
};
lazy_static! { lazy_static! {
pub static ref DEVICES: JsonValue = json!({ pub static ref DEVICES: JsonValue = json!({

View File

@ -1101,3 +1101,123 @@ lazy_static! {
"next_batch": "s1380317562_757269739_1655566_503953763_334052043_1209862_55290918_65705002_101146" "next_batch": "s1380317562_757269739_1655566_503953763_334052043_1209862_55290918_65705002_101146"
}); });
} }
lazy_static! {
pub static ref VOIP_SYNC: JsonValue = json!({
"device_one_time_keys_count": {},
"next_batch": "s526_47314_0_7_1_1_1_11444_1",
"device_lists": {
"changed": [
"@example:example.org"
],
"left": []
},
"rooms": {
"invite": {},
"join": {
"!SVkFJHzfwvuaIEawgC:localhost": {
"summary": {},
"account_data": {
"events": []
},
"ephemeral": {
"events": [ ]
},
"state": {
"events": []
},
"timeline": {
"events": [
{
"content": {
"call_id": "12345",
"lifetime": 60000,
"offer": {
"sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
"type": "offer"
},
"version": 0
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 143273582,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.call.invite",
"unsigned": {
"age": 1234
}
},
{
"content": {
"answer": {
"sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
"type": "answer"
},
"call_id": "12345",
"lifetime": 60000,
"version": 0
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 143273582,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.call.answer",
"unsigned": {
"age": 1234
}
},
{
"content": {
"call_id": "12345",
"candidates": [
{
"candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0",
"sdpMLineIndex": 0,
"sdpMid": "audio"
}
],
"version": 0
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 143273582,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.call.candidates",
"unsigned": {
"age": 1234
}
},
{
"content": {
"call_id": "12345",
"version": 0
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 143273582,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.call.hangup",
"unsigned": {
"age": 1234
}
}
],
"limited": true,
"prev_batch": "t392-516_47314_0_7_1_1_1_11444_1"
},
"unread_notifications": {
"highlight_count": 0,
"notification_count": 11
}
}
},
"leave": {}
},
"to_device": {
"events": []
},
"presence": {
"events": []
}
});
}