crypto: Return a higher level struct when decrypting olm messages instead of tuples
parent
efe659910f
commit
24592adbba
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue