http_signature_normalization_actix/digest/
mod.rs

1//! Types and Traits for creating and verifying Digest headers
2//!
3//! Digest headers are commonly used in conjunction with HTTP Signatures to verify the whole
4//! request when request bodies are present
5
6#[cfg(feature = "server")]
7pub mod middleware;
8#[cfg(feature = "ring")]
9pub mod ring;
10#[cfg(feature = "sha-2")]
11mod sha2;
12#[cfg(feature = "sha-3")]
13mod sha3;
14#[cfg(feature = "client")]
15mod sign;
16
17#[cfg(feature = "client")]
18pub use self::client::{DigestClient, DigestCreate, SignExt};
19
20#[cfg(feature = "server")]
21pub use self::server::{DigestPart, DigestVerify};
22
23/// Giving names to Digest implementations
24pub trait DigestName {
25    /// The name of the digest algorithm
26    const NAME: &'static str;
27}
28
29#[cfg(feature = "client")]
30mod client {
31    use crate::{Config, PrepareSignError, Sign, Spawn};
32    use actix_http::header::InvalidHeaderValue;
33    use actix_rt::task::JoinError;
34    use awc::{ClientRequest, SendClientRequest};
35    use std::{fmt::Display, future::Future, pin::Pin};
36
37    use super::DigestName;
38
39    /// A trait for creating digests of an array of bytes
40    pub trait DigestCreate: DigestName {
41        /// Compute the digest of the input bytes
42        fn compute(&mut self, input: &[u8]) -> String;
43    }
44
45    /// Extend the Sign trait with support for adding Digest Headers to the request
46    ///
47    /// It generates HTTP Signatures after the Digest header has been added, in order to have
48    /// verification that the body has not been tampered with, or that the request can't be replayed by
49    /// a malicious entity
50    pub trait SignExt: Sign {
51        /// Set the Digest and Authorization headers on the request
52        fn authorization_signature_with_digest<F, E, K, S, D, V>(
53            self,
54            config: Config<S>,
55            key_id: K,
56            digest: D,
57            v: V,
58            f: F,
59        ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
60        where
61            F: FnOnce(&str) -> Result<String, E> + Send + 'static,
62            E: From<JoinError>
63                + From<PrepareSignError>
64                + From<crate::Canceled>
65                + From<InvalidHeaderValue>
66                + std::fmt::Debug
67                + Send
68                + 'static,
69            K: Display + 'static,
70            S: Spawn + 'static,
71            D: DigestCreate + Send + 'static,
72            V: AsRef<[u8]> + Send + 'static,
73            Self: Sized;
74
75        /// Set the Digest and Signature headers on the request
76        fn signature_with_digest<F, E, K, S, D, V>(
77            self,
78            config: Config<S>,
79            key_id: K,
80            digest: D,
81            v: V,
82            f: F,
83        ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
84        where
85            F: FnOnce(&str) -> Result<String, E> + Send + 'static,
86            E: From<JoinError>
87                + From<PrepareSignError>
88                + From<crate::Canceled>
89                + From<InvalidHeaderValue>
90                + std::fmt::Debug
91                + Send
92                + 'static,
93            K: Display + 'static,
94            S: Spawn + 'static,
95            D: DigestCreate + Send + 'static,
96            V: AsRef<[u8]> + Send + 'static,
97            Self: Sized;
98    }
99
100    /// An intermediate type between setting the Digest and Signature or Authorization headers, and
101    /// actually sending the request
102    ///
103    /// This exists so that the return type for the [`SignExt`] trait can be named
104    pub struct DigestClient<V> {
105        req: ClientRequest,
106        body: V,
107    }
108
109    impl<V> DigestClient<V>
110    where
111        V: AsRef<[u8]>,
112    {
113        pub(super) fn new(req: ClientRequest, body: V) -> Self {
114            DigestClient { req, body }
115        }
116
117        /// Send the request
118        ///
119        /// This is analogous to `ClientRequest::send_body` and uses the body provided when producing
120        /// the digest
121        pub fn send(self) -> SendClientRequest {
122            self.req.send_body(self.body.as_ref().to_vec())
123        }
124
125        /// Split the parts of the request
126        ///
127        /// In case the caller needs to interrogate the ClientRequest before sending
128        pub fn split(self) -> (ClientRequest, V) {
129            (self.req, self.body)
130        }
131    }
132}
133
134#[cfg(feature = "server")]
135mod server {
136    use super::DigestName;
137
138    /// A trait for verifying digests
139    pub trait DigestVerify: DigestName {
140        /// Update the verifier with bytes from the request body
141        fn update(&mut self, part: &[u8]);
142
143        /// Verify the request body against the digests from the request headers
144        fn verify(&mut self, digests: &[DigestPart]) -> bool;
145    }
146
147    /// A parsed digest from the request
148    #[derive(Debug)]
149    pub struct DigestPart {
150        /// The alrogithm used to produce the digest
151        pub algorithm: String,
152
153        /// The digest itself
154        pub digest: String,
155    }
156}