http_sig/
rouille_impls.rs

1use std::convert::TryInto;
2use std::fmt::{self, Debug};
3use std::io::{self, Cursor, Read};
4use std::mem;
5
6use http::header::HeaderValue;
7
8use super::*;
9
10/// In order to verify the signature on a rouille request, the request body must
11/// be consumed by the verification process. This type is used to return the request body
12/// contents on completion of a successful signature verification.
13///
14/// The `std::io::Read` trait is implemented for this type.
15pub struct RouilleBody<'a>(RouilleBodyInner<'a>);
16
17enum RouilleBodyInner<'a> {
18    Digested(Result<Cursor<Vec<u8>>, io::Error>),
19    Undigested(rouille::RequestBody<'a>),
20}
21
22impl Debug for RouilleBody<'_> {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        f.write_str("RouilleBody { .. }")
25    }
26}
27
28impl Read for RouilleBody<'_> {
29    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
30        use RouilleBodyInner::*;
31        match self {
32            RouilleBody(Digested(Ok(x))) => x.read(buf),
33            RouilleBody(Digested(Err(e))) => Err(mem::replace(e, io::ErrorKind::Other.into())),
34            RouilleBody(Undigested(x)) => x.read(buf),
35        }
36    }
37}
38
39impl RequestLike for rouille::Request {
40    fn header(&self, header: &Header) -> Option<HeaderValue> {
41        match header {
42            Header::Normal(header) => rouille::Request::header(self, header.as_str())
43                .and_then(|v| HeaderValue::from_str(v).ok()),
44            Header::Pseudo(PseudoHeader::RequestTarget) => {
45                let method = self.method().to_ascii_lowercase();
46                let path = self.raw_url();
47                format!("{} {}", method, path).try_into().ok()
48            }
49            _ => None,
50        }
51    }
52}
53impl<'a> ServerRequestLike for &'a rouille::Request {
54    type Remnant = Option<RouilleBody<'a>>;
55
56    fn complete_with_digest(self, digest: &dyn HttpDigest) -> (Option<String>, Self::Remnant) {
57        if let Some(mut body) = self.data() {
58            let mut result = Vec::new();
59            if let Err(e) = body.read_to_end(&mut result) {
60                (None, Some(RouilleBody(RouilleBodyInner::Digested(Err(e)))))
61            } else if result.is_empty() {
62                (None, None)
63            } else {
64                let computed_digest = digest.http_digest(&result);
65                (
66                    Some(computed_digest),
67                    Some(RouilleBody(RouilleBodyInner::Digested(Ok(Cursor::new(
68                        result,
69                    ))))),
70                )
71            }
72        } else {
73            (None, None)
74        }
75    }
76    fn complete(self) -> Self::Remnant {
77        self.data()
78            .map(RouilleBodyInner::Undigested)
79            .map(RouilleBody)
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use std::sync::Arc;
86
87    use chrono::{offset::TimeZone, Utc};
88
89    use super::*;
90
91    #[test]
92    fn can_verify_post_request() {
93        let key_provider = SimpleKeyProvider::new(vec![(
94            "test_key",
95            Arc::new(DefaultSignatureAlgorithm::new("abcdefgh".as_bytes()))
96                as Arc<dyn HttpSignatureVerify>,
97        )]);
98        let config = VerifyingConfig::new(key_provider).with_validate_date(false);
99
100        let request = rouille::Request::fake_http(
101            "POST",
102            "/foo/bar",
103            vec![
104                ("Host".into(), "test.com".into()),
105                ("ContentType".into(), "application/json".into()),
106                ("Date".into(), Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).single().expect("valid date")
107                    .format("%a, %d %b %Y %T GMT")
108                    .to_string()),
109                ("Digest".into(), "SHA-256=2vgEVkfe4d6VW+tSWAziO7BUx7uT/rA9hn1EoxUJi2o=".into()),
110                ("Authorization".into(), "Signature keyId=\"test_key\",algorithm=\"hmac-sha256\",signature=\"uH2I9FSuCGUrIEygs7hR29oz0Afkz0bZyHpz6cW/mLQ=\",headers=\"(request-target) date digest host".into()),
111            ],
112            br#"{ "x": 1, "y": 2}"#[..].into(),
113        );
114
115        request.verify(&config).unwrap();
116    }
117
118    #[test]
119    fn can_verify_get_request() {
120        let key_provider = SimpleKeyProvider::new(vec![(
121            "test_key",
122            Arc::new(DefaultSignatureAlgorithm::new("abcdefgh".as_bytes()))
123                as Arc<dyn HttpSignatureVerify>,
124        )]);
125        let config = VerifyingConfig::new(key_provider).with_validate_date(false);
126
127        let request = rouille::Request::fake_http(
128            "GET",
129            "/foo/bar",
130            vec![
131                ("Date".into(), Utc.with_ymd_and_hms(2014, 7, 8,
132                    9, 10, 11).single().expect("valid date")
133                    .format("%a, %d %b %Y %T GMT")
134                    .to_string()),
135                ("Authorization".into(), "Signature keyId=\"test_key\",algorithm=\"hmac-sha256\",signature=\"sGQ3hA9KB40CU1pHbRLXLvLdUWYn+c3fcfL+Sw8kIZE=\",headers=\"(request-target) date".into()),
136            ],
137            Vec::new(),
138        );
139
140        request.verify(&config).unwrap();
141    }
142}