Skip to content

Commit 33e4f3f

Browse files
committed
digest and hmac: drop intermediate builder and directly take slices for input
It removes the need for intermediate traits. All callers can easily build a slice of slices but the S3 object content SHA 256 computation that is expensive anyway.
1 parent c9db3e2 commit 33e4f3f

7 files changed

Lines changed: 64 additions & 168 deletions

File tree

src/aws/client.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,15 @@ impl Request<'_> {
411411
if let Some(digest) = cached_digest {
412412
return Ok(digest);
413413
}
414-
let mut ctx = self.config.crypto()?.digest(DigestAlgorithm::Sha256)?;
415-
for part in &payload {
416-
ctx.update(part);
417-
}
418-
let digest = ctx.finish()?.try_into().unwrap();
414+
let digest = self
415+
.config
416+
.crypto()?
417+
.digest(
418+
DigestAlgorithm::Sha256,
419+
&payload.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
420+
)?
421+
.try_into()
422+
.unwrap();
419423
cached_digest = Some(digest);
420424
Ok(digest)
421425
};
@@ -582,11 +586,9 @@ impl S3Client {
582586
}
583587

584588
let crypto = self.config.crypto()?;
585-
let mut ctx = crypto.digest(DigestAlgorithm::Sha256)?;
586-
ctx.update(body.as_ref());
587-
let digest = ctx.finish()?;
589+
let digest = crypto.digest(DigestAlgorithm::Sha256, &[&body])?;
588590

589-
builder = builder.header(SHA256_CHECKSUM, BASE64_STANDARD.encode(digest));
591+
builder = builder.header(SHA256_CHECKSUM, BASE64_STANDARD.encode(&digest));
590592

591593
// S3 *requires* DeleteObjects to include a Content-MD5 header:
592594
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html
@@ -599,7 +601,7 @@ impl S3Client {
599601
let response = builder
600602
.header(CONTENT_TYPE, "application/xml")
601603
.body(body)
602-
.with_aws_sigv4(authorizer, Some(digest))?
604+
.with_aws_sigv4(authorizer, Some(digest.as_ref()))?
603605
.retryable(&self.config.retry_config)
604606
.retry_error_body(true)
605607
.send()

src/aws/credential.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,21 @@ impl AwsCredential {
105105
let date_string = date.format("%Y%m%d").to_string();
106106
let secret_key = format!("AWS4{}", self.secret_key);
107107

108-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, secret_key.as_bytes())?;
109-
ctx.update(date_string.as_bytes());
108+
let tag = crypto.hmac(
109+
DigestAlgorithm::Sha256,
110+
secret_key.as_bytes(),
111+
date_string.as_bytes(),
112+
)?;
110113

111-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?;
112-
ctx.update(region.as_bytes());
114+
let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, region.as_bytes())?;
113115

114-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?;
115-
ctx.update(service.as_bytes());
116+
let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, service.as_bytes())?;
116117

117-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?;
118-
ctx.update(b"aws4_request");
118+
let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, b"aws4_request")?;
119119

120-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, ctx.finish()?)?;
121-
ctx.update(to_sign.as_bytes());
120+
let tag = crypto.hmac(DigestAlgorithm::Sha256, &tag, to_sign.as_bytes())?;
122121

123-
Ok(hex_encode(ctx.finish()?))
122+
Ok(hex_encode(&tag))
124123
}
125124
}
126125

src/azure/client.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,8 @@ impl AzureEncryptionHeaders {
282282
}
283283

284284
let crypto = crypto_provider(crypto)?;
285-
let mut ctx = crypto.digest(DigestAlgorithm::Sha256)?;
286-
ctx.update(&decoded_key);
287-
let encryption_key_sha256 = BASE64_STANDARD.encode(ctx.finish()?);
285+
let encryption_key_sha256 =
286+
BASE64_STANDARD.encode(crypto.digest(DigestAlgorithm::Sha256, &[&decoded_key])?);
288287

