1use crate::{
2 base64::{decode_base64, encode_base64},
3 utils::{generate_random_numbers, get_byte_from_char, get_byte_from_number},
4 BCRYPT_SALT_LEN, BF_CRYPT_CIPHERTEXT, BLOWFISH_NUM_ROUNDS, P_ORIG, S_ORIG,
5};
6
7#[allow(non_snake_case)]
8pub struct BCrypt {
9 pub P: Vec<isize>,
10 pub S: Vec<isize>,
11}
12
13impl BCrypt {
14 fn new() -> Self {
15 Self {
16 P: vec![],
17 S: vec![],
18 }
19 }
20
21 fn init_key(&mut self) -> () {
22 self.P = P_ORIG.clone().to_vec();
23 self.S = S_ORIG.clone().to_vec();
24 }
25
26 fn encipher(&mut self, mut lr: Vec<isize>, off: usize) -> Vec<isize> {
27 let mut i;
28 let mut n;
29 let mut l = lr[off];
30 let mut r = lr[off + 1];
31
32 l ^= self.P[0];
33
34 i = 0;
35
36 while i <= BLOWFISH_NUM_ROUNDS - 2 {
37 n = self.S[(l >> 24) as usize & 0xff];
39 n = n
40 .checked_add(self.S[0x100 | ((l >> 16) as usize & 0xff)])
41 .unwrap_or(0);
42 n ^= self.S[0x200 | ((l >> 8) as usize & 0xff)];
43 n = n
44 .checked_add(self.S[0x300 | (l & 0xff) as usize])
45 .unwrap_or(0);
46
47 i = i + 1;
48
49 r ^= n ^ self.P[i];
50
51 n = self.S[(r >> 24) as usize & 0xff];
53 n = n
54 .checked_add(self.S[0x100 | ((r >> 16) as usize & 0xff)])
55 .unwrap_or(0);
56 n ^= self.S[0x200 | ((r >> 8) as usize & 0xff)];
57 n = n
58 .checked_add(self.S[0x300 | (r & 0xff) as usize])
59 .unwrap_or(0);
60
61 i = i + 1;
62
63 l ^= n ^ self.P[i];
64 }
65
66 lr[off] = r ^ self.P[BLOWFISH_NUM_ROUNDS + 1];
67 lr[off + 1] = l;
68
69 lr
70 }
71
72 fn streamtoword(data: Vec<isize>, mut offp: Vec<usize>) -> (isize, Vec<usize>) {
73 let mut i = 0;
74 let mut word = 0;
75 let mut off = offp[0];
76
77 while i < 4 {
78 word = (word << 8) | (data[off] & 0xff);
79 off = (off + 1) % data.len();
80
81 i = i + 1;
82 }
83
84 offp[0] = off;
85
86 return (word, offp);
87 }
88
89 fn key(&mut self, key: Vec<isize>) {
90 let mut i;
91 let mut koffp = vec![0];
92 let mut lr = vec![0, 0];
93 let plen = self.P.len();
94 let slen = self.S.len();
95
96 i = 0;
97
98 while i < plen {
99 let (word, offp) = BCrypt::streamtoword(key.clone(), koffp);
100
101 self.P[i] = self.P[i] ^ word;
102 koffp = offp;
103
104 i = i + 1;
105 }
106
107 i = 0;
108
109 while i < plen {
110 lr = self.encipher(lr.clone(), 0);
111 self.P[i] = lr[0];
112 self.P[i + 1] = lr[1];
113
114 i = i + 2;
115 }
116
117 i = 0;
118
119 while i < slen {
120 lr = self.encipher(lr, 0);
121 self.S[i] = lr[0];
122 self.S[i + 1] = lr[1];
123
124 i = i + 2;
125 }
126 }
127
128 fn ekskey(&mut self, data: Vec<isize>, key: Vec<isize>) {
129 let mut i;
130 let mut koffp = vec![0];
131 let mut doffp = vec![0];
132 let mut lr = vec![0, 0];
133 let plen = self.P.len();
134 let slen = self.S.len();
135
136 i = 0;
137
138 while i < plen {
139 let (word, offp) = BCrypt::streamtoword(key.clone(), koffp);
140
141 self.P[i] = self.P[i] ^ word;
142 koffp = offp;
143
144 i = i + 1;
145 }
146
147 i = 0;
148
149 while i < plen {
150 let (word, offp) = BCrypt::streamtoword(data.clone(), doffp.clone());
151
152 lr[0] ^= word;
153 doffp = offp;
154
155 let (word, offp) = BCrypt::streamtoword(data.clone(), doffp.clone());
156
157 lr[1] ^= word;
158 doffp = offp;
159
160 lr = self.encipher(lr, 0);
161
162 self.P[i] = lr[0];
163 self.P[i + 1] = lr[1];
164
165 i = i + 2;
166 }
167
168 i = 0;
169
170 while i < slen {
171 let (word, offp) = BCrypt::streamtoword(data.clone(), doffp.clone());
172
173 lr[0] ^= word;
174 doffp = offp;
175
176 let (word, offp) = BCrypt::streamtoword(data.clone(), doffp.clone());
177
178 lr[1] ^= word;
179 doffp = offp;
180
181 lr = self.encipher(lr, 0);
182
183 self.S[i] = lr[0];
184 self.S[i + 1] = lr[1];
185
186 i = i + 2;
187 }
188 }
189
190 fn crypt_raw<'a>(
191 &mut self,
192 password: Vec<isize>,
193 salt: Vec<isize>,
194 log_rounds: usize,
195 data: Vec<isize>,
196 ) -> Result<Vec<isize>, &'a str> {
197 let mut i;
198 let mut j;
199 let mut cdata = data;
200 let clen = cdata.len();
201 let mut ret: Vec<isize> = vec![];
202
203 let rounds = 1 << log_rounds;
204
205 if log_rounds < 4 || log_rounds > 30 {
206 return Err("Bad number of rounds");
207 }
208
209 if salt.len() != BCRYPT_SALT_LEN as usize {
210 return Err("Bad salt length");
211 }
212
213 self.init_key();
214 self.ekskey(salt.clone(), password.clone());
215
216 i = 0;
217
218 while i != rounds {
219 self.key(password.clone());
220 self.key(salt.clone());
221
222 i = i + 1;
223 }
224
225 i = 0;
226 j = 0;
227
228 while i < 64 {
229 while j < (clen >> 1) {
230 cdata = self.encipher(cdata.clone(), j << 1);
231
232 j = j + 1;
233 }
234
235 i = i + 1;
236 }
237
238 i = 0;
239
240 while i < clen {
241 ret.push(get_byte_from_number((cdata[i] >> 24) & 0xff));
242 ret.push(get_byte_from_number((cdata[i] >> 16) & 0xff));
243 ret.push(get_byte_from_number((cdata[i] >> 8) & 0xff));
244 ret.push(get_byte_from_number(cdata[i] & 0xff));
245
246 i = i + 1;
247 }
248
249 Ok(ret)
250 }
251
252 fn password_to_bytes<'a>(password: String) -> Result<Vec<isize>, &'a str> {
253 let mut passwordb: Vec<isize> = vec![];
254
255 for c in password.chars() {
256 let code = c as isize;
257
258 if code < 128 {
259 passwordb.push(code);
260 } else if code > 127 && code < 2048 {
261 passwordb.push((code >> 6) | 192);
262 passwordb.push((code & 63) | 128);
263 } else if code >= 55296 && code <= 56319 {
264 let next_code = password.chars().nth(1).unwrap_or('\0') as isize;
265
266 if next_code < 56320 || next_code > 57343 {
267 return Err("utf-16 Decoding error: trail surrogate not in the range of 0xdc00 through 0xdfff");
268 }
269
270 let decoded = ((code - 55296) << 10) + (next_code - 56320) + 65536;
271 passwordb.push((decoded >> 18) | 240);
272 passwordb.push(((decoded >> 12) & 63) | 128);
273 passwordb.push(((decoded >> 6) & 63) | 128);
274 passwordb.push((decoded & 63) | 128);
275 } else {
276 passwordb.push((code >> 12) | 224);
277 passwordb.push(((code >> 6) & 63) | 128);
278 passwordb.push((code & 63) | 128);
279 }
280 }
281
282 Ok(passwordb)
283 }
284
285 pub fn hashpw<'a>(password: String, salt: String) -> Result<String, &'a str> {
286 let mut bcrypt = BCrypt::new();
287 let real_salt: String;
288 let passwordb: Vec<isize>;
289 let saltb: Vec<isize>;
290 let hashed: Vec<isize>;
291 let mut minor: char = '0';
292 let rounds: usize;
293 let off;
294 let mut result: String = "".into();
295
296 if salt.chars().nth(0) != Some('$') || salt.chars().nth(1) != Some('2') {
297 return Err("Invalid salt version");
298 }
299
300 if salt.chars().nth(2) == Some('$') {
301 off = 3;
302 } else {
303 match salt.chars().nth(2) {
304 Some(c) => {
305 minor = c;
306
307 if minor != 'a' || salt.chars().nth(3) != Some('$') {
308 return Err("Invalid salt revision");
309 }
310
311 off = 4;
312 }
313 None => return Err("Invalid salt revision"),
314 }
315 }
316
317 if salt.chars().nth(off + 2) > Some('$') {
318 return Err("Missing salt rounds");
319 }
320
321 match salt[off..off + 2].parse::<usize>() {
322 Ok(x) => rounds = x,
323 Err(_) => return Err("Missing salt rounds"),
324 }
325
326 real_salt = salt[off + 3..off + 25].to_owned();
327 let password_ = format!("{}{}", password, if minor >= 'a' { "\0" } else { "" });
328
329 match BCrypt::password_to_bytes(password_) {
330 Ok(x) => passwordb = x,
331 Err(err) => return Err(err),
332 }
333
334 match decode_base64(real_salt, BCRYPT_SALT_LEN as usize) {
335 Ok(x) => saltb = x,
336 Err(err) => return Err(err),
337 }
338
339 match bcrypt.crypt_raw(
340 passwordb,
341 saltb.clone(),
342 rounds,
343 BF_CRYPT_CIPHERTEXT.to_vec().clone(),
344 ) {
345 Ok(x) => hashed = x,
346 Err(err) => return Err(err),
347 }
348
349 result.push_str("$2");
350
351 if minor >= 'a' {
352 result.push(minor);
353 }
354
355 result.push('$');
356
357 if rounds < 10 {
358 result.push('0');
359 }
360
361 if rounds > 30 {
362 return Err("Rounds exceeds maximum (30)");
363 }
364
365 result.push_str(rounds.to_string().as_str());
366 result.push('$');
367
368 match encode_base64(saltb.clone(), saltb.len()) {
369 Ok(x) => result.push_str(x.as_str()),
370 Err(err) => return Err(err),
371 }
372
373 match encode_base64(hashed, BF_CRYPT_CIPHERTEXT.to_vec().clone().len() * 4 - 1) {
374 Ok(x) => result.push_str(x.as_str()),
375 Err(err) => return Err(err),
376 }
377
378 Ok(result)
379 }
380
381 pub fn gensalt<'a>(rounds: u32) -> Result<String, &'a str> {
382 if rounds < 4 || rounds > 30 {
383 return Err("Rounds excededs maximum (30)!");
384 }
385
386 let mut output: String = "".into();
387
388 output.push_str("$2a$");
389
390 if rounds < 10 {
391 output.push('0');
392 }
393
394 output.push_str(rounds.to_string().as_str());
395 output.push('$');
396
397 match encode_base64(generate_random_numbers(), BCRYPT_SALT_LEN as usize) {
398 Ok(x) => output.push_str(x.as_str()),
399 Err(err) => return Err(err),
400 }
401
402 Ok(output)
403 }
404
405 pub fn checkpw(plaintext: String, hashed: String) -> bool {
406 let off;
407
408 if hashed.chars().nth(0) != Some('$') || hashed.chars().nth(1) != Some('2') {
409 return false;
410 }
411
412 if hashed.chars().nth(2) == Some('$') {
413 off = 3;
414 } else {
415 match hashed.chars().nth(2) {
416 Some(minor) => {
417 if (minor != 'a' && minor != 'b') || hashed.chars().nth(3) != Some('$') {
418 return false;
419 }
420
421 off = 4;
422 }
423 None => return false,
424 }
425 }
426
427 let salt = hashed[..off + 25].to_owned();
428
429 match BCrypt::hashpw(plaintext, salt) {
430 Ok(try_pass) => {
431 let mut ret = 0;
432
433 let mut i = 0;
434
435 while i < hashed.len() {
436 match (hashed.chars().nth(i), try_pass.chars().nth(i)) {
437 (Some(x), Some(y)) => ret |= get_byte_from_char(x) ^ get_byte_from_char(y),
438 _ => return false,
439 }
440
441 i = i + 1;
442 }
443
444 return ret == 0;
445 }
446 Err(_) => return false,
447 }
448 }
449}