crypto/
bcrypt_pbkdf.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use blowfish::Blowfish;
8use cryptoutil::{read_u32v_be, write_u32_be, write_u32_le};
9use sha2::Sha512;
10use digest::Digest;
11use step_by::RangeExt;
12
13fn bcrypt_hash(hpass: &[u8], hsalt: &[u8], output: &mut [u8; 32]) {
14    let mut bf = Blowfish::init_state();
15    bf.salted_expand_key(hsalt, hpass);
16
17    for _ in 0..64 {
18        bf.expand_key(hsalt);
19        bf.expand_key(hpass);
20    }
21
22    let mut buf = [0u32; 8];
23    read_u32v_be(&mut buf, b"OxychromaticBlowfishSwatDynamite");
24
25    for i in (0..8).step_up(2) {
26        for _ in 0..64 {
27            let (l, r) = bf.encrypt(buf[i], buf[i+1]);
28            buf[i] = l;
29            buf[i+1] = r;
30        }
31    }
32
33    for i in 0..8 {
34        write_u32_le(&mut output[i*4..(i+1)*4], buf[i]);
35    }
36}
37
38pub fn bcrypt_pbkdf(password: &[u8], salt: &[u8], rounds: u32, output: &mut [u8]) {
39    let mut hpass = [0u8; 64];
40
41    assert!(password.len() > 0);
42    assert!(salt.len() > 0);
43    assert!(rounds > 0);
44    assert!(output.len() > 0);
45    assert!(output.len() <= 1024);
46
47    let nblocks = (output.len() + 31) / 32;
48
49    let mut h = Sha512::new();
50    h.input(password);
51    h.result(&mut hpass);
52
53    for block in 1..(nblocks+1) {
54        let mut count = [0u8; 4];
55        let mut hsalt = [0u8; 64];
56        let mut out   = [0u8; 32];
57        write_u32_be(&mut count, block as u32);
58
59        h.reset();
60        h.input(salt);
61        h.input(&count);
62        h.result(&mut hsalt);
63
64        bcrypt_hash(&hpass, &hsalt, &mut out);
65        let mut tmp = out;
66
67        for _ in 1..rounds {
68            h.reset();
69            h.input(&tmp);
70            h.result(&mut hsalt);
71
72            bcrypt_hash(&hpass, &hsalt, &mut tmp);
73            for i in 0..out.len() {
74                out[i] ^= tmp[i];
75            }
76
77            for i in 0..out.len() {
78                let idx = i * nblocks + (block-1);
79                if idx < output.len() {
80                    output[idx] = out[i];
81                }
82            }
83        }
84    }
85}
86
87#[cfg(test)]
88mod test {
89    use std::iter::repeat;
90
91    use bcrypt_pbkdf::{bcrypt_pbkdf, bcrypt_hash};
92
93    #[test]
94    fn test_bcrypt_hash() {
95        struct Test {
96            hpass: [u8; 64],
97            hsalt: [u8; 64],
98            out:   [u8; 32],
99        }
100
101        let tests = vec!(
102            Test{
103                hpass: [
104                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
108                hsalt: [
109                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
113                out: [
114                    0x46, 0x02, 0x86, 0xe9, 0x72, 0xfa, 0x83, 0x3f, 0x8b, 0x12, 0x83, 0xad, 0x8f, 0xa9, 0x19, 0xfa,
115                    0x29, 0xbd, 0xe2, 0x0e, 0x23, 0x32, 0x9e, 0x77, 0x4d, 0x84, 0x22, 0xba, 0xc0, 0xa7, 0x92, 0x6c, ],
116            }, Test{
117                hpass: [
118                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
119                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
120                    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
121                    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f ],
122                hsalt: [
123                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
127                out: [
128                    0xb0, 0xb2, 0x29, 0xdb, 0xc6, 0xba, 0xde, 0xf0, 0xe1, 0xda, 0x25, 0x27, 0x47, 0x4a, 0x8b, 0x28,
129                    0x88, 0x8f, 0x8b, 0x06, 0x14, 0x76, 0xfe, 0x80, 0xc3, 0x22, 0x56, 0xe1, 0x14, 0x2d, 0xd0, 0x0d ],
130            }, Test{
131                hpass: [
132                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
136                hsalt: [
137                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
138                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
139                    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
140                    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f ],
141                out: [
142                    0xb6, 0x2b, 0x4e, 0x36, 0x7d, 0x31, 0x57, 0xf5, 0xc3, 0x1e, 0x4d, 0x2c, 0xba, 0xfb, 0x29, 0x31,
143                    0x49, 0x4d, 0x9d, 0x3b, 0xdd, 0x17, 0x1d, 0x55, 0xcf, 0x79, 0x9f, 0xa4, 0x41, 0x60, 0x42, 0xe2 ],
144            }, Test{
145                hpass: [
146                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
147                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
148                    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
149                    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f ],
150                hsalt: [
151                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
152                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
153                    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
154                    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f ],
155                out: [
156                    0xc6, 0xa9, 0x5f, 0xe6, 0x41, 0x31, 0x15, 0xfb, 0x57, 0xe9, 0x9f, 0x75, 0x74, 0x98, 0xe8, 0x5d,
157                    0xa3, 0xc6, 0xe1, 0xdf, 0x0c, 0x3c, 0x93, 0xaa, 0x97, 0x5c, 0x54, 0x8a, 0x34, 0x43, 0x26, 0xf8 ],
158            },
159        );
160
161        for t in tests.iter() {
162            let mut out = [0u8; 32];
163            bcrypt_hash(&t.hpass, &t.hsalt, &mut out);
164            assert!(out == t.out);
165        }
166    }
167
168    #[test]
169    fn test_openbsd_vectors() {
170        struct Test{
171            password: Vec<u8>,
172            salt: Vec<u8>,
173            rounds: u32,
174            out: Vec<u8>,
175        }
176
177        let tests = vec!(
178            Test{
179                password: b"password".to_vec(),
180                salt: b"salt".to_vec(),
181                rounds: 4,
182                out: vec![
183                    0x5b, 0xbf, 0x0c, 0xc2, 0x93, 0x58, 0x7f, 0x1c, 0x36, 0x35, 0x55, 0x5c, 0x27, 0x79, 0x65, 0x98,
184                    0xd4, 0x7e, 0x57, 0x90, 0x71, 0xbf, 0x42, 0x7e, 0x9d, 0x8f, 0xbe, 0x84, 0x2a, 0xba, 0x34, 0xd9],
185            }, Test{
186                password: b"password".to_vec(),
187                salt: vec![0],
188                rounds: 4,
189                out: vec![0xc1, 0x2b, 0x56, 0x62, 0x35, 0xee, 0xe0, 0x4c, 0x21, 0x25, 0x98, 0x97, 0x0a, 0x57, 0x9a, 0x67],
190            }, Test{
191                password: vec![0],
192                salt: b"salt".to_vec(),
193                rounds: 4,
194                out: vec![0x60, 0x51, 0xbe, 0x18, 0xc2, 0xf4, 0xf8, 0x2c, 0xbf, 0x0e, 0xfe, 0xe5, 0x47, 0x1b, 0x4b, 0xb9],
195            }, Test{
196                password: b"password\x00".to_vec(),
197                salt: b"salt\x00".to_vec(),
198                rounds: 4,
199                out: vec![
200                    0x74, 0x10, 0xe4, 0x4c, 0xf4, 0xfa, 0x07, 0xbf, 0xaa, 0xc8, 0xa9, 0x28, 0xb1, 0x72, 0x7f, 0xac,
201                    0x00, 0x13, 0x75, 0xe7, 0xbf, 0x73, 0x84, 0x37, 0x0f, 0x48, 0xef, 0xd1, 0x21, 0x74, 0x30, 0x50],
202            }, Test{
203                password: b"pass\x00wor".to_vec(),
204                salt: b"sa\x00l".to_vec(),
205                rounds: 4,
206                out: vec![0xc2, 0xbf, 0xfd, 0x9d, 0xb3, 0x8f, 0x65, 0x69, 0xef, 0xef, 0x43, 0x72, 0xf4, 0xde, 0x83, 0xc0],
207            }, Test{
208                password: b"pass\x00word".to_vec(),
209                salt: b"sa\x00lt".to_vec(),
210                rounds: 4,
211                out: vec![0x4b, 0xa4, 0xac, 0x39, 0x25, 0xc0, 0xe8, 0xd7, 0xf0, 0xcd, 0xb6, 0xbb, 0x16, 0x84, 0xa5, 0x6f],
212            }, Test{
213                password: b"password".to_vec(),
214                salt: b"salt".to_vec(),
215                rounds: 8,
216                out: vec![
217                    0xe1, 0x36, 0x7e, 0xc5, 0x15, 0x1a, 0x33, 0xfa, 0xac, 0x4c, 0xc1, 0xc1, 0x44, 0xcd, 0x23, 0xfa,
218                    0x15, 0xd5, 0x54, 0x84, 0x93, 0xec, 0xc9, 0x9b, 0x9b, 0x5d, 0x9c, 0x0d, 0x3b, 0x27, 0xbe, 0xc7,
219                    0x62, 0x27, 0xea, 0x66, 0x08, 0x8b, 0x84, 0x9b, 0x20, 0xab, 0x7a, 0xa4, 0x78, 0x01, 0x02, 0x46,
220                    0xe7, 0x4b, 0xba, 0x51, 0x72, 0x3f, 0xef, 0xa9, 0xf9, 0x47, 0x4d, 0x65, 0x08, 0x84, 0x5e, 0x8d],
221            }, Test{
222                password: b"password".to_vec(),
223                salt: b"salt".to_vec(),
224                rounds: 42,
225                out: vec![0x83, 0x3c, 0xf0, 0xdc, 0xf5, 0x6d, 0xb6, 0x56, 0x08, 0xe8, 0xf0, 0xdc, 0x0c, 0xe8, 0x82, 0xbd],
226            }, Test{
227                password: b"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.".to_vec(),
228                salt: b"salis\x00".to_vec(),
229                rounds: 8,
230                out: vec![0x10, 0x97, 0x8b, 0x07, 0x25, 0x3d, 0xf5, 0x7f, 0x71, 0xa1, 0x62, 0xeb, 0x0e, 0x8a, 0xd3, 0x0a],
231            }, Test{
232                password: vec![0x0d, 0xb3, 0xac, 0x94, 0xb3, 0xee, 0x53, 0x28, 0x4f, 0x4a, 0x22, 0x89, 0x3b, 0x3c, 0x24, 0xae],
233                salt: vec![0x3a, 0x62, 0xf0, 0xf0, 0xdb, 0xce, 0xf8, 0x23, 0xcf, 0xcc, 0x85, 0x48, 0x56, 0xea, 0x10, 0x28],
234                rounds: 8,
235                out: vec![0x20, 0x44, 0x38, 0x17, 0x5e, 0xee, 0x7c, 0xe1, 0x36, 0xc9, 0x1b, 0x49, 0xa6, 0x79, 0x23, 0xff],
236            }, Test{
237                password: vec![0x0d, 0xb3, 0xac, 0x94, 0xb3, 0xee, 0x53, 0x28, 0x4f, 0x4a, 0x22, 0x89, 0x3b, 0x3c, 0x24, 0xae],
238                salt: vec![0x3a, 0x62, 0xf0, 0xf0, 0xdb, 0xce, 0xf8, 0x23, 0xcf, 0xcc, 0x85, 0x48, 0x56, 0xea, 0x10, 0x28],
239                rounds: 8,
240                out: vec![
241                    0x20, 0x54, 0xb9, 0xff, 0xf3, 0x4e, 0x37, 0x21, 0x44, 0x03, 0x34, 0x74, 0x68, 0x28, 0xe9, 0xed,
242                    0x38, 0xde, 0x4b, 0x72, 0xe0, 0xa6, 0x9a, 0xdc, 0x17, 0x0a, 0x13, 0xb5, 0xe8, 0xd6, 0x46, 0x38,
243                    0x5e, 0xa4, 0x03, 0x4a, 0xe6, 0xd2, 0x66, 0x00, 0xee, 0x23, 0x32, 0xc5, 0xed, 0x40, 0xad, 0x55,
244                    0x7c, 0x86, 0xe3, 0x40, 0x3f, 0xbb, 0x30, 0xe4, 0xe1, 0xdc, 0x1a, 0xe0, 0x6b, 0x99, 0xa0, 0x71,
245                    0x36, 0x8f, 0x51, 0x8d, 0x2c, 0x42, 0x66, 0x51, 0xc9, 0xe7, 0xe4, 0x37, 0xfd, 0x6c, 0x91, 0x5b,
246                    0x1b, 0xbf, 0xc3, 0xa4, 0xce, 0xa7, 0x14, 0x91, 0x49, 0x0e, 0xa7, 0xaf, 0xb7, 0xdd, 0x02, 0x90,
247                    0xa6, 0x78, 0xa4, 0xf4, 0x41, 0x12, 0x8d, 0xb1, 0x79, 0x2e, 0xab, 0x27, 0x76, 0xb2, 0x1e, 0xb4,
248                    0x23, 0x8e, 0x07, 0x15, 0xad, 0xd4, 0x12, 0x7d, 0xff, 0x44, 0xe4, 0xb3, 0xe4, 0xcc, 0x4c, 0x4f,
249                    0x99, 0x70, 0x08, 0x3f, 0x3f, 0x74, 0xbd, 0x69, 0x88, 0x73, 0xfd, 0xf6, 0x48, 0x84, 0x4f, 0x75,
250                    0xc9, 0xbf, 0x7f, 0x9e, 0x0c, 0x4d, 0x9e, 0x5d, 0x89, 0xa7, 0x78, 0x39, 0x97, 0x49, 0x29, 0x66,
251                    0x61, 0x67, 0x07, 0x61, 0x1c, 0xb9, 0x01, 0xde, 0x31, 0xa1, 0x97, 0x26, 0xb6, 0xe0, 0x8c, 0x3a,
252                    0x80, 0x01, 0x66, 0x1f, 0x2d, 0x5c, 0x9d, 0xcc, 0x33, 0xb4, 0xaa, 0x07, 0x2f, 0x90, 0xdd, 0x0b,
253                    0x3f, 0x54, 0x8d, 0x5e, 0xeb, 0xa4, 0x21, 0x13, 0x97, 0xe2, 0xfb, 0x06, 0x2e, 0x52, 0x6e, 0x1d,
254                    0x68, 0xf4, 0x6a, 0x4c, 0xe2, 0x56, 0x18, 0x5b, 0x4b, 0xad, 0xc2, 0x68, 0x5f, 0xbe, 0x78, 0xe1,
255                    0xc7, 0x65, 0x7b, 0x59, 0xf8, 0x3a, 0xb9, 0xab, 0x80, 0xcf, 0x93, 0x18, 0xd6, 0xad, 0xd1, 0xf5,
256                    0x93, 0x3f, 0x12, 0xd6, 0xf3, 0x61, 0x82, 0xc8, 0xe8, 0x11, 0x5f, 0x68, 0x03, 0x0a, 0x12, 0x44],
257            },
258        );
259
260        for t in tests.iter() {
261            let mut out: Vec<u8> = repeat(0).take(t.out.len()).collect();
262            bcrypt_pbkdf(&t.password[..], &t.salt[..], t.rounds, &mut out[..]);
263            assert_eq!(out, t.out);
264        }
265    }
266}
267
268#[cfg(all(test, feature = "with-bench"))]
269mod bench {
270    use test::Bencher;
271    use bcrypt_pbkdf::bcrypt_pbkdf;
272
273    #[bench]
274    fn bench_bcrypt_pbkdf_5_32(b: &mut Bencher) {
275        let pass = [0u8; 16];
276        let salt = [0u8; 16];
277        let mut out  = [0u8; 32];
278
279        b.iter(|| {
280            bcrypt_pbkdf(&pass, &salt, 5, &mut out);
281        });
282    }
283}