digest_access/
lib.rs

1//! This crate provides HTTP Digest Access Authentication, as specified by ITEF RFC2069, RFC2617 and RFC7616
2//!
3
4mod digest_authenticator;
5
6pub use digest_authenticator::{DigestAccess, DigestParseError};
7
8#[cfg(test)]
9mod tests {
10    use std::convert::TryFrom;
11
12    use crate::DigestParseError;
13
14    #[test]
15    fn rfc2069() {
16        let rfc2069_test = r#"Digest realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41""#;
17        let mut d = rfc2069_test
18            .parse::<crate::digest_authenticator::DigestAccess>()
19            .unwrap();
20        d.set_username("Mufasa");
21        d.set_password("CircleOfLife");
22        let auth_str = d.generate_authorization("GET", "/dir/index.html", None, None);
23        assert_eq!(
24            auth_str.unwrap(),
25            r#"Digest username="Mufasa", realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", response="1949323746fe6a43ef61f9606e7febea", opaque="5ccc069c403ebaf9f0171e9517f40e41""#
26        );
27    }
28
29    #[test]
30    fn rfc2617() {
31        let rfc2617_test = r#"Digest realm="testrealm@host.com", qop="auth", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41""#;
32        let mut d = rfc2617_test
33            .parse::<crate::digest_authenticator::DigestAccess>()
34            .unwrap();
35        d.set_username("Mufasa");
36        d.set_password("Circle Of Life");
37        let auth_str = d.generate_authorization("GET", "/dir/index.html", None, Some("0a4f113b"));
38        assert_eq!(
39            auth_str.unwrap(),
40            r#"Digest username="Mufasa", realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", qop=auth, algorithm=MD5, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41""#
41        );
42    }
43
44    #[test]
45    fn rfc7616_md5() {
46        let rfc7616_test = r#"Digest
47        realm="http-auth@example.org",
48        qop="auth, auth-int",
49        algorithm=MD5,
50        nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
51        opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#;
52
53        let mut d = rfc7616_test
54            .parse::<crate::digest_authenticator::DigestAccess>()
55            .unwrap();
56        d.set_username("Mufasa");
57        d.set_password("Circle of Life");
58        let auth_str = d.generate_authorization(
59            "GET",
60            "/dir/index.html",
61            None,
62            Some("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ"),
63        );
64
65        assert_eq!(
66            auth_str.unwrap(),
67            r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, algorithm=MD5, nc=00000001, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="8ca523f5e9506fed4657c9700eebdbec", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#
68        );
69    }
70
71    #[test]
72    fn rfc7616_sha256() {
73        let rfc7616_test = r#"Digest
74        realm="http-auth@example.org",
75        qop="auth, auth-int",
76        algorithm=SHA-256,
77        nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
78        opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#;
79
80        let mut d = rfc7616_test
81            .parse::<crate::digest_authenticator::DigestAccess>()
82            .unwrap();
83        d.set_username("Mufasa");
84        d.set_password("Circle of Life");
85        let auth_str = d.generate_authorization(
86            "GET",
87            "/dir/index.html",
88            None,
89            Some("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ"),
90        );
91
92        assert_eq!(
93            auth_str.unwrap(),
94            r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, algorithm=SHA-256, nc=00000001, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#
95        );
96    }
97
98    #[test]
99    fn rfc7616_userhash_512_256() {
100        let rfc7616_userhash_test = r#"Digest
101        realm="api@example.org",
102        qop="auth",
103        algorithm=SHA-512-256,
104        nonce="5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK",
105        opaque="HRPCssKJSGjCrkzDg8OhwpzCiGPChXYjwrI2QmXDnsOS",
106        charset=UTF-8,
107        userhash=true"#;
108
109        let mut d = rfc7616_userhash_test
110            .parse::<crate::digest_authenticator::DigestAccess>()
111            .unwrap();
112        d.set_username("Jäsøn Doe");
113        d.set_password("Secret, or not?");
114        let auth_str = d.generate_authorization(
115            "GET",
116            "/doe.json",
117            None,
118            Some("NTg6RKcb9boFIAS3KrFK9BGeh+iDa/sm6jUMp2wds69v"),
119        );
120
121        // Expected results from erata page
122        assert_eq!(
123            auth_str.unwrap(),
124            r#"Digest username="793263caabb707a56211940d90411ea4a575adeccb7e360aeb624ed06ece9b0b", realm="api@example.org", nonce="5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK", uri="/doe.json", qop=auth, algorithm=SHA-512-256, nc=00000001, cnonce="NTg6RKcb9boFIAS3KrFK9BGeh+iDa/sm6jUMp2wds69v", response="3798d4131c277846293534c3edc11bd8a5e4cdcbff78b05db9d95eeb1cec68a5", opaque="HRPCssKJSGjCrkzDg8OhwpzCiGPChXYjwrI2QmXDnsOS", userhash=true"#
125        );
126    }
127
128    #[test]
129    fn very_short_input() {
130        let short_input = "digest";
131        let r = short_input.parse::<crate::digest_authenticator::DigestAccess>();
132        assert!(r.is_err());
133        assert_eq!(r.unwrap_err(), DigestParseError::Length);
134    }
135
136    #[test]
137    fn short_input() {
138        let short_input = r#"Digest realm=1, nonce=1"#;
139        let r = short_input.parse::<crate::digest_authenticator::DigestAccess>();
140        assert!(r.is_err());
141    }
142
143    #[test]
144    fn incorrect_digest_input() {
145        let bad_input = "digestible Lorem ipsum ";
146        let r = bad_input.parse::<crate::digest_authenticator::DigestAccess>();
147        assert!(r.is_err());
148        assert_eq!(r.unwrap_err(), DigestParseError::InvalidEncoding);
149    }
150
151    #[test]
152    fn missing_realm_input() {
153        let bad_input = "digest Lorem \"ipsum\" dolor";
154        let r = bad_input.parse::<crate::digest_authenticator::DigestAccess>();
155        assert!(r.is_err());
156        assert_eq!(r.unwrap_err(), DigestParseError::MissingRealm);
157    }
158
159    #[test]
160    fn missing_nonce_input() {
161        let missing_nonce = r#"Digest realm="testrealm@host.com""#;
162        let r = missing_nonce.parse::<crate::digest_authenticator::DigestAccess>();
163        assert!(r.is_err());
164        assert_eq!(r.unwrap_err(), DigestParseError::MissingNonce);
165    }
166
167    #[test]
168    fn missing_unterminated_nonce_string_input() {
169        let unterminated_nonce = r#"Digest realm="testrealm@host.com", nonce="testing"#;
170        let r = unterminated_nonce.parse::<crate::digest_authenticator::DigestAccess>();
171        assert!(r.is_err());
172        assert_eq!(r.unwrap_err(), DigestParseError::MissingNonce);
173    }
174
175    #[test]
176    fn invalid_character_at_split() {
177        let invalid_char = r#"Digesţ realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41""#;
178        let r = invalid_char.parse::<crate::digest_authenticator::DigestAccess>();
179        assert!(r.is_err());
180        assert_eq!(r.unwrap_err(), DigestParseError::InvalidEncoding);
181    }
182
183    #[test]
184    #[cfg(feature = "from-headers")]
185    fn from_headers() {
186        const WWW_AUTH_VALUE: &str =
187            r#"Digest realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093""#;
188        let mut headers = http::HeaderMap::new();
189        let r = crate::digest_authenticator::DigestAccess::try_from(&headers);
190        assert!(r.is_err());
191        headers.insert(
192            http::header::WWW_AUTHENTICATE,
193            WWW_AUTH_VALUE.parse().unwrap(),
194        );
195        let r = crate::digest_authenticator::DigestAccess::try_from(&headers);
196        assert!(r.is_ok());
197    }
198}