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