enums.rs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #![allow(clippy::upper_case_acronyms)]
  2. use crate::{Error, Error::*, Result};
  3. use std::fmt;
  4. use std::fmt::{Display, Formatter};
  5. use std::str::FromStr;
  6. use digest::{Digest, DynDigest};
  7. use md5::Md5;
  8. use sha2::{Sha256, Sha512Trunc256};
  9. use std::borrow::Cow;
  10. /// Algorithm type
  11. #[derive(Debug, PartialEq, Clone, Copy)]
  12. #[allow(non_camel_case_types)]
  13. pub enum AlgorithmType {
  14. MD5,
  15. SHA2_256,
  16. SHA2_512_256,
  17. }
  18. /// Algorithm and the -sess flag pair
  19. #[derive(Debug, PartialEq, Clone, Copy)]
  20. pub struct Algorithm {
  21. pub algo: AlgorithmType,
  22. pub sess: bool,
  23. }
  24. impl Algorithm {
  25. /// Compose from algorithm type and the -sess flag
  26. pub fn new(algo: AlgorithmType, sess: bool) -> Algorithm {
  27. Algorithm { algo, sess }
  28. }
  29. /// Calculate a hash of bytes using the selected algorithm
  30. pub fn hash(self, bytes: &[u8]) -> String {
  31. let mut hash: Box<dyn DynDigest> = match self.algo {
  32. AlgorithmType::MD5 => Box::new(Md5::new()),
  33. AlgorithmType::SHA2_256 => Box::new(Sha256::new()),
  34. AlgorithmType::SHA2_512_256 => Box::new(Sha512Trunc256::new()),
  35. };
  36. hash.update(bytes);
  37. hex::encode(hash.finalize())
  38. }
  39. /// Calculate a hash of string's bytes using the selected algorithm
  40. pub fn hash_str(self, bytes: &str) -> String {
  41. self.hash(bytes.as_bytes())
  42. }
  43. }
  44. impl FromStr for Algorithm {
  45. type Err = Error;
  46. /// Parse from the format used in WWW-Authorization
  47. fn from_str(s: &str) -> Result<Self> {
  48. match s {
  49. "MD5" => Ok(Algorithm::new(AlgorithmType::MD5, false)),
  50. "MD5-sess" => Ok(Algorithm::new(AlgorithmType::MD5, true)),
  51. "SHA-256" => Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
  52. "SHA-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
  53. "SHA-512-256" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
  54. "SHA-512-256-sess" => Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
  55. _ => Err(UnknownAlgorithm(s.into())),
  56. }
  57. }
  58. }
  59. impl Default for Algorithm {
  60. /// Get a MD5 instance
  61. fn default() -> Self {
  62. Algorithm::new(AlgorithmType::MD5, false)
  63. }
  64. }
  65. impl Display for Algorithm {
  66. /// Format to the form used in HTTP headers
  67. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  68. f.write_str(match self.algo {
  69. AlgorithmType::MD5 => "MD5",
  70. AlgorithmType::SHA2_256 => "SHA-256",
  71. AlgorithmType::SHA2_512_256 => "SHA-512-256",
  72. })?;
  73. if self.sess {
  74. f.write_str("-sess")?;
  75. }
  76. Ok(())
  77. }
  78. }
  79. /// QOP field values
  80. #[derive(Debug, PartialEq, Clone, Copy)]
  81. #[allow(non_camel_case_types)]
  82. pub enum Qop {
  83. AUTH,
  84. AUTH_INT,
  85. }
  86. impl FromStr for Qop {
  87. type Err = Error;
  88. /// Parse from "auth" or "auth-int" as used in HTTP headers
  89. fn from_str(s: &str) -> Result<Self> {
  90. match s {
  91. "auth" => Ok(Qop::AUTH),
  92. "auth-int" => Ok(Qop::AUTH_INT),
  93. _ => Err(BadQop(s.into())),
  94. }
  95. }
  96. }
  97. impl Display for Qop {
  98. fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
  99. f.write_str(match self {
  100. Qop::AUTH => "auth",
  101. Qop::AUTH_INT => "auth-int",
  102. })
  103. }
  104. }
  105. #[derive(Debug)]
  106. #[allow(non_camel_case_types)]
  107. pub enum QopAlgo<'a> {
  108. NONE,
  109. AUTH,
  110. AUTH_INT(&'a [u8]),
  111. }
  112. // casting back...
  113. impl<'a> From<QopAlgo<'a>> for Option<Qop> {
  114. fn from(algo: QopAlgo<'a>) -> Self {
  115. match algo {
  116. QopAlgo::NONE => None,
  117. QopAlgo::AUTH => Some(Qop::AUTH),
  118. QopAlgo::AUTH_INT(_) => Some(Qop::AUTH_INT),
  119. }
  120. }
  121. }
  122. /// Charset field value as specified by the server
  123. #[derive(Debug, PartialEq, Clone)]
  124. pub enum Charset {
  125. ASCII,
  126. UTF8,
  127. }
  128. impl FromStr for Charset {
  129. type Err = Error;
  130. /// Parse from string (only UTF-8 supported, as prescribed by the specification)
  131. fn from_str(s: &str) -> Result<Self> {
  132. match s {
  133. "UTF-8" => Ok(Charset::UTF8),
  134. _ => Err(BadCharset(s.into())),
  135. }
  136. }
  137. }
  138. impl Display for Charset {
  139. fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
  140. f.write_str(match self {
  141. Charset::ASCII => "ASCII",
  142. Charset::UTF8 => "UTF-8",
  143. })
  144. }
  145. }
  146. /// HTTP method (used when generating the response hash for some Qop options)
  147. #[derive(Debug, PartialEq, Clone)]
  148. pub enum HttpMethod<'a> {
  149. GET,
  150. POST,
  151. HEAD,
  152. OTHER(Cow<'a, str>),
  153. }
  154. impl<'a> Default for HttpMethod<'a> {
  155. fn default() -> Self {
  156. HttpMethod::GET
  157. }
  158. }
  159. impl<'a> Display for HttpMethod<'a> {
  160. /// Convert to uppercase string
  161. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  162. f.write_str(match self {
  163. HttpMethod::GET => "GET",
  164. HttpMethod::POST => "POST",
  165. HttpMethod::HEAD => "HEAD",
  166. HttpMethod::OTHER(s) => s,
  167. })
  168. }
  169. }
  170. impl<'a> From<&'a str> for HttpMethod<'a> {
  171. fn from(s: &'a str) -> Self {
  172. match s {
  173. "GET" => HttpMethod::GET,
  174. "POST" => HttpMethod::POST,
  175. "HEAD" => HttpMethod::HEAD,
  176. s => HttpMethod::OTHER(Cow::Borrowed(s)),
  177. }
  178. }
  179. }
  180. impl<'a> From<&'a [u8]> for HttpMethod<'a> {
  181. fn from(s: &'a [u8]) -> Self {
  182. String::from_utf8_lossy(s).into()
  183. }
  184. }
  185. impl<'a> From<String> for HttpMethod<'a> {
  186. fn from(s: String) -> Self {
  187. match &s[..] {
  188. "GET" => HttpMethod::GET,
  189. "POST" => HttpMethod::POST,
  190. "HEAD" => HttpMethod::HEAD,
  191. _ => HttpMethod::OTHER(Cow::Owned(s)),
  192. }
  193. }
  194. }
  195. impl<'a> From<Cow<'a, str>> for HttpMethod<'a> {
  196. fn from(s: Cow<'a, str>) -> Self {
  197. match &s[..] {
  198. "GET" => HttpMethod::GET,
  199. "POST" => HttpMethod::POST,
  200. "HEAD" => HttpMethod::HEAD,
  201. _ => HttpMethod::OTHER(s),
  202. }
  203. }
  204. }
  205. #[cfg(feature = "http")]
  206. impl From<http::Method> for HttpMethod<'static> {
  207. fn from(method: http::Method) -> Self {
  208. match method {
  209. http::Method::GET => HttpMethod::GET,
  210. http::Method::POST => HttpMethod::POST,
  211. http::Method::HEAD => HttpMethod::HEAD,
  212. other => HttpMethod::OTHER(other.to_string().into()),
  213. }
  214. }
  215. }
  216. #[cfg(test)]
  217. mod test {
  218. use crate::error::Error::{BadCharset, BadQop, UnknownAlgorithm};
  219. use crate::{Algorithm, AlgorithmType, Charset, HttpMethod, Qop, QopAlgo};
  220. use std::borrow::Cow;
  221. use std::str::FromStr;
  222. #[test]
  223. fn test_algorithm_type() {
  224. // String parsing
  225. assert_eq!(
  226. Ok(Algorithm::new(AlgorithmType::MD5, false)),
  227. Algorithm::from_str("MD5")
  228. );
  229. assert_eq!(
  230. Ok(Algorithm::new(AlgorithmType::MD5, true)),
  231. Algorithm::from_str("MD5-sess")
  232. );
  233. assert_eq!(
  234. Ok(Algorithm::new(AlgorithmType::SHA2_256, false)),
  235. Algorithm::from_str("SHA-256")
  236. );
  237. assert_eq!(
  238. Ok(Algorithm::new(AlgorithmType::SHA2_256, true)),
  239. Algorithm::from_str("SHA-256-sess")
  240. );
  241. assert_eq!(
  242. Ok(Algorithm::new(AlgorithmType::SHA2_512_256, false)),
  243. Algorithm::from_str("SHA-512-256")
  244. );
  245. assert_eq!(
  246. Ok(Algorithm::new(AlgorithmType::SHA2_512_256, true)),
  247. Algorithm::from_str("SHA-512-256-sess")
  248. );
  249. assert_eq!(
  250. Err(UnknownAlgorithm("OTHER_ALGORITHM".to_string())),
  251. Algorithm::from_str("OTHER_ALGORITHM")
  252. );
  253. // String building
  254. assert_eq!(
  255. "MD5".to_string(),
  256. Algorithm::new(AlgorithmType::MD5, false).to_string()
  257. );
  258. assert_eq!(
  259. "MD5-sess".to_string(),
  260. Algorithm::new(AlgorithmType::MD5, true).to_string()
  261. );
  262. assert_eq!(
  263. "SHA-256".to_string(),
  264. Algorithm::new(AlgorithmType::SHA2_256, false).to_string()
  265. );
  266. assert_eq!(
  267. "SHA-256-sess".to_string(),
  268. Algorithm::new(AlgorithmType::SHA2_256, true).to_string()
  269. );
  270. assert_eq!(
  271. "SHA-512-256".to_string(),
  272. Algorithm::new(AlgorithmType::SHA2_512_256, false).to_string()
  273. );
  274. assert_eq!(
  275. "SHA-512-256-sess".to_string(),
  276. Algorithm::new(AlgorithmType::SHA2_512_256, true).to_string()
  277. );
  278. // Default
  279. assert_eq!(
  280. Algorithm::new(AlgorithmType::MD5, false),
  281. Default::default()
  282. );
  283. // Hash calculation
  284. assert_eq!(
  285. "e2fc714c4727ee9395f324cd2e7f331f".to_string(),
  286. Algorithm::new(AlgorithmType::MD5, false).hash("abcd".as_bytes())
  287. );
  288. assert_eq!(
  289. "e2fc714c4727ee9395f324cd2e7f331f".to_string(),
  290. Algorithm::new(AlgorithmType::MD5, false).hash_str("abcd")
  291. );
  292. assert_eq!(
  293. "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589".to_string(),
  294. Algorithm::new(AlgorithmType::SHA2_256, false).hash("abcd".as_bytes())
  295. );
  296. assert_eq!(
  297. "d2891c7978be0e24948f37caa415b87cb5cbe2b26b7bad9dc6391b8a6f6ddcc9".to_string(),
  298. Algorithm::new(AlgorithmType::SHA2_512_256, false).hash("abcd".as_bytes())
  299. );
  300. }
  301. #[test]
  302. fn test_qop() {
  303. assert_eq!(Ok(Qop::AUTH), Qop::from_str("auth"));
  304. assert_eq!(Ok(Qop::AUTH_INT), Qop::from_str("auth-int"));
  305. assert_eq!(Err(BadQop("banana".to_string())), Qop::from_str("banana"));
  306. assert_eq!("auth".to_string(), Qop::AUTH.to_string());
  307. assert_eq!("auth-int".to_string(), Qop::AUTH_INT.to_string());
  308. }
  309. #[test]
  310. fn test_qop_algo() {
  311. assert_eq!(Option::<Qop>::None, QopAlgo::NONE.into());
  312. assert_eq!(Some(Qop::AUTH), QopAlgo::AUTH.into());
  313. assert_eq!(
  314. Some(Qop::AUTH_INT),
  315. QopAlgo::AUTH_INT("foo".as_bytes()).into()
  316. );
  317. }
  318. #[test]
  319. fn test_charset() {
  320. assert_eq!(Ok(Charset::UTF8), Charset::from_str("UTF-8"));
  321. assert_eq!(Err(BadCharset("ASCII".into())), Charset::from_str("ASCII"));
  322. assert_eq!("UTF-8".to_string(), Charset::UTF8.to_string());
  323. assert_eq!("ASCII".to_string(), Charset::ASCII.to_string());
  324. }
  325. #[test]
  326. fn test_http_method() {
  327. // Well known 'static
  328. assert_eq!(HttpMethod::GET, "GET".into());
  329. assert_eq!(HttpMethod::POST, "POST".into());
  330. assert_eq!(HttpMethod::HEAD, "HEAD".into());
  331. // As bytes
  332. assert_eq!(HttpMethod::GET, "GET".as_bytes().into());
  333. assert_eq!(
  334. HttpMethod::OTHER(Cow::Borrowed("ěščř")),
  335. "ěščř".as_bytes().into()
  336. );
  337. assert_eq!(
  338. HttpMethod::OTHER(Cow::Owned("AB�".to_string())),
  339. (&[65u8, 66, 156][..]).into()
  340. );
  341. // Well known String
  342. assert_eq!(HttpMethod::GET, String::from("GET").into());
  343. // Custom String
  344. assert_eq!(
  345. HttpMethod::OTHER(Cow::Borrowed("NonsenseMethod")),
  346. "NonsenseMethod".into()
  347. );
  348. assert_eq!(
  349. HttpMethod::OTHER(Cow::Owned("NonsenseMethod".to_string())),
  350. "NonsenseMethod".to_string().into()
  351. );
  352. // Custom Cow
  353. assert_eq!(HttpMethod::HEAD, Cow::Borrowed("HEAD").into());
  354. assert_eq!(
  355. HttpMethod::OTHER(Cow::Borrowed("NonsenseMethod")),
  356. Cow::Borrowed("NonsenseMethod").into()
  357. );
  358. // to string
  359. assert_eq!("GET".to_string(), HttpMethod::GET.to_string());
  360. assert_eq!("POST".to_string(), HttpMethod::POST.to_string());
  361. assert_eq!("HEAD".to_string(), HttpMethod::HEAD.to_string());
  362. assert_eq!(
  363. "NonsenseMethod".to_string(),
  364. HttpMethod::OTHER(Cow::Borrowed("NonsenseMethod")).to_string()
  365. );
  366. assert_eq!(
  367. "NonsenseMethod".to_string(),
  368. HttpMethod::OTHER(Cow::Owned("NonsenseMethod".to_string())).to_string()
  369. );
  370. }
  371. #[cfg(feature = "http")]
  372. #[test]
  373. fn test_http_crate() {
  374. assert_eq!(HttpMethod::GET, http::Method::GET.into());
  375. assert_eq!(
  376. HttpMethod::OTHER(Cow::Owned("BANANA".to_string())),
  377. http::Method::from_str("BANANA").unwrap().into()
  378. );
  379. }
  380. }