parent
c81e3caf17
commit
f4c6e4ab3d
@ -0,0 +1,182 @@ |
|||||||
|
use crate::{Error, Error::*, Result}; |
||||||
|
use crypto::{digest::Digest, md5::Md5, sha2::Sha256, sha2::Sha512Trunc256}; |
||||||
|
use std::fmt; |
||||||
|
use std::fmt::{Display, Formatter}; |
||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
/// Algorithm type
|
||||||
|
#[derive(Debug, PartialEq)] |
||||||
|
#[allow(non_camel_case_types)] |
||||||
|
pub enum AlgorithmType { |
||||||
|
MD5, |
||||||
|
SHA2_256, |
||||||
|
SHA2_512_256, |
||||||
|
} |
||||||
|
|
||||||
|
/// Algorithm and the -sess flag pair
|
||||||
|
#[derive(Debug, PartialEq)] |
||||||
|
pub struct Algorithm { |
||||||
|
pub algo: AlgorithmType, |
||||||
|
pub sess: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl Algorithm { |
||||||
|
/// Compose from algorithm type and the -sess flag
|
||||||
|
pub fn new(algo: AlgorithmType, sess: bool) -> Algorithm { |
||||||
|
Algorithm { algo, sess } |
||||||
|
} |
||||||
|
|
||||||
|
/// Calculate a hash of bytes using the selected algorithm
|
||||||
|
pub fn hash(&self, bytes: &[u8]) -> String { |
||||||
|
let mut hash: Box<dyn Digest> = match self.algo { |
||||||
|
AlgorithmType::MD5 => Box::new(Md5::new()), |
||||||
|
AlgorithmType::SHA2_256 => Box::new(Sha256::new()), |
||||||
|
AlgorithmType::SHA2_512_256 => Box::new(Sha512Trunc256::new()), |
||||||
|
}; |
||||||
|
|
||||||
|
hash.input(bytes); |
||||||
|
hash.result_str() |
||||||
|
} |
||||||
|
|
||||||
|
/// Calculate a hash of string's bytes using the selected algorithm
|
||||||
|
pub fn hash_str(&self, bytes: &str) -> String { |
||||||
|
self.hash(bytes.as_bytes()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for Algorithm { |
||||||
|
type Err = Error; |
||||||
|
|
||||||
|
/// Parse from the format used in WWW-Authorization
|
||||||
|
fn from_str(s: &str) -> Result<Self> { |
||||||
|
match s { |
||||||
|
"MD5" => Ok(Algorithm::new(AlgorithmType::MD5, false)), |
||||||
|
"MD5-sess" => Ok(Algorithm::new(AlgorithmType::MD5, true)), |
||||||
|
"SHA-256" => Ok(Algorithm::new(AlgorithmType::SHA2_256, false)), |
||||||
|
"SHA-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_256, true)), |
||||||
|
"SHA-512-256" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)), |
||||||
|
"SHA-512-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)), |
||||||
|
_ => Err(UnknownAlgorithm(s.into())), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for Algorithm { |
||||||
|
/// Get a MD5 instance
|
||||||
|
fn default() -> Self { |
||||||
|
Algorithm::new(AlgorithmType::MD5, false) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for Algorithm { |
||||||
|
/// Format to the form used in HTTP headers
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
||||||
|
f.write_str(match self.algo { |
||||||
|
AlgorithmType::MD5 => "MD5", |
||||||
|
AlgorithmType::SHA2_256 => "SHA-256", |
||||||
|
AlgorithmType::SHA2_512_256 => "SHA-512-256", |
||||||
|
})?; |
||||||
|
|
||||||
|
if self.sess { |
||||||
|
f.write_str("-sess")?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// QOP field values
|
||||||
|
#[derive(Debug, PartialEq)] |
||||||
|
#[allow(non_camel_case_types)] |
||||||
|
pub enum Qop { |
||||||
|
AUTH, |
||||||
|
AUTH_INT, |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for Qop { |
||||||
|
type Err = Error; |
||||||
|
|
||||||
|
/// Parse from "auth" or "auth-int" as used in HTTP headers
|
||||||
|
fn from_str(s: &str) -> Result<Self> { |
||||||
|
match s { |
||||||
|
"auth" => Ok(Qop::AUTH), |
||||||
|
"auth-int" => Ok(Qop::AUTH_INT), |
||||||
|
_ => Err(BadQop(s.into())), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for Qop { |
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||||
|
f.write_str(match self { |
||||||
|
Qop::AUTH => "auth", |
||||||
|
Qop::AUTH_INT => "auth-int", |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
#[allow(non_camel_case_types)] |
||||||
|
pub enum QopAlgo<'a> { |
||||||
|
NONE, |
||||||
|
AUTH, |
||||||
|
AUTH_INT(&'a [u8]), |
||||||
|
} |
||||||
|
|
||||||
|
// casting back...
|
||||||
|
impl<'a> Into<Option<Qop>> for QopAlgo<'a> { |
||||||
|
/// Convert to ?Qop
|
||||||
|
fn into(self) -> Option<Qop> { |
||||||
|
match self { |
||||||
|
QopAlgo::NONE => None, |
||||||
|
QopAlgo::AUTH => Some(Qop::AUTH), |
||||||
|
QopAlgo::AUTH_INT(_) => Some(Qop::AUTH_INT), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Charset field value as specified by the server
|
||||||
|
#[derive(Debug, PartialEq)] |
||||||
|
pub enum Charset { |
||||||
|
ASCII, |
||||||
|
UTF8, |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for Charset { |
||||||
|
type Err = Error; |
||||||
|
|
||||||
|
/// Parse from string (only UTF-8 supported, as prescribed by the specification)
|
||||||
|
fn from_str(s: &str) -> Result<Self> { |
||||||
|
match s { |
||||||
|
"UTF-8" => Ok(Charset::UTF8), |
||||||
|
_ => Err(BadCharset(s.into())), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// HTTP method (used when generating the response hash for some Qop options)
|
||||||
|
#[derive(Debug)] |
||||||
|
pub enum HttpMethod { |
||||||
|
GET, |
||||||
|
POST, |
||||||
|
HEAD, |
||||||
|
OTHER(&'static str), |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for HttpMethod { |
||||||
|
fn default() -> Self { |
||||||
|
HttpMethod::GET |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for HttpMethod { |
||||||
|
/// Convert to uppercase string
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
||||||
|
f.write_str(match self { |
||||||
|
HttpMethod::GET => "GET", |
||||||
|
HttpMethod::POST => "POST", |
||||||
|
HttpMethod::HEAD => "HEAD", |
||||||
|
HttpMethod::OTHER(s) => s, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
use std::fmt::{self, Display, Formatter}; |
||||||
|
use std::result; |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub enum Error { |
||||||
|
BadCharset(String), |
||||||
|
UnknownAlgorithm(String), |
||||||
|
BadQop(String), |
||||||
|
MissingRealm(String), |
||||||
|
MissingNonce(String), |
||||||
|
InvalidHeaderSyntax(String), |
||||||
|
BadQopOptions(String), |
||||||
|
} |
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, Error>; |
||||||
|
|
||||||
|
use Error::*; |
||||||
|
|
||||||
|
impl Display for Error { |
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
BadCharset(ctx) => write!(f, "Bad charset: {}", ctx), |
||||||
|
UnknownAlgorithm(ctx) => write!(f, "Unknown algorithm: {}", ctx), |
||||||
|
BadQop(ctx) => write!(f, "Bad Qop option: {}", ctx), |
||||||
|
MissingRealm(ctx) => write!(f, "Missing 'realm' in WWW-Authenticate: {}", ctx), |
||||||
|
MissingNonce(ctx) => write!(f, "Missing 'nonce' in WWW-Authenticate: {}", ctx), |
||||||
|
InvalidHeaderSyntax(ctx) => write!(f, "Invalid header syntax: {}", ctx), |
||||||
|
BadQopOptions(ctx) => write!(f, "Illegal Qop in prompt: {}", ctx), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,18 +0,0 @@ |
|||||||
use std::string::ToString; |
|
||||||
|
|
||||||
/// slash quoting for digest strings
|
|
||||||
pub trait QuoteForDigest { |
|
||||||
fn quote_for_digest(&self) -> String; |
|
||||||
} |
|
||||||
|
|
||||||
impl QuoteForDigest for &str { |
|
||||||
fn quote_for_digest(&self) -> String { |
|
||||||
self.to_string().quote_for_digest() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl QuoteForDigest for String { |
|
||||||
fn quote_for_digest(&self) -> String { |
|
||||||
self.replace("\\", "\\\\").replace("\"", "\\\"") |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue