diff --git a/src/aws/client.rs b/src/aws/client.rs index 4ea3b888..06992933 100644 --- a/src/aws/client.rs +++ b/src/aws/client.rs @@ -411,11 +411,15 @@ impl Request<'_> { if let Some(digest) = cached_digest { return Ok(digest); } - let mut ctx = self.config.crypto()?.digest(DigestAlgorithm::Sha256)?; - for part in &payload { - ctx.update(part); - } - let digest = ctx.finish()?.try_into().unwrap(); + let digest = self + .config + .crypto()? + .digest( + DigestAlgorithm::Sha256, + &payload.iter().map(|c| c.as_ref()).collect::>(), + )? + .try_into() + .unwrap(); cached_digest = Some(digest); Ok(digest) }; @@ -582,11 +586,9 @@ impl S3Client { } let crypto = self.config.crypto()?; - let mut ctx = crypto.digest(DigestAlgorithm::Sha256)?; - ctx.update(body.as_ref()); - let digest = ctx.finish()?; + let digest = crypto.digest(DigestAlgorithm::Sha256, &[&body])?; - builder = builder.header(SHA256_CHECKSUM, BASE64_STANDARD.encode(digest)); + builder = builder.header(SHA256_CHECKSUM, BASE64_STANDARD.encode(&digest)); // S3 *requires* DeleteObjects to include a Content-MD5 header: // https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html @@ -599,7 +601,7 @@ impl S3Client { let response = builder .header(CONTENT_TYPE, "application/xml") .body(body) - .with_aws_sigv4(authorizer, Some(digest))? + .with_aws_sigv4(authorizer, Some(digest.as_ref()))? .retryable(&self.config.retry_config) .retry_error_body(true) .send() diff --git a/src/aws/credential.rs b/src/aws/credential.rs index c16330a3..74d9f0d1 100644 --- a/src/aws/credential.rs +++ b/src/aws/credential.rs @@ -105,22 +105,21 @@ impl AwsCredential { let date_string = date.format("%Y%m%d").to_string(); let secret_key = format!("AWS4{}", self.secret_key); - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, secret_key.as_bytes())?; - ctx.update(date_string.as_bytes()); + let tag = crypto.hmac( + DigestAlgorithm::Sha256, + secret_key.as_bytes(), + date_string.as_bytes(), + )?; - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?; - ctx.update(region.as_bytes()); + let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, region.as_bytes())?; - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?; - ctx.update(service.as_bytes()); + let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, service.as_bytes())?; - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?; - ctx.update(b"aws4_request"); + let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, b"aws4_request")?; - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?; - ctx.update(to_sign.as_bytes()); + let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, to_sign.as_bytes())?; - Ok(hex_encode(ctx.finish()?)) + Ok(hex_encode(&tag)) } } diff --git a/src/azure/client.rs b/src/azure/client.rs index 1c21fa9e..9864d86a 100644 --- a/src/azure/client.rs +++ b/src/azure/client.rs @@ -282,9 +282,8 @@ impl AzureEncryptionHeaders { } let crypto = crypto_provider(crypto)?; - let mut ctx = crypto.digest(DigestAlgorithm::Sha256)?; - ctx.update(&decoded_key); - let encryption_key_sha256 = BASE64_STANDARD.encode(ctx.finish()?); + let encryption_key_sha256 = + BASE64_STANDARD.encode(crypto.digest(DigestAlgorithm::Sha256, &[&decoded_key])?); Ok(Self { encryption_key: Some(encryption_key), diff --git a/src/azure/credential.rs b/src/azure/credential.rs index f4c2989d..af5fbbb3 100644 --- a/src/azure/credential.rs +++ b/src/azure/credential.rs @@ -206,9 +206,11 @@ impl AzureSigner { ), None => string_to_sign_service_sas(url, method, &self.account, &self.start, &self.end), }; - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, &self.signing_key.0)?; - ctx.update(str_to_sign.as_bytes()); - let auth = ctx.finish()?; + let auth = crypto.hmac( + DigestAlgorithm::Sha256, + &self.signing_key.0, + str_to_sign.as_bytes(), + )?; url.query_pairs_mut().extend_pairs(query_pairs); url.query_pairs_mut() @@ -358,9 +360,7 @@ fn generate_authorization( key: &AzureAccessKey, ) -> crate::Result { let str_to_sign = string_to_sign(h, u, method, account); - let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, &key.0)?; - ctx.update(str_to_sign.as_bytes()); - let auth = ctx.finish()?; + let auth = crypto.hmac(DigestAlgorithm::Sha256, &key.0, str_to_sign.as_bytes())?; Ok(format!( "SharedKey {}:{}", account, diff --git a/src/client/crypto.rs b/src/client/crypto.rs index 41e3ef5c..ccd49353 100644 --- a/src/client/crypto.rs +++ b/src/client/crypto.rs @@ -18,7 +18,7 @@ use crate::Result; /// Algorithm for computing digests -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)] #[non_exhaustive] pub enum DigestAlgorithm { /// SHA-256 @@ -26,7 +26,7 @@ pub enum DigestAlgorithm { } /// Algorithm for signing payloads -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)] #[non_exhaustive] pub enum SigningAlgorithm { /// RSASSA-PKCS1-v1_5 using SHA-256 @@ -35,42 +35,16 @@ pub enum SigningAlgorithm { /// Provides cryptographic primitives pub trait CryptoProvider: std::fmt::Debug + Send + Sync { - /// Compute a digest - fn digest(&self, algorithm: DigestAlgorithm) -> Result>; + /// Compute the digest of `data` + fn digest(&self, algorithm: DigestAlgorithm, data: &[&[u8]]) -> Result>; - /// Compute an HMAC with the provided `secret` - fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result>; + /// Compute the HMAC of `data` with the provided `secret` + fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8], data: &[u8]) -> Result>; /// Sign a payload with the provided PEM-encoded secret fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result>; } -/// Incrementally compute a digest, see [`CryptoProvider::digest`] -pub trait DigestContext: Send { - /// Updates the digest with all the data in data. - /// - /// It is implementation-defined behaviour to call this after calling [`Self::finish`] - fn update(&mut self, data: &[u8]); - - /// Finalizes the digest calculation and returns the digest value. - /// - /// It is implementation-defined behaviour to call this after calling [`Self::finish`] - fn finish(&mut self) -> Result<&[u8]>; -} - -/// Incrementally compute a HMAC, see [`CryptoProvider::hmac`] -pub trait HmacContext: Send { - /// Updates the HMAC with all the data in data. - /// - /// It is implementation-defined behaviour to call this after calling [`Self::finish`] - fn update(&mut self, data: &[u8]); - - /// Finalizes the HMAC calculation and returns the HMAC value. - /// - /// It is implementation-defined behaviour to call this after calling [`Self::finish`] - fn finish(&mut self) -> Result<&[u8]>; -} - /// Sign a payload, see [`CryptoProvider::sign`] pub trait Signer: Send + Sync { /// Sign the provided payload @@ -150,26 +124,24 @@ pub(crate) mod ring { } impl CryptoProvider for RingCryptoProvider { - fn digest(&self, algorithm: DigestAlgorithm) -> Result> { + fn digest(&self, algorithm: DigestAlgorithm, data: &[&[u8]]) -> Result> { let algorithm = match algorithm { DigestAlgorithm::Sha256 => &digest::SHA256, }; - let ctx = digest::Context::new(algorithm); - Ok(Box::new(RingDigestContext { - ctx: Some(ctx), - out: None, - })) + let mut ctx = digest::Context::new(algorithm); + for chunk in data { + ctx.update(chunk); + } + Ok(ctx.finish().as_ref().to_vec()) } - fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result> { + fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8], data: &[u8]) -> Result> { let algorithm = match algorithm { DigestAlgorithm::Sha256 => hmac::HMAC_SHA256, }; - let ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret)); - Ok(Box::new(RingHmacContext { - ctx: Some(ctx), - out: None, - })) + let mut ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret)); + ctx.update(data); + Ok(ctx.sign().as_ref().to_vec()) } fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result> { @@ -179,38 +151,6 @@ pub(crate) mod ring { } } - struct RingDigestContext { - ctx: Option, - out: Option, - } - - impl DigestContext for RingDigestContext { - fn update(&mut self, data: &[u8]) { - self.ctx.as_mut().unwrap().update(data); - } - - fn finish(&mut self) -> Result<&[u8]> { - let digest = self.ctx.take().unwrap().finish(); - Ok(digest::Digest::as_ref(self.out.insert(digest))) - } - } - - struct RingHmacContext { - ctx: Option, - out: Option, - } - - impl HmacContext for RingHmacContext { - fn update(&mut self, data: &[u8]) { - self.ctx.as_mut().unwrap().update(data); - } - - fn finish(&mut self) -> Result<&[u8]> { - let tag = self.ctx.take().unwrap().sign(); - Ok(hmac::Tag::as_ref(self.out.insert(tag))) - } - } - /// A private RSA key for a service account #[derive(Debug)] pub(crate) struct RsaKeyPair(signature::RsaKeyPair); @@ -302,26 +242,24 @@ pub(crate) mod aws_lc_rs { } impl CryptoProvider for AwsLcCryptoProvider { - fn digest(&self, algorithm: DigestAlgorithm) -> Result> { + fn digest(&self, algorithm: DigestAlgorithm, data: &[&[u8]]) -> Result> { let algorithm = match algorithm { DigestAlgorithm::Sha256 => &digest::SHA256, }; - let ctx = digest::Context::new(algorithm); - Ok(Box::new(AwsLcDigestContext { - ctx: Some(ctx), - out: None, - })) + let mut ctx = digest::Context::new(algorithm); + for chunk in data { + ctx.update(chunk); + } + Ok(ctx.finish().as_ref().to_vec()) } - fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result> { + fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8], data: &[u8]) -> Result> { let algorithm = match algorithm { DigestAlgorithm::Sha256 => hmac::HMAC_SHA256, }; - let ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret)); - Ok(Box::new(AwsLcHmacContext { - ctx: Some(ctx), - out: None, - })) + let mut ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret)); + ctx.update(data); + Ok(ctx.sign().as_ref().to_vec()) } fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result> { @@ -331,38 +269,6 @@ pub(crate) mod aws_lc_rs { } } - struct AwsLcDigestContext { - ctx: Option, - out: Option, - } - - impl DigestContext for AwsLcDigestContext { - fn update(&mut self, data: &[u8]) { - self.ctx.as_mut().unwrap().update(data); - } - - fn finish(&mut self) -> Result<&[u8]> { - let digest = self.ctx.take().unwrap().finish(); - Ok(digest::Digest::as_ref(self.out.insert(digest))) - } - } - - struct AwsLcHmacContext { - ctx: Option, - out: Option, - } - - impl HmacContext for AwsLcHmacContext { - fn update(&mut self, data: &[u8]) { - self.ctx.as_mut().unwrap().update(data); - } - - fn finish(&mut self) -> Result<&[u8]> { - let tag = self.ctx.take().unwrap().sign(); - Ok(hmac::Tag::as_ref(self.out.insert(tag))) - } - } - /// A private RSA key for a service account #[derive(Debug)] pub(crate) struct RsaKeyPair(signature::RsaKeyPair); diff --git a/src/gcp/credential.rs b/src/gcp/credential.rs index 4a43929b..c4403969 100644 --- a/src/gcp/credential.rs +++ b/src/gcp/credential.rs @@ -927,9 +927,7 @@ impl CredentialExt for HttpRequestBuilder { #[cfg(test)] mod tests { use super::*; - use crate::client::{ - ClientOptions, DigestAlgorithm, DigestContext, HmacContext, StaticCredentialProvider, - }; + use crate::client::{ClientOptions, DigestAlgorithm, StaticCredentialProvider}; use crate::gcp::client::{GoogleCloudStorageClient, GoogleCloudStorageConfig}; const SIGNATURE_BYTES: &[u8] = &[0x00, 0x01, 0x02, 0xab, 0xcd]; @@ -946,15 +944,16 @@ mod tests { struct FixedCryptoProvider; impl CryptoProvider for FixedCryptoProvider { - fn digest(&self, _algorithm: DigestAlgorithm) -> crate::Result> { - Ok(Box::new(FixedDigestContext)) + fn digest(&self, _algorithm: DigestAlgorithm, _data: &[&[u8]]) -> crate::Result> { + Ok(vec![0x12, 0x34]) } fn hmac( &self, _algorithm: DigestAlgorithm, _secret: &[u8], - ) -> crate::Result> { + _data: &[u8], + ) -> crate::Result> { panic!("GCS signed URL should not use HMAC") } @@ -967,16 +966,6 @@ mod tests { } } - struct FixedDigestContext; - - impl DigestContext for FixedDigestContext { - fn update(&mut self, _data: &[u8]) {} - - fn finish(&mut self) -> crate::Result<&[u8]> { - Ok(&[0x12, 0x34]) - } - } - #[derive(Debug)] struct UnusedHttpService; diff --git a/src/util.rs b/src/util.rs index 0dd67456..ec98dea1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -307,9 +307,10 @@ pub(crate) fn hex_digest( crypto: &dyn crate::client::CryptoProvider, bytes: &[u8], ) -> Result { - let mut ctx = crypto.digest(crate::client::DigestAlgorithm::Sha256)?; - ctx.update(bytes); - Ok(hex_encode(ctx.finish()?)) + Ok(hex_encode(&crypto.digest( + crate::client::DigestAlgorithm::Sha256, + &[bytes], + )?)) } /// Returns `bytes` as a lower-case hex encoded string