1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Types and Traits for creating and verifying Digest headers
//!
//! Digest headers are commonly used in conjunction with HTTP Signatures to verify the whole
//! request when request bodies are present

#[cfg(feature = "server")]
pub mod middleware;
#[cfg(feature = "sha-2")]
mod sha2;
#[cfg(feature = "sha-3")]
mod sha3;
#[cfg(feature = "client")]
mod sign;

#[cfg(feature = "client")]
pub use self::client::{DigestClient, DigestCreate, SignExt};

#[cfg(feature = "server")]
pub use self::server::{DigestPart, DigestVerify};

/// Giving names to Digest implementations
pub trait DigestName {
    /// The name of the digest algorithm
    const NAME: &'static str;
}

#[cfg(feature = "client")]
mod client {
    use crate::{Config, PrepareSignError, Sign};
    use actix_http::{error::BlockingError, header::InvalidHeaderValue};
    use awc::{ClientRequest, SendClientRequest};
    use std::{fmt::Display, future::Future, pin::Pin};

    use super::DigestName;

    /// A trait for creating digests of an array of bytes
    pub trait DigestCreate: DigestName {
        /// Compute the digest of the input bytes
        fn compute(&mut self, input: &[u8]) -> String;
    }

    /// Extend the Sign trait with support for adding Digest Headers to the request
    ///
    /// It generates HTTP Signatures after the Digest header has been added, in order to have
    /// verification that the body has not been tampered with, or that the request can't be replayed by
    /// a malicious entity
    pub trait SignExt: Sign {
        /// Set the Digest and Authorization headers on the request
        fn authorization_signature_with_digest<F, E, K, D, V>(
            self,
            config: Config,
            key_id: K,
            digest: D,
            v: V,
            f: F,
        ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
        where
            F: FnOnce(&str) -> Result<String, E> + Send + 'static,
            E: From<BlockingError>
                + From<PrepareSignError>
                + From<InvalidHeaderValue>
                + std::fmt::Debug
                + Send
                + 'static,
            K: Display + 'static,
            D: DigestCreate + Send + 'static,
            V: AsRef<[u8]> + Send + 'static,
            Self: Sized;

        /// Set the Digest and Signature headers on the request
        fn signature_with_digest<F, E, K, D, V>(
            self,
            config: Config,
            key_id: K,
            digest: D,
            v: V,
            f: F,
        ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
        where
            F: FnOnce(&str) -> Result<String, E> + Send + 'static,
            E: From<BlockingError>
                + From<PrepareSignError>
                + From<InvalidHeaderValue>
                + std::fmt::Debug
                + Send
                + 'static,
            K: Display + 'static,
            D: DigestCreate + Send + 'static,
            V: AsRef<[u8]> + Send + 'static,
            Self: Sized;
    }

    /// An intermediate type between setting the Digest and Signature or Authorization headers, and
    /// actually sending the request
    ///
    /// This exists so that the return type for the [`SignExt`] trait can be named
    pub struct DigestClient<V> {
        req: ClientRequest,
        body: V,
    }

    impl<V> DigestClient<V>
    where
        V: AsRef<[u8]>,
    {
        pub(super) fn new(req: ClientRequest, body: V) -> Self {
            DigestClient { req, body }
        }

        /// Send the request
        ///
        /// This is analogous to `ClientRequest::send_body` and uses the body provided when producing
        /// the digest
        pub fn send(self) -> SendClientRequest {
            self.req.send_body(self.body.as_ref().to_vec())
        }

        /// Split the parts of the request
        ///
        /// In case the caller needs to interrogate the ClientRequest before sending
        pub fn split(self) -> (ClientRequest, V) {
            (self.req, self.body)
        }
    }
}

#[cfg(feature = "server")]
mod server {
    use super::DigestName;

    /// A trait for verifying digests
    pub trait DigestVerify: DigestName {
        /// Update the verifier with bytes from the request body
        fn update(&mut self, part: &[u8]);

        /// Verify the request body against the digests from the request headers
        fn verify(&mut self, digests: &[DigestPart]) -> bool;
    }

    /// A parsed digest from the request
    #[derive(Debug)]
    pub struct DigestPart {
        /// The alrogithm used to produce the digest
        pub algorithm: String,

        /// The digest itself
        pub digest: String,
    }
}