Browse Source

code import

Ondřej Hruška 2 years ago
commit
ed6b882fa0
Signed by: Ondřej Hruška <ondra@ondrovo.com> GPG key ID: 2C5FD5035250423D
5 changed files with 1151 additions and 0 deletions
  1. 5 0
      .gitignore
  2. 11 0
      Cargo.toml
  3. 1018 0
      src/digest.rs
  4. 99 0
      src/lib.rs
  5. 18 0
      src/utils.rs

+ 5 - 0
.gitignore View File

@@ -0,0 +1,5 @@
1
+/target
2
+**/*.rs.bk
3
+Cargo.lock
4
+.idea
5
+

+ 11 - 0
Cargo.toml View File

@@ -0,0 +1,11 @@
1
+[package]
2
+name = "digest_auth"
3
+version = "0.1.0"
4
+authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
5
+edition = "2018"
6
+
7
+[dependencies]
8
+rust-crypto = "0.2"
9
+rand = "0.6"
10
+hex = "0.3.2"
11
+failure = "0.1.5"

File diff suppressed because it is too large
+ 1018 - 0
src/digest.rs


+ 99 - 0
src/lib.rs View File

@@ -0,0 +1,99 @@
1
+//! This crate implements Digest Auth headers as specified by IETF RFCs 2069, 2617, and 7616.
2
+//! It can be used in conjunction with libraries like reqwest to access e.g. IP cameras
3
+//! that use this authentication scheme.
4
+//!
5
+//! This library is intended for the http client. The algorithm is symmetrical,
6
+//! it's just not optimized for / tested on the server side yet.
7
+//!
8
+//! # Examples
9
+//!
10
+//! Basic usage:
11
+//!
12
+//! ```
13
+//! use digest_auth::AuthContext;
14
+//!
15
+//! // Value from the WWW-Authenticate HTTP header (usually in a HTTP 401 response)
16
+//! let www_authenticate = r#"Digest realm="http-auth@example.org", qop="auth, auth-int", algorithm=MD5, nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#;
17
+//!
18
+//! // Prepare an authorization context. Note that this is a GET request. There are different
19
+//! // constructors available for POST or other request types. You can re-use it, but
20
+//! // it's cheap to create a fresh one each time, as the struct uses references only.
21
+//! let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
22
+//! // For this test, we inject a custom cnonce. It's generated for you otherwise
23
+//! // - you don't need `mut` in that case and needn't worry about this at all.
24
+//! context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
25
+//!
26
+//! // Parse the prompt header. You can inspect the parsed object, its fields are public.
27
+//! let mut prompt = digest_auth::parse(www_authenticate).unwrap();
28
+//!
29
+//! // Compute a value for the Authorization header that we'll send back to the server
30
+//! let answer = prompt.respond(&context).unwrap().to_string();
31
+//! assert_eq!(answer, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="8ca523f5e9506fed4657c9700eebdbec", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#);
32
+//!
33
+//! // The `prompt` variable is mutable, because the 'nc' counter (nonce reuse count)
34
+//! // is inside the struct and updated automatically.
35
+//!
36
+//! // You can re-use it for subsequent requests, assuming the server allows nonce re-use.
37
+//! // Some poorly implemented servers will reject it and give you 401 again, in which case
38
+//! // you should parse the new "WWW-Authenticate" header and use that instead.
39
+//!
40
+//! let answer2 = prompt.respond(&context).unwrap().to_string();
41
+//! // notice how the 'response' field changed - the 'nc' counter is included in the hash
42
+//! assert_eq!(answer2, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000002, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="4b5d595ecf2db9df612ea5b45cd97101", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#);
43
+//! ```
44
+//!
45
+
46
+#[macro_use] extern crate failure;
47
+use failure::Fallible;
48
+
49
+mod digest;
50
+mod utils;
51
+
52
+use std::str::FromStr;
53
+
54
+pub use crate::digest::{
55
+    Algorithm, AuthContext, AuthorizationHeader, HttpMethod, Qop, WwwAuthenticateHeader,
56
+};
57
+
58
+/// Parse the WWW-Authorization header value.
59
+/// It's just a convenience method to call `WwwAuthenticateHeader::from_str()`.
60
+pub fn parse(www_authorize : &str) -> Fallible<WwwAuthenticateHeader> {
61
+    WwwAuthenticateHeader::from_str(www_authorize)
62
+}
63
+
64
+#[test]
65
+fn test_parse_respond() {
66
+    let src = r#"
67
+    Digest
68
+       realm="http-auth@example.org",
69
+       qop="auth, auth-int",
70
+       algorithm=MD5,
71
+       nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
72
+       opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS"
73
+    "#;
74
+
75
+    let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
76
+    context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
77
+
78
+    let mut prompt = crate::parse(src).unwrap();
79
+    let answer = prompt.respond(&context).unwrap();
80
+
81
+    let str = answer.to_string().replace(", ", ",\n  ");
82
+
83
+    assert_eq!(
84
+        str,
85
+        r#"
86
+Digest username="Mufasa",
87
+  realm="http-auth@example.org",
88
+  nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
89
+  uri="/dir/index.html",
90
+  qop=auth,
91
+  nc=00000001,
92
+  cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ",
93
+  response="8ca523f5e9506fed4657c9700eebdbec",
94
+  opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS",
95
+  algorithm=MD5
96
+"#
97
+            .trim()
98
+    );
99
+}

+ 18 - 0
src/utils.rs View File

@@ -0,0 +1,18 @@
1
+use std::string::ToString;
2
+
3
+/// slash quoting for digest strings
4
+pub trait QuoteForDigest {
5
+    fn quote_for_digest(&self) -> String;
6
+}
7
+
8
+impl QuoteForDigest for &str {
9
+    fn quote_for_digest(&self) -> String {
10
+        self.to_string().quote_for_digest()
11
+    }
12
+}
13
+
14
+impl QuoteForDigest for String {
15
+    fn quote_for_digest(&self) -> String {
16
+        self.replace("\\", "\\\\").replace("\"", "\\\"")
17
+    }
18
+}