diff --git a/Cargo.toml b/Cargo.toml index 1bc8bd18..caf1bb69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ url = "2.2" walkdir = { version = "2", optional = true } # Cloud storage support +aws-lc-rs = { version = "1.15", default-features = false, features = ["non-fips", "ring-io"], optional = true } base64 = { version = "0.22", default-features = false, features = ["std"], optional = true } form_urlencoded = { version = "1.2", optional = true } http-body-util = { version = "0.1.2", optional = true } @@ -54,7 +55,7 @@ hyper = { version = "1.2", default-features = false, optional = true } md-5 = { version = "0.10.6", default-features = false, optional = true } quick-xml = { version = "0.38.0", features = ["serialize", "overlapped-lists"], optional = true } rand = { version = "0.9", default-features = false, features = ["std", "std_rng", "thread_rng"], optional = true } -reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"], optional = true } +reqwest = { version = "0.12", default-features = false, features = ["http2", "rustls-tls-webpki-roots-no-provider"], optional = true } ring = { version = "0.17", default-features = false, features = ["std"], optional = true } rustls-pki-types = { version = "1.9", default-features = false, features = ["std"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } @@ -71,14 +72,20 @@ wasm-bindgen-futures = "0.4.18" [features] default = ["fs"] -cloud = ["serde", "serde_json", "quick-xml", "hyper", "reqwest", "reqwest/stream", "chrono/serde", "base64", "rand", "ring", "http-body-util", "form_urlencoded", "serde_urlencoded"] -azure = ["cloud", "httparse"] fs = ["walkdir"] -gcp = ["cloud", "rustls-pki-types"] -aws = ["cloud", "md-5"] -http = ["cloud"] +cloud = ["serde", "serde_json", "quick-xml", "hyper", "reqwest", "reqwest/stream", "chrono/serde", "base64", "rand", "http-body-util", "form_urlencoded", "serde_urlencoded"] +azure = ["cloud", "httparse", "ring"] +gcp = ["cloud", "rustls-pki-types", "ring"] +aws = ["cloud", "md-5", "ring"] +http = ["cloud", "ring"] +azure-aws-lc = ["cloud", "httparse", "aws-lc"] +gcp-aws-lc = ["cloud", "rustls-pki-types", "aws-lc"] +aws-aws-lc = ["cloud", "md-5", "aws-lc"] +http-aws-lc = ["cloud", "aws-lc"] tls-webpki-roots = ["reqwest?/rustls-tls-webpki-roots"] integration = ["rand"] +ring = ["dep:ring"] +aws-lc = ["dep:aws-lc-rs"] [dev-dependencies] # In alphabetical order hyper = { version = "1.2", features = ["server"] } diff --git a/src/aws/client.rs b/src/aws/client.rs index bd9618ed..ca7c6433 100644 --- a/src/aws/client.rs +++ b/src/aws/client.rs @@ -33,6 +33,7 @@ use crate::client::s3::{ InitiateMultipartUploadResult, ListResponse, PartMetadata, }; use crate::client::{GetOptionsExt, HttpClient, HttpError, HttpResponse}; +use crate::crypto; use crate::list::{PaginatedListOptions, PaginatedListResult}; use crate::multipart::PartId; use crate::{ @@ -43,6 +44,8 @@ use async_trait::async_trait; use base64::Engine; use base64::prelude::BASE64_STANDARD; use bytes::{Buf, Bytes}; +use crypto::digest; +use crypto::digest::Context; use http::header::{ CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_TYPE, @@ -52,8 +55,6 @@ use itertools::Itertools; use md5::{Digest, Md5}; use percent_encoding::{PercentEncode, utf8_percent_encode}; use quick_xml::events::{self as xml_events}; -use ring::digest; -use ring::digest::Context; use serde::{Deserialize, Serialize}; use std::sync::Arc; diff --git a/src/client/builder.rs b/src/client/builder.rs index f74c5ec1..6a5b7058 100644 --- a/src/client/builder.rs +++ b/src/client/builder.rs @@ -63,7 +63,12 @@ impl HttpRequestBuilder { } } - #[cfg(any(feature = "aws", feature = "azure"))] + #[cfg(any( + feature = "aws", + feature = "azure", + feature = "aws-aws-lc", + feature = "azure-aws-lc" + ))] pub(crate) fn from_parts(client: HttpClient, request: HttpRequest) -> Self { Self { client, @@ -116,7 +121,7 @@ impl HttpRequestBuilder { self } - #[cfg(feature = "aws")] + #[cfg(any(feature = "aws", feature = "aws-aws-lc"))] pub(crate) fn headers(mut self, headers: http::HeaderMap) -> Self { use http::header::{Entry, OccupiedEntry}; @@ -151,7 +156,7 @@ impl HttpRequestBuilder { self } - #[cfg(feature = "gcp")] + #[cfg(any(feature = "gcp", feature = "gcp-aws-lc"))] pub(crate) fn bearer_auth(mut self, token: &str) -> Self { let value = HeaderValue::try_from(format!("Bearer {token}")); match (value, &mut self.request) { @@ -165,7 +170,7 @@ impl HttpRequestBuilder { self } - #[cfg(feature = "gcp")] + #[cfg(any(feature = "gcp", feature = "gcp-aws-lc"))] pub(crate) fn json(mut self, s: S) -> Self { match (serde_json::to_vec(&s), &mut self.request) { (Ok(json), Ok(request)) => { @@ -177,7 +182,15 @@ impl HttpRequestBuilder { self } - #[cfg(any(test, feature = "aws", feature = "gcp", feature = "azure"))] + #[cfg(any( + test, + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" + ))] pub(crate) fn query(mut self, query: &T) -> Self { let mut error = None; if let Ok(ref mut req) = self.request { @@ -205,7 +218,12 @@ impl HttpRequestBuilder { self } - #[cfg(any(feature = "gcp", feature = "azure"))] + #[cfg(any( + feature = "gcp", + feature = "azure", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" + ))] pub(crate) fn form(mut self, form: T) -> Self { let mut error = None; if let Ok(ref mut req) = self.request { @@ -226,7 +244,14 @@ impl HttpRequestBuilder { self } - #[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] + #[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" + ))] pub(crate) fn body(mut self, b: impl Into) -> Self { if let Ok(r) = &mut self.request { *r.body_mut() = b.into(); @@ -239,7 +264,7 @@ impl HttpRequestBuilder { } } -#[cfg(any(test, feature = "azure"))] +#[cfg(any(test, feature = "azure", feature = "azure-aws-lc"))] pub(crate) fn add_query_pairs(uri: &mut Uri, query_pairs: I) where I: IntoIterator, diff --git a/src/client/header.rs b/src/client/header.rs index 4c9470c3..1a8c2bd4 100644 --- a/src/client/header.rs +++ b/src/client/header.rs @@ -71,7 +71,14 @@ pub(crate) enum Error { } /// Extracts a PutResult from the provided [`HeaderMap`] -#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" +))] pub(crate) fn get_put_result( headers: &HeaderMap, version: &str, @@ -82,7 +89,14 @@ pub(crate) fn get_put_result( } /// Extracts a optional version from the provided [`HeaderMap`] -#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" +))] pub(crate) fn get_version(headers: &HeaderMap, version: &str) -> Result, Error> { Ok(match headers.get(version) { Some(x) => Some( diff --git a/src/client/http/body.rs b/src/client/http/body.rs index f59da97f..79970cd9 100644 --- a/src/client/http/body.rs +++ b/src/client/http/body.rs @@ -196,7 +196,14 @@ impl HttpResponseBody { String::from_utf8(b.into()).map_err(|e| HttpError::new(HttpErrorKind::Decode, e)) } - #[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] + #[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" + ))] pub(crate) async fn json(self) -> Result { let b = self.bytes().await?; serde_json::from_slice(&b).map_err(|e| HttpError::new(HttpErrorKind::Decode, e)) diff --git a/src/client/mod.rs b/src/client/mod.rs index fcc2a089..947393bb 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -30,26 +30,59 @@ pub(crate) mod mock_server; pub(crate) mod retry; -#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" +))] pub(crate) mod pagination; pub(crate) mod get; -#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" +))] pub(crate) mod list; -#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" +))] pub(crate) mod token; pub(crate) mod header; -#[cfg(any(feature = "aws", feature = "gcp"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "aws-aws-lc", + feature = "gcp-aws-lc" +))] pub(crate) mod s3; pub(crate) mod builder; mod http; -#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" +))] pub(crate) mod parts; pub use http::*; @@ -723,7 +756,14 @@ impl ClientOptions { /// In particular: /// * Allows HTTP as metadata endpoints do not use TLS /// * Configures a low connection timeout to provide quick feedback if not present - #[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))] + #[cfg(any( + feature = "aws", + feature = "gcp", + feature = "azure", + feature = "aws-aws-lc", + feature = "gcp-aws-lc", + feature = "azure-aws-lc" + ))] pub(crate) fn metadata_options(&self) -> Self { self.clone() .with_allow_http(true) @@ -926,7 +966,14 @@ where } } -#[cfg(any(feature = "aws", feature = "azure", feature = "gcp"))] +#[cfg(any( + feature = "aws", + feature = "azure", + feature = "gcp", + feature = "aws-aws-lc", + feature = "azure-aws-lc", + feature = "gcp-aws-lc" +))] mod cloud { use super::*; use crate::RetryConfig; @@ -952,7 +999,12 @@ mod cloud { } /// Override the minimum remaining TTL for a cached token to be used - #[cfg(any(feature = "aws", feature = "gcp"))] + #[cfg(any( + feature = "aws", + feature = "gcp", + feature = "aws-aws-lc", + feature = "gcp-aws-lc" + ))] pub(crate) fn with_min_ttl(mut self, min_ttl: Duration) -> Self { self.cache = self.cache.with_min_ttl(min_ttl); self @@ -983,7 +1035,14 @@ mod cloud { } use crate::client::builder::HttpRequestBuilder; -#[cfg(any(feature = "aws", feature = "azure", feature = "gcp"))] +#[cfg(any( + feature = "aws", + feature = "azure", + feature = "gcp", + feature = "aws-aws-lc", + feature = "azure-aws-lc", + feature = "gcp-aws-lc" +))] pub(crate) use cloud::*; #[cfg(test)] diff --git a/src/client/retry.rs b/src/client/retry.rs index ed9bf0c4..c7667fea 100644 --- a/src/client/retry.rs +++ b/src/client/retry.rs @@ -277,7 +277,7 @@ impl RetryableRequestBuilder { } /// Set whether this request should be retried on a 409 Conflict response. - #[cfg(feature = "aws")] + #[cfg(any(feature = "aws", feature = "aws-aws-lc"))] pub(crate) fn retry_on_conflict(mut self, retry_on_conflict: bool) -> Self { self.request.retry_on_conflict = retry_on_conflict; self diff --git a/src/client/s3.rs b/src/client/s3.rs index a1b113e0..be4ece69 100644 --- a/src/client/s3.rs +++ b/src/client/s3.rs @@ -92,7 +92,7 @@ pub(crate) struct InitiateMultipartUploadResult { pub upload_id: String, } -#[cfg(feature = "aws")] +#[cfg(any(feature = "aws", feature = "aws-aws-lc"))] #[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] pub(crate) struct CopyPartResult { diff --git a/src/client/token.rs b/src/client/token.rs index 81ffc110..56184aaa 100644 --- a/src/client/token.rs +++ b/src/client/token.rs @@ -52,7 +52,12 @@ impl Default for TokenCache { impl TokenCache { /// Override the minimum remaining TTL for a cached token to be used - #[cfg(any(feature = "aws", feature = "gcp"))] + #[cfg(any( + feature = "aws", + feature = "gcp", + feature = "aws-aws-lc", + feature = "gcp-aws-lc" + ))] pub(crate) fn with_min_ttl(self, min_ttl: Duration) -> Self { Self { min_ttl, ..self } } diff --git a/src/crypto_backend.rs b/src/crypto_backend.rs new file mode 100644 index 00000000..1ecd40ed --- /dev/null +++ b/src/crypto_backend.rs @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! Cryptographic backend abstraction +//! +//! This module provides a unified interface over different cryptographic backends +//! (ring and aws-lc-rs) to allow compile-time selection without code duplication. + +#[cfg(all(feature = "ring", feature = "aws-lc"))] +compile_error!("Features 'ring' and 'aws-lc' are mutually exclusive"); + +/// Re-export the chosen cryptographic backend as `crypto`. +/// +/// Since aws-lc-rs is API-compatible with ring for most operations, we can use +/// a simple alias pattern to switch between them at compile time. +#[cfg(feature = "ring")] +#[allow(unused_imports)] +pub(crate) use ring as crypto; + +#[cfg(feature = "aws-lc")] +#[allow(unused_imports)] +pub(crate) use aws_lc_rs as crypto; + +/// Get the modulus length for an RSA key pair. +/// +/// This abstracts the API difference between ring and aws-lc-rs: +/// - ring: `key.public().modulus_len()` +/// - aws-lc-rs: `key.public_key().modulus_len()` (requires KeyPair trait) +/// +/// Both libraries require a pre-allocated buffer for signing, but use different +/// methods to determine the required buffer size. This helper encapsulates that +/// difference so the main code doesn't need feature conditionals. +#[cfg(any(feature = "gcp", feature = "gcp-aws-lc"))] +pub(crate) fn rsa_key_modulus_len(key: &crypto::signature::RsaKeyPair) -> usize { + #[cfg(feature = "ring")] + { + key.public().modulus_len() + } + #[cfg(feature = "aws-lc")] + { + use crypto::signature::KeyPair; + key.public_key().modulus_len() + } +} diff --git a/src/gcp/credential.rs b/src/gcp/credential.rs index 75de68c1..215e6993 100644 --- a/src/gcp/credential.rs +++ b/src/gcp/credential.rs @@ -20,6 +20,8 @@ use crate::client::builder::HttpRequestBuilder; use crate::client::retry::RetryExt; use crate::client::token::TemporaryToken; use crate::client::{HttpClient, HttpError, TokenProvider}; +use crate::crypto; +use crate::crypto_backend::rsa_key_modulus_len; use crate::gcp::{GcpSigningCredentialProvider, STORE}; use crate::util::{STRICT_ENCODE_SET, hex_digest, hex_encode}; use crate::{RetryConfig, StaticCredentialProvider}; @@ -27,11 +29,11 @@ use async_trait::async_trait; use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; use chrono::{DateTime, Utc}; +use crypto::signature::RsaKeyPair; use futures::TryFutureExt; use http::{HeaderMap, Method}; use itertools::Itertools; use percent_encoding::utf8_percent_encode; -use ring::signature::RsaKeyPair; use serde::Deserialize; use std::collections::BTreeMap; use std::env; @@ -70,11 +72,11 @@ pub enum Error { #[error("Invalid RSA key: {}", source)] InvalidKey { #[from] - source: ring::error::KeyRejected, + source: crypto::error::KeyRejected, }, #[error("Error signing: {}", source)] - Sign { source: ring::error::Unspecified }, + Sign { source: crypto::error::Unspecified }, #[error("Error encoding jwt payload: {}", source)] Encode { source: serde_json::Error }, @@ -151,11 +153,12 @@ impl ServiceAccountKey { } fn sign(&self, string_to_sign: &str) -> Result { - let mut signature = vec![0; self.0.public().modulus_len()]; + let modulus_len = rsa_key_modulus_len(&self.0); + let mut signature = vec![0; modulus_len]; self.0 .sign( - &ring::signature::RSA_PKCS1_SHA256, - &ring::rand::SystemRandom::new(), + &crypto::signature::RSA_PKCS1_SHA256, + &crypto::rand::SystemRandom::new(), string_to_sign.as_bytes(), &mut signature, ) @@ -287,12 +290,13 @@ impl TokenProvider for SelfSignedJwt { let claim_str = b64_encode_obj(&claims)?; let message = [jwt_header.as_ref(), claim_str.as_ref()].join("."); - let mut sig_bytes = vec![0; self.private_key.0.public().modulus_len()]; + let modulus_len = rsa_key_modulus_len(&self.private_key.0); + let mut sig_bytes = vec![0; modulus_len]; self.private_key .0 .sign( - &ring::signature::RSA_PKCS1_SHA256, - &ring::rand::SystemRandom::new(), + &crypto::signature::RSA_PKCS1_SHA256, + &crypto::rand::SystemRandom::new(), message.as_bytes(), &mut sig_bytes, ) diff --git a/src/lib.rs b/src/lib.rs index 6ffdf6d1..aa7fa9b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -535,17 +535,17 @@ //! //! [`HttpConnector`]: client::HttpConnector -#[cfg(feature = "aws")] +#[cfg(any(feature = "aws", feature = "aws-aws-lc"))] pub mod aws; -#[cfg(feature = "azure")] +#[cfg(any(feature = "azure", feature = "azure-aws-lc"))] pub mod azure; pub mod buffered; #[cfg(not(target_arch = "wasm32"))] pub mod chunked; pub mod delimited; -#[cfg(feature = "gcp")] +#[cfg(any(feature = "gcp", feature = "gcp-aws-lc"))] pub mod gcp; -#[cfg(feature = "http")] +#[cfg(any(feature = "http", feature = "http-aws-lc"))] pub mod http; pub mod limit; #[cfg(all(feature = "fs", not(target_arch = "wasm32")))] @@ -599,6 +599,21 @@ pub use util::{GetRange, OBJECT_STORE_COALESCE_DEFAULT, coalesce_ranges, collect // Re-export HTTP types used in public API pub use ::http::{Extensions, HeaderMap, HeaderValue}; +// Cryptographic backend abstraction module +mod crypto_backend; + +#[cfg(any( + feature = "aws", + feature = "azure", + feature = "gcp", + feature = "http", + feature = "aws-aws-lc", + feature = "azure-aws-lc", + feature = "gcp-aws-lc", + feature = "http-aws-lc" +))] +pub(crate) use crypto_backend::crypto; + use crate::path::Path; #[cfg(all(feature = "fs", not(target_arch = "wasm32")))] use crate::util::maybe_spawn_blocking; diff --git a/src/util.rs b/src/util.rs index b7f9182c..15dcff4a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -25,11 +25,21 @@ use super::Result; use bytes::Bytes; use futures::{Stream, TryStreamExt, stream::StreamExt}; -#[cfg(any(feature = "azure", feature = "http"))] +#[cfg(any( + feature = "azure", + feature = "http", + feature = "azure-aws-lc", + feature = "http-aws-lc" +))] pub(crate) static RFC1123_FMT: &str = "%a, %d %h %Y %T GMT"; // deserialize dates according to rfc1123 -#[cfg(any(feature = "azure", feature = "http"))] +#[cfg(any( + feature = "azure", + feature = "http", + feature = "azure-aws-lc", + feature = "http-aws-lc" +))] pub(crate) fn deserialize_rfc1123<'de, D>( deserializer: D, ) -> Result, D::Error> @@ -42,10 +52,19 @@ where Ok(chrono::TimeZone::from_utc_datetime(&chrono::Utc, &naive)) } -#[cfg(any(feature = "aws", feature = "azure"))] -pub(crate) fn hmac_sha256(secret: impl AsRef<[u8]>, bytes: impl AsRef<[u8]>) -> ring::hmac::Tag { - let key = ring::hmac::Key::new(ring::hmac::HMAC_SHA256, secret.as_ref()); - ring::hmac::sign(&key, bytes.as_ref()) +#[cfg(any( + feature = "aws", + feature = "azure", + feature = "aws-aws-lc", + feature = "azure-aws-lc" +))] +pub(crate) fn hmac_sha256( + secret: impl AsRef<[u8]>, + bytes: impl AsRef<[u8]>, +) -> crate::crypto::hmac::Tag { + use crate::crypto; + let key = crypto::hmac::Key::new(crypto::hmac::HMAC_SHA256, secret.as_ref()); + crypto::hmac::sign(&key, bytes.as_ref()) } /// Collect a stream into [`Bytes`] avoiding copying in the event of a single chunk @@ -300,7 +319,12 @@ impl> From for GetRange { // // Do not URI-encode any of the unreserved characters that RFC 3986 defines: // A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ), and tilde ( ~ ). -#[cfg(any(feature = "aws", feature = "gcp"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "aws-aws-lc", + feature = "gcp-aws-lc" +))] pub(crate) const STRICT_ENCODE_SET: percent_encoding::AsciiSet = percent_encoding::NON_ALPHANUMERIC .remove(b'-') .remove(b'.') @@ -308,14 +332,25 @@ pub(crate) const STRICT_ENCODE_SET: percent_encoding::AsciiSet = percent_encodin .remove(b'~'); /// Computes the SHA256 digest of `body` returned as a hex encoded string -#[cfg(any(feature = "aws", feature = "gcp"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "aws-aws-lc", + feature = "gcp-aws-lc" +))] pub(crate) fn hex_digest(bytes: &[u8]) -> String { - let digest = ring::digest::digest(&ring::digest::SHA256, bytes); + use crate::crypto; + let digest = crypto::digest::digest(&crypto::digest::SHA256, bytes); hex_encode(digest.as_ref()) } /// Returns `bytes` as a lower-case hex encoded string -#[cfg(any(feature = "aws", feature = "gcp"))] +#[cfg(any( + feature = "aws", + feature = "gcp", + feature = "aws-aws-lc", + feature = "gcp-aws-lc" +))] pub(crate) fn hex_encode(bytes: &[u8]) -> String { use std::fmt::Write; let mut out = String::with_capacity(bytes.len() * 2);