zoe_state_machine/
state.rs1use serde::{Deserialize, Serialize};
2
3use zoe_app_primitives::GroupActivityEvent;
4use zoe_wire_protocol::{ChaCha20Poly1305Content, EncryptionKey, MessageId};
5
6pub use zoe_app_primitives::{GroupMember, GroupState};
9
10#[cfg(feature = "frb-api")]
11use flutter_rust_bridge::frb;
12
13#[derive(Debug, thiserror::Error)]
14pub enum GroupSessionError {
15 #[error("Crypto error: {0}")]
17 Crypto(String),
18 #[error("Serialization error: {0}")]
20 Serialization(String),
21 #[error("Deserialization error: {0}")]
23 Deserialization(String),
24}
25
26#[cfg_attr(feature = "frb-api", frb(opaque))]
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct GroupSession {
32 pub state: GroupState,
34 pub current_key: EncryptionKey,
36 pub previous_keys: Vec<EncryptionKey>,
38}
39
40pub fn encrypt_group_event_content<T>(
41 encryption_key: &EncryptionKey,
42 event: &GroupActivityEvent<T>,
43) -> Result<ChaCha20Poly1305Content, GroupSessionError>
44where
45 T: Serialize,
46{
47 let plaintext = postcard::to_stdvec(event)
49 .map_err(|e| GroupSessionError::Crypto(format!("Group event serialization failed: {e}")))?;
50
51 encryption_key
53 .encrypt_content(&plaintext)
54 .map_err(|e| GroupSessionError::Crypto(format!("Group event encryption failed: {e}")))
55}
56
57#[cfg_attr(feature = "frb-api", frb(ignore))]
58impl GroupSession {
59 pub fn new(state: GroupState, encryption_key: EncryptionKey) -> Self {
61 Self {
62 state,
63 current_key: encryption_key,
64 previous_keys: Vec::new(),
65 }
66 }
67
68 pub fn rotate_key(&mut self, new_key: EncryptionKey) {
70 let old_key = std::mem::replace(&mut self.current_key, new_key);
71 self.previous_keys.push(old_key);
72 }
73
74 pub fn all_keys(&self) -> impl Iterator<Item = &EncryptionKey> {
76 std::iter::once(&self.current_key).chain(self.previous_keys.iter())
77 }
78
79 pub fn encrypt_group_event_content<T>(
81 &self,
82 event: &GroupActivityEvent<T>,
83 ) -> Result<ChaCha20Poly1305Content, GroupSessionError>
84 where
85 T: Serialize,
86 {
87 encrypt_group_event_content(&self.current_key, event)
88 }
89
90 pub fn decrypt_group_event<T>(
92 &self,
93 payload: &ChaCha20Poly1305Content,
94 ) -> Result<GroupActivityEvent<T>, GroupSessionError>
95 where
96 T: for<'de> Deserialize<'de>,
97 {
98 let plaintext = self.current_key.decrypt_content(payload).map_err(|e| {
102 GroupSessionError::Crypto(format!("Group event decryption failed: {e}"))
103 })?;
104 let event: GroupActivityEvent<T> = postcard::from_bytes(&plaintext).map_err(|e| {
108 GroupSessionError::Crypto(format!("Group event deserialization failed: {e}"))
109 })?;
110 Ok(event)
111 }
112}
113
114#[cfg_attr(feature = "frb-api", frb(ignore))]
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct GroupStateSnapshot {
118 pub state: GroupState,
119 pub snapshot_at: u64,
120 pub snapshot_event_id: MessageId,
121}
122
123