/ admin_tool / src / main.rs
main.rs
  1  use pqcrypto_kyber::kyber1024;
  2  use pqcrypto_traits::kem::{PublicKey, SharedSecret, Ciphertext};
  3  use std::fs;
  4  use std::path::PathBuf;
  5  use std::io::{self, Write};
  6  use hex;
  7  use aes_gcm::{
  8      aead::{Aead, KeyInit, OsRng, Payload},
  9      Aes256Gcm, Nonce
 10  };
 11  use sha3::{Shake256, Sha3_256, digest::{ExtendableOutput, Update, XofReader, Digest}};
 12  use rand_core::RngCore;
 13  use zeroize::Zeroize;
 14  
 15  fn main() -> Result<(), Box<dyn std::error::Error>> {
 16      println!("=== GHOSTLINE OTP DISTRIBUTION (QUANTUM-SAFE MULTI-FRIEND) ===");
 17      
 18      // 1. GET OTP FILE
 19      println!("\n1. šŸ“ YOUR OTP FILE");
 20      print!("Path to OTP file: ");
 21      io::stdout().flush()?;
 22      let mut otp_path = String::new();
 23      io::stdin().read_line(&mut otp_path)?;
 24      let otp_path = otp_path.trim();
 25      
 26      let otp_data = fs::read(otp_path)
 27          .map_err(|e| format!("Failed to read OTP file '{}': {}", otp_path, e))?;
 28      println!("āœ… Loaded OTP: {} bytes", otp_data.len());
 29      
 30      // 2. GET FRIENDS' PUBLIC KEYS
 31      println!("\n2. šŸ”‘ FRIENDS' PUBLIC KEYS");
 32      print!("Directory containing friends' public keys: ");
 33      io::stdout().flush()?;
 34      let mut keys_dir = String::new();
 35      io::stdin().read_line(&mut keys_dir)?;
 36      let keys_dir = keys_dir.trim();
 37      
 38      let mut friend_public_keys = Vec::new();
 39      let mut friend_ids = Vec::new();
 40      
 41      for entry in fs::read_dir(keys_dir)? {
 42          let entry = entry?;
 43          let path = entry.path();
 44          if path.is_file() && path.extension().map_or(false, |ext| ext == "key") {
 45              if let Some(filename) = path.file_stem() {
 46                  let friend_id = filename.to_string_lossy().to_string();
 47                  println!("   Found: {} -> {}", friend_id, path.display());
 48                  
 49                  let pub_bytes = fs::read(&path)
 50                      .map_err(|e| format!("Failed to read key file '{}': {}", path.display(), e))?;
 51                  let pub_key = kyber1024::PublicKey::from_bytes(&pub_bytes)
 52                      .map_err(|e| format!("Failed to parse key '{}': {}", friend_id, e))?;
 53                  
 54                  friend_public_keys.push(pub_key);
 55                  friend_ids.push(friend_id);
 56              }
 57          }
 58      }
 59      
 60      if friend_public_keys.is_empty() {
 61          return Err("No public key files found!".into());
 62      }
 63      
 64      println!("āœ… Loaded {} friend(s)", friend_public_keys.len());
 65      
 66      // 3. GENERATE RANDOM AES KEY (will be shared with all friends)
 67      println!("\n3. šŸ”‘ GENERATING SESSION AES KEY");
 68      let mut aes_key = [0u8; 32];
 69      OsRng.fill_bytes(&mut aes_key);
 70      println!("āœ… Generated random AES-256 key: {}...", hex::encode(&aes_key[..8]));
 71      
 72      // 4. ENCRYPT OTP WITH AES KEY (SAME FOR EVERYONE)
 73      println!("\n4. šŸ”’ ENCRYPTING OTP (SAME FOR ALL FRIENDS)");
 74      let cipher = Aes256Gcm::new_from_slice(&aes_key)?;
 75      
 76      let mut nonce_bytes = [0u8; 12];
 77      OsRng.fill_bytes(&mut nonce_bytes);
 78      let nonce = Nonce::from_slice(&nonce_bytes);
 79      
 80      let payload = Payload {
 81          msg: &otp_data,
 82          aad: b"",
 83      };
 84      
 85      let encrypted_otp = cipher.encrypt(nonce, payload)
 86          .map_err(|e| format!("AES encryption failed: {}", e))?;
 87      
 88      println!("āœ… OTP encrypted: {} bytes (same for all friends)", encrypted_otp.len());
 89      
 90      // 5. CREATE PACKAGES FOR EACH FRIEND
 91      println!("\n5. šŸ“¦ CREATING FRIEND PACKAGES");
 92      
 93      let base_output_dir = "./distribution/";
 94      fs::create_dir_all(base_output_dir)?;
 95      
 96      let common_dir = PathBuf::from(base_output_dir).join("common");
 97      fs::create_dir_all(&common_dir)?;
 98      
 99      fs::write(common_dir.join("encrypted_otp.bin"), &encrypted_otp)?;
100      fs::write(common_dir.join("aes_nonce.bin"), nonce_bytes)?;
101      
102      let metadata = format!(
103          "GhostLine Multi-Friend Distribution\n\
104          Date: {}\n\
105          OTP size: {} bytes\n\
106          Number of friends: {}\n\
107          Friend IDs: {}\n\
108          Kyber: 1024\n\
109          KDF: SHAKE256\n\
110          AES: 256-GCM\n\
111          \nāš ļø SECURITY WARNING:\n\
112          • Each OTP distribution creates NEW Kyber encapsulations\n\
113          • NEVER reuse the same ciphertext for multiple OTPs!\n\
114          • This package is for ONE-TIME USE only\n",
115          chrono::Local::now(),
116          otp_data.len(),
117          friend_ids.len(),
118          friend_ids.join(", ")
119      );
120      
121      fs::write(common_dir.join("README.txt"), metadata)?;
122      
123      let mut friend_packages = Vec::new();
124      
125      for (i, (friend_pub, friend_id)) in friend_public_keys.iter().zip(&friend_ids).enumerate() {
126          println!("\n   Processing: {} (friend #{})", friend_id, i + 1);
127          
128          let (shared_secret, ciphertext) = kyber1024::encapsulate(friend_pub);
129          let secret_bytes = shared_secret.as_bytes();
130          let ciphertext_bytes = ciphertext.as_bytes();
131          
132          // KDF: SHAKE256 - FIXED: NO OTP SIZE INCLUDED
133          let mut shake = Shake256::default();
134          Update::update(&mut shake, secret_bytes);
135          Update::update(&mut shake, b"GHOSTLINE-AES-KEY-WRAP-v1");
136          Update::update(&mut shake, &ciphertext_bytes[..32]);
137          // NO OTP SIZE HERE - Friend doesn't know it!
138          
139          let mut wrap_key = [0u8; 32];
140          let mut reader = shake.finalize_xof();
141          reader.read(&mut wrap_key);
142          
143          let wrap_cipher = Aes256Gcm::new_from_slice(&wrap_key)?;
144          let mut wrap_nonce = [0u8; 12];
145          OsRng.fill_bytes(&mut wrap_nonce);
146          
147          let payload = Payload {
148              msg: &aes_key[..],
149              aad: b"",
150          };
151          
152          let wrapped_aes_key = wrap_cipher.encrypt(
153              Nonce::from_slice(&wrap_nonce), 
154              payload
155          ).map_err(|e| format!("Failed to wrap AES key for {}: {}", friend_id, e))?;
156          
157          let friend_dir = PathBuf::from(base_output_dir).join(friend_id);
158          fs::create_dir_all(&friend_dir)?;
159          
160          fs::write(friend_dir.join("kyber_ciphertext.bin"), ciphertext_bytes)?;
161          fs::write(friend_dir.join("wrapped_aes_key.bin"), &wrapped_aes_key)?;
162          fs::write(friend_dir.join("wrap_nonce.bin"), wrap_nonce)?;
163          
164          let mut hasher = Sha3_256::new();
165          Update::update(&mut hasher, ciphertext_bytes);
166          let ct_hash = hasher.finalize();
167          
168          let mut hasher = Sha3_256::new();
169          Update::update(&mut hasher, &wrapped_aes_key);
170          let wrapped_hash = hasher.finalize();
171          
172          let friend_manifest = format!(
173              "Friend: {}\n\
174              Kyber ciphertext SHA3-256: {}\n\
175              Wrapped AES key SHA3-256: {}\n\
176              \nFiles needed:\n\
177              1. kyber_ciphertext.bin (this file)\n\
178              2. wrapped_aes_key.bin (this file)\n\
179              3. wrap_nonce.bin (this file)\n\
180              4. encrypted_otp.bin (from common/ folder)\n\
181              5. aes_nonce.bin (from common/ folder)\n",
182              friend_id,
183              hex::encode(ct_hash),
184              hex::encode(wrapped_hash)
185          );
186          
187          fs::write(friend_dir.join("MANIFEST.txt"), friend_manifest)?;
188          
189          println!("   āœ… Package created in: {}", friend_dir.display());
190          friend_packages.push(friend_id.clone());
191      }
192      
193      aes_key.zeroize();
194      
195      println!("\n6. āœ… DISTRIBUTION COMPLETE");
196      println!("   Common files (same for all):");
197      println!("     • encrypted_otp.bin - Encrypted OTP");
198      println!("     • aes_nonce.bin     - AES nonce");
199      println!("     • README.txt        - Instructions");
200      
201      println!("\n   Individual packages for each friend:");
202      for (i, friend_id) in friend_packages.iter().enumerate() {
203          println!("     {}. {} - Contains their Kyber ciphertext", i + 1, friend_id);
204      }
205      
206      println!("\nšŸ“¤ DISTRIBUTION INSTRUCTIONS:");
207      println!("1. Give EACH friend their folder: distribution/{{friend_name}}/");
208      println!("2. Share COMMON folder with ALL: distribution/common/");
209      println!("3. Each friend needs: their folder + common folder");
210      
211      let mut hasher = Sha3_256::new();
212      Update::update(&mut hasher, &encrypted_otp);
213      let otp_hash = hasher.finalize();
214      
215      let overall_manifest = format!(
216          "=== GHOSTLINE DISTRIBUTION MANIFEST ===\n\
217          Encrypted OTP SHA3-256: {}\n\
218          Number of recipients: {}\n\
219          Recipients: {}\n\
220          Date: {}\n",
221          hex::encode(otp_hash),
222          friend_packages.len(),
223          friend_packages.join(", "),
224          chrono::Local::now()
225      );
226      
227      fs::write(PathBuf::from(base_output_dir).join("OVERALL_MANIFEST.txt"), overall_manifest)?;
228      
229      Ok(())
230  }