crypto/
pbkdf2.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
7/*!
8 * This module implements the PBKDF2 Key Derivation Function as specified by
9 * http://tools.ietf.org/html/rfc2898.
10 */
11
12use std::iter::repeat;
13use std::io;
14use cryptoutil::copy_memory;
15
16use rand::{OsRng, Rng};
17use serialize::base64;
18use serialize::base64::{FromBase64, ToBase64};
19
20use cryptoutil::{read_u32_be, write_u32_be};
21use hmac::Hmac;
22use mac::Mac;
23use sha2::Sha256;
24use util::fixed_time_eq;
25
26// Calculate a block of the output of size equal to the output_bytes of the underlying Mac function
27// mac - The Mac function to use
28// salt - the salt value to use
29// c - the iteration count
30// idx - the 1 based index of the block
31// scratch - a temporary variable the same length as the block
32// block - the block of the output to calculate
33fn calculate_block<M: Mac>(
34        mac: &mut M,
35        salt: &[u8],
36        c: u32,
37        idx: u32,
38        scratch: &mut [u8],
39        block: &mut [u8]) {
40    // Perform the 1st iteration. The output goes directly into block
41    mac.input(salt);
42    let mut idx_buf = [0u8; 4];
43    write_u32_be(&mut idx_buf, idx);
44    mac.input(&idx_buf);
45    mac.raw_result(block);
46    mac.reset();
47
48    // Perform the 2nd iteration. The input comes from block and is output into scratch. scratch is
49    // then exclusive-or added into block. After all this, the input to the next step is now in
50    // scratch and block is left to just accumulate the exclusive-of sum of remaining iterations.
51    if c > 1 {
52        mac.input(block);
53        mac.raw_result(scratch);
54        mac.reset();
55        for (output, &input) in block.iter_mut().zip(scratch.iter()) {
56            *output ^= input;
57        }
58    }
59
60    // Perform all remaining iterations
61    for _ in 2..c {
62        mac.input(scratch);
63        mac.raw_result(scratch);
64        mac.reset();
65        for (output, &input) in block.iter_mut().zip(scratch.iter()) {
66            *output ^= input;
67        }
68    }
69}
70
71/**
72 * Execute the PBKDF2 Key Derivation Function. The Scrypt Key Derivation Function generally provides
73 * better security, so, applications that do not have a requirement to use PBKDF2 specifically
74 * should consider using that function instead.
75 *
76 * # Arguments
77 * * mac - The Pseudo Random Function to use.
78 * * salt - The salt value to use.
79 * * c - The iteration count. Users should carefully determine this value as it is the primary
80 *       factor in determining the security of the derived key.
81 * * output - The output buffer to fill with the derived key value.
82 *
83 */
84pub fn pbkdf2<M: Mac>(mac: &mut M, salt: &[u8], c: u32, output: &mut [u8]) {
85    assert!(c > 0);
86
87    let os = mac.output_bytes();
88
89    // A temporary storage array needed by calculate_block. This is really only necessary if c > 1.
90    // Most users of pbkdf2 should use a value much larger than 1, so, this allocation should almost
91    // always be necessary. A big exception is Scrypt. However, this allocation is unlikely to be
92    // the bottleneck in Scrypt performance.
93    let mut scratch: Vec<u8> = repeat(0).take(os).collect();
94
95    let mut idx: u32 = 0;
96
97    for chunk in output.chunks_mut(os) {
98        // The block index starts at 1. So, this is supposed to run on the first execution.
99        idx = idx.checked_add(1).expect("PBKDF2 size limit exceeded.");
100
101        if chunk.len() == os {
102            calculate_block(mac, salt, c, idx, &mut scratch, chunk);
103        } else {
104            let mut tmp: Vec<u8> = repeat(0).take(os).collect();
105            calculate_block(mac, salt, c, idx, &mut scratch[..], &mut tmp[..]);
106            let chunk_len = chunk.len();
107            copy_memory(&tmp[..chunk_len], chunk);
108        }
109    }
110}
111
112/**
113 * pbkdf2_simple is a helper function that should be sufficient for the majority of cases where
114 * an application needs to use PBKDF2 to hash a password for storage. The result is a String that
115 * contains the parameters used as part of its encoding. The pbkdf2_check function may be used on
116 * a password to check if it is equal to a hashed value.
117 *
118 * # Format
119 *
120 * The format of the output is a modified version of the Modular Crypt Format that encodes algorithm
121 * used and iteration count. The format is indicated as "rpbkdf2" which is short for "Rust PBKF2
122 * format."
123 *
124 * $rpbkdf2$0$<base64(c)>$<base64(salt)>$<based64(hash)>$
125 *
126 * # Arguments
127 *
128 * * password - The password to process as a str
129 * * c - The iteration count
130 *
131 */
132pub fn pbkdf2_simple(password: &str, c: u32) -> io::Result<String> {
133    let mut rng = try!(OsRng::new());
134
135    // 128-bit salt
136    let salt: Vec<u8> = rng.gen_iter::<u8>().take(16).collect();
137
138    // 256-bit derived key
139    let mut dk = [0u8; 32];
140
141    let mut mac = Hmac::new(Sha256::new(), password.as_bytes());
142
143    pbkdf2(&mut mac, &salt[..], c, &mut dk);
144
145    let mut result = "$rpbkdf2$0$".to_string();
146    let mut tmp = [0u8; 4];
147    write_u32_be(&mut tmp, c);
148    result.push_str(&tmp.to_base64(base64::STANDARD)[..]);
149    result.push('$');
150    result.push_str(&salt.to_base64(base64::STANDARD)[..]);
151    result.push('$');
152    result.push_str(&dk.to_base64(base64::STANDARD)[..]);
153    result.push('$');
154
155    Ok(result)
156}
157
158/**
159 * pbkdf2_check compares a password against the result of a previous call to pbkdf2_simple and
160 * returns true if the passed in password hashes to the same value.
161 *
162 * # Arguments
163 *
164 * * password - The password to process as a str
165 * * hashed_value - A string representing a hashed password returned by pbkdf2_simple()
166 *
167 */
168pub fn pbkdf2_check(password: &str, hashed_value: &str) -> Result<bool, &'static str> {
169    static ERR_STR: &'static str = "Hash is not in Rust PBKDF2 format.";
170
171    let mut iter = hashed_value.split('$');
172
173    // Check that there are no characters before the first "$"
174    match iter.next() {
175        Some(x) => if x != "" { return Err(ERR_STR); },
176        None => return Err(ERR_STR)
177    }
178
179    // Check the name
180    match iter.next() {
181        Some(t) => if t != "rpbkdf2" { return Err(ERR_STR); },
182        None => return Err(ERR_STR)
183    }
184
185    // Parse format - currenlty only version 0 is supported
186    match iter.next() {
187        Some(fstr) => {
188            match fstr {
189                "0" => { }
190                _ => return Err(ERR_STR)
191            }
192        }
193        None => return Err(ERR_STR)
194    }
195
196    // Parse the iteration count
197    let c = match iter.next() {
198        Some(pstr) => match pstr.from_base64() {
199            Ok(pvec) => {
200                if pvec.len() != 4 { return Err(ERR_STR); }
201                read_u32_be(&pvec[..])
202            }
203            Err(_) => return Err(ERR_STR)
204        },
205        None => return Err(ERR_STR)
206    };
207
208    // Salt
209    let salt = match iter.next() {
210        Some(sstr) => match sstr.from_base64() {
211            Ok(salt) => salt,
212            Err(_) => return Err(ERR_STR)
213        },
214        None => return Err(ERR_STR)
215    };
216
217    // Hashed value
218    let hash = match iter.next() {
219        Some(hstr) => match hstr.from_base64() {
220            Ok(hash) => hash,
221            Err(_) => return Err(ERR_STR)
222        },
223        None => return Err(ERR_STR)
224    };
225
226    // Make sure that the input ends with a "$"
227    match iter.next() {
228        Some(x) => if x != "" { return Err(ERR_STR); },
229        None => return Err(ERR_STR)
230    }
231
232    // Make sure there is no trailing data after the final "$"
233    match iter.next() {
234        Some(_) => return Err(ERR_STR),
235        None => { }
236    }
237
238    let mut mac = Hmac::new(Sha256::new(), password.as_bytes());
239
240    let mut output: Vec<u8> = repeat(0).take(hash.len()).collect();
241    pbkdf2(&mut mac, &salt[..], c, &mut output[..]);
242
243    // Be careful here - its important that the comparison be done using a fixed time equality
244    // check. Otherwise an adversary that can measure how long this step takes can learn about the
245    // hashed value which would allow them to mount an offline brute force attack against the
246    // hashed password.
247    Ok(fixed_time_eq(&output[..], &hash[..]))
248}
249
250#[cfg(test)]
251mod test {
252    use std::iter::repeat;
253
254    use pbkdf2::{pbkdf2, pbkdf2_simple, pbkdf2_check};
255    use hmac::Hmac;
256    use sha1::Sha1;
257
258    struct Test {
259        password: Vec<u8>,
260        salt: Vec<u8>,
261        c: u32,
262        expected: Vec<u8>
263    }
264
265    // Test vectors from http://tools.ietf.org/html/rfc6070. The 4th test vector is omitted because
266    // it takes too long to run.
267
268    fn tests() -> Vec<Test> {
269        vec![
270            Test {
271                password: b"password".to_vec(),
272                salt: b"salt".to_vec(),
273                c: 1,
274                expected: vec![
275                    0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
276                    0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
277                    0x2f, 0xe0, 0x37, 0xa6 ]
278            },
279            Test {
280                password: b"password".to_vec(),
281                salt: b"salt".to_vec(),
282                c: 2,
283                expected: vec![
284                    0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
285                    0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
286                    0xd8, 0xde, 0x89, 0x57 ]
287            },
288            Test {
289                password: b"password".to_vec(),
290                salt: b"salt".to_vec(),
291                c: 4096,
292                expected: vec![
293                    0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
294                    0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
295                    0x65, 0xa4, 0x29, 0xc1 ]
296            },
297            Test {
298                password: b"passwordPASSWORDpassword".to_vec(),
299                salt: b"saltSALTsaltSALTsaltSALTsaltSALTsalt".to_vec(),
300                c: 4096,
301                expected: vec![
302                    0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
303                    0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
304                    0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, 0x38 ]
305            },
306            Test {
307                password: vec![112, 97, 115, 115, 0, 119, 111, 114, 100],
308                salt: vec![115, 97, 0, 108, 116],
309                c: 4096,
310                expected: vec![
311                    0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
312                    0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 ]
313            }
314        ]
315    }
316
317    #[test]
318    fn test_pbkdf2() {
319        let tests = tests();
320        for t in tests.iter() {
321            let mut mac = Hmac::new(Sha1::new(), &t.password[..]);
322            let mut result: Vec<u8> = repeat(0).take(t.expected.len()).collect();
323            pbkdf2(&mut mac, &t.salt[..], t.c, &mut result);
324            assert!(result == t.expected);
325        }
326    }
327
328    #[test]
329    fn test_pbkdf2_simple() {
330        let password = "password";
331
332        let out1 = pbkdf2_simple(password, 1024).unwrap();
333        let out2 = pbkdf2_simple(password, 1024).unwrap();
334
335        // This just makes sure that a salt is being applied. It doesn't verify that that salt is
336        // cryptographically strong, however.
337        assert!(out1 != out2);
338
339        match pbkdf2_check(password, &out1[..]) {
340            Ok(r) => assert!(r),
341            Err(_) => panic!()
342        }
343        match pbkdf2_check(password, &out2[..]) {
344            Ok(r) => assert!(r),
345            Err(_) => panic!()
346        }
347
348        match pbkdf2_check("wrong", &out1[..]) {
349            Ok(r) => assert!(!r),
350            Err(_) => panic!()
351        }
352        match pbkdf2_check("wrong", &out2[..]) {
353            Ok(r) => assert!(!r),
354            Err(_) => panic!()
355        }
356    }
357}