1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use std::thread;

use block::{KeySchedule};
use csprng::{Csprng};
use digest::{Digest};
use u256::{u256};

pub fn derive(key: &[u8], salt: &[u8], threads: usize, memory: usize, cost: usize) -> u256 {
	assert!(key.len() > 0);
	assert!(threads != 0);
	assert!(memory != 0);
	assert!(cost != 0);
	assert!(memory.checked_mul(cost).is_some());

	let key = derive_seed_key(key, salt, threads, memory, cost);
	let mut csprng = Csprng::new(key);

	let threads: Vec<_> = (0..threads).map(|_| csprng.next()).map(|key| thread::spawn(move || {
		let mut vec = vec!(u256(0, 0); memory);
		populate_memory(key, &mut vec);
		walk_memory(key, &mut vec, memory * cost)
	})).collect();

	let mut digest = Digest::keyed(key);

	for t in threads {
		digest.update(t.join().unwrap().as_ref());
	}

	digest.finish()
}

#[inline]
fn u64_to_bytes(v: u64) -> [u8; 8] {
	unsafe { ::std::mem::transmute(v.to_le()) }
}

#[inline]
fn derive_seed_key(key: &[u8], salt: &[u8], threads: usize, memory: usize, cost: usize) -> u256 {
	let mut digest = Digest::new();
	digest.update(&u64_to_bytes(key.len() as u64));
	digest.update(key);
	digest.update(&u64_to_bytes(salt.len() as u64));
	digest.update(salt);
	digest.update(&u64_to_bytes(threads as u64));
	digest.update(&u64_to_bytes(memory as u64));
	digest.update(&u64_to_bytes(cost as u64));
	digest.finish()
}

#[inline]
fn populate_memory(key: u256, memory: &mut [u256]) {
	let mut csprng = Csprng::new(key);

	for m in memory.iter_mut() {
		*m = csprng.next();
	}
}

#[inline]
fn walk_memory(mut key: u256, memory: &mut [u256], iter: usize) -> u256 {
	let schedule = KeySchedule::new(key);

	for _ in 0..iter {
		let i = key.0 as usize % memory.len();
		key = schedule.encrypt(key ^ memory[i]);
		memory[i] = memory[i] ^ key;
	}

	key
}