use super::ErrorCode;
use parser;
use std::collections::HashMap;
macro_rules! get_param {
($h:expr, $k:expr, $t:ty, $default:expr) => {{
if $h.contains_key($k) {
$h.get($k).unwrap().parse::<$t>().unwrap_or($default)
} else {
$default
}
}};
}
use std::fmt;
#[derive(Clone, Debug)]
pub struct PHCEncoded {
pub id: Option<String>,
pub parameters: HashMap<String, String>,
pub parameters_order: Vec<(String, String)>,
pub salt: Option<String>,
pub hash: Option<String>,
}
impl fmt::Display for PHCEncoded {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let empty_string: String = "".to_string();
let scheme_id = match self.id {
Some(ref scheme) => scheme,
None => &empty_string,
};
write!(f, "${}", scheme_id)?;
if !self.parameters_order.is_empty() {
write!(f, "$")?;
let mut parameters = self.parameters_order.iter();
if let Some(&(ref param, ref value)) = parameters.next() {
write!(f, "{}={}", param, value)?;
for (p, v) in parameters {
write!(f, ",{}={}", p, v)?;
}
}
}
if let Some(ref s) = self.salt {
write!(f, "${}", s)?;
if let Some(ref h) = self.hash {
write!(f, "${}", h)
} else {
Ok(())
}
} else {
Ok(())
}
}
}
impl PHCEncoded {
pub fn insert(&mut self, k: String, v: String) {
self.parameters.insert(k.clone(), v.clone());
self.parameters_order.push((k, v));
}
pub fn from_string(pch_formatted: &str) -> Result<PHCEncoded, ErrorCode> {
let mut encoded = PHCEncoded {
id: None,
parameters: HashMap::new(),
parameters_order: Vec::new(),
salt: None,
hash: None,
};
let mut parts: Vec<&str> = pch_formatted.split('$').collect();
if parts.len() < 2 || parts.len() > 5 {
return Err(ErrorCode::InvalidPasswordFormat);
}
parts.reverse();
parts.pop();
encoded.id = Some(parts.pop().unwrap().to_string());
let mut segment: &str = match parts.pop() {
Some(some) => some,
None => return Ok(encoded), };
if segment.contains('=') {
let params: Vec<&str> = segment.split(',').collect();
for item in params.iter() {
let pair: Vec<&str> = item.split('=').collect();
if pair.len() == 2 {
let param: String = pair[0].to_string();
let value: String = pair[1].to_string();
encoded.insert(param, value);
} else {
return Err(ErrorCode::InvalidPasswordFormat);
}
}
segment = match parts.pop() {
Some(some) => some,
None => return Ok(encoded),
};
} else if segment.is_empty() {
segment = match parts.pop() {
Some(some) => some,
None => return Ok(encoded),
};
}
if segment == "" {
return Err(ErrorCode::InvalidPasswordFormat);
} else {
encoded.salt = Some(segment.to_string());
}
encoded.hash = match parts.pop() {
Some(encoded_hash) => Some(encoded_hash.to_string()),
None => return Ok(encoded),
};
Ok(encoded)
}
pub fn salt(&self) -> Result<Vec<u8>, ErrorCode> {
if self.id.is_none() {
return Err(ErrorCode::InvalidPasswordFormat);
}
match self.salt {
Some(ref encoded_salt) => match parser::from_hex(encoded_salt) {
Ok(raw_salt) => Ok(raw_salt),
Err(_) => Err(ErrorCode::InvalidPasswordFormat),
},
None => Err(ErrorCode::InvalidPasswordFormat),
}
}
pub fn hash(&self) -> Result<Vec<u8>, ErrorCode> {
if self.id.is_none() {
return Err(ErrorCode::InvalidPasswordFormat);
}
match self.hash {
Some(ref encoded_hash) => match parser::from_hex(encoded_hash) {
Ok(raw_hash) => Ok(raw_hash),
Err(_) => Err(ErrorCode::InvalidPasswordFormat),
},
None => Err(ErrorCode::InvalidPasswordFormat),
}
}
}