myph-v2.git


myph-v2

~ rust shellcode loader with evasive capabilities
git clone https://github.com/djnnvx/myph-v2.git
log | files | refs | archive | README

chacha20_cipher.rs (5676B)

      1 use super::Encryptor;
      2 use chacha20::cipher::{KeyIvInit, StreamCipher};
      3 use chacha20::ChaCha20;
      4 use rand::Rng;
      5 use sha2::{Digest, Sha256};
      6 
      7 /// ChaCha20 stream cipher implementation
      8 pub struct ChaCha20Encryptor {
      9     key: [u8; 32],
     10 }
     11 
     12 impl ChaCha20Encryptor {
     13     /// Creates a new ChaCha20 encryptor with the given key
     14     /// Uses SHA-256 to derive a proper 32-byte key from the input
     15     pub fn new(key: &str) -> Self {
     16         let mut hasher = Sha256::new();
     17         hasher.update(key.as_bytes());
     18         let hash = hasher.finalize();
     19 
     20         // Use full 32 bytes of SHA-256 hash for ChaCha20 key
     21         let mut key_bytes = [0u8; 32];
     22         key_bytes.copy_from_slice(&hash[..32]);
     23 
     24         println!(
     25             "[ChaCha20] Derived key from input (SHA-256): {:02x?}",
     26             &key_bytes[..8]
     27         );
     28 
     29         Self { key: key_bytes }
     30     }
     31 }
     32 
     33 impl Encryptor for ChaCha20Encryptor {
     34     fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, String> {
     35         // Generate random nonce (12 bytes for ChaCha20)
     36         let mut rng = rand::thread_rng();
     37         let mut nonce = [0u8; 12];
     38         rng.fill(&mut nonce);
     39 
     40         println!(
     41             "[ChaCha20] Generated random nonce for encryption: {:02x?}",
     42             &nonce
     43         );
     44 
     45         let mut cipher = ChaCha20::new(&self.key.into(), &nonce.into());
     46         let mut buffer = data.to_vec();
     47         cipher.apply_keystream(&mut buffer);
     48 
     49         // Prepend nonce to ciphertext
     50         let mut result = nonce.to_vec();
     51         result.extend_from_slice(&buffer);
     52 
     53         Ok(result)
     54     }
     55 
     56     fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, String> {
     57         if data.len() < 12 {
     58             return Err("Ciphertext too short to contain nonce".to_string());
     59         }
     60 
     61         // Extract nonce from beginning of data
     62         let nonce: [u8; 12] = data[..12].try_into().unwrap();
     63         let ciphertext = &data[12..];
     64 
     65         println!("[ChaCha20] Extracted nonce for decryption: {:02x?}", &nonce);
     66 
     67         // ChaCha20 is symmetric - same operation for decrypt
     68         let mut cipher = ChaCha20::new(&self.key.into(), &nonce.into());
     69         let mut buffer = ciphertext.to_vec();
     70         cipher.apply_keystream(&mut buffer);
     71 
     72         Ok(buffer)
     73     }
     74 
     75     fn key_size(&self) -> usize {
     76         32 // ChaCha20 uses 32-byte keys
     77     }
     78 
     79     fn algorithm_name(&self) -> &'static str {
     80         "ChaCha20"
     81     }
     82 
     83     fn uses_iv(&self) -> bool {
     84         true // Uses nonce
     85     }
     86 
     87     fn generate_decrypt_code(&self) -> &'static str {
     88         r#"/// Decrypts ChaCha20 encrypted data using the provided key
     89 ///
     90 /// Returns `Ok(Vec<u8>)` with decrypted data if successful, otherwise returns an error.
     91 pub fn decrypt_chacha20(data: &[u8], key: &[u8]) -> Result<Vec<u8>, String> {
     92     use chacha20::cipher::{KeyIvInit, StreamCipher};
     93     use chacha20::ChaCha20;
     94     
     95     if data.len() < 12 {
     96         return Err("Ciphertext too short to contain nonce".to_string());
     97     }
     98     
     99     if key.len() != 32 {
    100         return Err("Key must be 32 bytes for ChaCha20".to_string());
    101     }
    102     
    103     let nonce: [u8; 12] = data[..12].try_into().unwrap();
    104     let ciphertext = &data[12..];
    105     
    106     let key_array: [u8; 32] = key.try_into().map_err(|_| "Invalid key length".to_string())?;
    107     
    108     let mut cipher = ChaCha20::new(&key_array.into(), &nonce.into());
    109     let mut buffer = ciphertext.to_vec();
    110     cipher.apply_keystream(&mut buffer);
    111     
    112     Ok(buffer)
    113 }"#
    114     }
    115 
    116     fn required_dependencies(&self) -> Vec<(&'static str, &'static str)> {
    117         vec![("chacha20", "0.9")]
    118     }
    119 }
    120 
    121 #[cfg(test)]
    122 mod tests {
    123     use super::*;
    124 
    125     #[test]
    126     fn test_chacha20_encrypt_decrypt() {
    127         let encryptor = ChaCha20Encryptor::new("my-secret-key-123");
    128         let plaintext = b"Hello, World!";
    129 
    130         let encrypted = encryptor.encrypt(plaintext).unwrap();
    131         assert_ne!(encrypted, plaintext);
    132 
    133         let decrypted = encryptor.decrypt(&encrypted).unwrap();
    134         assert_eq!(decrypted, plaintext);
    135     }
    136 
    137     #[test]
    138     fn test_chacha20_different_keys() {
    139         let encryptor1 = ChaCha20Encryptor::new("key1");
    140         let encryptor2 = ChaCha20Encryptor::new("key2");
    141         let plaintext = b"Test data";
    142 
    143         let encrypted1 = encryptor1.encrypt(plaintext).unwrap();
    144         let encrypted2 = encryptor2.encrypt(plaintext).unwrap();
    145 
    146         assert_ne!(encrypted1, encrypted2);
    147     }
    148 
    149     #[test]
    150     fn test_chacha20_empty_data() {
    151         let encryptor = ChaCha20Encryptor::new("key");
    152         let empty: &[u8] = &[];
    153 
    154         let encrypted = encryptor.encrypt(empty).unwrap();
    155         // Encrypted data includes 12-byte nonce
    156         assert_eq!(encrypted.len(), 12);
    157 
    158         let decrypted = encryptor.decrypt(&encrypted).unwrap();
    159         assert_eq!(decrypted.len(), 0);
    160     }
    161 
    162     #[test]
    163     fn test_chacha20_long_data() {
    164         let encryptor = ChaCha20Encryptor::new("secret-key");
    165         let plaintext = b"This is a much longer message that tests the stream cipher capabilities";
    166 
    167         let encrypted = encryptor.encrypt(plaintext).unwrap();
    168         assert_ne!(encrypted, plaintext);
    169         // Encrypted data includes 12-byte nonce + ciphertext
    170         assert_eq!(encrypted.len(), plaintext.len() + 12);
    171 
    172         let decrypted = encryptor.decrypt(&encrypted).unwrap();
    173         assert_eq!(decrypted, plaintext);
    174     }
    175 
    176     #[test]
    177     fn test_chacha20_binary_data() {
    178         let encryptor = ChaCha20Encryptor::new("key");
    179         let plaintext: Vec<u8> = (0..=255).collect();
    180 
    181         let encrypted = encryptor.encrypt(&plaintext).unwrap();
    182         assert_ne!(encrypted, plaintext);
    183 
    184         let decrypted = encryptor.decrypt(&encrypted).unwrap();
    185         assert_eq!(decrypted, plaintext);
    186     }
    187 }