/ client / src / main.rs
main.rs
  1  mod gui;
  2  mod key_operations;
  3  mod network_operations;
  4  mod key_exchange;
  5  mod authentication;
  6  mod encryption;
  7  use gui::create_rocket;
  8  use gui::MessagingApp;
  9  use key_operations::key_operations_dilithium;
 10  use key_operations::key_operations_eddsa;
 11  use network_operations::create_client_with_proxy;
 12  use network_operations::fetch_kyber_pubkey;
 13  use network_operations::fetch_dilithium_pubkeys;
 14  use network_operations::fetch_eddsa_pubkeys;
 15  use network_operations::fetch_ciphertext;
 16  use network_operations::send_kyber_pubkey;
 17  use network_operations::send_dilithium_pubkey;
 18  use network_operations::send_eddsa_pubkey;
 19  use network_operations::send_ciphertext;
 20  use network_operations::send_encrypted_message;
 21  use network_operations::receive_and_fetch_messages;
 22  use key_exchange::kyber_key_exchange;
 23  use key_exchange::perform_ecdh_key_exchange;
 24  use authentication::sign_data_with_dilithium;
 25  use authentication::sign_data_with_eddsa;
 26  use authentication::verify_signature_with_dilithium;
 27  use authentication::verify_signature_with_eddsa;
 28  use encryption::derive_salt_from_password;
 29  use encryption::derive_key;
 30  use encryption::combine_shared_secrets;
 31  use encryption::encrypt_data;
 32  use encryption::decrypt_data;
 33  
 34  use oqs::*;
 35  use oqs::sig::{Sig, PublicKey, SecretKey, Algorithm as SigAlgorithm};
 36  use rand::Rng;
 37  use reqwest::blocking::get;
 38  use std::fs;
 39  use std::fs::File;
 40  use std::path::Path;
 41  use std::process::Command;
 42  use std::str::FromStr;
 43  use hex;
 44  use std::io::{self, Write};
 45  use rpassword::read_password;
 46  use std::result::Result;
 47  use std::{
 48      collections::HashSet,
 49      error::Error,
 50  };
 51  use ipnetwork::IpNetwork;
 52  use serde::{Deserialize, Serialize};
 53  use chacha20poly1305::aead::OsRng;
 54  use rand::RngCore;
 55  use sha3::{Sha3_512, Digest};
 56  use ed25519_dalek::VerifyingKey as Ed25519PublicKey;
 57  
 58  // Function to get the raw bytes from PublicKey
 59  fn get_raw_bytes_public_key(pk: &PublicKey) -> &[u8] {
 60      pk.as_ref() // Directly return the raw bytes
 61  }
 62  
 63  // Function to get the raw bytes from SecretKey
 64  fn get_raw_bytes_secret_key(sk: &SecretKey) -> &[u8] {
 65      sk.as_ref() // Directly return the raw bytes
 66  }
 67  
 68  
 69  #[derive(Serialize, Deserialize, Debug)] // Make sure it can be serialized and deserialized
 70  struct MessageData {
 71      message: String,
 72      room_id: String,
 73  }
 74  
 75  fn fingerprint_dilithium_public_key(public_key: &PublicKey) -> String {
 76      // Access the raw bytes of the public key using as_ref()
 77      let raw_bytes = public_key.as_ref(); // This should return &[u8]
 78      let hashed = Sha3_512::digest(raw_bytes);
 79      hex::encode(hashed)
 80  }
 81  
 82  fn fingerprint_eddsa_public_key(public_key: &Ed25519PublicKey) -> String {
 83      // Hash the public key to generate a fingerprint (using SHA-512)
 84      let hashed = Sha3_512::digest(public_key);
 85      hex::encode(hashed)
 86  }
 87  
 88  fn request_user_confirmation(fingerprint: &str, own_fingerprint: &str) -> Result<bool, io::Error> {
 89      // If the fingerprint matches your own public key, auto-confirm
 90      if fingerprint == own_fingerprint {
 91          return Ok(true);
 92      }
 93  
 94      println!("The fingerprint of the received public key is: {}", fingerprint);
 95      print!("Do you confirm this fingerprint? (yes/no): ");
 96      io::stdout().flush()?;
 97      
 98      let mut input = String::new();
 99      io::stdin().read_line(&mut input)?;
100      let response = input.trim().to_lowercase();
101      
102      match response.as_str() {
103          "yes" => Ok(true),
104          "no" => Ok(false),
105          _ => {
106              println!("Invalid input. Please enter 'yes' or 'no'.");
107              request_user_confirmation(fingerprint, own_fingerprint) // Retry if invalid input
108          }
109      }
110  }
111  
112  fn generate_random_room_id() -> String {
113      const ID_LENGTH: usize = 16;
114      const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
115  
116      let mut rng = OsRng;
117      let mut room_id = String::with_capacity(ID_LENGTH);
118  
119      for _ in 0..ID_LENGTH {
120          let idx = (rng.next_u32() as usize) % CHARSET.len();
121          room_id.push(CHARSET[idx] as char);
122      }
123  
124      room_id
125  }
126  
127  fn load_blacklist(file_path: &str) -> HashSet<IpNetwork> {
128      match fs::read_to_string(file_path) {
129          Ok(contents) => contents
130              .lines()
131              .filter_map(|line| {
132                  let line = line.trim();
133                  IpNetwork::from_str(line).ok()
134              })
135              .collect(),
136          Err(_) => HashSet::new(),
137      }
138  }
139  
140  fn is_onion_site(url: &str) -> bool {
141      url.contains(".onion")
142  }
143  
144  fn is_eepsite(url: &str) -> bool {
145      url.contains(".i2p")
146  }
147  
148  fn resolve_dns(host: &str) -> Result<String, Box<dyn Error>> {
149      let output = Command::new("dig")
150          .args(["+short", host])
151          .output()?;
152  
153      if output.status.success() {
154          let response = String::from_utf8_lossy(&output.stdout);
155          // Find the first valid IP address in the response
156          if let Some(ip) = response
157              .lines()
158              .filter(|line| line.parse::<std::net::IpAddr>().is_ok())
159              .next()
160          {
161              return Ok(ip.to_string());
162          }
163      }
164  
165      Err("Failed to resolve DNS to an IP address.".into())
166  }
167  
168  fn is_ip_blacklisted(ip: &str, blacklist: &HashSet<IpNetwork>) -> bool {
169      // Try to parse the IP address. If it fails, return false.
170      let ip: std::net::IpAddr = match ip.parse() {
171          Ok(ip) => ip,
172          Err(_) => return false,  // Return false if parsing fails
173      };
174  
175      // Check if the IP is within any of the CIDR ranges in the blacklist
176      blacklist.iter().any(|range| range.contains(ip))
177  }
178  
179  fn pad_message(message: &str, max_length: usize) -> String {
180      let current_length = message.len();
181  
182      if current_length < max_length {
183          let padding_len = max_length - current_length;
184  
185          let mut rng = OsRng;  // Cryptographically secure RNG
186          let padding: String = (0..padding_len)
187              .map(|_| rng.gen_range(33..127) as u8 as char) // ASCII printable characters range
188              .collect();
189  
190          // Wrap the padding in <padding> and </padding> tags
191          return format!("{}<padding>{}</padding>", message, padding);
192      }
193  
194      message.to_string()  // Return the message as is if it's already at max length
195  }
196  
197  // The main function
198  fn main() -> Result<(), Box<dyn Error>> {
199      use std::sync::{Arc, Mutex};
200      use std::{io::{self, Write}, thread, time::Duration};
201  
202      let sigalg = sig::Sig::new(sig::Algorithm::Dilithium5)?;
203  
204      // Get user input for the choice of interface
205      let mut input = String::new();
206      print!("Choose interface (CLI or GUI): ");
207      io::stdout().flush()?;
208      io::stdin().read_line(&mut input)?;
209      let interface_choice = input.trim().to_string();
210      input.clear();
211  
212      // Step 1: Ask user to either create a room ID or join one
213      println!("Would you like to create a new room or join an existing one?");
214      println!("Type 'create' to create a new room or 'join' to join an existing one.");
215      let mut choice = String::new();
216      io::stdin().read_line(&mut choice)?;
217      let choice = choice.trim();
218  
219      let room_id = match choice {
220          "create" => {
221              let new_room_id = generate_random_room_id();
222              println!("Generated new room ID: {}", new_room_id);
223              new_room_id
224          }
225          "join" => {
226              println!("Enter the room ID to join:");
227              let mut room_input = String::new();
228              io::stdin().read_line(&mut room_input)?;
229              room_input.trim().to_string()
230          }
231          _ => {
232              println!("Invalid choice. Please restart the program and choose 'create' or 'join'.");
233              return Ok(());
234          }
235      };
236  
237  
238      // Check if cloudflare-ip-blacklist.txt exists in the current directory
239      let blacklist_file = "cloudflare-ip-blacklist.txt";
240      if !Path::new(blacklist_file).exists() {
241          println!("File '{}' not found. Fetching from Codeberg...", blacklist_file);
242  
243          // Fetch the blacklist file from the raw Codeberg URL
244          let url = "https://codeberg.org/umutcamliyurt/Amnezichat/raw/branch/main/client/cloudflare-ip-blacklist.txt"; // Replace with actual URL
245          let response = get(url)?;
246  
247          if response.status().is_success() {
248              let content = response.text()?;
249              // Save the fetched content to cloudflare-ip-blacklist.txt in the current directory
250              let mut file = File::create(blacklist_file)?;
251              file.write_all(content.as_bytes())?;
252              println!("File fetched and saved as '{}'.", blacklist_file);
253          } else {
254              println!("Failed to fetch the file from URL.");
255              return Err("Failed to fetch blacklist.".into());
256          }
257      }
258  
259      // Load the blacklist from the file
260      let blacklist = load_blacklist("cloudflare-ip-blacklist.txt");
261  
262      // Get the server URL
263      let mut input = String::new();
264      print!("Enter the server URL: ");
265      io::stdout().flush()?;
266      io::stdin().read_line(&mut input)?;
267      let url = input.trim().to_string();
268      input.clear();
269  
270      if is_onion_site(&url) {
271          println!("This is an .onion site. Skipping IP check.");
272      }
273      else if is_eepsite(&url)
274      {
275          println!("This is an .i2p site. Skipping IP check.");
276      } 
277      else {
278          // Extract the host portion of the URL
279          let host = url
280              .split('/')
281              .nth(2)
282              .unwrap_or(&url) // Extract the host portion
283              .split(':')
284              .next()
285              .unwrap_or(&url);
286  
287          // Resolve IP of the host
288          match resolve_dns(host) {
289              Ok(ip) => {
290                  // Check if the resolved IP is blacklisted
291                  if is_ip_blacklisted(&ip, &blacklist) {
292                      println!("WARNING! The IP {} is in the blacklist.", ip);
293                      println!("The server you're trying to access is behind a Cloudflare reverse proxy.");
294                      println!("Proceed with caution as this setup may expose you to several potential risks:");
295                      println!();
296                      println!("Deanonymization attacks (including 0-click exploits)");
297                      println!("Metadata leaks");
298                      println!("Encryption vulnerabilities");
299                      println!("AI-based traffic analysis");
300                      println!("Connectivity issues");
301                      println!("Other undetected malicious behavior");
302                      println!();
303                      println!("What you can do:");
304                      println!("1. Choose a different server");
305                      println!("2. Self-host your own server");
306                      println!("3. Proceed anyway (Dangerous!)");
307                      println!();
308                      println!("For more info: https://git.calitabby.net/mirrors/deCloudflare");
309                      println!();
310                      println!("Do you want to proceed? (yes/no)");
311                  
312                      let mut input = String::new();
313                      io::stdin()
314                          .read_line(&mut input)
315                          .expect("Failed to read input");
316                      let input = input.trim().to_lowercase();
317                  
318                      match input.as_str() {
319                          "yes" | "y" => {
320                              println!("Proceeding...");
321                          }
322                          "no" | "n" => {
323                              println!("Operation aborted!");
324                              return Ok(()); // Exit the main function, effectively closing the app
325                          }
326                          _ => {
327                              println!("Invalid input. Please enter 'yes' or 'no'.");
328                          }
329                      }
330                  }
331              }
332              Err(e) => {
333                  println!("Failed to resolve IP for the server: {}", e);
334              }
335          }
336      }
337  
338      // Get the username
339      print!("Enter your username: ");
340      io::stdout().flush()?;
341      io::stdin().read_line(&mut input)?;
342      let username = input.trim().to_string();
343      input.clear();
344  
345      print!("Enter private key encryption password: ");
346      io::stdout().flush()?;
347      let private_password = read_password()?.to_string();
348  
349      println!("Is this a group chat? (yes/no): ");
350      let mut is_group_chat = String::new();
351      io::stdin().read_line(&mut is_group_chat)?;
352      let is_group_chat = is_group_chat.trim().to_lowercase() == "yes";
353  
354      let room_password = if is_group_chat {
355          // Loop to get a valid room password for group chat
356          loop {
357              print!("Enter room password (must be longer than 8 characters): ");
358              io::stdout().flush()?; // Ensure the prompt is displayed immediately
359              let mut input = String::new();
360              io::stdin().read_line(&mut input)?;
361              let password_input = input.trim();
362              if password_input.len() > 8 {
363                  break password_input.to_string(); // Exit the loop with valid password
364              } else {
365                  println!("Error: Password must be longer than 8 characters. Please try again.");
366              }
367          }
368      } else {
369          // For one-to-one chat, skip password setup
370          String::new()
371      };
372  
373      // Derive the key from the room password if it's a group chat
374      let room_password = if is_group_chat {
375          let salt = derive_salt_from_password(&room_password);
376          let key = derive_key(&room_password, &salt);
377          hex::encode(key)
378      } else {
379          String::new() // No room password required for one-to-one chat
380      };
381  
382      // Skip key exchange and create hybrid_shared_secret if it's a group chat
383      if is_group_chat {
384          println!("Skipping key exchange. Using room password as shared secret.");
385          let hybrid_shared_secret = room_password.clone();  // Use room password directly
386          println!("Shared secret established.");
387          println!("You can now start messaging!");
388  
389          // Shared data setup for messaging
390          let shared_hybrid_secret = Arc::new(hybrid_shared_secret.clone());
391          let shared_room_id = Arc::new(Mutex::new(room_id.clone()));
392          let shared_url = Arc::new(Mutex::new(url.clone()));
393  
394          // Clone interface_choice before passing to thread
395          let interface_choice_clone = interface_choice.clone();  // Clone interface_choice
396  
397          let random_data_thread = {
398              let shared_room_id = Arc::clone(&shared_room_id);
399              let shared_url = Arc::clone(&shared_url);
400              let shared_hybrid_secret = Arc::clone(&shared_hybrid_secret);
401          
402              thread::spawn(move || loop {
403                  // Generate cryptographically secure random data
404                  let mut random_data = vec![0u8; OsRng.next_u32() as usize % 2048 + 1]; // Random size between 1 and 2048
405                  OsRng.fill_bytes(&mut random_data);
406          
407                  let dummy_message = format!("[DUMMY_DATA]: {:?}", random_data);
408          
409                  let encrypted_dummy_message = match encrypt_data(&dummy_message, &shared_hybrid_secret) {
410                      Ok(data) => data,
411                      Err(e) => {
412                          eprintln!("Error encrypting dummy message: {}", e);
413                          continue;
414                      }
415                  };
416          
417                  let room_id_locked = shared_room_id.lock().unwrap();
418                  let url_locked = shared_url.lock().unwrap();
419  
420                  // Pad the message to a fixed length (e.g., 2048 bytes)
421                  let padded_message = pad_message(&encrypted_dummy_message, 2048);
422          
423                  if let Err(e) = send_encrypted_message(&padded_message, &room_id_locked, &url_locked) {
424                      eprintln!("Error sending dummy message: {}", e);
425                  }
426          
427                  // Sleep for a random interval (1 to 120 seconds)
428                  let sleep_duration = Duration::from_secs(OsRng.next_u32() as u64 % 120 + 1);
429                  thread::sleep(sleep_duration);
430              })
431          };
432  
433          // Spawn message fetch thread
434          let fetch_thread = thread::spawn({
435              let shared_hybrid_secret = Arc::clone(&shared_hybrid_secret);
436              let shared_room_id = Arc::clone(&shared_room_id);
437              let shared_url = Arc::clone(&shared_url);
438              let interface_choice_clone = interface_choice_clone.clone();  // Clone here as well
439  
440              move || loop {
441                  // Lock the shared resources to access their values
442                  let room_id_locked = shared_room_id.lock().unwrap().clone();
443                  let url_locked = shared_url.lock().unwrap().clone();
444  
445                  // Fetch and process messages
446                  match receive_and_fetch_messages(
447                      &room_id_locked,
448                      &shared_hybrid_secret,
449                      &url_locked,
450                      interface_choice_clone.to_lowercase() == "gui", // Pass true for GUI, false for CLI
451                  ) {
452                      Ok(messages) => {
453                          // Print messages only if interface is CLI
454                          if interface_choice_clone.to_lowercase() == "cli" {
455                              for message in messages {
456                                  println!("{}", message);
457                              }
458                          }
459                      }
460                      Err(e) => {
461                          eprintln!("Error fetching messages: {}", e);
462                      }
463                  }
464  
465                  // Sleep for 10 seconds before the next fetch
466                  thread::sleep(Duration::from_secs(10));
467              }
468          });
469  
470  
471          // Handle GUI or CLI messaging
472          if interface_choice.to_lowercase() == "gui" {
473              let rt = rocket::tokio::runtime::Runtime::new().unwrap();
474              rt.block_on(async {
475                  // Wrap only for passing to run_gui
476                  let shared_hybrid_secret_for_gui = shared_hybrid_secret;
477          
478                  // Correctly clone Arc<Mutex<String>> instead of Arc<String>
479                  let shared_room_id_for_gui: Arc<Mutex<String>> = Arc::clone(&shared_room_id);
480                  let shared_url_for_gui: Arc<Mutex<String>> = Arc::clone(&shared_url);
481          
482                  // Pass the arguments
483                  let app = MessagingApp::new(
484                      username,
485                      shared_hybrid_secret_for_gui,
486                      shared_room_id_for_gui,
487                      shared_url_for_gui,
488                  );
489          
490                  // Await the async launch function
491                  if let Err(e) = create_rocket(app).launch().await {
492                      eprintln!("Rocket server failed: {}", e);
493                  }
494              });
495              
496          } else {
497              loop {
498                  let mut message = String::new();
499                  print!("Enter your message (or type 'exit' to quit): ");
500                  io::stdout().flush()?;
501                  io::stdin().read_line(&mut message)?;
502  
503                  let message = message.trim();
504  
505                  if message == "exit" {
506                      println!("Exiting messaging session.");
507                      break;
508                  }
509  
510                  let message = format!("<strong>{}</strong>: {}", username, message);
511  
512                  // Pad the message to a fixed length (e.g., 2048 bytes)
513                  let padded_message = pad_message(&message, 2048);
514  
515                  let encrypted_message = encrypt_data(&padded_message, &hybrid_shared_secret)?;
516                  send_encrypted_message(&encrypted_message, &room_id, &url)?;
517              }
518          }
519  
520          // Ensure both threads terminate gracefully
521          if let Err(e) = random_data_thread.join() {
522              eprintln!("Random data thread terminated with error: {:?}", e);
523          }
524  
525          if let Err(e) = fetch_thread.join() {
526              eprintln!("Fetch thread terminated with error: {:?}", e);
527          }
528  
529          return Ok(());
530      }
531  
532      // Continue with the key exchange process for one-to-one chat
533      // Step 2: Load or generate Dilithium5 and EdDSA keys for the user
534      let dilithium_keys = key_operations_dilithium(&sigalg, &username, &private_password);
535      let Ok((dilithium_pk, dilithium_sk)) = dilithium_keys else { todo!() };
536  
537      let eddsa_keys = key_operations_eddsa(&username, &private_password);
538      let Ok((eddsa_sk, eddsa_pk)) = eddsa_keys else { todo!() };
539  
540      let encoded_dilithium_pk = hex::encode(&dilithium_pk);
541      send_dilithium_pubkey(&room_id, &encoded_dilithium_pk, &url);
542  
543      let encoded_eddsa_pk = hex::encode(&eddsa_pk);
544      send_eddsa_pubkey(&room_id, &encoded_eddsa_pk, &url);
545  
546      let fingerprint_dilithium = fingerprint_dilithium_public_key(&dilithium_pk);
547  
548      println!("Own Dilithium5 fingerprint: {}", fingerprint_dilithium);
549  
550      let fingerprint_eddsa = match Ed25519PublicKey::from_bytes(&eddsa_pk) {
551          Ok(public_key) => fingerprint_eddsa_public_key(&public_key),
552          Err(e) => {
553              eprintln!("Failed to convert EdDSA public key: {}", e);
554              return Err(Box::new(e));
555          }
556      };
557  
558      println!("Own EdDSA fingerprint: {}", fingerprint_eddsa);
559  
560      let mut processed_fingerprints: HashSet<String> = HashSet::new();
561      processed_fingerprints.insert(fingerprint_dilithium.clone());
562      processed_fingerprints.insert(fingerprint_eddsa.clone());
563  
564      let mut all_other_dilithium_keys: Vec<oqs::sig::PublicKey> = Vec::new();
565  
566      while all_other_dilithium_keys.len() < 1 {
567          println!("Waiting for Dilithium public key...");
568          thread::sleep(Duration::from_secs(5));
569      
570          let encoded_other_dilithium_pks = fetch_dilithium_pubkeys(&room_id, &url);
571      
572          for encoded_pk in encoded_other_dilithium_pks {
573              if let Ok(decoded_pk) = hex::decode(&encoded_pk) {
574      
575                  // Create a Sig instance for the "Dilithium5" algorithm
576                  let algorithm = SigAlgorithm::Dilithium5;
577      
578                  // Create a Sig instance for the chosen algorithm
579                  let sig = Sig::new(algorithm).map_err(|_| "Failed to initialize signature scheme")?;
580      
581                  // Convert the decoded public key to a PublicKey using public_key_from_bytes
582                  if let Some(public_key_ref) = sig.public_key_from_bytes(&decoded_pk) {
583                      // Convert PublicKeyRef<'_> to PublicKey by calling to_owned()
584                      let public_key = public_key_ref.to_owned();
585      
586                      let fetched_fingerprint = fingerprint_dilithium_public_key(&public_key);
587      
588                      if fetched_fingerprint == fingerprint_dilithium {
589                          continue;
590                      }
591      
592                      if processed_fingerprints.contains(&fetched_fingerprint) {
593                          continue;
594                      }
595      
596                      if request_user_confirmation(&fetched_fingerprint, &fingerprint_dilithium)? {
597                          // Push the owned PublicKey to the list
598                          all_other_dilithium_keys.push(public_key);
599                          processed_fingerprints.insert(fetched_fingerprint);
600                      } else {
601                          eprintln!("User did not confirm the public key fingerprint.");
602                      }
603                  } else {
604                      eprintln!("Failed to decode valid public key.");
605                  }
606              } else {
607                  eprintln!("Failed to convert decoded key to PublicKey.");
608              }
609          }
610      }
611      
612      
613  
614      println!("Received Dilithium5 public key from the server.");
615  
616      let mut eddsa_key: Option<Ed25519PublicKey> = None;
617  
618      while eddsa_key.is_none() {
619          println!("Waiting for EdDSA public key...");
620          thread::sleep(Duration::from_secs(5));
621  
622          let encoded_other_eddsa_pks = fetch_eddsa_pubkeys(&room_id, &url);
623  
624          for encoded_pk in encoded_other_eddsa_pks {
625              if let Ok(decoded_pk) = hex::decode(&encoded_pk) {
626                  if let Ok(public_key) = Ed25519PublicKey::from_bytes(
627                      decoded_pk.as_slice().try_into().expect("Decoded public key must be 32 bytes long"),
628                  ) {
629                      let fetched_fingerprint = fingerprint_eddsa_public_key(&public_key);
630  
631                      if fetched_fingerprint == fingerprint_eddsa {
632                          continue;
633                      }
634  
635                      if processed_fingerprints.contains(&fetched_fingerprint) {
636                          continue;
637                      }
638  
639                      if request_user_confirmation(&fetched_fingerprint, &fingerprint_eddsa)? {
640                          eddsa_key = Some(public_key);
641                          processed_fingerprints.insert(fetched_fingerprint);
642                          break;
643                      } else {
644                          eprintln!("User did not confirm the public key fingerprint.");
645                      }
646                  } else {
647                      eprintln!("Failed to decode valid public key.");
648                  }
649              } else {
650                  eprintln!("Failed to convert decoded key to PublicKey.");
651              }
652          }
653      }
654  
655      println!("Received EdDSA public key from the server.");
656  
657      let mut all_dilithium_pks = vec![dilithium_pk];
658      all_dilithium_pks.extend(all_other_dilithium_keys);
659  
660      let kyber_shared_secret = kyber_key_exchange(&room_id, &all_dilithium_pks, &dilithium_sk, &url)?;
661      let ecdh_shared_secret = if let Some(ref eddsa_key) = eddsa_key {
662          perform_ecdh_key_exchange(&room_id, &eddsa_sk.to_bytes(), eddsa_key, &url)?
663      } else {
664          return Err("EdDSA public key is missing".into());
665      };
666  
667      let hybrid_shared_secret = combine_shared_secrets(&kyber_shared_secret, &ecdh_shared_secret)?;
668  
669      println!("Hybrid shared secret established.");
670      println!("You can now start messaging!");
671      
672      let shared_hybrid_secret = Arc::new(hybrid_shared_secret.clone());  // Keep as Arc<String>
673      let shared_room_id = Arc::new(Mutex::new(room_id.clone()));  // Wrap in Mutex
674      let shared_url = Arc::new(Mutex::new(url.clone()));  // Wrap in Mutex
675      
676      let interface_choice_clone = interface_choice.clone();  // Clone interface_choice before moving it into the thread
677      
678      let random_data_thread = {
679          let shared_room_id = Arc::clone(&shared_room_id);
680          let shared_url = Arc::clone(&shared_url);
681          let shared_hybrid_secret = Arc::clone(&shared_hybrid_secret);
682      
683          thread::spawn(move || loop {
684              // Generate cryptographically secure random data
685              let mut random_data = vec![0u8; OsRng.next_u32() as usize % 2048 + 1]; // Random size between 1 and 2048
686              OsRng.fill_bytes(&mut random_data);
687      
688              let dummy_message = format!("[DUMMY_DATA]: {:?}", random_data);
689  
690              // Pad the message to a fixed length (e.g., 2048 bytes)
691              let padded_message = pad_message(&dummy_message, 2048);
692      
693              let encrypted_dummy_message = match encrypt_data(&padded_message, &shared_hybrid_secret) {
694                  Ok(data) => data,
695                  Err(e) => {
696                      eprintln!("Error encrypting dummy message: {}", e);
697                      continue;
698                  }
699              };
700      
701              let room_id_locked = shared_room_id.lock().unwrap();
702              let url_locked = shared_url.lock().unwrap();
703      
704              if let Err(e) = send_encrypted_message(&encrypted_dummy_message, &room_id_locked, &url_locked) {
705                  eprintln!("Error sending dummy message: {}", e);
706              }
707      
708              // Sleep for a random interval (1 to 120 seconds)
709              let sleep_duration = Duration::from_secs(OsRng.next_u32() as u64 % 120 + 1);
710              thread::sleep(sleep_duration);
711          })
712      };
713      
714      // Spawn message fetch thread
715      let fetch_thread = thread::spawn({
716          let shared_hybrid_secret = Arc::clone(&shared_hybrid_secret);
717          let shared_room_id = Arc::clone(&shared_room_id);
718          let shared_url = Arc::clone(&shared_url);
719          let interface_choice_clone = interface_choice_clone.clone();  // Clone here as well
720  
721          move || loop {
722              // Lock the shared resources to access their values
723              let room_id_locked = shared_room_id.lock().unwrap().clone();
724              let url_locked = shared_url.lock().unwrap().clone();
725  
726              // Fetch and process messages
727              match receive_and_fetch_messages(
728                  &room_id_locked,
729                  &shared_hybrid_secret,
730                  &url_locked,
731                  interface_choice_clone.to_lowercase() == "gui", // Pass true for GUI, false for CLI
732              ) {
733                  Ok(messages) => {
734                      // Print messages only if interface is CLI
735                      if interface_choice_clone.to_lowercase() == "cli" {
736                          for message in messages {
737                              println!("{}", message);
738                          }
739                      }
740                  }
741                  Err(e) => {
742                      eprintln!("Error fetching messages: {}", e);
743                  }
744              }
745  
746              // Sleep for 10 seconds before the next fetch
747              thread::sleep(Duration::from_secs(10));
748          }
749      });
750      
751      
752      if interface_choice.to_lowercase() == "gui" {
753          let rt = rocket::tokio::runtime::Runtime::new().unwrap();
754          rt.block_on(async {
755              // Wrap only for passing to run_gui
756              let shared_hybrid_secret_for_gui = shared_hybrid_secret;
757      
758              // Correctly clone Arc<Mutex<String>> instead of Arc<String>
759              let shared_room_id_for_gui: Arc<Mutex<String>> = Arc::clone(&shared_room_id);
760              let shared_url_for_gui: Arc<Mutex<String>> = Arc::clone(&shared_url);
761      
762              // Pass the arguments
763              let app = MessagingApp::new(
764                  username,
765                  shared_hybrid_secret_for_gui,
766                  shared_room_id_for_gui,
767                  shared_url_for_gui,
768              );
769      
770              // Await the async launch function
771              if let Err(e) = create_rocket(app).launch().await {
772                  eprintln!("Rocket server failed: {}", e);
773              }
774          });
775      
776      } else {
777          loop {
778              let mut message = String::new();
779              print!("Enter your message (or type 'exit' to quit): ");
780              io::stdout().flush()?;
781              io::stdin().read_line(&mut message)?;
782      
783              let message = message.trim();
784      
785              if message == "exit" {
786                  println!("Exiting messaging session.");
787                  break;
788              }
789      
790              let message = format!("<strong>{}</strong>: {}", username, message);
791  
792              // Pad the message to a fixed length (e.g., 2048 bytes)
793              let padded_message = pad_message(&message, 2048);
794      
795              let encrypted_message = encrypt_data(&padded_message, &hybrid_shared_secret)?;
796              send_encrypted_message(&encrypted_message, &room_id, &url)?;
797          }
798      }
799      
800      // Ensure both threads terminate gracefully
801      if let Err(e) = random_data_thread.join() {
802          eprintln!("Random data thread terminated with error: {:?}", e);
803      }
804  
805      if let Err(e) = fetch_thread.join() {
806          eprintln!("Fetch thread terminated with error: {:?}", e);
807      }
808  
809      
810      Ok(())
811  }    
812  
813  // Function to clear the screen before printing new messages
814  fn clear_screen() {
815      if cfg!(target_os = "windows") {
816          // Windows
817          Command::new("cmd")
818              .args(&["/C", "cls"])
819              .output()
820              .expect("Failed to clear screen on Windows");
821      } else {
822          // Linux/macOS or others
823          print!("\x1b[2J\x1b[H");
824          std::io::stdout().flush().unwrap(); // Ensure the command is executed immediately
825      }
826  }