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