/ client / src / encryption.rs
encryption.rs
  1  use rand::RngCore;
  2  use sha3::{Sha3_512, Digest};
  3  use zeroize::Zeroize;
  4  use chacha20poly1305::{
  5      aead::{Aead, KeyInit, OsRng},
  6      ChaCha20Poly1305, Key, Nonce,
  7  };
  8  use argon2::{Argon2, password_hash::SaltString, PasswordHasher};
  9  
 10  // Derive a salt from the password itself
 11  pub fn derive_salt_from_password(password: &str) -> [u8; 16] {
 12      let mut hasher = Sha3_512::new();
 13      hasher.update(password.as_bytes());
 14      let hash_result = hasher.finalize();
 15  
 16      let mut salt = [0u8; 16];
 17      salt.copy_from_slice(&hash_result[..16]); // Use the first 16 bytes of the hash as the salt
 18      salt
 19  }
 20  
 21  // Derive encryption key using Argon2
 22  pub fn derive_key(password: &str, salt: &[u8]) -> [u8; 32] {
 23      let argon2 = Argon2::default();
 24      let salt = SaltString::encode_b64(salt).expect("Failed to generate salt string");
 25      let hash = argon2
 26          .hash_password(password.as_bytes(), &salt)
 27          .expect("Failed to hash password");
 28      let hash_bytes = hash.hash.expect("Hash missing in PasswordHash structure");
 29  
 30      let mut key = [0u8; 32];
 31      key.copy_from_slice(hash_bytes.as_bytes());
 32      key
 33  }
 34  
 35  pub fn combine_shared_secrets(
 36      kyber_secret: &str,
 37      ecdh_secret: &str,
 38  ) -> Result<String, Box<dyn std::error::Error>> {
 39      use sha3::{Digest, Sha3_512};
 40      use hex; // For hexadecimal encoding
 41  
 42      // Concatenate the secrets
 43      let combined = [kyber_secret.as_bytes(), ecdh_secret.as_bytes()].concat();
 44  
 45      // Hash the combined secrets to produce a fixed-length shared secret
 46      let mut hasher = Sha3_512::new();
 47      hasher.update(combined);
 48  
 49      // Convert the hash result to a hexadecimal string
 50      Ok(hex::encode(hasher.finalize()))
 51  }
 52  
 53  // Encrypt the data using ChaCha20Poly1305
 54  pub fn encrypt_data(plain_text: &str, password: &str) -> Result<String, Box<dyn std::error::Error>> {
 55      // Generate random salt for key derivation
 56      let mut salt = [0u8; 16];
 57      OsRng.fill_bytes(&mut salt);
 58  
 59      // Derive encryption key using Argon2
 60      let mut key = derive_key(password, &salt);
 61      let cipher = ChaCha20Poly1305::new(&Key::from_slice(&key));
 62  
 63      // Generate random nonce (12 bytes)
 64      let mut nonce_bytes = [0u8; 12];
 65      OsRng.fill_bytes(&mut nonce_bytes);
 66      let nonce = Nonce::from_slice(&nonce_bytes);
 67  
 68      // Encrypt the data
 69      let encrypted_data = cipher
 70          .encrypt(nonce, plain_text.as_bytes())
 71          .map_err(|_| "Encryption error")?;
 72  
 73      // Clear the key from memory after usage
 74      key.zeroize();
 75  
 76      // Return the formatted encrypted message with salt, nonce, and encrypted data
 77      Ok(format!(
 78          "{}:{}:{}",
 79          hex::encode(salt),
 80          hex::encode(nonce_bytes),
 81          hex::encode(encrypted_data)
 82      ))
 83  }
 84  
 85  // Decrypt the data using ChaCha20Poly1305
 86  pub fn decrypt_data(encrypted_text: &str, password: &str) -> Result<String, Box<dyn std::error::Error>> {
 87      // Split the encrypted data into salt, nonce, and encrypted part
 88      let parts: Vec<&str> = encrypted_text.split(':').collect();
 89      if parts.len() != 3 {
 90          return Err("Invalid encrypted data format".into());
 91      }
 92  
 93      // Decode hex-encoded salt, nonce, and encrypted data
 94      let salt = hex::decode(parts[0]).map_err(|_| "Decryption error: Invalid salt format")?;
 95      let nonce_bytes = hex::decode(parts[1]).map_err(|_| "Decryption error: Invalid nonce format")?;
 96      let encrypted_data = hex::decode(parts[2]).map_err(|_| "Decryption error: Invalid encrypted data format")?;
 97  
 98      // Derive the decryption key using the password and salt
 99      let mut key = derive_key(password, &salt);
100      let cipher = ChaCha20Poly1305::new(&Key::from_slice(&key));
101  
102      // Ensure nonce is of the correct length (12 bytes for ChaCha20Poly1305)
103      let nonce = Nonce::from_slice(&nonce_bytes);
104  
105      // Decrypt the data
106      let decrypted_data = cipher
107          .decrypt(nonce, encrypted_data.as_ref())
108          .map_err(|_| "Decryption error: Failed to decrypt")?;
109  
110      // Clear the key from memory after usage
111      key.zeroize();
112  
113      // Convert decrypted bytes into a string
114      Ok(String::from_utf8(decrypted_data).map_err(|_| "Decryption error: Invalid UTF-8 data")?)
115  }