1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
use byteorder::{ByteOrder, BigEndian};

use crypto::block::blowfish::Blowfish;

fn bcrypt_setup(cost: usize, salt: &[u8], key: &[u8]) -> Blowfish {
    let mut state = Blowfish::init().salted_expand_key(salt, key);

    for _ in 0..(1 << cost) {
        state = state.expand_key(key).expand_key(salt);
    }

    state
}

pub fn bcrypt<S: AsRef<[u8]>, I: AsRef<[u8]>, O: AsMut<[u8]>>(cost: usize,
                                                              salt: S,
                                                              input: I,
                                                              mut output: O) {
    assert_eq!(salt.as_ref().len(), 16);
    assert!(0 < input.as_ref().len() && input.as_ref().len() <= 72);
    assert_eq!(output.as_mut().len(), 24);

    let mut output = output.as_mut();

    let state = bcrypt_setup(cost, salt.as_ref(), input.as_ref());
    let mut ctext = [0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274];
    for (chunk, out) in ctext.chunks_mut(2).zip(output.chunks_mut(8)) {
        for _ in 0..64 {
            let (l, r) = state.encrypt_round((chunk[0], chunk[1]));
            chunk[0] = l;
            chunk[1] = r;
        }
        BigEndian::write_u32(&mut out[0..4], chunk[0]);
        BigEndian::write_u32(&mut out[4..8], chunk[1]);
    }
}