use anyhow::{Result, anyhow};
use rand::RngCore;
use schnorrkel::PUBLIC_KEY_LENGTH;
pub struct Scrypt {
pub salt: [u8; Self::SALT_LENGTH],
pub n: u32,
pub r: u32,
pub p: u32,
}
impl Scrypt {
pub const ENCODED_LENGTH: usize = 44;
const SALT_LENGTH: usize = 32;
pub fn decode(encoded: [u8; Self::ENCODED_LENGTH]) -> Self {
let mut salt = [0; Self::SALT_LENGTH];
salt.copy_from_slice(&encoded[..Self::SALT_LENGTH]);
let params = encoded[Self::SALT_LENGTH..]
.chunks(4)
.map(|bytes| {
let mut buf = [0; 4];
buf.copy_from_slice(bytes);
u32::from_le_bytes(buf)
})
.collect::<Vec<_>>();
Self {
salt,
n: params[0].ilog2(),
r: params[2],
p: params[1],
}
}
pub fn encode(&self) -> [u8; Self::ENCODED_LENGTH] {
let mut buf = [0; Self::ENCODED_LENGTH];
let n = 1 << self.n;
buf[..Self::SALT_LENGTH].copy_from_slice(&self.salt);
buf[Self::SALT_LENGTH..].copy_from_slice(
[n, self.p, self.r]
.iter()
.flat_map(|n| n.to_le_bytes())
.collect::<Vec<_>>()
.as_slice(),
);
buf
}
pub fn passwd(&self, passphrase: &[u8]) -> Result<[u8; 32]> {
let mut passwd = [0; 32];
let output = nacl::scrypt(
passphrase,
&self.salt,
self.n as u8,
self.r as usize,
self.p as usize,
PUBLIC_KEY_LENGTH,
&|_: u32| {},
)
.map_err(|e| anyhow!("{e:?}"))?;
passwd.copy_from_slice(&output[..32]);
Ok(passwd)
}
}
impl Default for Scrypt {
fn default() -> Self {
let mut salt = [0; Self::SALT_LENGTH];
rand::thread_rng().fill_bytes(&mut salt);
Self {
salt,
n: 15,
r: 8,
p: 1,
}
}
}