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