1use crate::room_state::member::MemberId;
2use crate::room_state::privacy::{PrivacyMode, SecretVersion};
3use crate::room_state::ChatRoomParametersV1;
4use crate::util::sign_struct;
5use crate::util::{truncated_base64, verify_struct};
6use crate::ChatRoomStateV1;
7use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
8use freenet_scaffold::util::{fast_hash, FastHash};
9use freenet_scaffold::ComposableState;
10use serde::{Deserialize, Serialize};
11use std::fmt;
12use std::time::SystemTime;
13
14#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
15pub struct MessagesV1 {
16 pub messages: Vec<AuthorizedMessageV1>,
17}
18
19impl ComposableState for MessagesV1 {
20 type ParentState = ChatRoomStateV1;
21 type Summary = Vec<MessageId>;
22 type Delta = Vec<AuthorizedMessageV1>;
23 type Parameters = ChatRoomParametersV1;
24
25 fn verify(
26 &self,
27 parent_state: &Self::ParentState,
28 parameters: &Self::Parameters,
29 ) -> Result<(), String> {
30 let members_by_id = parent_state.members.members_by_member_id();
31 let owner_id = parameters.owner_id();
32
33 for message in &self.messages {
34 let verifying_key = if message.message.author == owner_id {
35 ¶meters.owner
37 } else if let Some(member) = members_by_id.get(&message.message.author) {
38 &member.member.member_vk
40 } else {
41 return Err(format!(
42 "Message author not found: {:?}",
43 message.message.author
44 ));
45 };
46
47 if message.validate(verifying_key).is_err() {
48 return Err(format!("Invalid message signature: id:{:?}", message.id()));
49 }
50 }
51
52 Ok(())
53 }
54
55 fn summarize(
56 &self,
57 _parent_state: &Self::ParentState,
58 _parameters: &Self::Parameters,
59 ) -> Self::Summary {
60 self.messages.iter().map(|m| m.id()).collect()
61 }
62
63 fn delta(
64 &self,
65 _parent_state: &Self::ParentState,
66 _parameters: &Self::Parameters,
67 old_state_summary: &Self::Summary,
68 ) -> Option<Self::Delta> {
69 let delta: Vec<AuthorizedMessageV1> = self
70 .messages
71 .iter()
72 .filter(|m| !old_state_summary.contains(&m.id()))
73 .cloned()
74 .collect();
75 if delta.is_empty() {
76 None
77 } else {
78 Some(delta)
79 }
80 }
81
82 fn apply_delta(
83 &mut self,
84 parent_state: &Self::ParentState,
85 parameters: &Self::Parameters,
86 delta: &Option<Self::Delta>,
87 ) -> Result<(), String> {
88 let max_recent_messages = parent_state.configuration.configuration.max_recent_messages;
89 let max_message_size = parent_state.configuration.configuration.max_message_size;
90 let privacy_mode = &parent_state.configuration.configuration.privacy_mode;
91 let current_secret_version = parent_state.secrets.current_version;
92
93 if let Some(delta) = delta {
95 for msg in delta {
96 match &msg.message.content {
97 RoomMessageBody::Private { secret_version, .. } => {
98 if *privacy_mode == PrivacyMode::Private {
100 if *secret_version != current_secret_version {
101 return Err(format!(
102 "Private message secret version {} does not match current version {}",
103 secret_version, current_secret_version
104 ));
105 }
106 }
107
108 let members = parent_state.members.members_by_member_id();
110 if !parent_state.secrets.has_complete_distribution(&members) {
111 return Err(
112 "Cannot accept private messages: incomplete secret distribution"
113 .to_string(),
114 );
115 }
116 }
117 RoomMessageBody::Public { .. } => {
118 if *privacy_mode == PrivacyMode::Private {
120 return Err("Cannot send public messages in private room".to_string());
121 }
122 }
123 }
124 }
125
126 self.messages.extend(delta.iter().cloned());
127 }
128
129 self.messages
132 .retain(|m| m.message.content.content_len() <= max_message_size);
133
134 let members_by_id = parent_state.members.members_by_member_id();
136 let owner_id = MemberId::from(¶meters.owner);
137 self.messages.retain(|m| {
138 members_by_id.contains_key(&m.message.author) || m.message.author == owner_id
139 });
140
141 self.messages
143 .sort_by(|a, b| a.message.time.cmp(&b.message.time));
144
145 if self.messages.len() > max_recent_messages {
147 self.messages
148 .drain(0..self.messages.len() - max_recent_messages);
149 }
150
151 Ok(())
152 }
153}
154
155#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
157pub enum RoomMessageBody {
158 Public { plaintext: String },
160 Private {
162 ciphertext: Vec<u8>,
163 nonce: [u8; 12],
164 secret_version: SecretVersion,
165 },
166}
167
168impl RoomMessageBody {
169 pub fn public(plaintext: String) -> Self {
171 Self::Public { plaintext }
172 }
173
174 pub fn private(ciphertext: Vec<u8>, nonce: [u8; 12], secret_version: SecretVersion) -> Self {
176 Self::Private {
177 ciphertext,
178 nonce,
179 secret_version,
180 }
181 }
182
183 pub fn is_public(&self) -> bool {
185 matches!(self, Self::Public { .. })
186 }
187
188 pub fn is_private(&self) -> bool {
190 matches!(self, Self::Private { .. })
191 }
192
193 pub fn content_len(&self) -> usize {
195 match self {
196 Self::Public { plaintext } => plaintext.len(),
197 Self::Private { ciphertext, .. } => ciphertext.len(),
198 }
199 }
200
201 pub fn secret_version(&self) -> Option<SecretVersion> {
203 match self {
204 Self::Public { .. } => None,
205 Self::Private { secret_version, .. } => Some(*secret_version),
206 }
207 }
208
209 pub fn to_string_lossy(&self) -> String {
212 match self {
213 Self::Public { plaintext } => plaintext.clone(),
214 Self::Private {
215 ciphertext,
216 secret_version,
217 ..
218 } => {
219 format!(
220 "[Encrypted message: {} bytes, v{}]",
221 ciphertext.len(),
222 secret_version
223 )
224 }
225 }
226 }
227
228 pub fn as_public_string(&self) -> Option<&str> {
230 match self {
231 Self::Public { plaintext } => Some(plaintext),
232 Self::Private { .. } => None,
233 }
234 }
235}
236
237impl fmt::Display for RoomMessageBody {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 write!(f, "{}", self.to_string_lossy())
240 }
241}
242
243#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
244pub struct MessageV1 {
245 pub room_owner: MemberId,
246 pub author: MemberId,
247 pub time: SystemTime,
248 pub content: RoomMessageBody,
249}
250
251impl Default for MessageV1 {
252 fn default() -> Self {
253 Self {
254 room_owner: MemberId(FastHash(0)),
255 author: MemberId(FastHash(0)),
256 time: SystemTime::UNIX_EPOCH,
257 content: RoomMessageBody::public(String::new()),
258 }
259 }
260}
261
262#[derive(Clone, PartialEq, Serialize, Deserialize)]
263pub struct AuthorizedMessageV1 {
264 pub message: MessageV1,
265 pub signature: Signature,
266}
267
268impl fmt::Debug for AuthorizedMessageV1 {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 f.debug_struct("AuthorizedMessage")
271 .field("message", &self.message)
272 .field(
273 "signature",
274 &format_args!("{}", truncated_base64(self.signature.to_bytes())),
275 )
276 .finish()
277 }
278}
279
280#[derive(Eq, PartialEq, Hash, Serialize, Deserialize, Clone, Debug, Ord, PartialOrd)]
281pub struct MessageId(pub FastHash);
282
283impl fmt::Display for MessageId {
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 write!(f, "{:?}", self.0)
286 }
287}
288
289impl AuthorizedMessageV1 {
290 pub fn new(message: MessageV1, signing_key: &SigningKey) -> Self {
291 Self {
292 message: message.clone(),
293 signature: sign_struct(&message, signing_key),
294 }
295 }
296
297 pub fn validate(
298 &self,
299 verifying_key: &VerifyingKey,
300 ) -> Result<(), ed25519_dalek::SignatureError> {
301 verify_struct(&self.message, &self.signature, verifying_key)
302 }
303
304 pub fn id(&self) -> MessageId {
305 MessageId(fast_hash(&self.signature.to_bytes()))
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use ed25519_dalek::{Signer, SigningKey};
313 use rand::rngs::OsRng;
314 use std::time::Duration;
315
316 fn create_test_message(owner_id: MemberId, author_id: MemberId) -> MessageV1 {
317 MessageV1 {
318 room_owner: owner_id,
319 author: author_id,
320 time: SystemTime::now(),
321 content: RoomMessageBody::public("Test message".to_string()),
322 }
323 }
324
325 #[test]
326 fn test_messages_v1_default() {
327 let default_messages = MessagesV1::default();
328 assert!(default_messages.messages.is_empty());
329 }
330
331 #[test]
332 fn test_authorized_message_v1_debug() {
333 let signing_key = SigningKey::generate(&mut OsRng);
334 let owner_id = MemberId(FastHash(0));
335 let author_id = MemberId(FastHash(1));
336
337 let message = create_test_message(owner_id, author_id);
338 let authorized_message = AuthorizedMessageV1::new(message, &signing_key);
339
340 let debug_output = format!("{:?}", authorized_message);
341 assert!(debug_output.contains("AuthorizedMessage"));
342 assert!(debug_output.contains("message"));
343 assert!(debug_output.contains("signature"));
344 }
345
346 #[test]
347 fn test_authorized_message_new_and_validate() {
348 let signing_key = SigningKey::generate(&mut OsRng);
349 let verifying_key = signing_key.verifying_key();
350 let owner_id = MemberId(FastHash(0));
351 let author_id = MemberId(FastHash(1));
352
353 let message = create_test_message(owner_id, author_id);
354 let authorized_message = AuthorizedMessageV1::new(message.clone(), &signing_key);
355
356 assert_eq!(authorized_message.message, message);
357 assert!(authorized_message.validate(&verifying_key).is_ok());
358
359 let wrong_key = SigningKey::generate(&mut OsRng).verifying_key();
361 assert!(authorized_message.validate(&wrong_key).is_err());
362
363 let mut tampered_message = authorized_message.clone();
365 tampered_message.message.content = RoomMessageBody::public("Tampered content".to_string());
366 assert!(tampered_message.validate(&verifying_key).is_err());
367 }
368
369 #[test]
370 fn test_message_id() {
371 let signing_key = SigningKey::generate(&mut OsRng);
372 let owner_id = MemberId(FastHash(0));
373 let author_id = MemberId(FastHash(1));
374
375 let message = create_test_message(owner_id, author_id);
376 let authorized_message = AuthorizedMessageV1::new(message, &signing_key);
377
378 let id1 = authorized_message.id();
379 let id2 = authorized_message.id();
380
381 assert_eq!(id1, id2);
382
383 let message2 = create_test_message(owner_id, author_id);
385 let authorized_message2 = AuthorizedMessageV1::new(message2, &signing_key);
386 assert_ne!(authorized_message.id(), authorized_message2.id());
387 }
388
389 #[test]
390 fn test_messages_verify() {
391 let owner_signing_key = SigningKey::generate(&mut OsRng);
393 let owner_verifying_key = owner_signing_key.verifying_key();
394 let owner_id = MemberId::from(&owner_verifying_key);
395
396 let author_signing_key = SigningKey::generate(&mut OsRng);
398 let author_verifying_key = author_signing_key.verifying_key();
399 let author_id = MemberId::from(&author_verifying_key);
400
401 let message = create_test_message(owner_id, author_id);
403 let authorized_message = AuthorizedMessageV1::new(message, &author_signing_key);
404
405 let messages = MessagesV1 {
407 messages: vec![authorized_message],
408 };
409
410 let mut parent_state = ChatRoomStateV1::default();
412 let author_member = crate::room_state::member::Member {
413 owner_member_id: owner_id,
414 invited_by: owner_id,
415 member_vk: author_verifying_key,
416 };
417 let authorized_author =
418 crate::room_state::member::AuthorizedMember::new(author_member, &owner_signing_key);
419 parent_state.members.members = vec![authorized_author];
420
421 let parameters = ChatRoomParametersV1 {
423 owner: owner_verifying_key,
424 };
425
426 assert!(
428 messages.verify(&parent_state, ¶meters).is_ok(),
429 "Valid messages should pass verification: {:?}",
430 messages.verify(&parent_state, ¶meters)
431 );
432
433 let mut invalid_messages = messages.clone();
435 invalid_messages.messages[0].signature = Signature::from_bytes(&[0; 64]); assert!(
437 invalid_messages.verify(&parent_state, ¶meters).is_err(),
438 "Messages with invalid signature should fail verification"
439 );
440
441 let non_existent_author_id =
443 MemberId::from(&SigningKey::generate(&mut OsRng).verifying_key());
444 let invalid_message = create_test_message(owner_id, non_existent_author_id);
445 let invalid_authorized_message =
446 AuthorizedMessageV1::new(invalid_message, &author_signing_key);
447 let invalid_messages = MessagesV1 {
448 messages: vec![invalid_authorized_message],
449 };
450 assert!(
451 invalid_messages.verify(&parent_state, ¶meters).is_err(),
452 "Messages with non-existent author should fail verification"
453 );
454 }
455
456 #[test]
457 fn test_messages_summarize() {
458 let signing_key = SigningKey::generate(&mut OsRng);
459 let owner_id = MemberId(FastHash(0));
460 let author_id = MemberId(FastHash(1));
461
462 let message1 = create_test_message(owner_id, author_id);
463 let message2 = create_test_message(owner_id, author_id);
464
465 let authorized_message1 = AuthorizedMessageV1::new(message1, &signing_key);
466 let authorized_message2 = AuthorizedMessageV1::new(message2, &signing_key);
467
468 let messages = MessagesV1 {
469 messages: vec![authorized_message1.clone(), authorized_message2.clone()],
470 };
471
472 let parent_state = ChatRoomStateV1::default();
473 let parameters = ChatRoomParametersV1 {
474 owner: signing_key.verifying_key(),
475 };
476
477 let summary = messages.summarize(&parent_state, ¶meters);
478 assert_eq!(summary.len(), 2);
479 assert_eq!(summary[0], authorized_message1.id());
480 assert_eq!(summary[1], authorized_message2.id());
481
482 let empty_messages = MessagesV1 { messages: vec![] };
484 let empty_summary = empty_messages.summarize(&parent_state, ¶meters);
485 assert!(empty_summary.is_empty());
486 }
487
488 #[test]
489 fn test_messages_delta() {
490 let signing_key = SigningKey::generate(&mut OsRng);
491 let owner_id = MemberId(FastHash(0));
492 let author_id = MemberId(FastHash(1));
493
494 let message1 = create_test_message(owner_id, author_id);
495 let message2 = create_test_message(owner_id, author_id);
496 let message3 = create_test_message(owner_id, author_id);
497
498 let authorized_message1 = AuthorizedMessageV1::new(message1, &signing_key);
499 let authorized_message2 = AuthorizedMessageV1::new(message2, &signing_key);
500 let authorized_message3 = AuthorizedMessageV1::new(message3, &signing_key);
501
502 let messages = MessagesV1 {
503 messages: vec![
504 authorized_message1.clone(),
505 authorized_message2.clone(),
506 authorized_message3.clone(),
507 ],
508 };
509
510 let parent_state = ChatRoomStateV1::default();
511 let parameters = ChatRoomParametersV1 {
512 owner: signing_key.verifying_key(),
513 };
514
515 let old_summary = vec![authorized_message1.id(), authorized_message2.id()];
517 let delta = messages
518 .delta(&parent_state, ¶meters, &old_summary)
519 .unwrap();
520 assert_eq!(delta.len(), 1);
521 assert_eq!(delta[0], authorized_message3);
522
523 let empty_summary: Vec<MessageId> = vec![];
525 let full_delta = messages
526 .delta(&parent_state, ¶meters, &empty_summary)
527 .unwrap();
528 assert_eq!(full_delta.len(), 3);
529 assert_eq!(full_delta, messages.messages);
530
531 let full_summary = vec![
533 authorized_message1.id(),
534 authorized_message2.id(),
535 authorized_message3.id(),
536 ];
537 let no_delta = messages.delta(&parent_state, ¶meters, &full_summary);
538 assert!(no_delta.is_none());
539 }
540
541 #[test]
542 fn test_messages_apply_delta() {
543 let owner_signing_key = SigningKey::generate(&mut OsRng);
545 let owner_verifying_key = owner_signing_key.verifying_key();
546 let owner_id = MemberId::from(&owner_verifying_key);
547
548 let author_signing_key = SigningKey::generate(&mut OsRng);
549 let author_verifying_key = author_signing_key.verifying_key();
550 let author_id = MemberId::from(&author_verifying_key);
551
552 let mut parent_state = ChatRoomStateV1::default();
553 parent_state.configuration.configuration.max_recent_messages = 3;
554 parent_state.configuration.configuration.max_message_size = 100;
555 parent_state.members.members = vec![crate::room_state::member::AuthorizedMember {
556 member: crate::room_state::member::Member {
557 owner_member_id: owner_id,
558 invited_by: owner_id,
559 member_vk: author_verifying_key,
560 },
561 signature: owner_signing_key.try_sign(&[0; 32]).unwrap(),
562 }];
563
564 let parameters = ChatRoomParametersV1 {
565 owner: owner_verifying_key,
566 };
567
568 let create_message = |time: SystemTime| {
570 let message = MessageV1 {
571 room_owner: owner_id,
572 author: author_id,
573 time,
574 content: RoomMessageBody::public("Test message".to_string()),
575 };
576 AuthorizedMessageV1::new(message, &author_signing_key)
577 };
578
579 let now = SystemTime::now();
580 let message1 = create_message(now - Duration::from_secs(3));
581 let message2 = create_message(now - Duration::from_secs(2));
582 let message3 = create_message(now - Duration::from_secs(1));
583 let message4 = create_message(now);
584
585 let mut messages = MessagesV1 {
587 messages: vec![message1.clone(), message2.clone()],
588 };
589
590 let delta = vec![message3.clone(), message4.clone()];
592 assert!(messages
593 .apply_delta(&parent_state, ¶meters, &Some(delta))
594 .is_ok());
595
596 assert_eq!(
598 messages.messages.len(),
599 3,
600 "Should have 3 messages after applying delta"
601 );
602 assert!(
603 !messages.messages.contains(&message1),
604 "Oldest message should be removed"
605 );
606 assert!(
607 messages.messages.contains(&message2),
608 "Second oldest message should be retained"
609 );
610 assert!(
611 messages.messages.contains(&message3),
612 "New message should be added"
613 );
614 assert!(
615 messages.messages.contains(&message4),
616 "Newest message should be added"
617 );
618
619 let old_message = create_message(now - Duration::from_secs(4));
621 let delta = vec![old_message.clone()];
622 assert!(messages
623 .apply_delta(&parent_state, ¶meters, &Some(delta))
624 .is_ok());
625
626 assert_eq!(messages.messages.len(), 3, "Should still have 3 messages");
628 assert!(
629 !messages.messages.contains(&old_message),
630 "Older message should not be added"
631 );
632 assert!(
633 messages.messages.contains(&message2),
634 "Message2 should be retained"
635 );
636 assert!(
637 messages.messages.contains(&message3),
638 "Message3 should be retained"
639 );
640 assert!(
641 messages.messages.contains(&message4),
642 "Newest message should be retained"
643 );
644 }
645
646 #[test]
647 fn test_message_author_preservation_across_users() {
648 let user1_sk = SigningKey::generate(&mut OsRng);
650 let user1_vk = user1_sk.verifying_key();
651 let user1_id = MemberId::from(&user1_vk);
652
653 let user2_sk = SigningKey::generate(&mut OsRng);
654 let user2_vk = user2_sk.verifying_key();
655 let user2_id = MemberId::from(&user2_vk);
656
657 let owner_sk = SigningKey::generate(&mut OsRng);
658 let owner_vk = owner_sk.verifying_key();
659 let owner_id = MemberId::from(&owner_vk);
660
661 println!("User1 ID: {}", user1_id);
662 println!("User2 ID: {}", user2_id);
663 println!("Owner ID: {}", owner_id);
664
665 let msg1 = MessageV1 {
667 room_owner: owner_id,
668 author: user1_id,
669 content: RoomMessageBody::public("Message from user1".to_string()),
670 time: SystemTime::now(),
671 };
672
673 let msg2 = MessageV1 {
674 room_owner: owner_id,
675 author: user2_id,
676 content: RoomMessageBody::public("Message from user2".to_string()),
677 time: SystemTime::now() + Duration::from_secs(1),
678 };
679
680 let auth_msg1 = AuthorizedMessageV1::new(msg1.clone(), &user1_sk);
681 let auth_msg2 = AuthorizedMessageV1::new(msg2.clone(), &user2_sk);
682
683 let messages = MessagesV1 {
685 messages: vec![auth_msg1.clone(), auth_msg2.clone()],
686 };
687
688 assert_eq!(messages.messages.len(), 2);
690
691 let stored_msg1 = &messages.messages[0];
692 let stored_msg2 = &messages.messages[1];
693
694 assert_eq!(
695 stored_msg1.message.author, user1_id,
696 "Message 1 author should be user1, but got {}",
697 stored_msg1.message.author
698 );
699 assert_eq!(
700 stored_msg2.message.author, user2_id,
701 "Message 2 author should be user2, but got {}",
702 stored_msg2.message.author
703 );
704
705 assert_ne!(user1_id, user2_id, "User IDs should be different");
707
708 let user1_id_str = user1_id.to_string();
710 let user2_id_str = user2_id.to_string();
711
712 println!("User1 ID string: {}", user1_id_str);
713 println!("User2 ID string: {}", user2_id_str);
714
715 assert_ne!(
716 user1_id_str, user2_id_str,
717 "User ID strings should be different"
718 );
719 }
720}