1use crate::pbkdf2::pbkdf2_sha256;
10use crate::salsa20::{salsa20, BLOCK_SIZE as SALSA_BLOCK_SIZE};
11
12const SALSA_ROUNDS: usize = 4;
13const PBKDF2_ROUNDS: usize = 1;
14const BLOCK_SIZE: usize = 64;
15
16use std::error::Error;
17use std::fmt;
18use std::fmt::Display;
19
20type Block = Vec<u8>;
21
22#[derive(Debug, PartialEq)]
23pub enum ScryptError {
24 RIsTooSmall,
25 NIsTooSmall,
26 NIsNotAPowerOfTwo,
27 PIsTooSmall,
28}
29
30impl Display for ScryptError {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 write!(f, "ScryptError: {}", self.description())
33 }
34}
35
36impl Error for ScryptError {
37 fn description(&self) -> &str {
38 match self {
39 ScryptError::RIsTooSmall => "`r` must be larger than 1",
40 ScryptError::NIsTooSmall => "`n` must be larger than 1",
41 ScryptError::NIsNotAPowerOfTwo => "`n` must be a power of two",
42 ScryptError::PIsTooSmall => "`p` must be larger than 1",
43 }
44 }
45}
46
47pub struct Scrypt {
68 r: usize,
69 n: usize,
70 p: usize,
71}
72
73fn block_xor(a: &[u8], b: &[u8]) -> Block {
74 a.iter().zip(b.iter()).map(|(a, b)| a ^ b).collect()
75}
76
77fn integerify(x: &[Block]) -> u64 {
78 let last = &x[x.len() - 1];
79 let tail = &last[(last.len() - SALSA_BLOCK_SIZE)..];
80
81 u64::from(tail[0])
82 | (u64::from(tail[1]) << 8)
83 | (u64::from(tail[2]) << 16)
84 | (u64::from(tail[3]) << 24)
85 | (u64::from(tail[4]) << 32)
86 | (u64::from(tail[5]) << 40)
87 | (u64::from(tail[6]) << 48)
88 | (u64::from(tail[7]) << 56)
89}
90
91impl Scrypt {
92 pub fn new(r: usize, n: usize, p: usize) -> Self {
104 Self { r, n, p }
105 }
106
107 fn block_mix(self: &Scrypt, b: &[Block]) -> Vec<Block> {
108 let mut x = b[2 * self.r - 1].clone();
138
139 let mut y: Vec<Block> = Vec::with_capacity(2 * self.r);
141
142 for b_elem in b.iter() {
143 let t = block_xor(&x, b_elem);
144 salsa20(&t, SALSA_ROUNDS, &mut x);
145 y.push(x.clone());
146 }
147
148 let mut bs_head: Vec<Block> = Vec::with_capacity(2 * self.r);
150 let mut bs_tail: Vec<Block> = Vec::with_capacity(self.r);
151 for (i, y_elem) in y.into_iter().enumerate() {
152 if i % 2 == 0 {
153 bs_head.push(y_elem);
154 } else {
155 bs_tail.push(y_elem);
156 }
157 }
158 bs_head.append(&mut bs_tail);
159 bs_head
160 }
161
162 fn ro_mix(self: &Scrypt, b: Vec<Block>) -> Vec<Block> {
163 let mut x = b;
198
199 let mut v: Vec<Vec<Block>> = Vec::with_capacity(self.n);
201 for _i in 0..self.n {
202 let t = self.block_mix(&x);
203 v.push(x);
204 x = t;
205 }
206
207 for _i in 0..self.n {
209 let j = (integerify(&x) as usize) % self.n;
210 let t: Vec<Block> = x
211 .iter()
212 .zip(v[j].iter())
213 .map(|(x_block, v_block)| block_xor(x_block, v_block))
214 .collect();
215 x = self.block_mix(&t);
216 }
217
218 x
219 }
220
221 pub fn derive(
225 self: &Scrypt,
226 passphrase: &[u8],
227 salt: &[u8],
228 out: &mut [u8],
229 ) -> Result<(), ScryptError> {
230 if self.r < 1 {
231 return Err(ScryptError::RIsTooSmall);
232 }
233
234 if self.n < 1 {
235 return Err(ScryptError::NIsTooSmall);
236 }
237
238 if ((self.n - 1) & self.n) != 0 {
239 return Err(ScryptError::NIsNotAPowerOfTwo);
240 }
241
242 if self.p < 1 {
243 return Err(ScryptError::PIsTooSmall);
244 }
245
246 let mut raw_b: Vec<u8> = vec![0; self.p * 2 * BLOCK_SIZE * self.r];
280 pbkdf2_sha256(passphrase, salt, PBKDF2_ROUNDS, &mut raw_b);
281
282 let mut b: Vec<Vec<Block>> = raw_b
283 .chunks_exact(2 * BLOCK_SIZE * self.r)
284 .map(|chunk| {
285 chunk
286 .chunks_exact(BLOCK_SIZE)
287 .map(|sub_chunk| sub_chunk.to_vec())
288 .collect()
289 })
290 .collect();
291
292 b = b.into_iter().map(|elem| self.ro_mix(elem)).collect();
294
295 let b_salt: Vec<u8> = b.into_iter().flatten().flatten().collect();
297 pbkdf2_sha256(passphrase, &b_salt, PBKDF2_ROUNDS, out);
298
299 Ok(())
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 fn check_mix(r: usize, input: &[Block], expected: &[Block]) {
310 let s = Scrypt::new(r, 1, 1);
311 assert_eq!(s.block_mix(input), expected);
312 }
313
314 #[test]
315 fn it_should_compute_block_mix_for_vec0() {
316 check_mix(
317 1,
318 &[
319 vec![
320 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9,
321 0x12, 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04,
322 0xf3, 0xae, 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8,
323 0x7b, 0xcc, 0x3b, 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84,
324 0x63, 0x95, 0x74, 0xf3, 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7,
325 ],
326 vec![
327 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8,
328 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc,
329 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, 0x7c, 0x51, 0xce, 0x4a, 0xd5,
330 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, 0x7f, 0x4d, 0x1c, 0xad,
331 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, 0x7e, 0x89,
332 ],
333 ],
334 &[
335 vec![
336 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02,
337 0x0c, 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b,
338 0x1c, 0x63, 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6,
339 0xbc, 0xfe, 0x6b, 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10,
340 0x2c, 0x91, 0x74, 0x5c, 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81,
341 ],
342 vec![
343 0x20, 0xed, 0xc9, 0x75, 0x32, 0x38, 0x81, 0xa8, 0x05, 0x40, 0xf6, 0x4c, 0x16,
344 0x2d, 0xcd, 0x3c, 0x21, 0x07, 0x7c, 0xfe, 0x5f, 0x8d, 0x5f, 0xe2, 0xb1, 0xa4,
345 0x16, 0x8f, 0x95, 0x36, 0x78, 0xb7, 0x7d, 0x3b, 0x3d, 0x80, 0x3b, 0x60, 0xe4,
346 0xab, 0x92, 0x09, 0x96, 0xe5, 0x9b, 0x4d, 0x53, 0xb6, 0x5d, 0x2a, 0x22, 0x58,
347 0x77, 0xd5, 0xed, 0xf5, 0x84, 0x2c, 0xb9, 0xf1, 0x4e, 0xef, 0xe4, 0x25,
348 ],
349 ],
350 );
351 }
352
353 fn check_ro_mix(r: usize, n: usize, input: &[Block], expected: &[Block]) {
354 let s = Scrypt::new(r, n, 1);
355 assert_eq!(s.ro_mix(input.to_vec()), expected);
356 }
357
358 #[test]
359 fn it_should_compute_ro_mix_for_vec0() {
360 check_ro_mix(
361 1,
362 16,
363 &[
364 vec![
365 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9,
366 0x12, 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04,
367 0xf3, 0xae, 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8,
368 0x7b, 0xcc, 0x3b, 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84,
369 0x63, 0x95, 0x74, 0xf3, 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7,
370 ],
371 vec![
372 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8,
373 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc,
374 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, 0x7c, 0x51, 0xce, 0x4a, 0xd5,
375 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, 0x7f, 0x4d, 0x1c, 0xad,
376 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, 0x7e, 0x89,
377 ],
378 ],
379 &[
380 vec![
381 0x79, 0xcc, 0xc1, 0x93, 0x62, 0x9d, 0xeb, 0xca, 0x04, 0x7f, 0x0b, 0x70, 0x60,
382 0x4b, 0xf6, 0xb6, 0x2c, 0xe3, 0xdd, 0x4a, 0x96, 0x26, 0xe3, 0x55, 0xfa, 0xfc,
383 0x61, 0x98, 0xe6, 0xea, 0x2b, 0x46, 0xd5, 0x84, 0x13, 0x67, 0x3b, 0x99, 0xb0,
384 0x29, 0xd6, 0x65, 0xc3, 0x57, 0x60, 0x1f, 0xb4, 0x26, 0xa0, 0xb2, 0xf4, 0xbb,
385 0xa2, 0x00, 0xee, 0x9f, 0x0a, 0x43, 0xd1, 0x9b, 0x57, 0x1a, 0x9c, 0x71,
386 ],
387 vec![
388 0xef, 0x11, 0x42, 0xe6, 0x5d, 0x5a, 0x26, 0x6f, 0xdd, 0xca, 0x83, 0x2c, 0xe5,
389 0x9f, 0xaa, 0x7c, 0xac, 0x0b, 0x9c, 0xf1, 0xbe, 0x2b, 0xff, 0xca, 0x30, 0x0d,
390 0x01, 0xee, 0x38, 0x76, 0x19, 0xc4, 0xae, 0x12, 0xfd, 0x44, 0x38, 0xf2, 0x03,
391 0xa0, 0xe4, 0xe1, 0xc4, 0x7e, 0xc3, 0x14, 0x86, 0x1f, 0x4e, 0x90, 0x87, 0xcb,
392 0x33, 0x39, 0x6a, 0x68, 0x73, 0xe8, 0xf9, 0xd2, 0x53, 0x9a, 0x4b, 0x8e,
393 ],
394 ],
395 );
396 }
397
398 #[test]
401 fn it_should_compute_scrypt_for_vec0() {
402 let s = Scrypt::new(1, 16, 1);
403
404 let mut out: [u8; 64] = [0; 64];
405 s.derive(b"", b"", &mut out)
406 .expect("derivation to not fail");
407 assert_eq!(
408 out.to_vec(),
409 vec![
410 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a,
411 0x04, 0x97, 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa, 0x3f,
412 0xed, 0xe2, 0x14, 0x42, 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a,
413 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17, 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28,
414 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06
415 ]
416 );
417 }
418
419 #[test]
420 fn it_should_compute_scrypt_for_vec1() {
421 let s = Scrypt::new(8, 1024, 16);
422
423 let mut out: [u8; 64] = [0; 64];
424 s.derive(b"password", b"NaCl", &mut out)
425 .expect("derivation to not fail");
426 assert_eq!(
427 out.to_vec(),
428 vec![
429 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01,
430 0xe9, 0xfe, 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76, 0x63,
431 0x4b, 0x37, 0x31, 0x62, 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1,
432 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda, 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d,
433 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40
434 ]
435 );
436 }
437
438 #[test]
439 fn it_should_compute_scrypt_for_vec2() {
440 let s = Scrypt::new(8, 16384, 1);
441
442 let mut out: [u8; 64] = [0; 64];
443 s.derive(b"pleaseletmein", b"SodiumChloride", &mut out)
444 .expect("derivation to not fail");
445 assert_eq!(
446 out.to_vec(),
447 vec![
448 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd,
449 0x38, 0xeb, 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43, 0xf6,
450 0x54, 0x5d, 0xa1, 0xf2, 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4,
451 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9, 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40,
452 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87
453 ]
454 );
455 }
456
457 #[test]
458 fn it_should_compute_scrypt_for_vec3() {
459 let s = Scrypt::new(8, 1_048_576, 1);
460
461 let mut out: [u8; 64] = [0; 64];
462 s.derive(b"pleaseletmein", b"SodiumChloride", &mut out)
463 .expect("derivation to not fail");
464 assert_eq!(
465 out.to_vec(),
466 vec![
467 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70,
468 0xf8, 0x81, 0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee, 0x98,
469 0x20, 0xad, 0xaa, 0x47, 0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c,
470 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3, 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb,
471 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4
472 ]
473 );
474 }
475}