feat(crypto): Add customized event type for the forwarded room key

This commit is contained in:
Damir Jelić
2022-08-03 13:00:17 +02:00
parent 4914c595e9
commit 329d461a2f
13 changed files with 394 additions and 123 deletions

View File

@@ -250,6 +250,8 @@ pub fn migrate(
InboundGroupSession::from_libolm_pickle(&session.pickle, &data.pickle_key)?.pickle();
let sender_key = Curve25519PublicKey::from_base64(&session.sender_key)?;
let forwarding_chains: Result<Vec<Curve25519PublicKey>, _> =
session.forwarding_chains.iter().map(|k| Curve25519PublicKey::from_base64(k)).collect();
let pickle = matrix_sdk_crypto::olm::PickledInboundGroupSession {
pickle,
@@ -260,7 +262,7 @@ pub fn migrate(
.map(|(k, v)| Ok((DeviceKeyAlgorithm::try_from(k)?, v)))
.collect::<anyhow::Result<_>>()?,
room_id: RoomId::parse(session.room_id)?,
forwarding_chains: session.forwarding_chains,
forwarding_chains: forwarding_chains?,
imported: session.imported,
backed_up: session.backed_up,
history_visibility: None,

View File

@@ -26,7 +26,6 @@ use dashmap::{mapref::entry::Entry, DashMap, DashSet};
use ruma::{
api::client::keys::claim_keys::v3::Request as KeysClaimRequest,
events::{
forwarded_room_key::{ToDeviceForwardedRoomKeyEvent, ToDeviceForwardedRoomKeyEventContent},
room_key_request::{Action, RequestedKeyInfo, ToDeviceRoomKeyRequestEvent},
secret::request::{
RequestAction, SecretName, ToDeviceSecretRequestEvent as SecretRequestEvent,
@@ -46,6 +45,9 @@ use crate::{
session_manager::GroupSessionCache,
store::{Changes, CryptoStoreError, SecretImportError, Store},
types::events::{
forwarded_room_key::{
ForwardedMegolmV1AesSha2Content, ForwardedRoomKeyContent, ForwardedRoomKeyEvent,
},
secret_send::{SecretSendContent, SecretSendEvent},
EventType,
},
@@ -700,12 +702,12 @@ impl GossipMachine {
/// Get an outgoing key info that matches the forwarded room key content.
async fn get_key_info(
&self,
content: &ToDeviceForwardedRoomKeyEventContent,
content: &ForwardedMegolmV1AesSha2Content,
) -> Result<Option<GossipRequest>, CryptoStoreError> {
let info = RequestedKeyInfo::new(
content.algorithm.clone(),
EventEncryptionAlgorithm::MegolmV1AesSha2,
content.room_id.clone(),
content.sender_key.clone(),
content.claimed_sender_key.to_base64(),
content.session_id.clone(),
)
.into();
@@ -880,10 +882,11 @@ impl GossipMachine {
async fn accept_forwarded_room_key(
&self,
info: &GossipRequest,
sender: &UserId,
sender_key: Curve25519PublicKey,
event: &ToDeviceForwardedRoomKeyEvent,
content: &ForwardedMegolmV1AesSha2Content,
) -> Result<Option<InboundGroupSession>, CryptoStoreError> {
match InboundGroupSession::from_forwarded_key(sender_key, &event.content) {
match InboundGroupSession::from_forwarded_key(sender_key, content) {
Ok(session) => {
let old_session = self
.store
@@ -913,19 +916,19 @@ impl GossipMachine {
if let Some(s) = &session {
info!(
sender = event.sender.as_str(),
%sender,
sender_key = sender_key.to_base64(),
claimed_sender_key = event.content.sender_key.as_str(),
claimed_sender_key = content.claimed_sender_key.to_base64(),
room_id = s.room_id().as_str(),
session_id = session_id.as_str(),
"Received a forwarded room key",
);
} else {
info!(
sender = event.sender.as_str(),
%sender,
sender_key = sender_key.to_base64(),
claimed_sender_key = event.content.sender_key.as_str(),
room_id = event.content.room_id.as_str(),
claimed_sender_key = content.claimed_sender_key.to_base64(),
room_id = %content.room_id,
session_id = session_id.as_str(),
"Received a forwarded room key but we already have a better version of it",
);
@@ -935,10 +938,10 @@ impl GossipMachine {
}
Err(e) => {
warn!(
sender = event.sender.as_str(),
%sender,
sender_key = sender_key.to_base64(),
claimed_sender_key = event.content.sender_key.as_str(),
room_id = event.content.room_id.as_str(),
claimed_sender_key = content.claimed_sender_key.to_base64(),
room_id = content.room_id.as_str(),
"Couldn't create a group session from a received room key"
);
Err(e.into())
@@ -950,20 +953,36 @@ impl GossipMachine {
pub async fn receive_forwarded_room_key(
&self,
sender_key: Curve25519PublicKey,
event: &ToDeviceForwardedRoomKeyEvent,
event: &ForwardedRoomKeyEvent,
) -> Result<Option<InboundGroupSession>, CryptoStoreError> {
if let Some(info) = self.get_key_info(&event.content).await? {
self.accept_forwarded_room_key(&info, sender_key, event).await
} else {
warn!(
sender = event.sender.as_str(),
sender_key = sender_key.to_base64(),
room_id = event.content.room_id.as_str(),
session_id = event.content.session_id.as_str(),
claimed_sender_key = event.content.sender_key.as_str(),
"Received a forwarded room key that we didn't request",
);
Ok(None)
match &event.content {
ForwardedRoomKeyContent::MegolmV1AesSha2(content) => {
if let Some(info) = self.get_key_info(content).await? {
self.accept_forwarded_room_key(&info, &event.sender, sender_key, content).await
} else {
warn!(
sender = event.sender.as_str(),
sender_key = sender_key.to_base64(),
room_id = content.room_id.as_str(),
session_id = content.session_id.as_str(),
claimed_sender_key = content.claimed_sender_key.to_base64(),
algorithm = %event.algorithm(),
"Received a forwarded room key that we didn't request",
);
Ok(None)
}
}
ForwardedRoomKeyContent::Unknown(_) => {
warn!(
sender = event.sender.as_str(),
sender_key = sender_key.to_base64(),
algorithm = %event.algorithm(),
"Received a forwarded room key with an unsupported algorithm",
);
Ok(None)
}
}
}
}
@@ -979,10 +998,9 @@ mod tests {
use ruma::{
device_id,
events::{
forwarded_room_key::ToDeviceForwardedRoomKeyEventContent,
room::encrypted::ToDeviceRoomEncryptedEventContent,
secret::request::{RequestAction, SecretName, ToDeviceSecretRequestEventContent},
AnyToDeviceEvent, ToDeviceEvent as RumaToDeviceEvent, ToDeviceEventContent,
ToDeviceEvent as RumaToDeviceEvent, ToDeviceEventContent,
},
room_id, user_id, DeviceId, RoomId, UserId,
};
@@ -994,6 +1012,9 @@ mod tests {
olm::{Account, OutboundGroupSession, PrivateCrossSigningIdentity, ReadOnlyAccount},
session_manager::GroupSessionCache,
store::{Changes, CryptoStore, MemoryStore, Store},
types::events::{
forwarded_room_key::ForwardedRoomKeyContent, ToDeviceEvent, ToDeviceEvents,
},
utilities::json_convert,
verification::VerificationMachine,
OutgoingRequest, OutgoingRequests,
@@ -1284,9 +1305,10 @@ mod tests {
let export = session.export_at_index(10).await;
let content: ToDeviceForwardedRoomKeyEventContent = export.try_into().unwrap();
let content: ForwardedRoomKeyContent = export.try_into().unwrap();
let event = RumaToDeviceEvent { sender: alice_id().to_owned(), content };
let event =
ToDeviceEvent { sender: alice_id().to_owned(), content, other: Default::default() };
assert!(machine
.store
@@ -1332,9 +1354,10 @@ mod tests {
let export = session.export_at_index(15).await;
let content: ToDeviceForwardedRoomKeyEventContent = export.try_into().unwrap();
let content: ForwardedRoomKeyContent = export.try_into().unwrap();
let event = RumaToDeviceEvent { sender: alice_id().to_owned(), content };
let event =
ToDeviceEvent { sender: alice_id().to_owned(), content, other: Default::default() };
let second_session = machine
.receive_forwarded_room_key(alice_device.curve25519_key().unwrap(), &event)
@@ -1345,9 +1368,10 @@ mod tests {
let export = session.export_at_index(0).await;
let content: ToDeviceForwardedRoomKeyEventContent = export.try_into().unwrap();
let content: ForwardedRoomKeyContent = export.try_into().unwrap();
let event = RumaToDeviceEvent { sender: alice_id().to_owned(), content };
let event =
ToDeviceEvent { sender: alice_id().to_owned(), content, other: Default::default() };
let second_session = machine
.receive_forwarded_room_key(alice_device.curve25519_key().unwrap(), &event)
@@ -1516,7 +1540,7 @@ mod tests {
let decrypted = alice_account.decrypt_to_device_event(&event).await.unwrap();
if let AnyToDeviceEvent::ForwardedRoomKey(e) = decrypted.event.deserialize().unwrap() {
if let ToDeviceEvents::ForwardedRoomKey(e) = decrypted.event.deserialize_as().unwrap() {
let session =
alice_machine.receive_forwarded_room_key(decrypted.sender_key, &e).await.unwrap();
alice_machine.store.save_inbound_group_sessions(&[session.unwrap()]).await.unwrap();
@@ -1674,7 +1698,7 @@ mod tests {
let decrypted = alice_account.decrypt_to_device_event(&event).await.unwrap();
if let AnyToDeviceEvent::ForwardedRoomKey(e) = decrypted.event.deserialize().unwrap() {
if let ToDeviceEvents::ForwardedRoomKey(e) = decrypted.event.deserialize_as().unwrap() {
let session =
alice_machine.receive_forwarded_room_key(decrypted.sender_key, &e).await.unwrap();
alice_machine.store.save_inbound_group_sessions(&[session.unwrap()]).await.unwrap();

View File

@@ -26,13 +26,8 @@ use atomic::Atomic;
use matrix_sdk_common::locks::Mutex;
use ruma::{
api::client::keys::upload_signatures::v3::Request as SignatureUploadRequest,
events::{
forwarded_room_key::ToDeviceForwardedRoomKeyEventContent,
key::verification::VerificationMethod,
},
serde::Raw,
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, OwnedDeviceId,
OwnedDeviceKeyId, UserId,
events::key::verification::VerificationMethod, serde::Raw, DeviceId, DeviceKeyAlgorithm,
DeviceKeyId, EventEncryptionAlgorithm, OwnedDeviceId, OwnedDeviceKeyId, UserId,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
@@ -48,8 +43,11 @@ use crate::{
olm::{InboundGroupSession, Session, SignedJsonObject, VerifyJson},
store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult},
types::{
events::room::encrypted::ToDeviceEncryptedEventContent, DeviceKey, DeviceKeys, Signatures,
SignedKey,
events::{
forwarded_room_key::ForwardedRoomKeyContent,
room::encrypted::ToDeviceEncryptedEventContent, EventType,
},
DeviceKey, DeviceKeys, Signatures, SignedKey,
},
verification::VerificationMachine,
OutgoingVerificationRequest, ReadOnlyAccount, Sas, ToDeviceRequest, VerificationRequest,
@@ -277,7 +275,7 @@ impl Device {
session.export().await
};
let content: ToDeviceForwardedRoomKeyEventContent = if let Ok(c) = export.try_into() {
let content: ForwardedRoomKeyContent = if let Ok(c) = export.try_into() {
c
} else {
// TODO remove this panic.
@@ -290,9 +288,10 @@ impl Device {
);
};
let event_type = content.event_type();
let content = serde_json::to_value(content)?;
self.encrypt("m.forwarded_room_key", content).await
self.encrypt(event_type, content).await
}
}

View File

@@ -1037,7 +1037,11 @@ impl OlmMachine {
algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
curve25519_key: session.sender_key().to_base64(),
sender_claimed_keys: session.signing_keys().to_owned(),
forwarding_curve25519_key_chain: session.forwarding_key_chain().to_vec(),
forwarding_curve25519_key_chain: session
.forwarding_key_chain()
.iter()
.map(|k| k.to_base64())
.collect(),
},
verification_state,
})

View File

@@ -23,10 +23,7 @@ use std::{
use matrix_sdk_common::locks::Mutex;
use ruma::{
events::{
forwarded_room_key::ToDeviceForwardedRoomKeyEventContent,
room::history_visibility::HistoryVisibility, AnyRoomEvent,
},
events::{room::history_visibility::HistoryVisibility, AnyRoomEvent},
serde::Raw,
DeviceKeyAlgorithm, EventEncryptionAlgorithm, OwnedRoomId, RoomId,
};
@@ -34,7 +31,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use vodozemac::{
megolm::{
DecryptedMessage, DecryptionError, ExportedSessionKey, InboundGroupSession as InnerSession,
DecryptedMessage, DecryptionError, InboundGroupSession as InnerSession,
InboundGroupSessionPickle, MegolmMessage, SessionConfig, SessionOrdering,
},
Curve25519PublicKey, PickleError,
@@ -45,7 +42,10 @@ use crate::{
error::{EventError, MegolmResult},
types::{
deserialize_curve_key,
events::room::encrypted::{EncryptedEvent, RoomEventEncryptionScheme},
events::{
forwarded_room_key::ForwardedMegolmV1AesSha2Content,
room::encrypted::{EncryptedEvent, RoomEventEncryptionScheme},
},
serialize_curve_key,
},
};
@@ -71,7 +71,7 @@ pub struct InboundGroupSession {
pub signing_keys: Arc<BTreeMap<DeviceKeyAlgorithm, String>>,
/// The Room this GroupSession belongs to
pub room_id: Arc<RoomId>,
forwarding_chains: Arc<Vec<String>>,
forwarding_chains: Arc<Vec<Curve25519PublicKey>>,
imported: bool,
algorithm: Arc<EventEncryptionAlgorithm>,
backed_up: Arc<AtomicBool>,
@@ -165,25 +165,24 @@ impl InboundGroupSession {
/// to create the `InboundGroupSession`.
pub fn from_forwarded_key(
sender_key: Curve25519PublicKey,
content: &ToDeviceForwardedRoomKeyEventContent,
content: &ForwardedMegolmV1AesSha2Content,
) -> Result<Self, SessionCreationError> {
let key = ExportedSessionKey::from_base64(&content.session_key)?;
let algorithm = EventEncryptionAlgorithm::from(content.algorithm.as_str());
let algorithm = EventEncryptionAlgorithm::MegolmV1AesSha2;
let session = InnerSession::import(&key, SessionConfig::version_1());
let session = InnerSession::import(&content.session_key, SessionConfig::version_1());
let first_known_index = session.first_known_index();
let mut forwarding_chains = content.forwarding_curve25519_key_chain.clone();
forwarding_chains.push(sender_key.to_base64());
forwarding_chains.push(sender_key);
let mut sender_claimed_key = BTreeMap::new();
sender_claimed_key
.insert(DeviceKeyAlgorithm::Ed25519, content.sender_claimed_ed25519_key.to_owned());
.insert(DeviceKeyAlgorithm::Ed25519, content.claimed_ed25519_key.to_base64());
Ok(InboundGroupSession {
inner: Mutex::new(session).into(),
session_id: content.session_id.as_str().into(),
sender_key,
sender_key: content.claimed_sender_key,
first_known_index,
history_visibility: None.into(),
signing_keys: sender_claimed_key.into(),
@@ -256,7 +255,7 @@ impl InboundGroupSession {
/// Each ed25519 key represents a single device. If device A forwards the
/// session to device B and device B to C this list will contain the ed25519
/// keys of A and B.
pub fn forwarding_key_chain(&self) -> &[String] {
pub fn forwarding_key_chain(&self) -> &[Curve25519PublicKey] {
&self.forwarding_chains
}
@@ -446,7 +445,7 @@ pub struct PickledInboundGroupSession {
/// The list of claimed ed25519 that forwarded us this key. Will be None if
/// we directly received this session.
#[serde(default)]
pub forwarding_chains: Vec<String>,
pub forwarding_chains: Vec<Curve25519PublicKey>,
/// Flag remembering if the session was directly sent to us by the sender
/// or if it was imported.
pub imported: bool,

View File

@@ -14,12 +14,7 @@
use std::collections::BTreeMap;
use ruma::{
events::forwarded_room_key::{
ToDeviceForwardedRoomKeyEventContent, ToDeviceForwardedRoomKeyEventContentInit,
},
DeviceKeyAlgorithm, EventEncryptionAlgorithm, OwnedRoomId,
};
use ruma::{DeviceKeyAlgorithm, EventEncryptionAlgorithm, OwnedRoomId};
use serde::{Deserialize, Serialize};
mod inbound;
@@ -32,10 +27,13 @@ pub use outbound::{
};
use thiserror::Error;
pub use vodozemac::megolm::{ExportedSessionKey, SessionKey};
use vodozemac::{megolm::SessionKeyDecodeError, Curve25519PublicKey};
use zeroize::Zeroize;
use vodozemac::{megolm::SessionKeyDecodeError, Curve25519PublicKey, Ed25519PublicKey};
use crate::types::{deserialize_curve_key, serialize_curve_key};
use crate::types::{
deserialize_curve_key,
events::forwarded_room_key::{ForwardedMegolmV1AesSha2Content, ForwardedRoomKeyContent},
serialize_curve_key,
};
/// An error type for the creation of group sessions.
#[derive(Debug, Error)]
@@ -77,7 +75,7 @@ pub struct ExportedRoomKey {
/// Chain of Curve25519 keys through which this session was forwarded, via
/// m.forwarded_room_key events.
#[serde(default)]
pub forwarding_curve25519_key_chain: Vec<String>,
pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
}
/// A backed up version of an `InboundGroupSession`
@@ -101,10 +99,10 @@ pub struct BackedUpRoomKey {
/// Chain of Curve25519 keys through which this session was forwarded, via
/// m.forwarded_room_key events.
pub forwarding_curve25519_key_chain: Vec<String>,
pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
}
impl TryInto<ToDeviceForwardedRoomKeyEventContent> for ExportedRoomKey {
impl TryInto<ForwardedRoomKeyContent> for ExportedRoomKey {
type Error = ();
/// Convert an exported room key into a content for a forwarded room key
@@ -113,7 +111,7 @@ impl TryInto<ToDeviceForwardedRoomKeyEventContent> for ExportedRoomKey {
/// This will fail if the exported room key has multiple sender claimed keys
/// or if the algorithm of the claimed sender key isn't
/// `DeviceKeyAlgorithm::Ed25519`.
fn try_into(self) -> Result<ToDeviceForwardedRoomKeyEventContent, Self::Error> {
fn try_into(self) -> Result<ForwardedRoomKeyContent, Self::Error> {
if self.sender_claimed_keys.len() != 1 {
Err(())
} else {
@@ -123,16 +121,19 @@ impl TryInto<ToDeviceForwardedRoomKeyEventContent> for ExportedRoomKey {
return Err(());
}
Ok(ToDeviceForwardedRoomKeyEventContentInit {
algorithm: self.algorithm,
room_id: self.room_id,
sender_key: self.sender_key.to_base64(),
session_id: self.session_id,
session_key: self.session_key.to_base64(),
sender_claimed_ed25519_key: claimed_key.to_owned(),
forwarding_curve25519_key_chain: self.forwarding_curve25519_key_chain,
}
.into())
Ok(ForwardedRoomKeyContent::MegolmV1AesSha2(
ForwardedMegolmV1AesSha2Content {
room_id: self.room_id,
session_id: self.session_id,
session_key: self.session_key,
claimed_sender_key: self.sender_key,
claimed_ed25519_key: Ed25519PublicKey::from_base64(claimed_key)
.map_err(|_| ())?,
forwarding_curve25519_key_chain: self.forwarding_curve25519_key_chain.clone(),
other: Default::default(),
}
.into(),
))
}
}
}
@@ -149,29 +150,28 @@ impl From<ExportedRoomKey> for BackedUpRoomKey {
}
}
impl TryFrom<ToDeviceForwardedRoomKeyEventContent> for ExportedRoomKey {
impl TryFrom<ForwardedRoomKeyContent> for ExportedRoomKey {
type Error = SessionKeyDecodeError;
/// Convert the content of a forwarded room key into a exported room key.
fn try_from(
mut forwarded_key: ToDeviceForwardedRoomKeyEventContent,
) -> Result<Self, Self::Error> {
let mut sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String> = BTreeMap::new();
sender_claimed_keys
.insert(DeviceKeyAlgorithm::Ed25519, forwarded_key.sender_claimed_ed25519_key);
fn try_from(forwarded_key: ForwardedRoomKeyContent) -> Result<Self, Self::Error> {
match forwarded_key {
ForwardedRoomKeyContent::MegolmV1AesSha2(content) => {
let mut sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String> = BTreeMap::new();
sender_claimed_keys
.insert(DeviceKeyAlgorithm::Ed25519, content.claimed_ed25519_key.to_base64());
let session_key = ExportedSessionKey::from_base64(&forwarded_key.session_key)?;
forwarded_key.session_key.zeroize();
let sender_key = Curve25519PublicKey::from_base64(&forwarded_key.sender_key)?;
Ok(Self {
algorithm: forwarded_key.algorithm,
room_id: forwarded_key.room_id,
session_id: forwarded_key.session_id,
forwarding_curve25519_key_chain: forwarded_key.forwarding_curve25519_key_chain,
sender_claimed_keys,
sender_key,
session_key,
})
Ok(Self {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
room_id: content.room_id,
session_id: content.session_id,
forwarding_curve25519_key_chain: content.forwarding_curve25519_key_chain,
sender_claimed_keys,
sender_key: content.claimed_sender_key,
session_key: content.session_key,
})
}
ForwardedRoomKeyContent::Unknown(_) => Err(SessionKeyDecodeError::Version(1, 2)),
}
}
}

View File

@@ -34,7 +34,7 @@ pub use group_sessions::{
pub use session::{PickledSession, Session};
pub use signing::{CrossSigningStatus, PickledCrossSigningIdentity, PrivateCrossSigningIdentity};
pub(crate) use utility::{SignedJsonObject, VerifyJson};
pub use vodozemac::olm::IdentityKeys;
pub use vodozemac::{olm::IdentityKeys, Curve25519PublicKey};
#[cfg(test)]
pub(crate) mod tests {
@@ -43,7 +43,6 @@ pub(crate) mod tests {
use ruma::{
device_id, event_id,
events::{
forwarded_room_key::ToDeviceForwardedRoomKeyEventContent,
room::message::{Relation, Replacement, RoomMessageEventContent},
AnyMessageLikeEvent, AnyRoomEvent, MessageLikeEvent,
},
@@ -54,7 +53,9 @@ pub(crate) mod tests {
use crate::{
olm::{ExportedRoomKey, InboundGroupSession, ReadOnlyAccount, Session},
types::events::room::encrypted::EncryptedEvent,
types::events::{
forwarded_room_key::ForwardedRoomKeyContent, room::encrypted::EncryptedEvent,
},
utilities::json_convert,
};
@@ -321,7 +322,7 @@ pub(crate) mod tests {
let (_, inbound) = alice.create_group_session_pair_with_defaults(room_id).await;
let export = inbound.export().await;
let export: ToDeviceForwardedRoomKeyEventContent = export.try_into().unwrap();
let export: ForwardedRoomKeyContent = export.try_into().unwrap();
let export = ExportedRoomKey::try_from(export).unwrap();
let imported = InboundGroupSession::from_export(&export)

View File

@@ -109,6 +109,8 @@ impl Session {
/// encrypted, this needs to be the device that was used to create this
/// session with.
///
/// * `event_type` - The type of the event content.
///
/// * `content` - The content of the event.
pub async fn encrypt(
&mut self,

View File

@@ -18,8 +18,8 @@ macro_rules! cryptostore_integration_tests {
testing::{get_device, get_other_identity, get_own_identity},
ReadOnlyDevice,
olm::{
InboundGroupSession, OlmMessageHash, PrivateCrossSigningIdentity,
ReadOnlyAccount, Session,
Curve25519PublicKey, InboundGroupSession, OlmMessageHash,
PrivateCrossSigningIdentity, ReadOnlyAccount, Session,
},
store::{CryptoStore, GossipRequest, Changes, DeviceChanges, IdentityChanges, RecoveryKey},
};
@@ -286,7 +286,8 @@ macro_rules! cryptostore_integration_tests {
let mut export = session.export().await;
export.forwarding_curve25519_key_chain = vec!["some_chain".to_owned()];
let curve_key = Curve25519PublicKey::from_base64("Nn0L2hkcCMFKqynTjyGsJbth7QrVmX3lbrksMkrGOAw").unwrap();
export.forwarding_curve25519_key_chain = vec![curve_key];
let session = InboundGroupSession::from_export(&export).unwrap();

View File

@@ -0,0 +1,190 @@
// Copyright 2022 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.
//! Types for `m.forwarded_room_key` to-device events.
use std::collections::BTreeMap;
use ruma::{EventEncryptionAlgorithm, OwnedRoomId};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use vodozemac::{megolm::ExportedSessionKey, Curve25519PublicKey, Ed25519PublicKey};
use super::{EventType, ToDeviceEvent};
use crate::types::{
deserialize_curve_key, deserialize_curve_key_vec, deserialize_ed25519_key, serialize_curve_key,
serialize_curve_key_vec, serialize_ed25519_key,
};
/// The `m.forwarded_room_key` to-device event.
pub type ForwardedRoomKeyEvent = ToDeviceEvent<ForwardedRoomKeyContent>;
impl ForwardedRoomKeyEvent {
/// Get the algorithm of the forwarded room key.
pub fn algorithm(&self) -> EventEncryptionAlgorithm {
self.content.algorithm()
}
}
/// The `m.forwarded_room_key` event content.
///
/// This is an enum over the different room key algorithms we support.
///
/// This event type is used to forward keys for end-to-end encryption.
/// Typically it is encrypted as an m.room.encrypted event, then sent as a
/// to-device event.
#[derive(Debug, Deserialize)]
#[serde(try_from = "RoomKeyHelper")]
pub enum ForwardedRoomKeyContent {
/// The `m.megolm.v1.aes-sha2` variant of the `m.forwarded_room_key`
/// content.
MegolmV1AesSha2(Box<ForwardedMegolmV1AesSha2Content>),
/// An unknown and unsupported variant of the `m.forwarded_room_key`
/// content.
Unknown(UnknownRoomKeyContent),
}
impl ForwardedRoomKeyContent {
/// Get the algorithm of the forwarded room key content.
pub fn algorithm(&self) -> EventEncryptionAlgorithm {
match self {
ForwardedRoomKeyContent::MegolmV1AesSha2(_) => {
EventEncryptionAlgorithm::MegolmV1AesSha2
}
ForwardedRoomKeyContent::Unknown(c) => c.algorithm.to_owned(),
}
}
}
impl EventType for ForwardedRoomKeyContent {
const EVENT_TYPE: &'static str = "m.forwarded_room_key";
}
/// The `m.megolm.v1.aes-sha2` variant of the `m.room_key` content.
#[derive(Deserialize, Serialize)]
pub struct ForwardedMegolmV1AesSha2Content {
/// The room where the key is used.
pub room_id: OwnedRoomId,
/// The ID of the session that the key is for.
pub session_id: String,
/// The key to be exchanged. Can be used to create a [`InboundGroupSession`]
/// that can be used to decrypt room events.
///
/// [`InboundGroupSession`]: vodozemac::megolm::InboundGroupSession
pub session_key: ExportedSessionKey,
/// Chain of Curve25519 keys. It starts out empty, but each time the key is
/// forwarded to another device, the previous sender in the chain is added
/// to the end of the list.
#[serde(
deserialize_with = "deserialize_curve_key_vec",
serialize_with = "serialize_curve_key_vec"
)]
pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
/// The Curve25519 key of the device which initiated the session originally.
///
/// It is claimed because the receiving device has no way to tell that
/// the original room_key actually came from a device which owns the private
/// part of this key.
#[serde(
rename = "sender_key",
deserialize_with = "deserialize_curve_key",
serialize_with = "serialize_curve_key"
)]
pub claimed_sender_key: Curve25519PublicKey,
/// The Ed25519 key of the device which initiated the session originally.
///
/// It is claimed because the receiving device has no way to tell that
/// the original room_key actually came from a device which owns the private
/// part of this key.
#[serde(
rename = "sender_claimed_ed25519_key",
deserialize_with = "deserialize_ed25519_key",
serialize_with = "serialize_ed25519_key"
)]
pub claimed_ed25519_key: Ed25519PublicKey,
#[serde(flatten)]
pub(crate) other: BTreeMap<String, Value>,
}
/// An unknown and unsupported `m.room_key` algorithm.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UnknownRoomKeyContent {
/// The algorithm of the unknown room key.
algorithm: EventEncryptionAlgorithm,
/// The other data of the unknown room key.
#[serde(flatten)]
other: BTreeMap<String, Value>,
}
impl std::fmt::Debug for ForwardedMegolmV1AesSha2Content {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ForwardedMegolmV1AesSha2Content")
.field("room_id", &self.room_id)
.field("session_id", &self.session_id)
.field("forwarding_curve25519_key_chain", &self.forwarding_curve25519_key_chain)
.field("claimed_sender_key", &self.claimed_sender_key)
.field("claimed_ed25519_key", &self.claimed_ed25519_key)
.finish_non_exhaustive()
}
}
#[derive(Deserialize, Serialize)]
struct RoomKeyHelper {
algorithm: EventEncryptionAlgorithm,
#[serde(flatten)]
other: Value,
}
impl TryFrom<RoomKeyHelper> for ForwardedRoomKeyContent {
type Error = serde_json::Error;
fn try_from(value: RoomKeyHelper) -> Result<Self, Self::Error> {
Ok(match value.algorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2 => {
let content: ForwardedMegolmV1AesSha2Content = serde_json::from_value(value.other)?;
Self::MegolmV1AesSha2(content.into())
}
_ => Self::Unknown(UnknownRoomKeyContent {
algorithm: value.algorithm,
other: serde_json::from_value(value.other)?,
}),
})
}
}
impl Serialize for ForwardedRoomKeyContent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let helper = match self {
Self::MegolmV1AesSha2(r) => RoomKeyHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
},
Self::Unknown(r) => RoomKeyHelper {
algorithm: r.algorithm.clone(),
other: serde_json::to_value(r.other.clone()).map_err(serde::ser::Error::custom)?,
},
};
helper.serialize(serializer)
}
}

View File

@@ -18,6 +18,7 @@
//! types. Once deserialized they aim to zeroize all the secret material once
//! the type is dropped.
pub mod forwarded_room_key;
pub mod room;
pub mod room_key;
pub mod secret_send;

View File

@@ -17,7 +17,6 @@ use std::{collections::BTreeMap, fmt::Debug};
use ruma::{
events::{
dummy::ToDeviceDummyEvent,
forwarded_room_key::ToDeviceForwardedRoomKeyEvent,
key::verification::{
accept::ToDeviceKeyVerificationAcceptEvent, cancel::ToDeviceKeyVerificationCancelEvent,
done::ToDeviceKeyVerificationDoneEvent, key::ToDeviceKeyVerificationKeyEvent,
@@ -39,7 +38,10 @@ use serde_json::{
use zeroize::Zeroize;
use super::{
room::encrypted::EncryptedToDeviceEvent, room_key::RoomKeyEvent, secret_send::SecretSendEvent,
forwarded_room_key::{ForwardedRoomKeyContent, ForwardedRoomKeyEvent},
room::encrypted::EncryptedToDeviceEvent,
room_key::RoomKeyEvent,
secret_send::SecretSendEvent,
EventType,
};
use crate::types::events::from_str;
@@ -76,7 +78,7 @@ pub enum ToDeviceEvents {
/// The `m.room_key_request` to-device event.
RoomKeyRequest(ToDeviceRoomKeyRequestEvent),
/// The `m.forwarded_room_key` to-device event.
ForwardedRoomKey(ToDeviceForwardedRoomKeyEvent),
ForwardedRoomKey(Box<ForwardedRoomKeyEvent>),
/// The `m.secret.send` to-device event.
SecretSend(SecretSendEvent),
/// The `m.secret.request` to-device event.
@@ -127,7 +129,7 @@ impl ToDeviceEvents {
ToDeviceEvents::RoomEncrypted(_) => ToDeviceEventType::RoomEncrypted,
ToDeviceEvents::RoomKey(_) => ToDeviceEventType::RoomKey,
ToDeviceEvents::RoomKeyRequest(e) => e.content.event_type(),
ToDeviceEvents::ForwardedRoomKey(e) => e.content.event_type(),
ToDeviceEvents::ForwardedRoomKey(_) => ToDeviceEventType::ForwardedRoomKey,
ToDeviceEvents::SecretSend(_) => ToDeviceEventType::SecretSend,
ToDeviceEvents::SecretRequest(e) => e.content.event_type(),
@@ -197,7 +199,11 @@ impl ToDeviceEvents {
Raw::from_json(raw_value)
}
ToDeviceEvents::ForwardedRoomKey(mut e) => {
e.content.session_key.zeroize();
match &mut e.content {
ForwardedRoomKeyContent::MegolmV1AesSha2(c) => c.session_key.zeroize(),
ForwardedRoomKeyContent::Unknown(_) => (),
}
Raw::from_json(to_raw_value(&e)?)
}
ToDeviceEvents::SecretSend(mut e) => {
@@ -410,7 +416,11 @@ mod test {
"sender_claimed_ed25519_key": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y",
"sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
"session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
"session_key": "AQAAAAq2JpkMceK5f6JrZPJWwzQTn59zliuIv0F7apVLXDcZCCT\
3LqBjD21sULYEO5YTKdpMVhi9i6ZSZhdvZvp//tzRpDT7wpWVWI\
00Y3EPEjmpm/HfZ4MMAKpk+tzJVuuvfAcHBZgpnxBGzYOc/DAqa\
pK7Tk3t3QJ1UMSD94HfAqlb1JF5QBPwoh0fOvD8pJdanB8zxz05\
tKFdR73/vo2Q/zE3"
},
"type": "m.forwarded_room_key"
})

View File

@@ -37,7 +37,7 @@ pub use device_keys::*;
pub use one_time_keys::*;
use ruma::{DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceKeyId, OwnedUserId, UserId};
use serde::{Deserialize, Serialize, Serializer};
use vodozemac::{Curve25519PublicKey, Ed25519Signature};
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, Ed25519Signature, KeyError};
/// Represents a potentially decoded signature (but *not* a validated one).
///
@@ -248,3 +248,41 @@ where
let key = key.to_base64();
s.serialize_str(&key)
}
pub(crate) fn deserialize_ed25519_key<'de, D>(de: D) -> Result<Ed25519PublicKey, D::Error>
where
D: serde::Deserializer<'de>,
{
let key: String = Deserialize::deserialize(de)?;
Ed25519PublicKey::from_base64(&key).map_err(serde::de::Error::custom)
}
pub(crate) fn serialize_ed25519_key<S>(key: &Ed25519PublicKey, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let key = key.to_base64();
s.serialize_str(&key)
}
pub(crate) fn deserialize_curve_key_vec<'de, D>(de: D) -> Result<Vec<Curve25519PublicKey>, D::Error>
where
D: serde::Deserializer<'de>,
{
let keys: Vec<String> = Deserialize::deserialize(de)?;
let keys: Result<Vec<Curve25519PublicKey>, KeyError> =
keys.iter().map(|k| Curve25519PublicKey::from_base64(k)).collect();
keys.map_err(serde::de::Error::custom)
}
pub(crate) fn serialize_curve_key_vec<S>(
keys: &[Curve25519PublicKey],
s: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let keys: Vec<String> = keys.iter().map(|k| k.to_base64()).collect();
keys.serialize(s)
}