crypto: Return a higher level struct when decrypting olm messages instead of tuples

master
Damir Jelić 2020-12-01 12:41:11 +01:00
parent efe659910f
commit 24592adbba
6 changed files with 107 additions and 78 deletions

View File

@ -14,10 +14,10 @@ fn decode_url_safe(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
decode_config(input, URL_SAFE_NO_PAD) decode_config(input, URL_SAFE_NO_PAD)
} }
fn encode(input: impl AsRef<[u8]>) -> String { pub fn encode(input: impl AsRef<[u8]>) -> String {
encode_config(input, STANDARD_NO_PAD) encode_config(input, STANDARD_NO_PAD)
} }
fn encode_url_safe(input: impl AsRef<[u8]>) -> String { pub fn encode_url_safe(input: impl AsRef<[u8]>) -> String {
encode_config(input, URL_SAFE_NO_PAD) encode_config(input, URL_SAFE_NO_PAD)
} }

View File

@ -1137,12 +1137,11 @@ mod test {
.unwrap() .unwrap()
.is_none()); .is_none());
let (_, decrypted, sender_key, _) = let decrypted = alice_account.decrypt_to_device_event(&event).await.unwrap();
alice_account.decrypt_to_device_event(&event).await.unwrap();
if let AnyToDeviceEvent::ForwardedRoomKey(mut e) = decrypted.deserialize().unwrap() { if let AnyToDeviceEvent::ForwardedRoomKey(mut e) = decrypted.event.deserialize().unwrap() {
let (_, session) = alice_machine let (_, session) = alice_machine
.receive_forwarded_room_key(&sender_key, &mut e) .receive_forwarded_room_key(&decrypted.sender_key, &mut e)
.await .await
.unwrap(); .unwrap();
alice_machine alice_machine
@ -1157,7 +1156,11 @@ mod test {
// Check that alice now does have the session. // Check that alice now does have the session.
let session = alice_machine let session = alice_machine
.store .store
.get_inbound_group_session(&room_id(), &sender_key, group_session.session_id()) .get_inbound_group_session(
&room_id(),
&decrypted.sender_key,
group_session.session_id(),
)
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -1325,12 +1328,11 @@ mod test {
.unwrap() .unwrap()
.is_none()); .is_none());
let (_, decrypted, sender_key, _) = let decrypted = alice_account.decrypt_to_device_event(&event).await.unwrap();
alice_account.decrypt_to_device_event(&event).await.unwrap();
if let AnyToDeviceEvent::ForwardedRoomKey(mut e) = decrypted.deserialize().unwrap() { if let AnyToDeviceEvent::ForwardedRoomKey(mut e) = decrypted.event.deserialize().unwrap() {
let (_, session) = alice_machine let (_, session) = alice_machine
.receive_forwarded_room_key(&sender_key, &mut e) .receive_forwarded_room_key(&decrypted.sender_key, &mut e)
.await .await
.unwrap(); .unwrap();
alice_machine alice_machine
@ -1345,7 +1347,11 @@ mod test {
// Check that alice now does have the session. // Check that alice now does have the session.
let session = alice_machine let session = alice_machine
.store .store
.get_inbound_group_session(&room_id(), &sender_key, group_session.session_id()) .get_inbound_group_session(
&room_id(),
&decrypted.sender_key,
group_session.session_id(),
)
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();

View File

@ -52,7 +52,7 @@ use crate::{
key_request::KeyRequestMachine, key_request::KeyRequestMachine,
olm::{ olm::{
Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys, Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys,
InboundGroupSession, PrivateCrossSigningIdentity, ReadOnlyAccount, Session, InboundGroupSession, OlmDecryptionInfo, PrivateCrossSigningIdentity, ReadOnlyAccount,
}, },
requests::{IncomingResponse, OutgoingRequest, UploadSigningKeysRequest}, requests::{IncomingResponse, OutgoingRequest, UploadSigningKeysRequest},
session_manager::{GroupSessionManager, SessionManager}, session_manager::{GroupSessionManager, SessionManager},
@ -555,24 +555,23 @@ impl OlmMachine {
async fn decrypt_to_device_event( async fn decrypt_to_device_event(
&self, &self,
event: &ToDeviceEvent<EncryptedEventContent>, event: &ToDeviceEvent<EncryptedEventContent>,
) -> OlmResult<(Session, Raw<AnyToDeviceEvent>, Option<InboundGroupSession>)> { ) -> OlmResult<OlmDecryptionInfo> {
let (session, decrypted_event, sender_key, signing_key) = let mut decrypted = self.account.decrypt_to_device_event(event).await?;
self.account.decrypt_to_device_event(event).await?;
// Handle the decrypted event, e.g. fetch out Megolm sessions out of // Handle the decrypted event, e.g. fetch out Megolm sessions out of
// the event. // the event.
if let (Some(event), group_session) = self if let (Some(event), group_session) =
.handle_decrypted_to_device_event(&sender_key, &signing_key, &decrypted_event) self.handle_decrypted_to_device_event(&decrypted).await?
.await?
{ {
// Some events may have sensitive data e.g. private keys, while we // Some events may have sensitive data e.g. private keys, while we
// want to notify our users that a private key was received we // want to notify our users that a private key was received we
// don't want them to be able to do silly things with it. Handling // don't want them to be able to do silly things with it. Handling
// events modifies them and returns a modified one, so replace it // events modifies them and returns a modified one, so replace it
// here if we get one. // here if we get one.
Ok((session, event, group_session)) decrypted.event = event;
} else { decrypted.inbound_group_session = group_session;
Ok((session, decrypted_event, None))
} }
Ok(decrypted)
} }
/// Create a group session from a room key and add it to our crypto store. /// Create a group session from a room key and add it to our crypto store.
@ -704,27 +703,29 @@ impl OlmMachine {
/// * `event` - The decrypted to-device event. /// * `event` - The decrypted to-device event.
async fn handle_decrypted_to_device_event( async fn handle_decrypted_to_device_event(
&self, &self,
sender_key: &str, decrypted: &OlmDecryptionInfo,
signing_key: &str,
event: &Raw<AnyToDeviceEvent>,
) -> OlmResult<(Option<Raw<AnyToDeviceEvent>>, Option<InboundGroupSession>)> { ) -> OlmResult<(Option<Raw<AnyToDeviceEvent>>, Option<InboundGroupSession>)> {
let event = if let Ok(e) = event.deserialize() { let event = match decrypted.event.deserialize() {
e Ok(e) => e,
} else { Err(e) => {
warn!("Decrypted to-device event failed to be parsed correctly"); warn!(
return Ok((None, None)); "Decrypted to-device event failed to be parsed correctly {:?}",
e
);
return Ok((None, None));
}
}; };
match event { match event {
AnyToDeviceEvent::RoomKey(mut e) => { AnyToDeviceEvent::RoomKey(mut e) => Ok(self
Ok(self.add_room_key(sender_key, signing_key, &mut e).await?) .add_room_key(&decrypted.sender_key, &decrypted.signing_key, &mut e)
} .await?),
AnyToDeviceEvent::ForwardedRoomKey(mut e) => Ok(self AnyToDeviceEvent::ForwardedRoomKey(mut e) => Ok(self
.key_request_machine .key_request_machine
.receive_forwarded_room_key(sender_key, &mut e) .receive_forwarded_room_key(&decrypted.sender_key, &mut e)
.await?), .await?),
_ => { _ => {
warn!("Received a unexpected encrypted to-device event"); warn!("Received an unexpected encrypted to-device event");
Ok((None, None)) Ok((None, None))
} }
} }
@ -808,38 +809,37 @@ impl OlmMachine {
match &mut event { match &mut event {
AnyToDeviceEvent::RoomEncrypted(e) => { AnyToDeviceEvent::RoomEncrypted(e) => {
let (session, decrypted_event, group_session) = let decrypted = match self.decrypt_to_device_event(e).await {
match self.decrypt_to_device_event(e).await { Ok(e) => e,
Ok(e) => e, Err(err) => {
Err(err) => { warn!(
warn!( "Failed to decrypt to-device event from {} {}",
"Failed to decrypt to-device event from {} {}", e.sender, err
e.sender, err );
);
if let OlmError::SessionWedged(sender, curve_key) = err { if let OlmError::SessionWedged(sender, curve_key) = err {
if let Err(e) = self if let Err(e) = self
.session_manager .session_manager
.mark_device_as_wedged(&sender, &curve_key) .mark_device_as_wedged(&sender, &curve_key)
.await .await
{ {
error!( error!(
"Couldn't mark device from {} to be unwedged {:?}", "Couldn't mark device from {} to be unwedged {:?}",
sender, e sender, e
); );
}
} }
continue;
} }
}; continue;
}
};
changes.sessions.push(session); changes.sessions.push(decrypted.session);
if let Some(group_session) = group_session { if let Some(group_session) = decrypted.inbound_group_session {
changes.inbound_group_sessions.push(group_session); changes.inbound_group_sessions.push(group_session);
} }
*event_result = decrypted_event; *event_result = decrypted.event;
} }
AnyToDeviceEvent::RoomKeyRequest(e) => { AnyToDeviceEvent::RoomKeyRequest(e) => {
self.key_request_machine.receive_incoming_key_request(e) self.key_request_machine.receive_incoming_key_request(e)
@ -1283,8 +1283,8 @@ pub(crate) mod test {
content, content,
}; };
let (session, _, _) = bob.decrypt_to_device_event(&event).await.unwrap(); let decrypted = bob.decrypt_to_device_event(&event).await.unwrap();
bob.store.save_sessions(&[session]).await.unwrap(); bob.store.save_sessions(&[decrypted.session]).await.unwrap();
(alice, bob) (alice, bob)
} }
@ -1578,7 +1578,7 @@ pub(crate) mod test {
.decrypt_to_device_event(&event) .decrypt_to_device_event(&event)
.await .await
.unwrap() .unwrap()
.1 .event
.deserialize() .deserialize()
.unwrap(); .unwrap();
@ -1614,14 +1614,14 @@ pub(crate) mod test {
.get_outbound_group_session(&room_id) .get_outbound_group_session(&room_id)
.unwrap(); .unwrap();
let (session, event, group_session) = bob.decrypt_to_device_event(&event).await.unwrap(); let decrypted = bob.decrypt_to_device_event(&event).await.unwrap();
bob.store.save_sessions(&[session]).await.unwrap(); bob.store.save_sessions(&[decrypted.session]).await.unwrap();
bob.store bob.store
.save_inbound_group_sessions(&[group_session.unwrap()]) .save_inbound_group_sessions(&[decrypted.inbound_group_session.unwrap()])
.await .await
.unwrap(); .unwrap();
let event = event.deserialize().unwrap(); let event = decrypted.event.deserialize().unwrap();
if let AnyToDeviceEvent::RoomKey(event) = event { if let AnyToDeviceEvent::RoomKey(event) = event {
assert_eq!(&event.sender, alice.user_id()); assert_eq!(&event.sender, alice.user_id());
@ -1661,7 +1661,11 @@ pub(crate) mod test {
content: to_device_requests_to_content(to_device_requests), content: to_device_requests_to_content(to_device_requests),
}; };
let (_, _, group_session) = bob.decrypt_to_device_event(&event).await.unwrap(); let group_session = bob
.decrypt_to_device_event(&event)
.await
.unwrap()
.inbound_group_session;
bob.store bob.store
.save_inbound_group_sessions(&[group_session.unwrap()]) .save_inbound_group_sessions(&[group_session.unwrap()])
.await .await

View File

@ -15,6 +15,7 @@
use matrix_sdk_common::events::ToDeviceEvent; use matrix_sdk_common::events::ToDeviceEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use sha2::{Digest, Sha256};
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
@ -53,6 +54,7 @@ use olm_rs::{
use crate::{ use crate::{
error::{EventError, OlmResult, SessionCreationError}, error::{EventError, OlmResult, SessionCreationError},
file_encryption::encode,
identities::ReadOnlyDevice, identities::ReadOnlyDevice,
requests::UploadSigningKeysRequest, requests::UploadSigningKeysRequest,
store::Store, store::Store,
@ -70,6 +72,15 @@ pub struct Account {
pub(crate) store: Store, pub(crate) store: Store,
} }
pub struct OlmDecryptionInfo {
pub session: Session,
pub message_hash: String,
pub event: Raw<AnyToDeviceEvent>,
pub signing_key: String,
pub sender_key: String,
pub inbound_group_session: Option<InboundGroupSession>,
}
impl Deref for Account { impl Deref for Account {
type Target = ReadOnlyAccount; type Target = ReadOnlyAccount;
@ -82,7 +93,7 @@ impl Account {
pub async fn decrypt_to_device_event( pub async fn decrypt_to_device_event(
&self, &self,
event: &ToDeviceEvent<EncryptedEventContent>, event: &ToDeviceEvent<EncryptedEventContent>,
) -> OlmResult<(Session, Raw<AnyToDeviceEvent>, String, String)> { ) -> OlmResult<OlmDecryptionInfo> {
debug!("Decrypting to-device event"); debug!("Decrypting to-device event");
let content = if let EncryptedEventContent::OlmV1Curve25519AesSha2(c) = &event.content { let content = if let EncryptedEventContent::OlmV1Curve25519AesSha2(c) = &event.content {
@ -103,23 +114,33 @@ impl Account {
.try_into() .try_into()
.map_err(|_| EventError::UnsupportedOlmType)?; .map_err(|_| EventError::UnsupportedOlmType)?;
let sha = Sha256::new()
.chain(&content.sender_key)
.chain(&[message_type])
.chain(&ciphertext.body);
let message_hash = encode(sha.finalize().as_slice());
// Create a OlmMessage from the ciphertext and the type. // Create a OlmMessage from the ciphertext and the type.
let message = let message =
OlmMessage::from_type_and_ciphertext(message_type.into(), ciphertext.body.clone()) OlmMessage::from_type_and_ciphertext(message_type.into(), ciphertext.body.clone())
.map_err(|_| EventError::UnsupportedOlmType)?; .map_err(|_| EventError::UnsupportedOlmType)?;
// Decrypt the OlmMessage and get a Ruma event out of it. // Decrypt the OlmMessage and get a Ruma event out of it.
let (session, decrypted_event, signing_key) = self let (session, event, signing_key) = self
.decrypt_olm_message(&event.sender, &content.sender_key, message) .decrypt_olm_message(&event.sender, &content.sender_key, message)
.await?; .await?;
debug!("Decrypted a to-device event {:?}", decrypted_event); debug!("Decrypted a to-device event {:?}", event);
Ok((
Ok(OlmDecryptionInfo {
session, session,
decrypted_event, message_hash,
content.sender_key.clone(), event,
signing_key, signing_key,
)) sender_key: content.sender_key.clone(),
inbound_group_session: None,
})
} else { } else {
warn!("Olm event doesn't contain a ciphertext for our key"); warn!("Olm event doesn't contain a ciphertext for our key");
Err(EventError::MissingCiphertext.into()) Err(EventError::MissingCiphertext.into())

View File

@ -23,7 +23,7 @@ mod session;
mod signing; mod signing;
mod utility; mod utility;
pub(crate) use account::Account; pub(crate) use account::{Account, OlmDecryptionInfo};
pub use account::{AccountPickle, PickledAccount, ReadOnlyAccount}; pub use account::{AccountPickle, PickledAccount, ReadOnlyAccount};
pub use group_sessions::{ pub use group_sessions::{
EncryptionSettings, ExportedRoomKey, InboundGroupSession, InboundGroupSessionPickle, EncryptionSettings, ExportedRoomKey, InboundGroupSession, InboundGroupSessionPickle,

View File

@ -126,9 +126,7 @@ impl Session {
"content": content, "content": content,
}); });
let plaintext = serde_json::to_string(&payload) let plaintext = serde_json::to_string(&payload)?;
.unwrap_or_else(|_| panic!(format!("Can't serialize {} to canonical JSON", payload)));
let ciphertext = self.encrypt_helper(&plaintext).await.to_tuple(); let ciphertext = self.encrypt_helper(&plaintext).await.to_tuple();
let message_type = ciphertext.0; let message_type = ciphertext.0;