enigma_protocol/
session.rs1use std::collections::HashMap;
2
3use enigma_packet::{Message, MessageMeta};
4use uuid::Uuid;
5
6use crate::crypto::{conversation_ad_bytes, SessionRatchet};
7use crate::error::{EnigmaProtocolError, Result};
8use crate::types::SessionBootstrap;
9
10pub struct Session {
11 local_id: String,
12 remote_id: String,
13 ratchet: SessionRatchet,
14 attachments: HashMap<Uuid, AttachmentTracker>,
15}
16
17struct AttachmentTracker {
18 meta: enigma_packet::AttachmentMeta,
19 received_chunks: u32,
20 received_size: u64,
21}
22
23pub enum AttachmentUpdate {
24 Init {
25 attachment_id: Uuid,
26 total_chunks: u32,
27 },
28 Chunk {
29 attachment_id: Uuid,
30 received_chunks: u32,
31 total_chunks: u32,
32 },
33 End {
34 attachment_id: Uuid,
35 total_size: u64,
36 },
37}
38
39impl Session {
40 pub fn new(local: &str, remote: &str, bootstrap: &SessionBootstrap) -> Result<Self> {
41 let ratchet = SessionRatchet::from_bootstrap(bootstrap, local, remote)?;
42 Ok(Self {
43 local_id: local.to_string(),
44 remote_id: remote.to_string(),
45 ratchet,
46 attachments: HashMap::new(),
47 })
48 }
49
50 pub fn conversation_id(&self) -> Uuid {
51 self.ratchet.conversation_id()
52 }
53
54 pub fn local_id(&self) -> &str {
55 &self.local_id
56 }
57
58 pub fn remote_id(&self) -> &str {
59 &self.remote_id
60 }
61
62 pub fn encrypt_message(&mut self, message: &Message) -> Result<Vec<u8>> {
63 let encoded = enigma_packet::encode_message(message)?;
64 let key = self.ratchet.next_send_key()?;
65 let box_ = enigma_aead::AeadBox::new(key);
66 let ad = conversation_ad_bytes(&self.conversation_id(), &self.local_id, &self.remote_id);
67 let ciphertext = box_.encrypt(&encoded, &ad)?;
68 Ok(ciphertext)
69 }
70
71 pub fn decrypt_packet(&mut self, packet: &[u8]) -> Result<Message> {
72 let key = self.ratchet.next_recv_key()?;
73 let box_ = enigma_aead::AeadBox::new(key);
74 let ad = conversation_ad_bytes(&self.conversation_id(), &self.remote_id, &self.local_id);
75 let plaintext = box_.decrypt(packet, &ad)?;
76 let message = enigma_packet::decode_message(&plaintext)?;
77 Ok(message)
78 }
79
80 pub fn handle_attachment_message(
81 &mut self,
82 message: &Message,
83 ) -> Result<Option<AttachmentUpdate>> {
84 match &message.meta {
85 MessageMeta::AttachmentInit(meta) => {
86 let tracker = AttachmentTracker {
87 meta: meta.clone(),
88 received_chunks: 0,
89 received_size: 0,
90 };
91 self.attachments.insert(meta.attachment_id, tracker);
92 Ok(Some(AttachmentUpdate::Init {
93 attachment_id: meta.attachment_id,
94 total_chunks: meta.chunk_count,
95 }))
96 }
97 MessageMeta::AttachmentChunk(chunk) => {
98 let tracker = self
99 .attachments
100 .get_mut(&chunk.attachment_id)
101 .ok_or(EnigmaProtocolError::Attachment)?;
102 if chunk.index != tracker.received_chunks {
103 return Err(EnigmaProtocolError::Attachment);
104 }
105 tracker.received_chunks = tracker
106 .received_chunks
107 .checked_add(1)
108 .ok_or(EnigmaProtocolError::Attachment)?;
109 tracker.received_size = tracker
110 .received_size
111 .checked_add(chunk.chunk_size as u64)
112 .ok_or(EnigmaProtocolError::Attachment)?;
113 Ok(Some(AttachmentUpdate::Chunk {
114 attachment_id: chunk.attachment_id,
115 received_chunks: tracker.received_chunks,
116 total_chunks: tracker.meta.chunk_count,
117 }))
118 }
119 MessageMeta::AttachmentEnd {
120 attachment_id,
121 total_size,
122 chunk_count,
123 ..
124 } => {
125 let tracker = self
126 .attachments
127 .remove(attachment_id)
128 .ok_or(EnigmaProtocolError::Attachment)?;
129 if &tracker.meta.chunk_count != chunk_count {
130 return Err(EnigmaProtocolError::Attachment);
131 }
132 if &tracker.meta.total_size != total_size {
133 return Err(EnigmaProtocolError::Attachment);
134 }
135 Ok(Some(AttachmentUpdate::End {
136 attachment_id: *attachment_id,
137 total_size: *total_size,
138 }))
139 }
140 _ => Ok(None),
141 }
142 }
143}