#[cfg(any(test, feature = "use_serde", feature = "default"))] use serde;
use std::cmp::{ PartialEq, Eq };
use std::fmt;
use std::io;
pub const HASHED_PASSWORD_LEN: usize = 64;
pub struct HashedPassword {
data: [u8; HASHED_PASSWORD_LEN],
}
impl HashedPassword {
pub fn from_reader<R: io::Read>(r: &mut R) -> io::Result<HashedPassword> {
let mut hash = HashedPassword { data: [0_u8; HASHED_PASSWORD_LEN] };
r.read_exact(hash.as_slice_mut())?;
Ok(hash)
}
pub fn as_slice(&self) -> &[u8] {
&self.data[..]
}
pub(super) fn new() -> HashedPassword {
HashedPassword { data: [0_u8; HASHED_PASSWORD_LEN] }
}
pub(super) 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, "HashedPassword[ ")?;
for x in &self.data[..] { write!(f, "{:02X}", *x)?; }
write!(f, " ]")
}
}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
impl serde::Serialize for HashedPassword {
fn serialize<S: serde::Serializer>(&self, ser: &mut S) -> Result<(), S::Error> {
ser.serialize_newtype_struct("HashedPassword", serde::bytes::Bytes::from(self.as_slice()))
}
}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
impl serde::Deserialize for HashedPassword {
fn deserialize<D: serde::Deserializer>(de: &mut D) -> Result<Self, D::Error> {
de.deserialize_newtype_struct("HashedPassword", HashedPasswordSerdeVisitor {})
}
}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
struct HashedPasswordSerdeVisitor {}
#[cfg(any(test, feature = "use_serde", feature = "default"))]
impl serde::de::Visitor for HashedPasswordSerdeVisitor {
type Value = HashedPassword;
fn visit_newtype_struct<D: serde::Deserializer>(&mut self, de: &mut D)
-> Result<HashedPassword, D::Error> {
use serde::{ Deserialize, Error };
let bytes = serde::bytes::ByteBuf::deserialize(de)?;
if bytes.len() != HASHED_PASSWORD_LEN { return Err(D::Error::invalid_length(bytes.len())); }
let mut hash = HashedPassword { data: [0_u8; HASHED_PASSWORD_LEN] };
hash.copy_from_slice(&bytes[..]);
Ok(hash)
}
fn visit_bytes<E: serde::Error>(&mut self, v: &[u8]) -> Result<HashedPassword, E> {
if v.len() == HASHED_PASSWORD_LEN {
let mut hash = HashedPassword { data: [0_u8; HASHED_PASSWORD_LEN] };
hash.copy_from_slice(v);
Ok(hash)
}
else { Err(E::invalid_length(v.len())) }
}
fn visit_seq<V: serde::de::SeqVisitor>(&mut self, mut visitor: V)
-> Result<HashedPassword, 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() != HASHED_PASSWORD_LEN { return Err(V::Error::invalid_length(bytes.len())); }
let mut hash = HashedPassword { data: [0_u8; HASHED_PASSWORD_LEN] };
hash.copy_from_slice(&bytes[..]);
Ok(hash)
}
}
impl PartialEq for HashedPassword {
fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() }
}
impl Eq for HashedPassword {}
impl fmt::Debug for HashedPassword {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_impl(f) }
}
impl fmt::Display for HashedPassword {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_impl(f) }
}
#[cfg(test)]
mod test {
use super::{ HashedPassword, HASHED_PASSWORD_LEN };
use serde_test::{ Token, assert_tokens };
use std::io;
#[test]
fn max_len_is_reasonably_large() {
assert!(HASHED_PASSWORD_LEN >= 16, "Are you sure you want less than 128 bits?");
}
#[test]
fn eq_does_its_thing() {
assert!(
HashedPassword { data: [42_u8; HASHED_PASSWORD_LEN] }
== HashedPassword { data: [42_u8; HASHED_PASSWORD_LEN] }
);
assert!(
HashedPassword { data: [3_u8; HASHED_PASSWORD_LEN] }
!= HashedPassword { data: [4_u8; HASHED_PASSWORD_LEN] }
);
}
#[test]
fn as_slice_does_its_thing() {
assert_eq!(
HashedPassword { data: [7_u8; HASHED_PASSWORD_LEN] }.as_slice(),
&[7_u8; HASHED_PASSWORD_LEN][..]
);
}
#[test]
fn serde_works() {
let hash = HashedPassword { data: [42_u8; HASHED_PASSWORD_LEN] };
let mut tokens: Vec<Token> = Vec::with_capacity(3 + (2 * hash.as_slice().len()));
tokens.push(Token::StructNewType("HashedPassword"));
tokens.push(Token::SeqStart(Some(HASHED_PASSWORD_LEN)));
for x in hash.as_slice() {
tokens.push(Token::SeqSep);
tokens.push(Token::U8(*x));
}
tokens.push(Token::SeqEnd);
assert_tokens(&hash, &tokens[..]);
}
#[test]
fn from_reader_works() {
const ARR1: &'static [u8] = &[42_u8; HASHED_PASSWORD_LEN];
const ARR2: &'static [u8] = &[ 1_u8; 1];
let mut data1 = io::Cursor::new(ARR1);
let mut data2 = io::Cursor::new(ARR2);
assert_eq!(
HashedPassword::from_reader(&mut data1).unwrap(),
HashedPassword { data: [42_u8; HASHED_PASSWORD_LEN] }
);
assert_eq!(
HashedPassword::from_reader(&mut data2).unwrap_err().kind(),
io::ErrorKind::UnexpectedEof
);
}
}