289288
Ok(Self {
290289
encryption_key: Some(encryption_key),

src/azure/credential.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,11 @@ impl AzureSigner {
206206
),
207207
None => string_to_sign_service_sas(url, method, &self.account, &self.start, &self.end),
208208
};
209-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, &self.signing_key.0)?;
210-
ctx.update(str_to_sign.as_bytes());
211-
let auth = ctx.finish()?;
209+
let auth = crypto.hmac(
210+
DigestAlgorithm::Sha256,
211+
&self.signing_key.0,
212+
str_to_sign.as_bytes(),
213+
)?;
212214

213215
url.query_pairs_mut().extend_pairs(query_pairs);
214216
url.query_pairs_mut()
@@ -358,9 +360,7 @@ fn generate_authorization(
358360
key: &AzureAccessKey,
359361
) -> crate::Result<String> {
360362
let str_to_sign = string_to_sign(h, u, method, account);
361-
let mut ctx = crypto.hmac(DigestAlgorithm::Sha256, &key.0)?;
362-
ctx.update(str_to_sign.as_bytes());
363-
let auth = ctx.finish()?;
363+
let auth = crypto.hmac(DigestAlgorithm::Sha256, &key.0, str_to_sign.as_bytes())?;
364364
Ok(format!(
365365
"SharedKey {}:{}",
366366
account,

src/client/crypto.rs

Lines changed: 25 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818
use crate::Result;
1919

2020
/// Algorithm for computing digests
21-
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
21+
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
2222
#[non_exhaustive]
2323
pub enum DigestAlgorithm {
2424
/// SHA-256
2525
Sha256,
2626
}
2727

2828
/// Algorithm for signing payloads
29-
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
29+
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
3030
#[non_exhaustive]
3131
pub enum SigningAlgorithm {
3232
/// RSASSA-PKCS1-v1_5 using SHA-256
@@ -35,42 +35,16 @@ pub enum SigningAlgorithm {
3535

3636
/// Provides cryptographic primitives
3737
pub trait CryptoProvider: std::fmt::Debug + Send + Sync {
38-
/// Compute a digest
39-
fn digest(&self, algorithm: DigestAlgorithm) -> Result<Box<dyn DigestContext>>;
38+
/// Compute the digest of `data`
39+
fn digest(&self, algorithm: DigestAlgorithm, data: &[&[u8]]) -> Result<Vec<u8>>;
4040

41-
/// Compute an HMAC with the provided `secret`
42-
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result<Box<dyn HmacContext>>;
41+
/// Compute the HMAC of `data` with the provided `secret`
42+
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8], data: &[u8]) -> Result<Vec<u8>>;
4343

4444
/// Sign a payload with the provided PEM-encoded secret
4545
fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result<Box<dyn Signer>>;
4646
}
4747

48-
/// Incrementally compute a digest, see [`CryptoProvider::digest`]
49-
pub trait DigestContext: Send {
50-
/// Updates the digest with all the data in data.
51-
///
52-
/// It is implementation-defined behaviour to call this after calling [`Self::finish`]
53-
fn update(&mut self, data: &[u8]);
54-
55-
/// Finalizes the digest calculation and returns the digest value.
56-
///
57-
/// It is implementation-defined behaviour to call this after calling [`Self::finish`]
58-
fn finish(&mut self) -> Result<&[u8]>;
59-
}
60-
61-
/// Incrementally compute a HMAC, see [`CryptoProvider::hmac`]
62-
pub trait HmacContext: Send {
63-
/// Updates the HMAC with all the data in data.
64-
///
65-
/// It is implementation-defined behaviour to call this after calling [`Self::finish`]
66-
fn update(&mut self, data: &[u8]);
67-
68-
/// Finalizes the HMAC calculation and returns the HMAC value.
69-
///
70-
/// It is implementation-defined behaviour to call this after calling [`Self::finish`]
71-
fn finish(&mut self) -> Result<&[u8]>;
72-
}
73-
7448
/// Sign a payload, see [`CryptoProvider::sign`]
7549
pub trait Signer: Send + Sync {
7650
/// Sign the provided payload
@@ -150,26 +124,24 @@ pub(crate) mod ring {
150124
}
151125

152126
impl CryptoProvider for RingCryptoProvider {
153-
fn digest(&self, algorithm: DigestAlgorithm) -> Result<Box<dyn DigestContext>> {
127+
fn digest(&self, algorithm: DigestAlgorithm, data: &[&[u8]]) -> Result<Vec<u8>> {
154128
let algorithm = match algorithm {
155129
DigestAlgorithm::Sha256 => &digest::SHA256,
156130
};
157-
let ctx = digest::Context::new(algorithm);
158-
Ok(Box::new(RingDigestContext {
159-
ctx: Some(ctx),
160-
out: None,
161-
}))
131+
let mut ctx = digest::Context::new(algorithm);
132+
for chunk in data {
133+
ctx.update(chunk);
134+
}
135+
Ok(ctx.finish().as_ref().to_vec())
162136
}
163137

164-
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result<Box<dyn HmacContext>> {
138+
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8], data: &[u8]) -> Result<Vec<u8>> {
165139
let algorithm = match algorithm {
166140
DigestAlgorithm::Sha256 => hmac::HMAC_SHA256,
167141
};
168142
let ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret));
169-
Ok(Box::new(RingHmacContext {
170-
ctx: Some(ctx),
171-
out: None,
172-
}))
143+
ctx.update(data);
144+
Ok(ctx.sign().as_ref().to_vec())
173145
}
174146

175147
fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result<Box<dyn Signer>> {
@@ -179,38 +151,6 @@ pub(crate) mod ring {
179151
}
180152
}
181153

182-
struct RingDigestContext {
183-
ctx: Option<digest::Context>,
184-
out: Option<digest::Digest>,
185-
}
186-
187-
impl DigestContext for RingDigestContext {
188-
fn update(&mut self, data: &[u8]) {
189-
self.ctx.as_mut().unwrap().update(data);
190-
}
191-
192-
fn finish(&mut self) -> Result<&[u8]> {
193-
let digest = self.ctx.take().unwrap().finish();
194-
Ok(digest::Digest::as_ref(self.out.insert(digest)))
195-
}
196-
}
197-
198-
struct RingHmacContext {
199-
ctx: Option<hmac::Context>,
200-
out: Option<hmac::Tag>,
201-
}
202-
203-
impl HmacContext for RingHmacContext {
204-
fn update(&mut self, data: &[u8]) {
205-
self.ctx.as_mut().unwrap().update(data);
206-
}
207-
208-
fn finish(&mut self) -> Result<&[u8]> {
209-
let tag = self.ctx.take().unwrap().sign();
210-
Ok(hmac::Tag::as_ref(self.out.insert(tag)))
211-
}
212-
}
213-
214154
/// A private RSA key for a service account
215155
#[derive(Debug)]
216156
pub(crate) struct RsaKeyPair(signature::RsaKeyPair);
@@ -302,26 +242,24 @@ pub(crate) mod aws_lc_rs {
302242
}
303243

304244
impl CryptoProvider for AwsLcCryptoProvider {
305-
fn digest(&self, algorithm: DigestAlgorithm) -> Result<Box<dyn DigestContext>> {
245+
fn digest(&self, algorithm: DigestAlgorithm, data: &[&[u8]]) -> Result<Vec<u8>> {
306246
let algorithm = match algorithm {
307247
DigestAlgorithm::Sha256 => &digest::SHA256,
308248
};
309-
let ctx = digest::Context::new(algorithm);
310-
Ok(Box::new(AwsLcDigestContext {
311-
ctx: Some(ctx),
312-
out: None,
313-
}))
249+
let mut ctx = digest::Context::new(algorithm);
250+
for chunk in data {
251+
ctx.update(chunk);
252+
}
253+
Ok(ctx.finish().as_ref().to_vec())
314254
}
315255

316-
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result<Box<dyn HmacContext>> {
256+
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8], data: &[u8]) -> Result<Vec<u8>> {
317257
let algorithm = match algorithm {
318258
DigestAlgorithm::Sha256 => hmac::HMAC_SHA256,
319259
};
320-
let ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret));
321-
Ok(Box::new(AwsLcHmacContext {
322-
ctx: Some(ctx),
323-
out: None,
324-
}))
260+
let mut ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret));
261+
ctx.update(data);
262+
Ok(ctx.sign().as_ref().to_vec())
325263
}
326264

