use crate::{Config, Sign, SignError}; use reqwest::{Body, Request, RequestBuilder}; use std::{fmt::Display, future::Future, pin::Pin}; #[cfg(feature = "sha-2")] mod sha2; #[cfg(feature = "sha-3")] mod sha3; /// A trait for creating digests of an array of bytes pub trait DigestCreate { /// The name of the digest algorithm const NAME: &'static str; /// 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 { fn authorization_signature_with_digest( self, config: Config, key_id: K, digest: D, v: V, f: F, ) -> Pin> + Send>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized; fn signature_with_digest( self, config: Config, key_id: K, digest: D, v: V, f: F, ) -> Pin> + Send>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized; } impl SignExt for RequestBuilder { fn authorization_signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, ) -> Pin> + Send>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized, { Box::pin(async move { let (v, digest) = tokio::task::spawn_blocking(move || { let digest = digest.compute(v.as_ref()); (v, digest) }) .await .map_err(|_| SignError::Canceled)?; let mut req = self .header("Digest", format!("{}={}", D::NAME, digest)) .authorization_signature(&config, key_id, f)?; *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); Ok(req) }) } fn signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, ) -> Pin> + Send>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized, { Box::pin(async move { let (v, digest) = tokio::task::spawn_blocking(move || { let digest = digest.compute(v.as_ref()); (v, digest) }) .await .map_err(|_| SignError::Canceled)?; let mut req = self .header("Digest", format!("{}={}", D::NAME, digest)) .signature(&config, key_id, f)?; *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); Ok(req) }) } } #[cfg(feature = "middleware")] mod middleware { use super::{Config, DigestCreate, Sign, SignError, SignExt}; use reqwest::{Body, Request}; use reqwest_middleware::RequestBuilder; use std::{fmt::Display, future::Future, pin::Pin}; impl SignExt for RequestBuilder { fn authorization_signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, ) -> Pin> + Send>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized, { Box::pin(async move { let (v, digest) = tokio::task::spawn_blocking(move || { let digest = digest.compute(v.as_ref()); (v, digest) }) .await .map_err(|_| SignError::Canceled)?; let mut req = self .header("Digest", format!("{}={}", D::NAME, digest)) .authorization_signature(&config, key_id, f)?; *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); Ok(req) }) } fn signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, ) -> Pin> + Send>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized, { Box::pin(async move { let (v, digest) = tokio::task::spawn_blocking(move || { let digest = digest.compute(v.as_ref()); (v, digest) }) .await .map_err(|_| SignError::Canceled)?; let mut req = self .header("Digest", format!("{}={}", D::NAME, digest)) .signature(&config, key_id, f)?; *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); Ok(req) }) } } }