use crate::types::{CrackResult, PasswordCracker};
use anyhow::Result;
use openssl::pkcs12::Pkcs12;
use rayon::prelude::*;
use std::sync::{Arc, Mutex};
pub struct PatternCracker {
pattern: String,
charset: String,
pattern_symbol: char,
}
impl PatternCracker {
pub fn new(pattern: String, charset: String, pattern_symbol: char) -> Self {
Self {
pattern,
charset,
pattern_symbol,
}
}
#[inline(always)]
fn process_chunk(
chunk: &[String],
pattern: &str,
unknown_positions: &[usize],
pkcs12: &Pkcs12,
result: &Arc<Mutex<CrackResult>>,
) -> bool {
let mut password_chars = Vec::with_capacity(pattern.len());
for combination in chunk {
{
let result_guard = result.lock().unwrap();
if result_guard.password.is_some() {
return true;
}
result_guard.increment_attempts();
}
password_chars.clear();
password_chars.extend(pattern.chars());
for (pos, c) in unknown_positions.iter().zip(combination.chars()) {
password_chars[*pos] = c;
}
let password: String = password_chars.iter().collect();
if super::check_password(pkcs12, &password, result) {
return true;
}
}
false
}
fn generate_pattern_combinations(
charset: &[char],
length: u8,
current: &str,
result: &mut Vec<String>,
) {
if length == 0 {
result.push(current.to_owned());
return;
}
let mut new_str = current.to_owned();
for &c in charset {
new_str.push(c);
Self::generate_pattern_combinations(charset, length - 1, &new_str, result);
new_str.pop();
}
}
fn process_chunks_in_parallel(
charset: &[char],
unknown_count: usize,
chunk_size: usize,
pkcs12: &Arc<Pkcs12>,
result: &Arc<Mutex<CrackResult>>,
pattern: &str,
positions: &[usize],
) -> bool {
let charset_len = charset.len();
let mut total_combinations: usize = 1;
for _ in 0..unknown_count {
if total_combinations > usize::MAX / charset_len {
total_combinations = usize::MAX / 2;
break;
}
total_combinations *= charset_len;
}
let adjusted_chunk_size = if unknown_count > 4 {
charset_len.pow(3)
} else {
chunk_size
};
println!(
"Processing {} combinations in chunks of ~{}",
total_combinations, adjusted_chunk_size
);
let num_chunks = (total_combinations + adjusted_chunk_size - 1) / adjusted_chunk_size;
let chunks_range = 0..num_chunks;
chunks_range
.into_par_iter()
.find_any(|chunk_idx| {
let start_idx = chunk_idx * adjusted_chunk_size;
let end_idx = (start_idx + adjusted_chunk_size).min(total_combinations);
let mut chunk_combinations = Vec::with_capacity(end_idx - start_idx);
for combo_idx in start_idx..end_idx {
let mut indices = Vec::with_capacity(unknown_count);
let mut remaining = combo_idx;
for _ in 0..unknown_count {
indices.push(remaining % charset_len);
remaining /= charset_len;
}
let combination: String = indices.into_iter().map(|idx| charset[idx]).collect();
chunk_combinations.push(combination);
}
Self::process_chunk(&chunk_combinations, pattern, positions, pkcs12, result)
})
.is_some()
}
}
impl PasswordCracker for PatternCracker {
fn crack(&self, pkcs12: &Arc<Pkcs12>, result: &Arc<Mutex<CrackResult>>) -> Result<()> {
let mut password = String::with_capacity(self.pattern.len());
let mut unknown_positions = Vec::with_capacity(self.pattern.len());
for (i, c) in self.pattern.chars().enumerate() {
if c == self.pattern_symbol {
unknown_positions.push(i);
password.push('?');
} else {
password.push(c);
}
}
let charset: Vec<char> = self.charset.chars().collect();
let unknown_count = unknown_positions.len();
println!(
"Generating pattern combinations for {} unknown positions",
unknown_count
);
let found = if unknown_count >= 4 {
Self::process_chunks_in_parallel(
&charset,
unknown_count,
super::CHUNK_SIZE,
pkcs12,
result,
&password,
&unknown_positions,
)
} else {
let mut combinations = Vec::new();
Self::generate_pattern_combinations(
&charset,
unknown_count as u8,
"",
&mut combinations,
);
combinations
.par_chunks(super::CHUNK_SIZE)
.find_any(|chunk| {
Self::process_chunk(chunk, &password, &unknown_positions, pkcs12, result)
})
.is_some()
};
if !found {
println!("All combinations exhausted, password not found");
}
Ok(())
}
}