1use crate::{
4 error::{PhalanxError, Result},
5 identity::{Identity, PublicKey},
6 crypto::{SymmetricKey, EncryptedData, hash_multiple},
7};
8use ed25519_dalek::Signature;
9use bytes::Bytes;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12#[cfg(feature = "serde")]
13use serde::{Serialize, Deserialize};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub enum MessageType {
19 Text,
21 System,
23 KeyRotation,
25 MemberJoin,
27 MemberLeave,
29 Heartbeat,
31}
32
33#[derive(Debug, Clone)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub struct GroupMessage {
37 pub version: u8,
39 pub sender: PublicKey,
41 pub message_type: MessageType,
43 pub sequence: u64,
45 pub timestamp: u64,
47 pub encrypted_content: EncryptedData,
49 pub signature: Signature,
51 pub message_id: [u8; 32],
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58pub struct MessageContent {
59 pub data: Bytes,
61 pub reply_to: Option<[u8; 32]>,
63 pub thread_id: Option<[u8; 32]>,
65 pub metadata: std::collections::HashMap<String, String>,
67}
68
69#[derive(Debug, Clone)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72pub struct EncryptedMessage {
73 pub version: u8,
75 pub encrypted_data: EncryptedData,
77 pub sender_id: [u8; 32],
79 pub timestamp: u64,
81 pub sequence: u64,
83}
84
85impl GroupMessage {
86 pub fn new(
88 sender: &Identity,
89 message_type: MessageType,
90 content: &MessageContent,
91 sequence: u64,
92 group_key: &SymmetricKey,
93 ) -> Result<Self> {
94 let timestamp = SystemTime::now()
95 .duration_since(UNIX_EPOCH)
96 .map_err(|e| PhalanxError::crypto(format!("System time error: {}", e)))?
97 .as_secs();
98
99 let sender_public = sender.public_key();
100
101 let content_bytes = Self::serialize_content(content)?;
103
104 let aad = Self::create_aad(&sender_public, message_type.clone(), sequence, timestamp);
106
107 let encrypted_content = group_key.encrypt(&content_bytes, &aad)?;
109
110 let message_id = hash_multiple(&[
112 &sender_public.id(),
113 &sequence.to_be_bytes(),
114 ×tamp.to_be_bytes(),
115 &encrypted_content.ciphertext,
116 ]);
117
118 let signature_data = Self::create_signature_data(
120 &sender_public,
121 &message_type,
122 sequence,
123 timestamp,
124 &encrypted_content,
125 &message_id,
126 );
127
128 let signature = sender.sign(&signature_data);
130
131 Ok(Self {
132 version: crate::constants::PROTOCOL_VERSION,
133 sender: sender_public,
134 message_type,
135 sequence,
136 timestamp,
137 encrypted_content,
138 signature,
139 message_id,
140 })
141 }
142
143 pub fn decrypt(&self, group_key: &SymmetricKey) -> Result<MessageContent> {
145 self.verify_signature()?;
147
148 let aad = Self::create_aad(&self.sender, self.message_type.clone(), self.sequence, self.timestamp);
150
151 let decrypted_bytes = group_key.decrypt(&self.encrypted_content, &aad)?;
153
154 Self::deserialize_content(&decrypted_bytes)
156 }
157
158 pub fn verify_signature(&self) -> Result<()> {
160 let signature_data = Self::create_signature_data(
161 &self.sender,
162 &self.message_type,
163 self.sequence,
164 self.timestamp,
165 &self.encrypted_content,
166 &self.message_id,
167 );
168
169 self.sender.verify(&signature_data, &self.signature)
170 }
171
172 pub fn is_from(&self, public_key: &PublicKey) -> bool {
174 self.sender.id() == public_key.id()
175 }
176
177 pub fn age_seconds(&self) -> u64 {
179 SystemTime::now()
180 .duration_since(UNIX_EPOCH)
181 .map(|d| d.as_secs())
182 .unwrap_or(0)
183 .saturating_sub(self.timestamp)
184 }
185
186 fn create_aad(sender: &PublicKey, msg_type: MessageType, sequence: u64, timestamp: u64) -> Vec<u8> {
188 let mut aad = Vec::new();
189 aad.extend_from_slice(&sender.id());
190 aad.push(msg_type as u8);
191 aad.extend_from_slice(&sequence.to_be_bytes());
192 aad.extend_from_slice(×tamp.to_be_bytes());
193 aad.extend_from_slice(b"PHALANX_MSG_V1");
194 aad
195 }
196
197 fn create_signature_data(
199 sender: &PublicKey,
200 msg_type: &MessageType,
201 sequence: u64,
202 timestamp: u64,
203 encrypted_content: &EncryptedData,
204 message_id: &[u8; 32],
205 ) -> Vec<u8> {
206 let mut sig_data = Vec::new();
207 sig_data.push(crate::constants::PROTOCOL_VERSION);
208 sig_data.extend_from_slice(&sender.id());
209 sig_data.push(msg_type.clone() as u8);
210 sig_data.extend_from_slice(&sequence.to_be_bytes());
211 sig_data.extend_from_slice(×tamp.to_be_bytes());
212 sig_data.extend_from_slice(&encrypted_content.ciphertext);
213 sig_data.extend_from_slice(&encrypted_content.nonce);
214 sig_data.extend_from_slice(&encrypted_content.aad_hash);
215 sig_data.extend_from_slice(message_id);
216 sig_data.extend_from_slice(b"PHALANX_SIG_V1");
217 sig_data
218 }
219
220 #[cfg(feature = "serde")]
222 fn serialize_content(content: &MessageContent) -> Result<Vec<u8>> {
223 serde_json::to_vec(content)
224 .map_err(|e| PhalanxError::protocol(format!("Content serialization failed: {}", e)))
225 }
226
227 #[cfg(not(feature = "serde"))]
229 fn serialize_content(content: &MessageContent) -> Result<Vec<u8>> {
230 let mut bytes = Vec::new();
232
233 let data_len = content.data.len() as u32;
235 bytes.extend_from_slice(&data_len.to_be_bytes());
236 bytes.extend_from_slice(&content.data);
237
238 if let Some(reply_to) = &content.reply_to {
240 bytes.push(1); bytes.extend_from_slice(reply_to);
242 } else {
243 bytes.push(0); }
245
246 if let Some(thread_id) = &content.thread_id {
248 bytes.push(1); bytes.extend_from_slice(thread_id);
250 } else {
251 bytes.push(0); }
253
254 let metadata_str = format!("{:?}", content.metadata);
256 let metadata_bytes = metadata_str.as_bytes();
257 let metadata_len = metadata_bytes.len() as u32;
258 bytes.extend_from_slice(&metadata_len.to_be_bytes());
259 bytes.extend_from_slice(metadata_bytes);
260
261 Ok(bytes)
262 }
263
264 #[cfg(feature = "serde")]
266 fn deserialize_content(bytes: &[u8]) -> Result<MessageContent> {
267 serde_json::from_slice(bytes)
268 .map_err(|e| PhalanxError::protocol(format!("Content deserialization failed: {}", e)))
269 }
270
271 #[cfg(not(feature = "serde"))]
273 fn deserialize_content(bytes: &[u8]) -> Result<MessageContent> {
274 if bytes.len() < 4 {
275 return Err(PhalanxError::protocol("Invalid content format"));
276 }
277
278 let mut pos = 0;
279
280 let data_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize;
282 pos += 4;
283
284 if pos + data_len > bytes.len() {
285 return Err(PhalanxError::protocol("Invalid data length"));
286 }
287
288 let data = Bytes::copy_from_slice(&bytes[pos..pos + data_len]);
289 pos += data_len;
290
291 if pos >= bytes.len() {
293 return Err(PhalanxError::protocol("Truncated content"));
294 }
295
296 let reply_to = if bytes[pos] == 1 {
297 pos += 1;
298 if pos + 32 > bytes.len() {
299 return Err(PhalanxError::protocol("Invalid reply-to"));
300 }
301 let mut reply_bytes = [0u8; 32];
302 reply_bytes.copy_from_slice(&bytes[pos..pos + 32]);
303 pos += 32;
304 Some(reply_bytes)
305 } else {
306 pos += 1;
307 None
308 };
309
310 if pos >= bytes.len() {
312 return Err(PhalanxError::protocol("Truncated content"));
313 }
314
315 let thread_id = if bytes[pos] == 1 {
316 pos += 1;
317 if pos + 32 > bytes.len() {
318 return Err(PhalanxError::protocol("Invalid thread ID"));
319 }
320 let mut thread_bytes = [0u8; 32];
321 thread_bytes.copy_from_slice(&bytes[pos..pos + 32]);
322 pos += 32;
323 Some(thread_bytes)
324 } else {
325 pos += 1;
326 None
327 };
328
329 if pos + 4 > bytes.len() {
331 return Err(PhalanxError::protocol("Truncated metadata length"));
332 }
333
334 let metadata_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize;
335 pos += 4;
336
337 if pos + metadata_len > bytes.len() {
338 return Err(PhalanxError::protocol("Truncated metadata"));
339 }
340
341 let metadata = std::collections::HashMap::new();
343
344 Ok(MessageContent {
345 data,
346 reply_to,
347 thread_id,
348 metadata,
349 })
350 }
351}
352
353impl MessageContent {
354 pub fn text(message: impl Into<String>) -> Self {
356 Self {
357 data: Bytes::from(message.into()),
358 reply_to: None,
359 thread_id: None,
360 metadata: std::collections::HashMap::new(),
361 }
362 }
363
364 pub fn reply(message: impl Into<String>, reply_to: [u8; 32]) -> Self {
366 Self {
367 data: Bytes::from(message.into()),
368 reply_to: Some(reply_to),
369 thread_id: None,
370 metadata: std::collections::HashMap::new(),
371 }
372 }
373
374 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
376 self.metadata.insert(key.into(), value.into());
377 self
378 }
379
380 pub fn with_thread(mut self, thread_id: [u8; 32]) -> Self {
382 self.thread_id = Some(thread_id);
383 self
384 }
385
386 pub fn as_string(&self) -> Result<String> {
388 String::from_utf8(self.data.to_vec())
389 .map_err(|e| PhalanxError::protocol(format!("Invalid UTF-8: {}", e)))
390 }
391}
392
393impl From<MessageType> for u8 {
395 fn from(msg_type: MessageType) -> u8 {
396 match msg_type {
397 MessageType::Text => 0,
398 MessageType::System => 1,
399 MessageType::KeyRotation => 2,
400 MessageType::MemberJoin => 3,
401 MessageType::MemberLeave => 4,
402 MessageType::Heartbeat => 5,
403 }
404 }
405}
406
407impl TryFrom<u8> for MessageType {
408 type Error = PhalanxError;
409
410 fn try_from(value: u8) -> Result<Self> {
411 match value {
412 0 => Ok(MessageType::Text),
413 1 => Ok(MessageType::System),
414 2 => Ok(MessageType::KeyRotation),
415 3 => Ok(MessageType::MemberJoin),
416 4 => Ok(MessageType::MemberLeave),
417 5 => Ok(MessageType::Heartbeat),
418 _ => Err(PhalanxError::protocol(format!("Unknown message type: {}", value))),
419 }
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426 use crate::crypto::SymmetricKey;
427
428 #[test]
429 fn test_message_creation_and_decryption() {
430 let sender = Identity::generate();
431 let group_key = SymmetricKey::generate();
432 let content = MessageContent::text("Hello, world!");
433
434 let message = GroupMessage::new(
435 &sender,
436 MessageType::Text,
437 &content,
438 1,
439 &group_key,
440 ).unwrap();
441
442 let decrypted = message.decrypt(&group_key).unwrap();
443 assert_eq!(decrypted.as_string().unwrap(), "Hello, world!");
444 }
445
446 #[test]
447 fn test_message_signature_verification() {
448 let sender = Identity::generate();
449 let group_key = SymmetricKey::generate();
450 let content = MessageContent::text("Test message");
451
452 let message = GroupMessage::new(
453 &sender,
454 MessageType::Text,
455 &content,
456 1,
457 &group_key,
458 ).unwrap();
459
460 assert!(message.verify_signature().is_ok());
461 }
462
463 #[test]
464 fn test_reply_messages() {
465 let sender = Identity::generate();
466 let group_key = SymmetricKey::generate();
467
468 let original_id = [1u8; 32];
469 let reply_content = MessageContent::reply("This is a reply", original_id);
470
471 let message = GroupMessage::new(
472 &sender,
473 MessageType::Text,
474 &reply_content,
475 1,
476 &group_key,
477 ).unwrap();
478
479 let decrypted = message.decrypt(&group_key).unwrap();
480 assert_eq!(decrypted.reply_to, Some(original_id));
481 assert_eq!(decrypted.as_string().unwrap(), "This is a reply");
482 }
483}