gpu_scatter_gather/
lib.rs1pub mod charset;
42pub mod keyspace;
43pub mod mask;
44pub mod transpose;
45
46#[cfg(feature = "cuda")]
48pub mod gpu;
49#[cfg(feature = "cuda")]
50pub mod multigpu;
51
52pub mod bindings;
53
54#[cfg(feature = "cuda")]
55pub mod ffi;
56
57pub use charset::Charset;
59pub use keyspace::{calculate_keyspace, index_to_word};
60pub use mask::Mask;
61
62use anyhow::Result;
63use std::collections::HashMap;
64
65pub struct WordlistGeneratorBuilder {
67 charsets: HashMap<usize, Charset>,
68 mask: Option<Vec<usize>>,
69 batch_size: usize,
70}
71
72impl WordlistGeneratorBuilder {
73 pub fn new() -> Self {
75 Self {
76 charsets: HashMap::new(),
77 mask: None,
78 batch_size: 10_000_000, }
80 }
81
82 pub fn charset(mut self, id: usize, charset: Charset) -> Self {
84 self.charsets.insert(id, charset);
85 self
86 }
87
88 pub fn mask(mut self, mask: &[usize]) -> Self {
90 self.mask = Some(mask.to_vec());
91 self
92 }
93
94 pub fn batch_size(mut self, size: usize) -> Self {
96 self.batch_size = size;
97 self
98 }
99
100 pub fn build(self) -> Result<WordlistGenerator> {
102 let mask = self.mask.ok_or_else(|| anyhow::anyhow!("Mask not set"))?;
103
104 for &charset_id in &mask {
106 if !self.charsets.contains_key(&charset_id) {
107 anyhow::bail!("Mask references undefined charset ID: {charset_id}");
108 }
109 }
110
111 Ok(WordlistGenerator {
112 charsets: self.charsets,
113 mask,
114 batch_size: self.batch_size,
115 })
116 }
117}
118
119impl Default for WordlistGeneratorBuilder {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125pub struct WordlistGenerator {
127 charsets: HashMap<usize, Charset>,
128 mask: Vec<usize>,
129 #[allow(dead_code)]
130 batch_size: usize,
131}
132
133impl WordlistGenerator {
134 pub fn builder() -> WordlistGeneratorBuilder {
136 WordlistGeneratorBuilder::new()
137 }
138
139 pub fn keyspace_size(&self) -> u128 {
141 self.mask
143 .iter()
144 .map(|&id| self.charsets[&id].len() as u128)
145 .product()
146 }
147
148 pub fn index_to_word(&self, index: u64) -> Vec<u8> {
150 let mut output = vec![0u8; self.mask.len()];
151 let mut remaining = index;
152
153 for pos in (0..self.mask.len()).rev() {
155 let charset_id = self.mask[pos];
156 let charset = &self.charsets[&charset_id];
157 let charset_size = charset.len() as u64;
158
159 let char_idx = (remaining % charset_size) as usize;
161 output[pos] = charset.as_bytes()[char_idx];
162 remaining /= charset_size;
163 }
164
165 output
166 }
167
168 pub fn iter(&self) -> WordlistIterator<'_> {
170 WordlistIterator {
171 generator: self,
172 current_index: 0,
173 total_keyspace: self.keyspace_size(),
174 }
175 }
176}
177
178pub struct WordlistIterator<'a> {
180 generator: &'a WordlistGenerator,
181 current_index: u64,
182 total_keyspace: u128,
183}
184
185impl<'a> Iterator for WordlistIterator<'a> {
186 type Item = Vec<u8>;
187
188 fn next(&mut self) -> Option<Self::Item> {
189 if self.current_index as u128 >= self.total_keyspace {
190 return None;
191 }
192
193 let word = self.generator.index_to_word(self.current_index);
194 self.current_index += 1;
195 Some(word)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_builder_pattern() {
205 let generator = WordlistGenerator::builder()
206 .charset(1, Charset::from("abc"))
207 .charset(2, Charset::from("123"))
208 .mask(&[1, 2])
209 .build()
210 .unwrap();
211
212 assert_eq!(generator.keyspace_size(), 9); }
214
215 #[test]
216 fn test_missing_charset() {
217 let result = WordlistGenerator::builder()
218 .charset(1, Charset::from("abc"))
219 .mask(&[1, 2]) .build();
221
222 assert!(result.is_err());
223 }
224}