#[cfg(any(test, feature = "use_serde", feature = "default"))] use serde;
use rand::os::OsRng;
use rand::Rng;
use std::fmt;
use std::io;
use std::cmp::{ PartialEq, Eq };
pub const HASH_SALT_LEN: usize = 32;
pub struct HashSalt {
data: [u8; HASH_SALT_LEN],
}
impl HashSalt {
pub fn new() -> io::Result<HashSalt> {
let mut osrng = OsRng::new()?;
let mut spicy = HashSalt { data: [0; HASH_SALT_LEN] };
osrng.fill_bytes(&mut spicy.data[..]);
Ok(spicy)
}
pub fn from_reader<R: io::Read>(r: &mut R) -> io::Result<HashSalt> {
let mut spicy = HashSalt { data: [0; HASH_SALT_LEN] };
r.read_exact(spicy.as_slice_mut())?;
Ok(spicy)
}
pub fn as_slice(&self) -> &[u8] { &self.data[..] }
fn as_slice_mut(&mut self) -> &mut [u8] { &mut self.data[..] }
fn copy_from_slice(&mut self, other: &[u8]) {
self.as_slice_mut().copy_from_slice(other);
}
fn fmt_impl(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HashSalt[ ")?;
for x in &self.data[..] { write!(f, "{:02X}", *x)?; }
write!(f, " ]")
}
}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
impl serde::Serialize for HashSalt {
fn serialize<S: serde::Serializer>(&self, ser: &mut S) -> Result<(), S::Error> {
ser.serialize_newtype_struct("HashSalt", serde::bytes::Bytes::from(self.as_slice()))
}
}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
impl serde::Deserialize for HashSalt {
fn deserialize<D: serde::Deserializer>(de: &mut D) -> Result<Self, D::Error> {
de.deserialize_newtype_struct("HashSalt", HashSaltSerdeVisitor {})
}
}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
struct HashSaltSerdeVisitor {}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
impl serde::de::Visitor for HashSaltSerdeVisitor {
type Value = HashSalt;
fn visit_newtype_struct<D: serde::Deserializer>(&mut self, de: &mut D) -> Result<HashSalt, D::Error> {
use serde::{ Deserialize, Error };
let bytes = serde::bytes::ByteBuf::deserialize(de)?;
if bytes.len() != HASH_SALT_LEN { return Err(D::Error::invalid_length(bytes.len())); }
let mut spicy = HashSalt { data: [0_u8; HASH_SALT_LEN] };
spicy.copy_from_slice(&bytes[..]);
Ok(spicy)
}
fn visit_bytes<E: serde::Error>(&mut self, v: &[u8]) -> Result<HashSalt, E> {
if v.len() == HASH_SALT_LEN {
let mut spicy = HashSalt { data: [0_u8; HASH_SALT_LEN] };
spicy.copy_from_slice(v);
Ok(spicy)
}
else { Err(E::invalid_length(v.len())) }
}
fn visit_seq<V: serde::de::SeqVisitor>(&mut self, mut visitor: V) -> Result<HashSalt, V::Error> {
use serde::Error;
let bytes = match visitor.visit::<serde::bytes::ByteBuf>()? {
Some(bytes) => { visitor.end()?; bytes },
None => { visitor.end()?; return Err(V::Error::invalid_length(0)); },
};
if bytes.len() != HASH_SALT_LEN { return Err(V::Error::invalid_length(bytes.len())); }
let mut spicy = HashSalt { data: [0_u8; HASH_SALT_LEN] };
spicy.copy_from_slice(&bytes[..]);
Ok(spicy)
}
}
impl PartialEq for HashSalt {
fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() }
}
impl Eq for HashSalt {}
impl fmt::Debug for HashSalt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_impl(f) }
}
impl fmt::Display for HashSalt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_impl(f) }
}
#[cfg(test)]
mod test {
use super::{HashSalt, HASH_SALT_LEN};
use serde_test::{ Token, assert_tokens };
use std::io;
#[test]
fn salt_len_is_reasonably_large() {
assert!(HASH_SALT_LEN >= 16, "Why not just make salts a constant 'asdf'?");
}
#[test]
fn generating_salts_works() {
let spicy = HashSalt::new().unwrap();
assert_eq!(spicy.as_slice().len(), HASH_SALT_LEN);
let mut test = 0;
for x in spicy.as_slice() { test |= *x; }
assert!(test != 0, "Try running the test again. Generating all-zeros is not impossible.");
}
#[test]
fn from_reader_works() {
const ARR1: &'static [u8] = &[42_u8; HASH_SALT_LEN];
const ARR2: &'static [u8] = &[ 1_u8; 1];
let mut data1 = io::Cursor::new(ARR1);
let mut data2 = io::Cursor::new(ARR2);
assert_eq!(
HashSalt::from_reader(&mut data1).unwrap(),
HashSalt { data: [42_u8; HASH_SALT_LEN] }
);
assert_eq!(
HashSalt::from_reader(&mut data2).unwrap_err().kind(),
io::ErrorKind::UnexpectedEof
);
}
#[test]
fn serde_works() {
let spicy = HashSalt::new().unwrap();
let mut tokens: Vec<Token> = Vec::with_capacity(3 + (2 * spicy.as_slice().len()));
tokens.push(Token::StructNewType("HashSalt"));
tokens.push(Token::SeqStart(Some(HASH_SALT_LEN)));
for x in spicy.as_slice() {
tokens.push(Token::SeqSep);
tokens.push(Token::U8(*x));
}
tokens.push(Token::SeqEnd);
assert_tokens(&spicy, &tokens[..]);
}
}