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 }