use crate::types::*;
use crate::crypto::{get_key_manager, KeyManager};
use crate::zip::ZipProcessor;
use crate::error::{PkCrackError, Result};
use std::path::PathBuf;
pub struct Stage3Processor {
key_manager: KeyManager,
zip_processor: Option<ZipProcessor>,
}
impl Stage3Processor {
pub fn new() -> Result<Self> {
Ok(Self {
key_manager: get_key_manager()?.clone(),
zip_processor: None,
})
}
pub fn with_zip_support() -> Result<Self> {
Ok(Self {
key_manager: get_key_manager()?.clone(),
zip_processor: Some(ZipProcessor::new()?),
})
}
pub fn execute(&self, solution: &Stage2Result, config: &crate::Config) -> Result<AttackResult> {
let mut key_state = self.reconstruct_key_state(solution)?;
if let Some(output_zip) = &config.output_zip {
self.decrypt_zip(config, &mut key_state, output_zip.to_str().unwrap())?;
Ok(AttackResult {
keys: key_state,
password: None, best_offset: solution.offset,
num_candidates: 1,
})
} else {
let password = self.search_password(key_state)?;
Ok(AttackResult {
keys: key_state,
password: Some(password),
best_offset: solution.offset,
num_candidates: 1,
})
}
}
fn reconstruct_key_state(&self, solution: &Stage2Result) -> Result<KeyState> {
let mut state = KeyState {
key0: solution.key0,
key1: solution.key1,
key2: solution.key2,
};
Ok(state)
}
fn search_password(&self, target_keys: KeyState) -> Result<String> {
println!("Starting password search...");
let mut password = String::new();
if let Some(found_password) = self.try_common_passwords(target_keys)? {
return Ok(found_password);
}
if let Some(found_password) = self.brute_force_search(target_keys, 8)? {
return Ok(found_password);
}
Err(PkCrackError::NoSolutionFound)
}
fn try_common_passwords(&self, target_keys: KeyState) -> Result<Option<String>> {
let common_passwords = vec![
"password", "123456", "secret", "admin", "test",
"qwerty", "abc123", "password123", "123456789",
"welcome", "login", "letmein", "dragon",
];
for password in common_passwords {
let keys = self.key_manager.keys_from_password(password.as_bytes());
if self.keys_match(keys, target_keys, 4) { println!("Found common password: {}", password);
return Ok(Some(password.to_string()));
}
}
Ok(None)
}
fn brute_force_search(&self, target_keys: KeyState, max_length: usize) -> Result<Option<String>> {
let charset: Vec<char> = "abcdefghijklmnopqrstuvwxyz0123456789".chars().collect();
for length in 1..=max_length {
println!("Trying passwords of length {}...", length);
if let Some(password) = self.brute_force_length(&charset, length, target_keys)? {
return Ok(Some(password));
}
}
Ok(None)
}
fn brute_force_length(&self, charset: &[char], length: usize, target_keys: KeyState) -> Result<Option<String>> {
if length == 0 {
return Ok(None);
}
let mut password = vec![' '; length];
if self.try_password_recursive(&mut password, 0, charset, target_keys)? {
let password_str: String = password.into_iter().collect();
return Ok(Some(password_str));
}
Ok(None)
}
fn try_password_recursive(&self, password: &mut [char], pos: usize, charset: &[char], target_keys: KeyState) -> Result<bool> {
if pos >= password.len() {
let password_bytes: Vec<u8> = password.iter().map(|&c| c as u8).collect();
let keys = self.key_manager.keys_from_password(&password_bytes);
if self.keys_match(keys, target_keys, 0) { println!("Found password: {}", password.iter().collect::<String>());
return Ok(true);
}
return Ok(false);
}
for &c in charset {
password[pos] = c;
if self.try_password_recursive(password, pos + 1, charset, target_keys)? {
return Ok(true);
}
}
Ok(false)
}
fn keys_match(&self, keys1: KeyState, keys2: KeyState, tolerance: u32) -> bool {
let diff0 = (keys1.key0 ^ keys2.key0).count_ones();
let diff1 = (keys1.key1 ^ keys2.key1).count_ones();
let diff2 = (keys1.key2 ^ keys2.key2).count_ones();
(diff0 + diff1 + diff2) <= tolerance
}
fn decrypt_zip(&self, config: &crate::Config, key_state: &mut KeyState, output_path: &str) -> Result<()> {
let zip_processor = self.zip_processor.as_ref()
.ok_or_else(|| PkCrackError::config("ZIP support not enabled"))?;
let ciphertext_zip = config.ciphertext_zip.as_ref()
.ok_or_else(|| PkCrackError::config("No ciphertext ZIP specified"))?;
let ciphertext_filename = config.ciphertext_filename()
.ok_or_else(|| PkCrackError::config("No ciphertext filename available"))?;
zip_processor.decrypt_zip_to_file(
ciphertext_zip,
&ciphertext_filename,
output_path,
key_state,
)?;
println!("Successfully decrypted ZIP to: {}", output_path);
Ok(())
}
pub fn validate_password(&self, password: &str, plaintext: &[u8], ciphertext: &[u8]) -> Result<bool> {
let keys = self.key_manager.keys_from_password(password.as_bytes());
Ok(self.key_manager.validate_keys(keys, plaintext, ciphertext))
}
pub fn estimate_password_complexity(&self, password: &str) -> f64 {
let charset_size = self.estimate_charset_size(password);
let length = password.len();
(length as f64) * (charset_size as f64).log2()
}
fn estimate_charset_size(&self, password: &str) -> usize {
let mut has_lower = false;
let mut has_upper = false;
let mut has_digit = false;
let mut has_special = false;
for c in password.chars() {
if c.is_lowercase() {
has_lower = true;
} else if c.is_uppercase() {
has_upper = true;
} else if c.is_ascii_digit() {
has_digit = true;
} else {
has_special = true;
}
}
let mut size = 0;
if has_lower { size += 26; }
if has_upper { size += 26; }
if has_digit { size += 10; }
if has_special { size += 32; }
size.max(1)
}
pub fn generate_progress_report(&self, current_password: &str, total_tried: u64, est_remaining: Option<u64>) -> String {
let complexity = self.estimate_password_complexity(current_password);
match est_remaining {
Some(remaining) => {
let progress_pct = (total_tried as f64 / (total_tried + remaining) as f64) * 100.0;
format!(
"Password search progress: {:.2}% | Current: {} | Complexity: {:.1} bits | Tried: {} | Est. remaining: {}",
progress_pct, current_password, complexity, total_tried, remaining
)
}
None => {
format!(
"Password search | Current: {} | Complexity: {:.1} bits | Tried: {}",
current_password, complexity, total_tried
)
}
}
}
}
impl Default for Stage3Processor {
fn default() -> Self {
Self::new().expect("Failed to create Stage3Processor")
}
}
pub fn execute_stage3(solution: &Stage2Result, config: &crate::Config) -> Result<AttackResult> {
let processor = if config.output_zip.is_some() {
Stage3Processor::with_zip_support()?
} else {
Stage3Processor::new()?
};
processor.execute(solution, config)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Config;
#[test]
fn test_stage3_processor_creation() {
let processor = Stage3Processor::new();
assert!(processor.is_ok());
}
#[test]
fn test_password_validation() {
let processor = Stage3Processor::new().unwrap();
let password = "test123";
let plaintext = b"Hello, World!";
let keys = processor.key_manager.keys_from_password(password.as_bytes());
let mut ciphertext = plaintext.to_vec();
processor.key_manager.encrypt(&mut KeyState::new(keys.key0, keys.key1, keys.key2), &mut ciphertext);
let result = processor.validate_password(password, plaintext, &ciphertext);
assert!(result.is_ok());
assert!(result.unwrap());
let wrong_password = "wrongpass";
let result = processor.validate_password(wrong_password, plaintext, &ciphertext);
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_keys_match() {
let processor = Stage3Processor::new().unwrap();
let keys1 = KeyState::new(0x12345678, 0x87654321, 0xABCDEF00);
let keys2 = KeyState::new(0x12345678, 0x87654321, 0xABCDEF00);
let keys3 = KeyState::new(0x12345679, 0x87654321, 0xABCDEF00);
assert!(processor.keys_match(keys1, keys2, 0)); assert!(processor.keys_match(keys1, keys3, 1)); assert!(!processor.keys_match(keys1, keys3, 0)); }
#[test]
fn test_password_complexity() {
let processor = Stage3Processor::new().unwrap();
let simple = "abc";
let complex = "Abc123!@#";
let simple_complexity = processor.estimate_password_complexity(simple);
let complex_complexity = processor.estimate_password_complexity(complex);
assert!(simple_complexity < complex_complexity);
assert!(simple_complexity > 0.0);
assert!(complex_complexity > 0.0);
}
#[test]
fn test_generate_progress_report() {
let processor = Stage3Processor::new().unwrap();
let report = processor.generate_progress_report("test123", 1000, Some(9000));
assert!(report.contains("Password search"));
assert!(report.contains("test123"));
assert!(report.contains("10.00%"));
let report2 = processor.generate_progress_report("abc", 500, None);
assert!(report2.contains("Password search"));
assert!(report2.contains("abc"));
}
#[test]
fn test_common_passwords() {
let processor = Stage3Processor::new().unwrap();
let password = "password";
let target_keys = processor.key_manager.keys_from_password(password.as_bytes());
let result = processor.try_common_passwords(target_keys);
assert!(result.is_ok());
let found = result.unwrap();
assert!(found.is_some());
assert_eq!(found.unwrap(), password);
}
#[test]
fn test_brute_force_short() {
let processor = Stage3Processor::new().unwrap();
let password = "abc";
let target_keys = processor.key_manager.keys_from_password(password.as_bytes());
let result = processor.brute_force_search(target_keys, 3);
assert!(result.is_ok());
let found = result.unwrap();
assert!(found.is_some());
assert_eq!(found.unwrap(), password);
}
}