@@ -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
115199impl 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