crypto/
scrypt.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 Scrypt key derivation function as specified in [1].
9 *
10 * # References
11 * [1] - C. Percival. Stronger Key Derivation Via Sequential Memory-Hard Functions.
12 *       http://www.tarsnap.com/scrypt/scrypt.pdf
13 */
14
15use std;
16use std::iter::repeat;
17use std::io;
18use std::mem::size_of;
19use cryptoutil::copy_memory;
20
21use rand::{OsRng, Rng};
22use serialize::base64;
23use serialize::base64::{FromBase64, ToBase64};
24
25use cryptoutil::{read_u32_le, read_u32v_le, write_u32_le};
26use hmac::Hmac;
27use pbkdf2::pbkdf2;
28use sha2::Sha256;
29use util::fixed_time_eq;
30
31// The salsa20/8 core function.
32fn salsa20_8(input: &[u8], output: &mut [u8]) {
33
34    let mut x = [0u32; 16];
35    read_u32v_le(&mut x, input);
36
37    let rounds = 8;
38
39    macro_rules! run_round (
40        ($($set_idx:expr, $idx_a:expr, $idx_b:expr, $rot:expr);*) => { {
41            $( x[$set_idx] ^= x[$idx_a].wrapping_add(x[$idx_b]).rotate_left($rot); )*
42        } }
43    );
44
45    for _ in 0..rounds / 2 {
46        run_round!(
47            0x4, 0x0, 0xc, 7;
48            0x8, 0x4, 0x0, 9;
49            0xc, 0x8, 0x4, 13;
50            0x0, 0xc, 0x8, 18;
51            0x9, 0x5, 0x1, 7;
52            0xd, 0x9, 0x5, 9;
53            0x1, 0xd, 0x9, 13;
54            0x5, 0x1, 0xd, 18;
55            0xe, 0xa, 0x6, 7;
56            0x2, 0xe, 0xa, 9;
57            0x6, 0x2, 0xe, 13;
58            0xa, 0x6, 0x2, 18;
59            0x3, 0xf, 0xb, 7;
60            0x7, 0x3, 0xf, 9;
61            0xb, 0x7, 0x3, 13;
62            0xf, 0xb, 0x7, 18;
63            0x1, 0x0, 0x3, 7;
64            0x2, 0x1, 0x0, 9;
65            0x3, 0x2, 0x1, 13;
66            0x0, 0x3, 0x2, 18;
67            0x6, 0x5, 0x4, 7;
68            0x7, 0x6, 0x5, 9;
69            0x4, 0x7, 0x6, 13;
70            0x5, 0x4, 0x7, 18;
71            0xb, 0xa, 0x9, 7;
72            0x8, 0xb, 0xa, 9;
73            0x9, 0x8, 0xb, 13;
74            0xa, 0x9, 0x8, 18;
75            0xc, 0xf, 0xe, 7;
76            0xd, 0xc, 0xf, 9;
77            0xe, 0xd, 0xc, 13;
78            0xf, 0xe, 0xd, 18
79        )
80    }
81
82    for i in 0..16 {
83        write_u32_le(
84            &mut output[i * 4..(i + 1) * 4],
85            x[i].wrapping_add(read_u32_le(&input[i * 4..(i + 1) * 4])));
86    }
87}
88
89fn xor(x: &[u8], y: &[u8], output: &mut [u8]) {
90    for ((out, &x_i), &y_i) in output.iter_mut().zip(x.iter()).zip(y.iter()) {
91        *out = x_i ^ y_i;
92    }
93}
94
95// Execute the BlockMix operation
96// input - the input vector. The length must be a multiple of 128.
97// output - the output vector. Must be the same length as input.
98fn scrypt_block_mix(input: &[u8], output: &mut [u8]) {
99    let mut x = [0u8; 64];
100    copy_memory(&input[input.len() - 64..], &mut x);
101
102    let mut t = [0u8; 64];
103
104    for (i, chunk) in input.chunks(64).enumerate() {
105        xor(&x, chunk, &mut t);
106        salsa20_8(&t, &mut x);
107        let pos = if i % 2 == 0 { (i / 2) * 64 } else { (i / 2) * 64 + input.len() / 2 };
108        copy_memory(&x, &mut output[pos..pos + 64]);
109    }
110}
111
112// Execute the ROMix operation in-place.
113// b - the data to operate on
114// v - a temporary variable to store the vector V
115// t - a temporary variable to store the result of the xor
116// n - the scrypt parameter N
117fn scrypt_ro_mix(b: &mut [u8], v: &mut [u8], t: &mut [u8], n: usize) {
118    fn integerify(x: &[u8], n: usize) -> usize {
119        // n is a power of 2, so n - 1 gives us a bitmask that we can use to perform a calculation
120        // mod n using a simple bitwise and.
121        let mask = n - 1;
122        // This cast is safe since we're going to get the value mod n (which is a power of 2), so we
123        // don't have to care about truncating any of the high bits off
124        let result = (read_u32_le(&x[x.len() - 64..x.len() - 60]) as usize) & mask;
125        result
126    }
127
128    let len = b.len();
129
130    for chunk in v.chunks_mut(len) {
131        copy_memory(b, chunk);
132        scrypt_block_mix(chunk, b);
133    }
134
135    for _ in 0..n {
136        let j = integerify(b, n);
137        xor(b, &v[j * len..(j + 1) * len], t);
138        scrypt_block_mix(t, b);
139    }
140}
141
142/**
143 * The Scrypt parameter values.
144 */
145#[derive(Clone, Copy)]
146pub struct ScryptParams {
147    log_n: u8,
148    r: u32,
149    p: u32
150}
151
152impl ScryptParams {
153    /**
154     * Create a new instance of ScryptParams.
155     *
156     * # Arguments
157     *
158     * * log_n - The log2 of the Scrypt parameter N
159     * * r - The Scrypt parameter r
160     * * p - The Scrypt parameter p
161     *
162     */
163    pub fn new(log_n: u8, r: u32, p: u32) -> ScryptParams {
164        assert!(r > 0);
165        assert!(p > 0);
166        assert!(log_n > 0);
167        assert!((log_n as usize) < size_of::<usize>() * 8);
168        assert!(size_of::<usize>() >= size_of::<u32>() || (r <= std::usize::MAX as u32 && p < std::usize::MAX as u32));
169
170        let r = r as usize;
171        let p = p as usize;
172
173        let n: usize = 1 << log_n;
174
175        // check that r * 128 doesn't overflow
176        let r128 = match r.checked_mul(128) {
177            Some(x) => x,
178            None => panic!("Invalid Scrypt parameters.")
179        };
180
181        // check that n * r * 128 doesn't overflow
182        match r128.checked_mul(n) {
183            Some(_) => { },
184            None => panic!("Invalid Scrypt parameters.")
185        };
186
187        // check that p * r * 128 doesn't overflow
188        match r128.checked_mul(p) {
189            Some(_) => { },
190            None => panic!("Invalid Scrypt parameters.")
191        };
192
193        // This check required by Scrypt:
194        // check: n < 2^(128 * r / 8)
195        // r * 16 won't overflow since r128 didn't
196        assert!((log_n as usize) < r * 16);
197
198        // This check required by Scrypt:
199        // check: p <= ((2^32-1) * 32) / (128 * r)
200        // It takes a bit of re-arranging to get the check above into this form, but, it is indeed
201        // the same.
202        assert!(r * p < 0x40000000);
203
204        ScryptParams {
205            log_n: log_n,
206            r: r as u32,
207            p: p as u32
208        }
209    }
210}
211
212/**
213 * The scrypt key derivation function.
214 *
215 * # Arguments
216 *
217 * * password - The password to process as a byte vector
218 * * salt - The salt value to use as a byte vector
219 * * params - The ScryptParams to use
220 * * output - The resulting derived key is returned in this byte vector.
221 *
222 */
223pub fn scrypt(password: &[u8], salt: &[u8], params: &ScryptParams, output: &mut [u8]) {
224    // This check required by Scrypt:
225    // check output.len() > 0 && output.len() <= (2^32 - 1) * 32
226    assert!(output.len() > 0);
227    assert!(output.len() / 32 <= 0xffffffff);
228
229    // The checks in the ScryptParams constructor guarantee that the following is safe:
230    let n = 1 << params.log_n;
231    let r128 = (params.r as usize) * 128;
232    let pr128 = (params.p as usize) * r128;
233    let nr128 = n * r128;
234
235    let mut mac = Hmac::new(Sha256::new(), password);
236
237    let mut b: Vec<u8> = repeat(0).take(pr128).collect();
238    pbkdf2(&mut mac, salt, 1, &mut b);
239
240    let mut v: Vec<u8> = repeat(0).take(nr128).collect();
241    let mut t: Vec<u8> = repeat(0).take(r128).collect();
242
243    for chunk in &mut b.chunks_mut(r128) {
244        scrypt_ro_mix(chunk, &mut v, &mut t, n);
245    }
246
247    pbkdf2(&mut mac, &*b, 1, output);
248}
249
250/**
251 * scrypt_simple is a helper function that should be sufficient for the majority of cases where
252 * an application needs to use Scrypt to hash a password for storage. The result is a String that
253 * contains the parameters used as part of its encoding. The scrypt_check function may be used on
254 * a password to check if it is equal to a hashed value.
255 *
256 * # Format
257 *
258 * The format of the output is a modified version of the Modular Crypt Format that encodes algorithm
259 * used and the parameter values. If all parameter values can each fit within a single byte, a
260 * compact format is used (format 0). However, if any value cannot, an expanded format where the r
261 * and p parameters are encoded using 4 bytes (format 1) is used. Both formats use a 128-bit salt
262 * and a 256-bit hash. The format is indicated as "rscrypt" which is short for "Rust Scrypt format."
263 *
264 * $rscrypt$<format>$<base64(log_n,r,p)>$<base64(salt)>$<based64(hash)>$
265 *
266 * # Arguments
267 *
268 * * password - The password to process as a str
269 * * params - The ScryptParams to use
270 *
271 */
272pub fn scrypt_simple(password: &str, params: &ScryptParams) -> io::Result<String> {
273    let mut rng = try!(OsRng::new());
274
275    // 128-bit salt
276    let salt: Vec<u8> = rng.gen_iter::<u8>().take(16).collect();
277
278    // 256-bit derived key
279    let mut dk = [0u8; 32];
280
281    scrypt(password.as_bytes(), &*salt, params, &mut dk);
282
283    let mut result = "$rscrypt$".to_string();
284    if params.r < 256 && params.p < 256 {
285        result.push_str("0$");
286        let mut tmp = [0u8; 3];
287        tmp[0] = params.log_n;
288        tmp[1] = params.r as u8;
289        tmp[2] = params.p as u8;
290        result.push_str(&*tmp.to_base64(base64::STANDARD));
291    } else {
292        result.push_str("1$");
293        let mut tmp = [0u8; 9];
294        tmp[0] = params.log_n;
295        write_u32_le(&mut tmp[1..5], params.r);
296        write_u32_le(&mut tmp[5..9], params.p);
297        result.push_str(&*tmp.to_base64(base64::STANDARD));
298    }
299    result.push('$');
300    result.push_str(&*salt.to_base64(base64::STANDARD));
301    result.push('$');
302    result.push_str(&*dk.to_base64(base64::STANDARD));
303    result.push('$');
304
305    Ok(result)
306}
307
308/**
309 * scrypt_check compares a password against the result of a previous call to scrypt_simple and
310 * returns true if the passed in password hashes to the same value.
311 *
312 * # Arguments
313 *
314 * * password - The password to process as a str
315 * * hashed_value - A string representing a hashed password returned by scrypt_simple()
316 *
317 */
318pub fn scrypt_check(password: &str, hashed_value: &str) -> Result<bool, &'static str> {
319    static ERR_STR: &'static str = "Hash is not in Rust Scrypt format.";
320
321    let mut iter = hashed_value.split('$');
322
323    // Check that there are no characters before the first "$"
324    match iter.next() {
325        Some(x) => if x != "" { return Err(ERR_STR); },
326        None => return Err(ERR_STR)
327    }
328
329    // Check the name
330    match iter.next() {
331        Some(t) => if t != "rscrypt" { return Err(ERR_STR); },
332        None => return Err(ERR_STR)
333    }
334
335    // Parse format - currenlty only version 0 (compact) and 1 (expanded) are supported
336    let params: ScryptParams;
337    match iter.next() {
338        Some(fstr) => {
339            // Parse the parameters - the size of them depends on the if we are using the compact or
340            // expanded format
341            let pvec = match iter.next() {
342                Some(pstr) => match pstr.from_base64() {
343                    Ok(x) => x,
344                    Err(_) => return Err(ERR_STR)
345                },
346                None => return Err(ERR_STR)
347            };
348            match fstr {
349                "0" => {
350                    if pvec.len() != 3 { return Err(ERR_STR); }
351                    let log_n = pvec[0];
352                    let r = pvec[1] as u32;
353                    let p = pvec[2] as u32;
354                    params = ScryptParams::new(log_n, r, p);
355                }
356                "1" => {
357                    if pvec.len() != 9 { return Err(ERR_STR); }
358                    let log_n = pvec[0];
359                    let mut pval = [0u32; 2];
360                    read_u32v_le(&mut pval, &pvec[1..9]);
361                    params = ScryptParams::new(log_n, pval[0], pval[1]);
362                }
363                _ => return Err(ERR_STR)
364            }
365        }
366        None => return Err(ERR_STR)
367    }
368
369    // Salt
370    let salt = match iter.next() {
371        Some(sstr) => match sstr.from_base64() {
372            Ok(salt) => salt,
373            Err(_) => return Err(ERR_STR)
374        },
375        None => return Err(ERR_STR)
376    };
377
378    // Hashed value
379    let hash = match iter.next() {
380        Some(hstr) => match hstr.from_base64() {
381            Ok(hash) => hash,
382            Err(_) => return Err(ERR_STR)
383        },
384        None => return Err(ERR_STR)
385    };
386
387    // Make sure that the input ends with a "$"
388    match iter.next() {
389        Some(x) => if x != "" { return Err(ERR_STR); },
390        None => return Err(ERR_STR)
391    }
392
393    // Make sure there is no trailing data after the final "$"
394    match iter.next() {
395        Some(_) => return Err(ERR_STR),
396        None => { }
397    }
398
399    let mut output: Vec<u8> = repeat(0).take(hash.len()).collect();
400    scrypt(password.as_bytes(), &*salt, &params, &mut output);
401
402    // Be careful here - its important that the comparison be done using a fixed time equality
403    // check. Otherwise an adversary that can measure how long this step takes can learn about the
404    // hashed value which would allow them to mount an offline brute force attack against the
405    // hashed password.
406    Ok(fixed_time_eq(&*output, &*hash))
407}
408
409#[cfg(test)]
410mod test {
411    use std::iter::repeat;
412
413    use scrypt::{scrypt, scrypt_simple, scrypt_check, ScryptParams};
414
415    struct Test {
416        password: &'static str,
417        salt: &'static str,
418        log_n: u8,
419        r: u32,
420        p: u32,
421        expected: Vec<u8>
422    }
423
424    // Test vectors from [1]. The last test vector is omitted because it takes too long to run.
425
426    fn tests() -> Vec<Test> {
427        vec![
428            Test {
429                password: "",
430                salt: "",
431                log_n: 4,
432                r: 1,
433                p: 1,
434                expected: vec![
435                    0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20,
436                    0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97,
437                    0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8,
438                    0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42,
439                    0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8,
440                    0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17,
441                    0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28,
442                    0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06 ]
443            },
444            Test {
445                password: "password",
446                salt: "NaCl",
447                log_n: 10,
448                r: 8,
449                p: 16,
450                expected: vec![
451                    0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00,
452                    0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe,
453                    0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30,
454                    0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62,
455                    0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88,
456                    0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda,
457                    0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d,
458                    0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40 ]
459            },
460            Test {
461                password: "pleaseletmein",
462                salt: "SodiumChloride",
463                log_n: 14,
464                r: 8,
465                p: 1,
466                expected: vec![
467                    0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48,
468                    0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb,
469                    0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e,
470                    0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2,
471                    0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf,
472                    0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9,
473                    0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40,
474                    0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87 ]
475            },
476        ]
477    }
478
479    #[test]
480    fn test_scrypt() {
481        let tests = tests();
482        for t in tests.iter() {
483            let mut result: Vec<u8> = repeat(0).take(t.expected.len()).collect();
484            let params = ScryptParams::new(t.log_n, t.r, t.p);
485            scrypt(t.password.as_bytes(), t.salt.as_bytes(), &params, &mut result);
486            assert!(result == t.expected);
487        }
488    }
489
490    fn test_scrypt_simple(log_n: u8, r: u32, p: u32) {
491        let password = "password";
492
493        let params = ScryptParams::new(log_n, r, p);
494        let out1 = scrypt_simple(password, &params).unwrap();
495        let out2 = scrypt_simple(password, &params).unwrap();
496
497        // This just makes sure that a salt is being applied. It doesn't verify that that salt is
498        // cryptographically strong, however.
499        assert!(out1 != out2);
500
501        match scrypt_check(password, &out1[..]) {
502            Ok(r) => assert!(r),
503            Err(_) => panic!()
504        }
505        match scrypt_check(password, &out2[..]) {
506            Ok(r) => assert!(r),
507            Err(_) => panic!()
508        }
509
510        match scrypt_check("wrong", &out1[..]) {
511            Ok(r) => assert!(!r),
512            Err(_) => panic!()
513        }
514        match scrypt_check("wrong", &out2[..]) {
515            Ok(r) => assert!(!r),
516            Err(_) => panic!()
517        }
518    }
519
520    #[test]
521    fn test_scrypt_simple_compact() {
522        // These parameters are intentionally very weak - the goal is to make the test run quickly!
523        test_scrypt_simple(7, 8, 1);
524    }
525
526    #[test]
527    fn test_scrypt_simple_expanded() {
528        // These parameters are intentionally very weak - the goal is to make the test run quickly!
529        test_scrypt_simple(3, 1, 256);
530    }
531}