327265
fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result<Box<dyn Signer>> {
@@ -331,38 +269,6 @@ pub(crate) mod aws_lc_rs {
331269
}
332270
}
333271

334-
struct AwsLcDigestContext {
335-
ctx: Option<digest::Context>,
336-
out: Option<digest::Digest>,
337-
}
338-
339-
impl DigestContext for AwsLcDigestContext {
340-
fn update(&mut self, data: &[u8]) {
341-
self.ctx.as_mut().unwrap().update(data);
342-
}
343-
344-
fn finish(&mut self) -> Result<&[u8]> {
345-
let digest = self.ctx.take().unwrap().finish();
346-
Ok(digest::Digest::as_ref(self.out.insert(digest)))
347-
}
348-
}
349-
350-
struct AwsLcHmacContext {
351-
ctx: Option<hmac::Context>,
352-
out: Option<hmac::Tag>,
353-
}
354-
355-
impl HmacContext for AwsLcHmacContext {
356-
fn update(&mut self, data: &[u8]) {
357-
self.ctx.as_mut().unwrap().update(data);
358-
}
359-
360-
fn finish(&mut self) -> Result<&[u8]> {
361-
let tag = self.ctx.take().unwrap().sign();
362-
Ok(hmac::Tag::as_ref(self.out.insert(tag)))
363-
}
364-
}
365-
366272
/// A private RSA key for a service account
367273
#[derive(Debug)]
368274
pub(crate) struct RsaKeyPair(signature::RsaKeyPair);

