/ otp-chat-client / src / main.rs
main.rs
  1  use std::{
  2      fs::File,
  3      io::{stdin, stdout, Read, Write},
  4      path::{Path, PathBuf},
  5      sync::Arc,
  6  };
  7  use anyhow::{Context, Result};
  8  use serde::{Deserialize, Serialize};
  9  use tokio::{
 10      io::{AsyncReadExt, AsyncWriteExt},
 11      net::TcpStream,
 12      sync::Mutex,
 13  };
 14  use num_bigint::BigUint;
 15  use num_traits::Zero;
 16  
 17  // True information-theoretic authentication parameters
 18  const HASH_KEY_A_SIZE: usize = 32;
 19  const HASH_KEY_B_SIZE: usize = 32;
 20  const ONE_TIME_MASK_SIZE: usize = 32;
 21  const AUTH_TAG_SIZE: usize = 32;
 22  const TOTAL_AUTH_OVERHEAD: usize = HASH_KEY_A_SIZE + HASH_KEY_B_SIZE + ONE_TIME_MASK_SIZE;
 23  
 24  // Message type indicators
 25  const MSG_TYPE_TEXT: u8 = 0x01;
 26  const MSG_TYPE_FILE: u8 = 0x02;
 27  
 28  #[derive(Serialize, Deserialize, Debug)]
 29  struct State {
 30      offset: usize,
 31  }
 32  
 33  fn load_otp(path: &Path) -> Result<Vec<u8>> {
 34      let mut f = File::open(path)
 35          .with_context(|| format!("failed to open otp file at '{}'", path.display()))?;
 36      let mut buf = Vec::new();
 37      f.read_to_end(&mut buf).context("failed to read otp file")?;
 38      println!("๐Ÿ” Loaded OTP file: {} bytes", buf.len());
 39      Ok(buf)
 40  }
 41  
 42  fn save_state_file(path: &Path, state: &State) -> Result<()> {
 43      let tmp = path.with_extension("state.tmp");
 44      let s = serde_json::to_string_pretty(state).context("serializing state")?;
 45      let mut f = File::create(&tmp).context("creating temp state file")?;
 46      f.write_all(s.as_bytes()).context("writing temp state file")?;
 47      f.sync_all().context("flushing temp state file")?;
 48      std::fs::rename(&tmp, path).context("rename state tmp -> state")?;
 49      Ok(())
 50  }
 51  
 52  fn load_or_create_state(path: &Path) -> Result<State> {
 53      if path.exists() {
 54          let s = std::fs::read_to_string(path).context("reading state file")?;
 55          let st: State = serde_json::from_str(&s).context("parsing state.json")?;
 56          println!("๐Ÿ“Š Loaded state: offset = {}", st.offset);
 57          Ok(st)
 58      } else {
 59          let st = State { offset: 0 };
 60          save_state_file(path, &st)?;
 61          println!("๐Ÿ“Š Created new state file");
 62          Ok(st)
 63      }
 64  }
 65  
 66  fn universal_hash(message: &[u8], key_a: &[u8], key_b: &[u8]) -> Result<Vec<u8>> {
 67      let p_hex = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43";
 68      let p = BigUint::parse_bytes(p_hex.as_bytes(), 16)
 69          .ok_or_else(|| anyhow::anyhow!("Failed to parse prime constant"))?;
 70      
 71      let a = BigUint::from_bytes_be(key_a);
 72      let b = BigUint::from_bytes_be(key_b);
 73      
 74      let mut encoded = Vec::with_capacity(8 + message.len());
 75      encoded.extend_from_slice(&(message.len() as u64).to_be_bytes());
 76      encoded.extend_from_slice(message);
 77      
 78      let m = if encoded.is_empty() {
 79          BigUint::zero()
 80      } else {
 81          BigUint::from_bytes_be(&encoded)
 82      };
 83      
 84      let am = &a * &m;
 85      let am_plus_b = am + &b;
 86      let hash_mod_p = am_plus_b % &p;
 87      
 88      let result_bytes = hash_mod_p.to_bytes_be();
 89      
 90      let mut padded_result = vec![0u8; 32];
 91      let start_idx = if result_bytes.len() >= 32 {
 92          0
 93      } else {
 94          32 - result_bytes.len()
 95      };
 96      
 97      let copy_len = std::cmp::min(result_bytes.len(), 32);
 98      padded_result[start_idx..start_idx + copy_len]
 99          .copy_from_slice(&result_bytes[result_bytes.len() - copy_len..]);
100      
101      Ok(padded_result)
102  }
103  
104  fn otp_encrypt_with_it_auth(message: &[u8], otp: &[u8], offset: usize) -> Result<Vec<u8>> {
105      let total_needed = TOTAL_AUTH_OVERHEAD + message.len();
106      if offset + total_needed > otp.len() {
107          anyhow::bail!(
108              "OTP exhausted: need {} bytes (offset {} + auth_overhead {} + message {}), available {}",
109              total_needed, offset, TOTAL_AUTH_OVERHEAD, message.len(), otp.len()
110          );
111      }
112      
113      let hash_key_a = &otp[offset..offset + HASH_KEY_A_SIZE];
114      let hash_key_b = &otp[offset + HASH_KEY_A_SIZE..offset + HASH_KEY_A_SIZE + HASH_KEY_B_SIZE];
115      
116      let mask_start = offset + HASH_KEY_A_SIZE + HASH_KEY_B_SIZE;
117      let one_time_mask = &otp[mask_start..mask_start + ONE_TIME_MASK_SIZE];
118      
119      let enc_key_start = offset + TOTAL_AUTH_OVERHEAD;
120      let enc_key = &otp[enc_key_start..enc_key_start + message.len()];
121      
122      let ciphertext: Vec<u8> = message.iter()
123          .zip(enc_key.iter())
124          .map(|(m, k)| m ^ k)
125          .collect();
126      
127      let hash_result = universal_hash(&ciphertext, hash_key_a, hash_key_b)?;
128      
129      let mut auth_tag = [0u8; AUTH_TAG_SIZE];
130      for i in 0..AUTH_TAG_SIZE {
131          auth_tag[i] = hash_result[i] ^ one_time_mask[i];
132      }
133      
134      let mut result = ciphertext;
135      result.extend_from_slice(&auth_tag);
136      Ok(result)
137  }
138  
139  fn otp_decrypt_with_it_auth(encrypted: &[u8], otp: &[u8], offset: usize) -> Result<Vec<u8>> {
140      if encrypted.len() < AUTH_TAG_SIZE {
141          anyhow::bail!("Message too short for authentication tag");
142      }
143      
144      let (ciphertext, received_tag) = encrypted.split_at(encrypted.len() - AUTH_TAG_SIZE);
145      let total_needed = TOTAL_AUTH_OVERHEAD + ciphertext.len();
146      if offset + total_needed > otp.len() {
147          anyhow::bail!(
148              "OTP exhausted during decryption: need {} bytes, available {}",
149              total_needed, otp.len() - offset
150          );
151      }
152      
153      let hash_key_a = &otp[offset..offset + HASH_KEY_A_SIZE];
154      let hash_key_b = &otp[offset + HASH_KEY_A_SIZE..offset + HASH_KEY_A_SIZE + HASH_KEY_B_SIZE];
155      
156      let mask_start = offset + HASH_KEY_A_SIZE + HASH_KEY_B_SIZE;
157      let one_time_mask = &otp[mask_start..mask_start + ONE_TIME_MASK_SIZE];
158      
159      let hash_result = universal_hash(ciphertext, hash_key_a, hash_key_b)?;
160      
161      let mut expected_tag = [0u8; AUTH_TAG_SIZE];
162      for i in 0..AUTH_TAG_SIZE {
163          expected_tag[i] = hash_result[i] ^ one_time_mask[i];
164      }
165      
166      if expected_tag.as_slice() != received_tag {
167          anyhow::bail!("Information-theoretic authentication failed - message may be corrupted or forged");
168      }
169      
170      let dec_key_start = offset + TOTAL_AUTH_OVERHEAD;
171      let dec_key = &otp[dec_key_start..dec_key_start + ciphertext.len()];
172      
173      let plaintext: Vec<u8> = ciphertext.iter()
174          .zip(dec_key.iter())
175          .map(|(c, k)| c ^ k)
176          .collect();
177      Ok(plaintext)
178  }
179  
180  fn format_size(bytes: usize) -> String {
181      if bytes < 1024 {
182          format!("{} B", bytes)
183      } else if bytes < 1024 * 1024 {
184          format!("{:.2} KB", bytes as f64 / 1024.0)
185      } else if bytes < 1024 * 1024 * 1024 {
186          format!("{:.2} MB", bytes as f64 / (1024.0 * 1024.0))
187      } else {
188          format!("{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
189      }
190  }
191  
192  #[tokio::main]
193  async fn main() -> Result<()> {
194      println!("๐Ÿ” Pure OTP Chat Client - True Information-Theoretic Edition");
195      println!("โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—");
196      println!("๐Ÿ›ก๏ธ  Perfect Secrecy + Information-Theoretic Authentication");
197      println!("๐Ÿ”’ Encryption: One-Time Pad (XOR cipher)");
198      println!("๐Ÿ” Authentication: Universal Hash + Wegman-Carter Construction");
199      println!("๐Ÿ“Š Each message consumes: message_length + 96 bytes");
200      println!("๐Ÿงฎ Universal hash family: h(a,b)(m) = ((aยทm + b) mod p) mod 2^256");
201      println!("๐Ÿ”ง Fixed: Length-prefixed encoding ensures ฮต-ASUโ‚‚ security");
202      println!("โœ… CORRECTED: Using actual prime p = 2^256 - 189");
203      println!("๐Ÿ‘‘ MODERATION: Gentlemen's Agreement Protocol Enabled");
204      println!("๐Ÿ›ก๏ธ SECURITY: Enhanced input validation and error handling");
205      println!("๐Ÿ“Ž FILE TRANSFER: Send encrypted & authenticated files with !file command");
206      println!("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n");
207  
208      print!("๐Ÿ‘ค Enter your name: ");
209      stdout().flush()?;
210      let mut name = String::new();
211      stdin().read_line(&mut name)?;
212      let name = name.trim().to_string();
213      if name.is_empty() {
214          anyhow::bail!("Name cannot be empty");
215      }
216  
217      let otp_path: PathBuf = loop {
218          print!("๐Ÿ—๏ธ  Enter full path to your OTP file: ");
219          stdout().flush()?;
220          let mut otp_path = String::new();
221          stdin().read_line(&mut otp_path)?;
222          let otp_path = otp_path.trim();
223          let p = PathBuf::from(otp_path);
224          match load_otp(&p) {
225              Ok(buf) => {
226                  let estimated_messages = buf.len() / 150;
227                  println!("โœ… OTP loaded successfully");
228                  println!("๐Ÿ“ˆ Estimated capacity: ~{} average messages", estimated_messages);
229                  println!("โš ๏ธ  Visit Enkryp");
230                  println!("๐Ÿงฎ Using universal hash family with Wegman-Carter for IT authentication");
231                  println!("โœ… FIXED: Now using correct prime p = 2^256 - 189\n");
232                  break p;
233              }
234              Err(e) => {
235                  eprintln!("โŒ {}", e);
236                  continue;
237              }
238          }
239      };
240  
241      let otp = load_otp(&otp_path).context("loading otp")?;
242      let otp_dir = otp_path.parent().map(Path::to_path_buf).unwrap_or_else(|| PathBuf::from("."));
243      let state_path = otp_dir.join("state.json");
244      let initial_state = load_or_create_state(&state_path).context("prepare state")?;
245      let state = Arc::new(Mutex::new(initial_state));
246  
247      // Create received_files directory
248      let received_files_dir = otp_dir.join("received_files");
249      std::fs::create_dir_all(&received_files_dir).context("creating received_files directory")?;
250      println!("๐Ÿ“ Files will be saved to: {}", received_files_dir.display());
251  
252      print!("๐ŸŒ Enter server address (default: 127.0.0.1): ");
253      stdout().flush()?;
254      let mut server_input = String::new();
255      stdin().read_line(&mut server_input)?;
256      let server_addr = server_input.trim();
257      let server_addr = if server_addr.is_empty() {
258          "127.0.0.1"
259      } else {
260          server_addr
261      };
262  
263      let otp_addr = format!("{}:8080", server_addr);
264      let stream = TcpStream::connect(&otp_addr)
265          .await
266          .with_context(|| format!("Failed to connect to OTP server at {}", otp_addr))?;
267      println!("๐ŸŒ Connected to OTP server at {}", otp_addr);
268  
269      let mod_addr = format!("{}:8081", server_addr);
270      let mod_stream = TcpStream::connect(&mod_addr).await
271          .with_context(|| format!("Failed to connect to moderation server at {}", mod_addr))?;
272      println!("๐Ÿ‘ฅ Connected to moderation server at {}\n", mod_addr);
273      
274      let (mut mod_reader, mut mod_writer) = mod_stream.into_split();
275      
276      mod_writer.write_all(format!("{}\n", name).as_bytes()).await?;
277  
278      let is_moderator = Arc::new(Mutex::new(false));
279      let can_speak = Arc::new(Mutex::new(false));
280      let mod_is_moderator = Arc::clone(&is_moderator);
281      let mod_can_speak = Arc::clone(&can_speak);
282      let name_for_mod = name.clone();
283  
284      tokio::spawn(async move {
285          let mut buf = [0; 512];
286          loop {
287              match mod_reader.read(&mut buf).await {
288                  Ok(n) if n > 0 => {
289                      let received = String::from_utf8_lossy(&buf[..n]);
290                      
291                      for line in received.lines() {
292                          let msg = line.trim();
293                          if msg.is_empty() {
294                              continue;
295                          }
296                          
297                          if msg.starts_with("MOD:") {
298                              let parts: Vec<&str> = msg.splitn(2, ':').collect();
299                              if parts.len() == 2 {
300                                  let mod_name = parts[1];
301                                  println!("๐Ÿ‘‘ {} is now the moderator", mod_name);
302                                  let is_me_moderator = mod_name == name_for_mod;
303                                  *mod_is_moderator.lock().await = is_me_moderator;
304                                  if is_me_moderator {
305                                      *mod_can_speak.lock().await = true;
306                                      println!("๐Ÿ‘‘ You are the moderator! You can grant/deny speaking permissions.");
307                                      println!("๐Ÿ’ก Commands: 'grant <username>' or 'deny <username>'");
308                                  }
309                              }
310                          } else if msg.starts_with("REQ:") {
311                              let parts: Vec<&str> = msg.splitn(2, ':').collect();
312                              if parts.len() == 2 {
313                                  let requester = parts[1];
314                                  if *mod_is_moderator.lock().await {
315                                      println!("๐Ÿ™‹ {} requests permission to speak. Grant? (grant {} / deny {})", 
316                                          requester, requester, requester);
317                                  }
318                              }
319                          } else if msg.starts_with("GRANTED:") {
320                              let parts: Vec<&str> = msg.splitn(2, ':').collect();
321                              if parts.len() == 2 {
322                                  let granted_user = parts[1];
323                                  
324                                  if granted_user.to_lowercase() == name_for_mod.to_lowercase() {
325                                      *mod_can_speak.lock().await = true;
326                                      println!("โœ… You have been granted permission to speak!");
327                                  } else {
328                                      println!("โœ… {} has been granted permission to speak", granted_user);
329                                  }
330                              }
331                          } else if msg.starts_with("DENIED:") {
332                              let parts: Vec<&str> = msg.splitn(2, ':').collect();
333                              if parts.len() == 2 {
334                                  let denied_user = parts[1];
335                                  
336                                  if denied_user.to_lowercase() == name_for_mod.to_lowercase() {
337                                      *mod_can_speak.lock().await = false;
338                                      println!("โŒ Your request to speak has been denied");
339                                  } else {
340                                      println!("โŒ {} has been denied permission to speak", denied_user);
341                                  }
342                              }
343                          } else if msg.starts_with("๐Ÿ‘ฅ") {
344                              println!("{}", msg);
345                          } else if !msg.is_empty() {
346                              println!("{}", msg);
347                          }
348                      }
349                  },
350                  Ok(_) => {
351                      eprintln!("๐Ÿ”Œ Moderation server disconnected");
352                      break;
353                  }
354                  Err(e) => {
355                      eprintln!("๐Ÿ”Œ Moderation connection error: {}", e);
356                      break;
357                  }
358              }
359          }
360      });
361  
362      let (mut reader, mut writer) = stream.into_split();
363      let otp_recv = otp.clone();
364      let state_recv = Arc::clone(&state);
365      let state_path_recv = state_path.clone();
366      let received_files_dir_clone = received_files_dir.clone();
367  
368      tokio::spawn(async move {
369          loop {
370              let mut header = [0u8; 4];
371              if let Err(e) = reader.read_exact(&mut header).await {
372                  eprintln!("๐Ÿ”Œ Connection lost: {}", e);
373                  break;
374              }
375              let len = u32::from_be_bytes(header) as usize;
376              
377              let mut encrypted = vec![0u8; len];
378              if let Err(e) = reader.read_exact(&mut encrypted).await {
379                  eprintln!("โŒ Failed to read message payload: {}", e);
380                  break;
381              }
382  
383              let mut st = state_recv.lock().await;
384              match otp_decrypt_with_it_auth(&encrypted, &otp_recv, st.offset) {
385                  Ok(plaintext) => {
386                      let message_len = encrypted.len() - AUTH_TAG_SIZE;
387                      st.offset += TOTAL_AUTH_OVERHEAD + message_len;
388                      
389                      if let Err(e) = save_state_file(&state_path_recv, &st) {
390                          eprintln!("โš ๏ธ  Failed to save state: {}", e);
391                      }
392                      
393                      // Check message type (first byte)
394                      if plaintext.is_empty() {
395                          eprintln!("โŒ Received empty message");
396                          continue;
397                      }
398                      
399                      let msg_type = plaintext[0];
400                      let payload = &plaintext[1..];
401                      
402                      match msg_type {
403                          MSG_TYPE_TEXT => {
404                              match String::from_utf8(payload.to_vec()) {
405                                  Ok(text) => println!("๐Ÿ’ฌ {}", text),
406                                  Err(_) => eprintln!("โŒ Received non-UTF8 message"),
407                              }
408                          }
409                          MSG_TYPE_FILE => {
410                              // File format: filename_len (4 bytes) + filename + file_data
411                              if payload.len() < 4 {
412                                  eprintln!("โŒ Invalid file message format");
413                                  continue;
414                              }
415                              
416                              let filename_len = u32::from_be_bytes([
417                                  payload[0], payload[1], payload[2], payload[3]
418                              ]) as usize;
419                              
420                              if payload.len() < 4 + filename_len {
421                                  eprintln!("โŒ Invalid file message: filename truncated");
422                                  continue;
423                              }
424                              
425                              let filename_bytes = &payload[4..4 + filename_len];
426                              let file_data = &payload[4 + filename_len..];
427                              
428                              match String::from_utf8(filename_bytes.to_vec()) {
429                                  Ok(filename) => {
430                                      let save_path = received_files_dir_clone.join(&filename);
431                                      match std::fs::write(&save_path, file_data) {
432                                          Ok(_) => {
433                                              println!("๐Ÿ“Ž File received: {} ({}) -> {}", 
434                                                  filename, 
435                                                  format_size(file_data.len()),
436                                                  save_path.display()
437                                              );
438                                          }
439                                          Err(e) => {
440                                              eprintln!("โŒ Failed to save file '{}': {}", filename, e);
441                                          }
442                                      }
443                                  }
444                                  Err(_) => eprintln!("โŒ Invalid filename encoding"),
445                              }
446                          }
447                          _ => {
448                              eprintln!("โŒ Unknown message type: 0x{:02X}", msg_type);
449                          }
450                      }
451                  }
452                  Err(e) => eprintln!("๐Ÿ›ก๏ธ  Message rejected: {}", e),
453              }
454          }
455      });
456  
457      println!("โœ… Ready to chat! Type your messages below:");
458      println!("๐Ÿ‘‘ Moderation: First user becomes moderator automatically");
459      println!("๐Ÿ™‹ Others: Type 'request' to ask for permission to speak");
460      println!("๐Ÿ“Ž File transfer: Type '!file /path/to/file.ext' to send a file");
461      println!("๐Ÿ’ก Each message encrypted with OTP + authenticated with universal hash");
462      println!("๐Ÿ”ฌ Information-theoretic security: unbreakable even with quantum computers");
463      println!("๐Ÿงฎ Authentication uses universal hash family with Wegman-Carter construction");
464      println!("โœ… CORRECTED: Now using mathematically correct prime p = 2^256 - 189");
465      println!("๐Ÿ›ก๏ธ SECURITY: Enhanced with input validation and proper error handling");
466      println!("โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—\n");
467  
468      loop {
469          print!("๐Ÿ’ญ > ");
470          stdout().flush()?;
471          let mut input = String::new();
472          stdin().read_line(&mut input)?;
473          let input = input.trim_end();
474          if input.is_empty() {
475              continue;
476          }
477  
478          // Handle moderation commands
479          if input == "request" {
480              if !*can_speak.lock().await && !*is_moderator.lock().await {
481                  mod_writer.write_all(format!("REQUEST:{}\n", name).as_bytes()).await?;
482                  println!("๐Ÿ™‹ Permission to speak requested...");
483              } else {
484                  println!("๐Ÿ’ก You already have permission to speak or are the moderator");
485              }
486              continue;
487          }
488  
489          if input.starts_with("grant ") || input.starts_with("deny ") {
490              if *is_moderator.lock().await {
491                  mod_writer.write_all(format!("{}\n", input.to_uppercase()).as_bytes()).await?;
492              } else {
493                  println!("โŒ Only the moderator can grant or deny permissions");
494              }
495              continue;
496          }
497  
498          // Handle file sending
499          if input.starts_with("!file ") {
500              let can_speak_val = *can_speak.lock().await;
501              let is_moderator_val = *is_moderator.lock().await;
502              
503              if !can_speak_val && !is_moderator_val {
504                  println!("๐Ÿ”‡ You need permission to speak. Type 'request' to ask the moderator.");
505                  continue;
506              }
507  
508              let file_path_str = input.trim_start_matches("!file ").trim();
509              let file_path = PathBuf::from(file_path_str);
510              
511              if !file_path.exists() {
512                  eprintln!("โŒ File not found: {}", file_path.display());
513                  continue;
514              }
515              
516              if !file_path.is_file() {
517                  eprintln!("โŒ Path is not a file: {}", file_path.display());
518                  continue;
519              }
520              
521              println!("๐Ÿ“‚ Reading file: {}", file_path.display());
522              
523              let file_data = match std::fs::read(&file_path) {
524                  Ok(data) => data,
525                  Err(e) => {
526                      eprintln!("โŒ Failed to read file: {}", e);
527                      continue;
528                  }
529              };
530              
531              let filename = file_path.file_name()
532                  .and_then(|n| n.to_str())
533                  .unwrap_or("unknown_file")
534                  .to_string();
535              
536              println!("๐Ÿ” Encrypting file: {} ({})", filename, format_size(file_data.len()));
537              
538              // Construct file message: MSG_TYPE_FILE + filename_len + filename + file_data
539              let filename_bytes = filename.as_bytes();
540              let filename_len = filename_bytes.len() as u32;
541              
542              let mut file_message = Vec::with_capacity(1 + 4 + filename_bytes.len() + file_data.len());
543              file_message.push(MSG_TYPE_FILE);
544              file_message.extend_from_slice(&filename_len.to_be_bytes());
545              file_message.extend_from_slice(filename_bytes);
546              file_message.extend_from_slice(&file_data);
547              
548              let mut st = state.lock().await;
549              let total_consumption = TOTAL_AUTH_OVERHEAD + file_message.len();
550              
551              if st.offset + total_consumption > otp.len() {
552                  eprintln!("\nโŒ OTP EXHAUSTED!");
553                  eprintln!("๐Ÿ“Š Need: {} bytes (current offset: {}, file: {} + auth: {})",
554                      total_consumption, st.offset, file_message.len(), TOTAL_AUTH_OVERHEAD);
555                  eprintln!("๐Ÿ“Š Available: {} bytes", otp.len());
556                  eprintln!("๐Ÿ”„ Please generate a fresh OTP file to continue.\n");
557                  break;
558              }
559  
560              print!("๐Ÿ”’ Encrypting... ");
561              stdout().flush()?;
562              
563              match otp_encrypt_with_it_auth(&file_message, &otp, st.offset) {
564                  Ok(encrypted) => {
565                      println!("โœ…");
566                      print!("๐Ÿ“ค Sending file ({})... ", format_size(encrypted.len()));
567                      stdout().flush()?;
568                      
569                      let len_be = (encrypted.len() as u32).to_be_bytes();
570                      if let Err(e) = writer.write_all(&len_be).await {
571                          eprintln!("โŒ Failed to send header: {}", e);
572                          break;
573                      }
574                      if let Err(e) = writer.write_all(&encrypted).await {
575                          eprintln!("โŒ Failed to send file: {}", e);
576                          break;
577                      }
578                      
579                      println!("โœ…");
580                      println!("๐Ÿ“Ž File '{}' sent successfully!", filename);
581                      
582                      st.offset += total_consumption;
583                      if let Err(e) = save_state_file(&state_path, &st) {
584                          eprintln!("โš ๏ธ  Failed to save state: {}", e);
585                      }
586                      
587                      let remaining = otp.len() - st.offset;
588                      println!("๐Ÿ“Š OTP consumed: {} | Remaining: {}", 
589                          format_size(total_consumption), 
590                          format_size(remaining)
591                      );
592                      
593                      if remaining < 10000 {
594                          eprintln!("โš ๏ธ  OTP running low: {} bytes remaining", remaining);
595                      }
596                  }
597                  Err(e) => {
598                      eprintln!("โŒ Encryption failed: {}", e);
599                  }
600              }
601              
602              continue;
603          }
604  
605          // Regular text message
606          let can_speak_val = *can_speak.lock().await;
607          let is_moderator_val = *is_moderator.lock().await;
608          
609          if !can_speak_val && !is_moderator_val {
610              println!("๐Ÿ”‡ You need permission to speak. Type 'request' to ask the moderator.");
611              continue;
612          }
613  
614          let msg = format!("{}: {}", name, input);
615          
616          // Construct text message: MSG_TYPE_TEXT + message_text
617          let mut text_message = Vec::with_capacity(1 + msg.len());
618          text_message.push(MSG_TYPE_TEXT);
619          text_message.extend_from_slice(msg.as_bytes());
620          
621          let mut st = state.lock().await;
622          let total_consumption = TOTAL_AUTH_OVERHEAD + text_message.len();
623          
624          if st.offset + total_consumption > otp.len() {
625              eprintln!("\nโŒ OTP EXHAUSTED!");
626              eprintln!("๐Ÿ“Š Need: {} bytes (current offset: {}, message: {} + auth: {})",
627                  total_consumption, st.offset, text_message.len(), TOTAL_AUTH_OVERHEAD);
628              eprintln!("๐Ÿ“Š Available: {} bytes", otp.len());
629              eprintln!("๐Ÿ”„ Please generate a fresh OTP file to continue chatting.\n");
630              break;
631          }
632  
633          match otp_encrypt_with_it_auth(&text_message, &otp, st.offset) {
634              Ok(encrypted) => {
635                  let len_be = (encrypted.len() as u32).to_be_bytes();
636                  if let Err(e) = writer.write_all(&len_be).await {
637                      eprintln!("โŒ Failed to send header: {}", e);
638                      break;
639                  }
640                  if let Err(e) = writer.write_all(&encrypted).await {
641                      eprintln!("โŒ Failed to send message: {}", e);
642                      break;
643                  }
644                  st.offset += total_consumption;
645                  if let Err(e) = save_state_file(&state_path, &st) {
646                      eprintln!("โš ๏ธ  Failed to save state: {}", e);
647                  }
648                  
649                  let remaining = otp.len() - st.offset;
650                  if remaining < 1000 {
651                      eprintln!("โš ๏ธ  OTP running low: {} bytes remaining", remaining);
652                  }
653              }
654              Err(e) => {
655                  eprintln!("โŒ Encryption failed: {}", e);
656                  break;
657              }
658          }
659      }
660  
661      println!("๐Ÿ‘‹ Chat session ended.");
662      Ok(())
663  }
664