1mod account;
21mod group_sessions;
22mod session;
23mod signing;
24mod utility;
25
26pub use account::{Account, OlmMessageHash, PickledAccount, StaticAccountData};
27pub(crate) use account::{OlmDecryptionInfo, SessionType};
28pub(crate) use group_sessions::{
29 sender_data_finder::{self, SenderDataFinder},
30 ShareState,
31};
32pub use group_sessions::{
33 BackedUpRoomKey, EncryptionSettings, ExportedRoomKey, InboundGroupSession, KnownSenderData,
34 OutboundGroupSession, PickledInboundGroupSession, PickledOutboundGroupSession, SenderData,
35 SenderDataType, SessionCreationError, SessionExportError, SessionKey, ShareInfo,
36};
37pub use session::{PickledSession, Session};
38pub use signing::{CrossSigningStatus, PickledCrossSigningIdentity, PrivateCrossSigningIdentity};
39pub(crate) use utility::{SignedJsonObject, VerifyJson};
40pub use vodozemac::{olm::IdentityKeys, Curve25519PublicKey};
41
42#[cfg(test)]
43pub(crate) mod tests {
44 use assert_matches::assert_matches;
45 use matrix_sdk_test::{async_test, message_like_event_content};
46 use ruma::{
47 device_id, event_id,
48 events::{
49 relation::Replacement,
50 room::message::{Relation, RoomMessageEventContent},
51 AnyMessageLikeEvent, AnyTimelineEvent, MessageLikeEvent,
52 },
53 room_id,
54 serde::Raw,
55 user_id, DeviceId, UserId,
56 };
57 use serde_json::{from_value, json, Value};
58 use vodozemac::{
59 olm::{OlmMessage, SessionConfig},
60 Curve25519PublicKey, Ed25519PublicKey,
61 };
62
63 use crate::{
64 olm::{Account, ExportedRoomKey, InboundGroupSession, SenderData, Session},
65 types::events::{
66 forwarded_room_key::ForwardedRoomKeyContent, room::encrypted::EncryptedEvent,
67 },
68 utilities::json_convert,
69 };
70
71 fn alice_id() -> &'static UserId {
72 user_id!("@alice:example.org")
73 }
74
75 fn alice_device_id() -> &'static DeviceId {
76 device_id!("ALICEDEVICE")
77 }
78
79 fn bob_id() -> &'static UserId {
80 user_id!("@bob:example.org")
81 }
82
83 fn bob_device_id() -> &'static DeviceId {
84 device_id!("BOBDEVICE")
85 }
86
87 pub(crate) fn get_account_and_session_test_helper() -> (Account, Session) {
88 let alice = Account::with_device_id(alice_id(), alice_device_id());
89 let mut bob = Account::with_device_id(bob_id(), bob_device_id());
90
91 bob.generate_one_time_keys(1);
92 let one_time_key = *bob.one_time_keys().values().next().unwrap();
93 let sender_key = bob.identity_keys().curve25519;
94 let session = alice.create_outbound_session_helper(
95 SessionConfig::default(),
96 sender_key,
97 one_time_key,
98 false,
99 alice.device_keys(),
100 );
101
102 (alice, session)
103 }
104
105 #[test]
106 fn test_account_creation() {
107 let mut account = Account::with_device_id(alice_id(), alice_device_id());
108
109 assert!(!account.shared());
110
111 account.mark_as_shared();
112 assert!(account.shared());
113 }
114
115 #[test]
116 fn test_one_time_keys_creation() {
117 let mut account = Account::with_device_id(alice_id(), alice_device_id());
118 let one_time_keys = account.one_time_keys();
119
120 assert!(!one_time_keys.is_empty());
121 assert_ne!(account.max_one_time_keys(), 0);
122
123 account.generate_one_time_keys(10);
124 let one_time_keys = account.one_time_keys();
125
126 assert_ne!(one_time_keys.values().len(), 0);
127 assert_ne!(one_time_keys.keys().len(), 0);
128 assert_ne!(one_time_keys.iter().len(), 0);
129
130 account.mark_keys_as_published();
131 let one_time_keys = account.one_time_keys();
132 assert!(one_time_keys.is_empty());
133 }
134
135 #[async_test]
136 async fn test_session_creation() {
137 let mut alice = Account::with_device_id(alice_id(), alice_device_id());
138 let bob = Account::with_device_id(bob_id(), bob_device_id());
139 let alice_keys = alice.identity_keys();
140 alice.generate_one_time_keys(1);
141 let one_time_keys = alice.one_time_keys();
142 alice.mark_keys_as_published();
143
144 let one_time_key = *one_time_keys.values().next().unwrap();
145
146 let mut bob_session = bob.create_outbound_session_helper(
147 SessionConfig::default(),
148 alice_keys.curve25519,
149 one_time_key,
150 false,
151 bob.device_keys(),
152 );
153
154 let plaintext = "Hello world";
155
156 let message = bob_session.encrypt_helper(plaintext).await;
157
158 let prekey_message = match message {
159 OlmMessage::PreKey(m) => m,
160 OlmMessage::Normal(_) => panic!("Incorrect message type"),
161 };
162
163 let bob_keys = bob.identity_keys();
164 let result = alice
165 .create_inbound_session(bob_keys.curve25519, alice.device_keys(), &prekey_message)
166 .unwrap();
167
168 assert_eq!(bob_session.session_id(), result.session.session_id());
169
170 assert_eq!(plaintext, result.plaintext);
171 }
172
173 #[async_test]
174 async fn test_group_session_creation() {
175 let alice = Account::with_device_id(alice_id(), alice_device_id());
176 let room_id = room_id!("!test:localhost");
177
178 let (outbound, _) = alice.create_group_session_pair_with_defaults(room_id).await;
179
180 assert_eq!(0, outbound.message_index().await);
181 assert!(!outbound.shared());
182 outbound.mark_as_shared();
183 assert!(outbound.shared());
184
185 let inbound = InboundGroupSession::new(
186 Curve25519PublicKey::from_base64("Nn0L2hkcCMFKqynTjyGsJbth7QrVmX3lbrksMkrGOAw")
187 .unwrap(),
188 Ed25519PublicKey::from_base64("ee3Ek+J2LkkPmjGPGLhMxiKnhiX//xcqaVL4RP6EypE").unwrap(),
189 room_id,
190 &outbound.session_key().await,
191 SenderData::unknown(),
192 outbound.settings().algorithm.to_owned(),
193 None,
194 )
195 .expect("We can always create an inbound group session from an outbound one");
196
197 assert_eq!(0, inbound.first_known_index());
198
199 assert_eq!(outbound.session_id(), inbound.session_id());
200
201 let plaintext = "This is a secret to everybody".to_owned();
202 let ciphertext = outbound.encrypt_helper(plaintext.clone()).await;
203
204 assert_eq!(
205 plaintext.as_bytes(),
206 inbound.decrypt_helper(&ciphertext).await.unwrap().plaintext
207 );
208 }
209
210 #[async_test]
211 async fn test_edit_decryption() {
212 let alice = Account::with_device_id(alice_id(), alice_device_id());
213 let room_id = room_id!("!test:localhost");
214 let event_id = event_id!("$1234adfad:asdf");
215
216 let (outbound, _) = alice.create_group_session_pair_with_defaults(room_id).await;
217
218 assert_eq!(0, outbound.message_index().await);
219 assert!(!outbound.shared());
220 outbound.mark_as_shared();
221 assert!(outbound.shared());
222
223 let mut content = RoomMessageEventContent::text_plain("Hello");
224 content.relates_to = Some(Relation::Replacement(Replacement::new(
225 event_id.to_owned(),
226 RoomMessageEventContent::text_plain("Hello edit").into(),
227 )));
228
229 let inbound = InboundGroupSession::new(
230 Curve25519PublicKey::from_base64("Nn0L2hkcCMFKqynTjyGsJbth7QrVmX3lbrksMkrGOAw")
231 .unwrap(),
232 Ed25519PublicKey::from_base64("ee3Ek+J2LkkPmjGPGLhMxiKnhiX//xcqaVL4RP6EypE").unwrap(),
233 room_id,
234 &outbound.session_key().await,
235 SenderData::unknown(),
236 outbound.settings().algorithm.to_owned(),
237 None,
238 )
239 .unwrap();
240
241 assert_eq!(0, inbound.first_known_index());
242
243 assert_eq!(outbound.session_id(), inbound.session_id());
244
245 let encrypted_content =
246 outbound.encrypt("m.room.message", &Raw::new(&content).unwrap().cast()).await;
247
248 let event = json!({
249 "sender": alice.user_id(),
250 "event_id": event_id,
251 "origin_server_ts": 0u64,
252 "room_id": room_id,
253 "type": "m.room.encrypted",
254 "content": encrypted_content,
255 });
256
257 let event = json_convert(&event).unwrap();
258 let decrypted = inbound.decrypt(&event).await.unwrap().0;
259
260 if let AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(
261 MessageLikeEvent::Original(e),
262 )) = from_value(decrypted.into()).unwrap()
263 {
264 assert_matches!(e.content.relates_to, Some(Relation::Replacement(_)));
265 } else {
266 panic!("Invalid event type")
267 }
268 }
269
270 #[async_test]
271 async fn test_relates_to_decryption() {
272 let alice = Account::with_device_id(alice_id(), alice_device_id());
273 let room_id = room_id!("!test:localhost");
274 let event_id = event_id!("$1234adfad:asdf");
275
276 let relation_json = json!({
277 "rel_type": "m.reference",
278 "event_id": "$WUreEJERkFzO8i2dk6CmTex01cP1dZ4GWKhKCwkWHrQ"
279 });
280
281 let (outbound, inbound) = alice.create_group_session_pair_with_defaults(room_id).await;
282
283 let content = message_like_event_content!({
286 "m.relates_to": relation_json,
287 });
288 let encrypted = outbound.encrypt("m.dummy", &content).await;
289
290 let event = json!({
291 "sender": alice.user_id(),
292 "event_id": event_id,
293 "origin_server_ts": 0u64,
294 "room_id": room_id,
295 "type": "m.room.encrypted",
296 "content": encrypted,
297 });
298 let event: EncryptedEvent = json_convert(&event).unwrap();
299
300 assert_eq!(
301 event.content.relates_to.as_ref(),
302 Some(&relation_json),
303 "The encrypted event should contain an unencrypted relation"
304 );
305
306 let (decrypted, _) = inbound.decrypt(&event).await.unwrap();
307
308 let decrypted: Value = json_convert(&decrypted).unwrap();
309 let relation = decrypted.get("content").and_then(|c| c.get("m.relates_to"));
310 assert_eq!(relation, Some(&relation_json), "The decrypted event should contain a relation");
311
312 let content = message_like_event_content!({});
313 let encrypted = outbound.encrypt("m.dummy", &content).await;
314 let mut encrypted: Value = json_convert(&encrypted).unwrap();
315 encrypted.as_object_mut().unwrap().insert("m.relates_to".to_owned(), relation_json.clone());
316
317 let event = json!({
320 "sender": alice.user_id(),
321 "event_id": event_id,
322 "origin_server_ts": 0u64,
323 "room_id": room_id,
324 "type": "m.room.encrypted",
325 "content": encrypted,
326 });
327 let event: EncryptedEvent = json_convert(&event).unwrap();
328
329 assert_eq!(
330 event.content.relates_to,
331 Some(relation_json),
332 "The encrypted event should contain an unencrypted relation"
333 );
334
335 let (decrypted, _) = inbound.decrypt(&event).await.unwrap();
336
337 let decrypted: Value = json_convert(&decrypted).unwrap();
338 let relation = decrypted.get("content").and_then(|c| c.get("m.relates_to"));
339 assert!(relation.is_some(), "The decrypted event should contain a relation");
340 }
341
342 #[async_test]
343 async fn test_group_session_export() {
344 let alice = Account::with_device_id(alice_id(), alice_device_id());
345 let room_id = room_id!("!test:localhost");
346
347 let (_, inbound) = alice.create_group_session_pair_with_defaults(room_id).await;
348
349 let export = inbound.export().await;
350 let export: ForwardedRoomKeyContent = export.try_into().unwrap();
351 let export = ExportedRoomKey::try_from(export).unwrap();
352
353 let imported = InboundGroupSession::from_export(&export)
354 .expect("We can always import an inbound group session from a fresh export");
355
356 assert_eq!(inbound.session_id(), imported.session_id());
357 }
358}