http-signature-normalization-actix 0.12.0

An HTTP Signatures library that leaves the signing to you
Documentation
//! 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 = "ring")]
pub mod ring;
#[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, Spawn};
    use actix_http::header::InvalidHeaderValue;
    use actix_rt::task::JoinError;
    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, S, D, V>(
            self,
            config: Config<S>,
            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<JoinError>
                + From<PrepareSignError>
                + From<crate::Canceled>
                + From<InvalidHeaderValue>
                + std::fmt::Debug
                + Send
                + 'static,
            K: Display + 'static,
            S: Spawn + '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, S, D, V>(
            self,
            config: Config<S>,
            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<JoinError>
                + From<PrepareSignError>
                + From<crate::Canceled>
                + From<InvalidHeaderValue>
                + std::fmt::Debug
                + Send
                + 'static,
            K: Display + 'static,
            S: Spawn + '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,
    }
}