Skip to content

Commit 03dc672

Browse files
committed
crypto: Store sender_data in InboundGroupSessions
1 parent 3ffeef5 commit 03dc672

File tree

3 files changed

+309
-6
lines changed

3 files changed

+309
-6
lines changed

crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs

Lines changed: 208 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ use vodozemac::{
3838
};
3939

4040
use super::{
41-
BackedUpRoomKey, ExportedRoomKey, OutboundGroupSession, SessionCreationError, SessionKey,
41+
inbound_group_session_sender_data::InboundGroupSessionSenderData, BackedUpRoomKey,
42+
ExportedRoomKey, OutboundGroupSession, SessionCreationError, SessionKey,
4243
};
4344
use crate::{
4445
error::{EventError, MegolmResult},
@@ -123,6 +124,13 @@ pub struct InboundGroupSession {
123124
/// on how the session was received.
124125
pub(crate) creator_info: SessionCreatorInfo,
125126

127+
/// Information about the sender of this session and how much we trust that
128+
/// information. Holds the information we have about the device that created
129+
/// the session, or, if we can use that device information to find the
130+
/// sender's cross-signing identity, holds the user ID and cross-signing
131+
/// key.
132+
pub(crate) sender_data: InboundGroupSessionSenderData,
133+
126134
/// The Room this GroupSession belongs to
127135
pub room_id: OwnedRoomId,
128136

@@ -191,6 +199,7 @@ impl InboundGroupSession {
191199
curve25519_key: sender_key,
192200
signing_keys: keys.into(),
193201
},
202+
sender_data: InboundGroupSessionSenderData::default(),
194203
room_id: room_id.into(),
195204
imported: false,
196205
algorithm: encryption_algorithm.into(),
@@ -243,6 +252,7 @@ impl InboundGroupSession {
243252
pickle,
244253
sender_key: self.creator_info.curve25519_key,
245254
signing_key: (*self.creator_info.signing_keys).clone(),
255+
sender_data: self.sender_data.clone(),
246256
room_id: self.room_id().to_owned(),
247257
imported: self.imported,
248258
backed_up: self.backed_up(),
@@ -326,6 +336,7 @@ impl InboundGroupSession {
326336
curve25519_key: pickle.sender_key,
327337
signing_keys: pickle.signing_key.into(),
328338
},
339+
sender_data: pickle.sender_data,
329340
history_visibility: pickle.history_visibility.into(),
330341
first_known_index,
331342
room_id: (*pickle.room_id).into(),
@@ -494,6 +505,9 @@ pub struct PickledInboundGroupSession {
494505
pub sender_key: Curve25519PublicKey,
495506
/// The public ed25519 key of the account that sent us the session.
496507
pub signing_key: SigningKeys<DeviceKeyAlgorithm>,
508+
/// Information on the device/sender who sent us this session
509+
#[serde(default)]
510+
pub sender_data: InboundGroupSessionSenderData,
497511
/// The id of the room that the session is used in.
498512
pub room_id: OwnedRoomId,
499513
/// Flag remembering if the session was directly sent to us by the sender
@@ -528,6 +542,8 @@ impl TryFrom<&ExportedRoomKey> for InboundGroupSession {
528542
curve25519_key: key.sender_key,
529543
signing_keys: key.sender_claimed_keys.to_owned().into(),
530544
},
545+
// TODO: in future, exported keys should contain sender data that we can use here
546+
sender_data: InboundGroupSessionSenderData::default(),
531547
history_visibility: None.into(),
532548
first_known_index,
533549
room_id: key.room_id.to_owned(),
@@ -555,6 +571,8 @@ impl From<&ForwardedMegolmV1AesSha2Content> for InboundGroupSession {
555571
)])
556572
.into(),
557573
},
574+
// TODO: in future, forwarded keys should contain sender data that we can use here
575+
sender_data: InboundGroupSessionSenderData::default(),
558576
history_visibility: None.into(),
559577
first_known_index,
560578
room_id: value.room_id.to_owned(),
@@ -578,6 +596,8 @@ impl From<&ForwardedMegolmV2AesSha2Content> for InboundGroupSession {
578596
curve25519_key: value.claimed_sender_key,
579597
signing_keys: value.claimed_signing_keys.to_owned().into(),
580598
},
599+
// TODO: in future, forwarded keys should contain sender data that we can use here
600+
sender_data: InboundGroupSessionSenderData::default(),
581601
history_visibility: None.into(),
582602
first_known_index,
583603
room_id: value.room_id.to_owned(),
@@ -606,10 +626,23 @@ impl TryFrom<&DecryptedForwardedRoomKeyEvent> for InboundGroupSession {
606626
#[cfg(test)]
607627
mod tests {
608628
use matrix_sdk_test::async_test;
609-
use ruma::{device_id, room_id, user_id, DeviceId, UserId};
610-
use vodozemac::{megolm::SessionOrdering, Curve25519PublicKey};
611-
612-
use crate::{olm::InboundGroupSession, Account};
629+
use ruma::{
630+
device_id, events::room::history_visibility::HistoryVisibility, room_id, user_id, DeviceId,
631+
MilliSecondsSinceUnixEpoch, UInt, UserId,
632+
};
633+
use vodozemac::{
634+
megolm::{SessionKey, SessionOrdering},
635+
Curve25519PublicKey, Ed25519PublicKey,
636+
};
637+
638+
use crate::{
639+
olm::{
640+
group_sessions::inbound_group_session_sender_data::InboundGroupSessionSenderData,
641+
InboundGroupSession,
642+
},
643+
types::EventEncryptionAlgorithm,
644+
Account,
645+
};
613646

614647
fn alice_id() -> &'static UserId {
615648
user_id!("@alice:example.org")
@@ -620,7 +653,8 @@ mod tests {
620653
}
621654

622655
#[async_test]
623-
async fn inbound_group_session_serialization() {
656+
async fn test_can_deserialise_pickled_session_without_sender_data() {
657+
// Given the raw JSON for a picked inbound group sessions
624658
let pickle = r#"
625659
{
626660
"pickle": {
@@ -657,11 +691,166 @@ mod tests {
657691
}
658692
"#;
659693

694+
// When we deserialise it to from JSON
660695
let deserialized = serde_json::from_str(pickle).unwrap();
661696

697+
// And unpickle it
662698
let unpickled = InboundGroupSession::from_pickle(deserialized).unwrap();
663699

700+
// Then it was parsed correctly
664701
assert_eq!(unpickled.session_id(), "XbmrPa1kMwmdtNYng1B2gsfoo8UtF+NklzsTZiaVKyY");
702+
703+
// We populated the InboundGroupSession's sender_data with a default value,
704+
// with legacy_session set to true.
705+
let InboundGroupSessionSenderData::UnknownDevice { retry_details, legacy_session } =
706+
unpickled.sender_data
707+
else {
708+
panic!("Expected sender_data to be UnknownDevice!");
709+
};
710+
assert_eq!(retry_details.retry_count, 0);
711+
assert!(legacy_session);
712+
}
713+
714+
#[async_test]
715+
async fn test_can_serialise_pickled_session_with_sender_data() {
716+
// Given an InboundGroupSession
717+
let igs = InboundGroupSession::new(
718+
Curve25519PublicKey::from_base64("AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8")
719+
.unwrap(),
720+
Ed25519PublicKey::from_base64("wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww").unwrap(),
721+
room_id!("!test:localhost"),
722+
&create_session_key(),
723+
EventEncryptionAlgorithm::MegolmV1AesSha2,
724+
Some(HistoryVisibility::Shared),
725+
)
726+
.unwrap();
727+
728+
// When we pickle it and set its retry time to a known value
729+
let mut pickled = igs.pickle().await;
730+
731+
let InboundGroupSessionSenderData::UnknownDevice { retry_details, .. } =
732+
&mut pickled.sender_data
733+
else {
734+
panic!("Expected sender_data to be UnknownDevice");
735+
};
736+
(*retry_details).next_retry_time_ms = MilliSecondsSinceUnixEpoch(UInt::new(1234).unwrap());
737+
738+
// And serialise it
739+
let serialised = serde_json::to_string(&pickled).unwrap();
740+
741+
// Then it looks as we expect
742+
743+
// (Break out this list of numbers as otherwise it bothers the json macro below)
744+
let expected_inner = vec![
745+
193, 203, 223, 152, 33, 132, 200, 168, 24, 197, 79, 174, 231, 202, 45, 245, 128, 131,
746+
178, 165, 148, 37, 241, 214, 178, 218, 25, 33, 68, 48, 153, 104, 122, 6, 249, 198, 97,
747+
226, 214, 75, 64, 128, 25, 138, 98, 90, 138, 93, 52, 206, 174, 3, 84, 149, 101, 140,
748+
238, 156, 103, 107, 124, 144, 139, 104, 253, 5, 100, 251, 186, 118, 208, 87, 31, 218,
749+
123, 234, 103, 34, 246, 100, 39, 90, 216, 72, 187, 86, 202, 150, 100, 116, 204, 254,
750+
10, 154, 216, 133, 61, 250, 75, 100, 195, 63, 138, 22, 17, 13, 156, 123, 195, 132, 111,
751+
95, 250, 24, 236, 0, 246, 93, 230, 100, 211, 165, 211, 190, 181, 87, 42, 181,
752+
];
753+
assert_eq!(
754+
serde_json::from_str::<serde_json::Value>(&serialised).unwrap(),
755+
serde_json::json!({
756+
"pickle":{
757+
"initial_ratchet":{
758+
"inner": expected_inner,
759+
"counter":0
760+
},
761+
"signing_key":[
762+
213,161,95,135,114,153,162,127,217,74,64,2,59,143,93,5,190,157,120,
763+
80,89,8,87,129,115,148,104,144,152,186,178,109
764+
],
765+
"signing_key_verified":true,
766+
"config":{"version":"V1"}
767+
},
768+
"sender_key":"AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8",
769+
"signing_key":{"ed25519":"wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww"},
770+
"sender_data":{
771+
"UnknownDevice":{
772+
"retry_details":{
773+
"retry_count":0,
774+
"next_retry_time_ms":1234
775+
},
776+
"legacy_session":true
777+
}
778+
},
779+
"room_id":"!test:localhost",
780+
"imported":false,
781+
"backed_up":false,
782+
"history_visibility":"shared",
783+
"algorithm":"m.megolm.v1.aes-sha2"
784+
})
785+
);
786+
}
787+
788+
#[async_test]
789+
async fn test_can_deserialise_pickled_session_with_sender_data() {
790+
// Given the raw JSON for a picked inbound group sessions
791+
let pickle = r#"
792+
{
793+
"pickle": {
794+
"initial_ratchet": {
795+
"inner": [ 124, 251, 213, 204, 108, 247, 54, 7, 179, 162, 15, 107, 154, 215,
796+
220, 46, 123, 113, 120, 162, 225, 246, 237, 203, 125, 102, 190, 212,
797+
229, 195, 136, 185, 26, 31, 77, 140, 144, 181, 152, 177, 46, 105,
798+
202, 6, 53, 158, 157, 170, 31, 155, 130, 87, 214, 110, 143, 55, 68,
799+
138, 41, 35, 242, 230, 194, 15, 16, 145, 116, 94, 89, 35, 79, 145,
800+
245, 117, 204, 173, 166, 178, 49, 131, 143, 61, 61, 15, 211, 167, 17,
801+
2, 79, 110, 149, 200, 223, 23, 185, 200, 29, 64, 55, 39, 147, 167,
802+
205, 224, 159, 101, 218, 249, 203, 30, 175, 174, 48, 252, 40, 131,
803+
52, 135, 91, 57, 211, 96, 105, 58, 55, 68, 250, 24 ],
804+
"counter": 0
805+
},
806+
"signing_key": [ 93, 185, 171, 61, 173, 100, 51, 9, 157, 180, 214, 39, 131, 80, 118,
807+
130, 199, 232, 163, 197, 45, 23, 227, 100, 151, 59, 19, 102, 38,
808+
149, 43, 38 ],
809+
"signing_key_verified": true,
810+
"config": {
811+
"version": "V1"
812+
}
813+
},
814+
"sender_key": "AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8",
815+
"signing_key": {
816+
"ed25519": "wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww"
817+
},
818+
"sender_data":{
819+
"UnknownDevice":{
820+
"retry_details":{
821+
"retry_count":0,
822+
"next_retry_time_ms":98765
823+
},
824+
"legacy_session":false
825+
}
826+
},
827+
"room_id": "!test:localhost",
828+
"forwarding_chains": ["tb6kQKjk+SJl2KnfQ0lKVOZl6gDFMcsb9HcUP9k/4hc"],
829+
"imported": false,
830+
"backed_up": false,
831+
"history_visibility": "shared",
832+
"algorithm": "m.megolm.v1.aes-sha2"
833+
}
834+
"#;
835+
836+
// When we deserialise it to from JSON
837+
let deserialized = serde_json::from_str(pickle).unwrap();
838+
839+
// And unpickle it
840+
let unpickled = InboundGroupSession::from_pickle(deserialized).unwrap();
841+
842+
// Then it was parsed correctly
843+
assert_eq!(unpickled.session_id(), "XbmrPa1kMwmdtNYng1B2gsfoo8UtF+NklzsTZiaVKyY");
844+
845+
// We populated the InboundGroupSession's sender_data with a default value
846+
let InboundGroupSessionSenderData::UnknownDevice { retry_details, legacy_session } =
847+
unpickled.sender_data
848+
else {
849+
panic!("Expected sender_data to be UnknownDevice!");
850+
};
851+
assert_eq!(retry_details.retry_count, 0);
852+
assert_eq!(retry_details.next_retry_time_ms.0, UInt::new(98765).unwrap());
853+
assert!(!legacy_session);
665854
}
666855

667856
#[async_test]
@@ -685,4 +874,17 @@ mod tests {
685874

686875
assert_eq!(inbound.compare(&copy).await, SessionOrdering::Unconnected);
687876
}
877+
878+
fn create_session_key() -> SessionKey {
879+
SessionKey::from_base64(
880+
"\
881+
AgAAAADBy9+YIYTIqBjFT67nyi31gIOypZQl8day2hkhRDCZaHoG+cZh4tZLQIAZimJail0\
882+
0zq4DVJVljO6cZ2t8kIto/QVk+7p20Fcf2nvqZyL2ZCda2Ei7VsqWZHTM/gqa2IU9+ktkwz\
883+
+KFhENnHvDhG9f+hjsAPZd5mTTpdO+tVcqtdWhX4dymaJ/2UpAAjuPXQW+nXhQWQhXgXOUa\
884+
JCYurJtvbCbqZGeDMmVIoqukBs2KugNJ6j5WlTPoeFnMl6Guy9uH2iWWxGg8ZgT2xspqVl5\
885+
CwujjC+m7Dh1toVkvu+bAw\
886+
",
887+
)
888+
.unwrap()
889+
}
688890
}

0 commit comments

Comments
 (0)