use ::std::fmt::Debug;
use ::std::fmt::Error;
use ::std::fmt::Formatter;
use ::std::hash::Hash;
use ::std::hash::Hasher;
use ::rand::rngs::OsRng;
use ::rand::RngCore;
use crate::util::base64::base64str_to_u8s;
use crate::util::base64::u8s_to_base64str;
use crate::util::errors::add_err;
use crate::util::FedResult;
const SALT_LEN: usize = 64;
#[derive(Clone)]
pub struct Salt {
pub salt: [u8; SALT_LEN],
}
impl PartialEq for Salt {
fn eq(&self, other: &Self) -> bool {
if self.salt.len() != other.salt.len() {
return false;
}
for i in 0..self.salt.len() {
if self.salt[i] != other.salt[i] {
return false;
}
}
true
}
}
impl Eq for Salt {}
impl Hash for Salt {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.salt)
}
}
impl Debug for Salt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str("salt[")?;
write!(f, "{}", self.salt[0])?;
f.write_str("...")?;
write!(f, "{}", self.salt[self.salt.len() - 1])?;
f.write_str("]")
}
}
impl Salt {
pub fn generate_random() -> FedResult<Self> {
let mut long = [0u8; SALT_LEN];
OsRng.fill_bytes(&mut long);
Ok(Salt { salt: long })
}
#[cfg(any(test, feature = "expose"))]
pub fn fixed_for_test(salt: u64) -> Self {
let mut repeated: [u8; 64] = [0u8; SALT_LEN];
let input: [u8; 8] = salt.to_le_bytes();
for i in 0..repeated.len() {
repeated[i] = input[i % input.len()];
}
Salt { salt: repeated }
}
pub fn parse_base64(base64: &str, verbose: bool) -> FedResult<Self> {
match base64str_to_u8s(base64) {
Ok(salt_vec) => {
if salt_vec.len() == 64 {
let mut salt = [0; SALT_LEN];
salt.clone_from_slice(&salt_vec);
Ok(Salt { salt })
} else {
Err(if verbose {
format!(
"could not determine the salt used by fileenc that encrypted this \
file; got {} which is invalid because it has the wrong length",
base64
)
} else {
"could not determine the salt used by fileenc to encrypt this file"
.to_owned()
})
}
}
Err(err) => Err(add_err(
format!(
"could not determine the salt used \
by fileenc that encrypted this file; got {} which is invalid",
base64
),
verbose,
err,
)),
}
}
pub fn as_base64(&self) -> String {
u8s_to_base64str(&self.salt)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn debug_impl() {
let salt = Salt::fixed_for_test(1_111_111_111_111_111_111);
let debug = format!("{:?}", salt);
assert_eq!("salt[199...15]", &debug);
}
#[test]
fn generate_salt_entropy() {
let salt = Salt::generate_random().unwrap();
let r = salt.salt[0];
for i in 0..salt.salt.len() {
if salt.salt[i] != r {
return;
}
}
panic!(); }
#[test]
fn generate_salt_different() {
let salt1 = Salt::generate_random().unwrap();
let salt2 = Salt::generate_random().unwrap();
assert!(salt1 != salt2);
}
}