1use argon2::Argon2;
2use base64::prelude::*;
3use hkdf::Hkdf;
4use hmac::{Hmac, Mac};
5use indicatif::ParallelProgressIterator;
6use indicatif::ProgressStyle;
7use pbkdf2::{password_hash::PasswordHasher, Pbkdf2};
8use rayon::prelude::*;
9use sha2::{Digest, Sha256};
10
11pub mod cli;
12pub mod log;
13pub use cli::KDFConfig;
14
15pub fn password_hash(kdf_config: KDFConfig, password: &[u8], salt: &[u8]) -> [u8; 32] {
16 match kdf_config {
17 KDFConfig::Pbkdf2 { iterations } => {
18 let salt = pbkdf2::password_hash::SaltString::b64_encode(salt).unwrap();
19
20 Pbkdf2
21 .hash_password_customized(
22 password,
23 None,
24 None,
25 pbkdf2::Params {
26 rounds: iterations,
27 output_length: 32,
28 },
29 &salt,
30 )
31 .unwrap()
32 .hash
33 .unwrap()
34 .as_bytes()
35 .try_into()
36 .unwrap()
37 }
38 KDFConfig::Argon2 {
39 memory,
40 iterations,
41 parallelism,
42 } => {
43 let mut hasher = Sha256::new();
44 hasher.update(salt);
45 let salt = hasher.finalize();
46
47 let mut password_hash = [0; 32];
48 Argon2::new(
49 argon2::Algorithm::default(),
50 argon2::Version::default(),
51 argon2::Params::new(memory * 1024, iterations, parallelism, Some(32)).unwrap(),
52 )
53 .hash_password_into(password, &salt, &mut password_hash)
54 .unwrap();
55 password_hash
56 }
57 }
58}
59
60pub fn parse_encrypted(encrypted: &str) -> (Vec<u8>, Vec<u8>) {
61 let mut split = encrypted.split('.');
62 split.next();
63 let data = split.next().unwrap();
64
65 let mut split = data.split('|');
66 let iv = BASE64_STANDARD.decode(split.next().unwrap()).unwrap();
67 let ciphertext = BASE64_STANDARD.decode(split.next().unwrap()).unwrap();
68 let mac = BASE64_STANDARD.decode(split.next().unwrap()).unwrap();
69
70 let mut data = Vec::with_capacity(iv.len() + ciphertext.len());
71 data.extend(iv);
72 data.extend(ciphertext);
73
74 (data, mac)
75}
76
77pub fn stretch_key(password_hash: &[u8]) -> [u8; 32] {
78 let hkdf = Hkdf::<Sha256>::from_prk(password_hash).unwrap();
79 let mut mac_key = [0; 32];
80 hkdf.expand(b"mac", &mut mac_key).unwrap();
81 mac_key
82}
83
84pub fn mac_verify(mac_key: &[u8], data: &[u8], mac: &[u8]) -> bool {
85 let mut mac_verify = Hmac::<Sha256>::new_from_slice(mac_key).unwrap();
86 mac_verify.update(data);
87 mac_verify.verify_slice(mac).is_ok()
88}
89
90pub fn brute_force_pin(
91 encrypted: &str,
92 email: &str,
93 kdf_config: KDFConfig,
94 pins: impl Iterator<Item = String> + Send,
95 progress_max: Option<usize>,
96) -> Option<String> {
97 let (data, mac) = parse_encrypted(encrypted);
98
99 let find = |pin: &String| {
100 let password_hash = password_hash(kdf_config, pin.as_bytes(), email.as_bytes());
101 let mac_key = stretch_key(&password_hash);
102
103 mac_verify(&mac_key, &data, &mac)
104 };
105
106 if let Some(max) = progress_max {
107 pins.par_bridge()
108 .progress_count(max as u64)
109 .with_message("Cracking...")
110 .with_style(
111 ProgressStyle::default_bar()
112 .template("[{bar:.cyan/blue}] {pos:>7}/{len:7} - {msg} ({elapsed} + ETA {eta}, {per_sec})")
113 .unwrap(),
114 )
115 .find_any(find)
116 } else {
117 pins.par_bridge().find_any(find)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 const EMAIL: &str = "tenire3448@fashlend.com";
127
128 #[test]
129 fn memory_pbkdf2_600000() {
130 let encrypted = "2.P6TpPPpMf5zkHUfTplnocw==|KZ7/pR8ft+LwcjfXs2ym9hmxE7DLIeA9Kl+IPwTVCwLmbpkFtYKPWvK53DEDDrVUeYvz/rPcl3MEH3wXl200HCsV5ZbGLGVU4bha5Aw20fk=|+Y46Za3Oo63XRbvqLFz5cVuvbqMvBqopD16+8HV83mk=";
132 let kdf_config = KDFConfig::Pbkdf2 { iterations: 600000 };
133
134 let pins = (1330..1340).map(|pin| format!("{pin:04}"));
136
137 assert_eq!(
138 brute_force_pin(encrypted, EMAIL, kdf_config, pins, None),
139 Some("1337".to_string())
140 );
141 }
142
143 #[test]
144 fn disk_pbkdf2_600000() {
145 let encrypted = "2.AVcSzI6mgEA9a2oKtV9WOw==|mSLNpc7qoZFnwnoGnL08N1eDiauYM5VRnr0QSZ134cjR6xgVYD7JjAkmsXDmVNP6lL1lB2fOq7uFTynIqNdA41ZUCj6KoceIz4edGrLi8TY=|UgXNesoPaz0gup17dfw6pqQsab8rtAHb6MvFDrrjSAY=";
147 let kdf_config = KDFConfig::Pbkdf2 { iterations: 600000 };
148
149 let pins = (1330..1340).map(|pin| format!("{pin:04}"));
150
151 assert_eq!(
152 brute_force_pin(encrypted, EMAIL, kdf_config, pins, None),
153 Some("1337".to_string())
154 );
155 }
156
157 #[test]
158 fn memory_argon2_64_3_4() {
159 let encrypted = "2.GcLsRNPIwGWG+Q7X+KspXw==|tgn2oSFE6uXzlJzJ6rFBfqmlMjVaFVTe/weQRwBXF+BLlh8g5aE7VTw4yd5H3+j4f+YMGMiVTsHmphHdwrbKifmjkxcf35KPYJ93O6Zp4T0=|Im0X4t25NP+lf3oFo1Dp2ag1pc3eQwrRuEu9a5ecvVM=";
161 let kdf_config = KDFConfig::Argon2 {
162 memory: 64,
163 iterations: 3,
164 parallelism: 4,
165 };
166
167 let pins = (1330..1340).map(|pin| format!("{pin:04}"));
168
169 assert_eq!(
170 brute_force_pin(encrypted, EMAIL, kdf_config, pins, None),
171 Some("1337".to_string())
172 );
173 }
174
175 #[test]
176 fn disk_argon2_64_3_4() {
177 let encrypted = "2.FA4aPsq/5jKajc8tGqYKaQ==|CO/t9f1EQ4O5LL6O1anBAd1/4Hb+l4I32UMlW+3O7CoxTRXlEuLK5xvDCFmeRCYmylt206B22roFXycaRG3Z9fnN1aVVbBJ59qfCDEGusHw=|vmWmAb9kfqPPljRNhDMe+fDlwwat8XN5BZSsMAH8p8w=";
178 let kdf_config = KDFConfig::Argon2 {
179 memory: 64,
180 iterations: 3,
181 parallelism: 4,
182 };
183
184 let pins = (1330..1340).map(|pin| format!("{pin:04}"));
185
186 assert_eq!(
187 brute_force_pin(encrypted, EMAIL, kdf_config, pins, None),
188 Some("1337".to_string())
189 );
190 }
191}