src/gcp/credential.rs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -927,9 +927,7 @@ impl CredentialExt for HttpRequestBuilder {
927927
#[cfg(test)]
928928
mod tests {
929929
use super::*;
930-
use crate::client::{
931-
ClientOptions, DigestAlgorithm, DigestContext, HmacContext, StaticCredentialProvider,
932-
};
930+
use crate::client::{ClientOptions, DigestAlgorithm, StaticCredentialProvider};
933931
use crate::gcp::client::{GoogleCloudStorageClient, GoogleCloudStorageConfig};
934932

935933
const SIGNATURE_BYTES: &[u8] = &[0x00, 0x01, 0x02, 0xab, 0xcd];
@@ -946,15 +944,16 @@ mod tests {
946944
struct FixedCryptoProvider;
947945

948946
impl CryptoProvider for FixedCryptoProvider {
949-
fn digest(&self, _algorithm: DigestAlgorithm) -> crate::Result<Box<dyn DigestContext>> {
950-
Ok(Box::new(FixedDigestContext))
947+
fn digest(&self, _algorithm: DigestAlgorithm, _data: &[&[u8]]) -> crate::Result<Vec<u8>> {
948+
Ok(vec![0x12, 0x34])
951949
}
952950

953951
fn hmac(
954952
&self,
955953
_algorithm: DigestAlgorithm,
956954
_secret: &[u8],
957-
) -> crate::Result<Box<dyn HmacContext>> {
955+
_data: &[u8],
956+
) -> crate::Result<Vec<u8>> {
958957
panic!("GCS signed URL should not use HMAC")
959958
}
960959

@@ -967,16 +966,6 @@ mod tests {
967966
}
968967
}
969968

970-
struct FixedDigestContext;
971-
972-
impl DigestContext for FixedDigestContext {
973-
fn update(&mut self, _data: &[u8]) {}
974-
975-
fn finish(&mut self) -> crate::Result<&[u8]> {
976-
Ok(&[0x12, 0x34])
977-
}
978-
}
979-
980969
#[derive(Debug)]
981970
struct UnusedHttpService;
982971

src/util.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,10 @@ pub(crate) fn hex_digest(
307307
crypto: &dyn crate::client::CryptoProvider,
308308
bytes: &[u8],
309309
) -> Result<String> {
310-
let mut ctx = crypto.digest(crate::client::DigestAlgorithm::Sha256)?;
311-
ctx.update(bytes);
312-
Ok(hex_encode(ctx.finish()?))
310+
Ok(hex_encode(&crypto.digest(
311+
crate::client::DigestAlgorithm::Sha256,
312+
&[bytes],
313+
)?))
313314
}
314315

315316
/// Returns `bytes` as a lower-case hex encoded string

0 commit comments

Comments
 (0)