Skip to content

Commit a1f40e8

Browse files
committed
fix: unknown positions attack
1 parent 0c0af3e commit a1f40e8

File tree

1 file changed

+116
-13
lines changed

1 file changed

+116
-13
lines changed

src/crackers/pattern.rs

Lines changed: 116 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,90 @@ impl PatternCracker {
110110
new_str.pop();
111111
}
112112
}
113+
114+
/// Generates chunks of combinations for large pattern sizes to avoid memory issues
115+
/// and improve parallelism.
116+
///
117+
/// # Arguments
118+
///
119+
/// * `charset` - Characters to use in combinations
120+
/// * `unknown_positions` - Number of unknown positions
121+
/// * `chunk_size` - Size of each chunk
122+
/// * `pkcs12` - The PKCS#12 certificate to crack
123+
/// * `result` - Shared result tracking structure
124+
/// * `pattern` - The template pattern
125+
/// * `positions` - Indices of variable positions in the pattern
126+
///
127+
/// # Returns
128+
///
129+
/// Returns `true` if the password was found, `false` otherwise.
130+
fn process_chunks_in_parallel(
131+
charset: &[char],
132+
unknown_count: usize,
133+
chunk_size: usize,
134+
pkcs12: &Arc<Pkcs12>,
135+
result: &Arc<Mutex<CrackResult>>,
136+
pattern: &str,
137+
positions: &[usize],
138+
) -> bool {
139+
let charset_len = charset.len();
140+
let mut total_combinations: usize = 1;
141+
for _ in 0..unknown_count {
142+
// Overflow protection for very large combination spaces
143+
if total_combinations > usize::MAX / charset_len {
144+
total_combinations = usize::MAX / 2;
145+
break;
146+
}
147+
total_combinations *= charset_len;
148+
}
149+
150+
let adjusted_chunk_size = if unknown_count > 4 {
151+
charset_len.pow(3)
152+
} else {
153+
chunk_size
154+
};
155+
156+
println!(
157+
"Processing {} combinations in chunks of ~{}",
158+
total_combinations, adjusted_chunk_size
159+
);
160+
161+
// We'll use position indices to iterate through the combination space
162+
// The "position indices" approach allows us to process combinations
163+
// without generating them all at once
164+
165+
let num_chunks = (total_combinations + adjusted_chunk_size - 1) / adjusted_chunk_size;
166+
let chunks_range = 0..num_chunks;
167+
168+
// Use Rayon for parallel processing of chunks
169+
chunks_range
170+
.into_par_iter()
171+
.find_any(|chunk_idx| {
172+
let start_idx = chunk_idx * adjusted_chunk_size;
173+
let end_idx = (start_idx + adjusted_chunk_size).min(total_combinations);
174+
175+
// Generate just this chunk of combinations
176+
let mut chunk_combinations = Vec::with_capacity(end_idx - start_idx);
177+
for combo_idx in start_idx..end_idx {
178+
// Convert the linear index to a combination
179+
let mut indices = Vec::with_capacity(unknown_count);
180+
let mut remaining = combo_idx;
181+
182+
for _ in 0..unknown_count {
183+
indices.push(remaining % charset_len);
184+
remaining /= charset_len;
185+
}
186+
187+
// Generate the actual combination string
188+
let combination: String = indices.into_iter().map(|idx| charset[idx]).collect();
189+
190+
chunk_combinations.push(combination);
191+
}
192+
193+
Self::process_chunk(&chunk_combinations, pattern, positions, pkcs12, result)
194+
})
195+
.is_some()
196+
}
113197
}
114198

115199
impl PasswordCracker for PatternCracker {
@@ -138,24 +222,43 @@ impl PasswordCracker for PatternCracker {
138222
}
139223

140224
let charset: Vec<char> = self.charset.chars().collect();
141-
let mut combinations = Vec::new();
225+
let unknown_count = unknown_positions.len();
142226

143227
println!(
144228
"Generating pattern combinations for {} unknown positions",
145-
unknown_positions.len()
146-
);
147-
Self::generate_pattern_combinations(
148-
&charset,
149-
unknown_positions.len() as u8,
150-
"",
151-
&mut combinations,
229+
unknown_count
152230
);
153231

154-
combinations
155-
.par_chunks(super::CHUNK_SIZE)
156-
.find_any(|chunk| {
157-
Self::process_chunk(chunk, &password, &unknown_positions, pkcs12, result)
158-
});
232+
let found = if unknown_count >= 4 {
233+
Self::process_chunks_in_parallel(
234+
&charset,
235+
unknown_count,
236+
super::CHUNK_SIZE,
237+
pkcs12,
238+
result,
239+
&password,
240+
&unknown_positions,
241+
)
242+
} else {
243+
let mut combinations = Vec::new();
244+
Self::generate_pattern_combinations(
245+
&charset,
246+
unknown_count as u8,
247+
"",
248+
&mut combinations,
249+
);
250+
251+
combinations
252+
.par_chunks(super::CHUNK_SIZE)
253+
.find_any(|chunk| {
254+
Self::process_chunk(chunk, &password, &unknown_positions, pkcs12, result)
255+
})
256+
.is_some()
257+
};
258+
259+
if !found {
260+
println!("All combinations exhausted, password not found");
261+
}
159262

160263
Ok(())
161264
}

0 commit comments

Comments
 (0)