From b26ed09e42242ae2407937421b5d54deed5189d8 Mon Sep 17 00:00:00 2001 From: Alfred Mathew <53824362+jhonboy121@users.noreply.github.com> Date: Sat, 22 Mar 2025 14:30:37 +0400 Subject: [PATCH] optimize: use ring hash and hmac implementations as an opt-in feature (#141) Defaults to sha2 and hmac. --- Cargo.toml | 8 ++++--- src/s3/signer.rs | 19 ++++++++++++--- src/s3/utils.rs | 36 ++++++++++++++++++++++------ tests/test_upload_download_object.rs | 26 ++++++++++++++++---- 4 files changed, 72 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 615722a..398926d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,11 @@ default-features = false features = ["stream"] [features] -default = ["default-tls"] +default = ["default-tls", "dep:hmac", "dep:sha2"] default-tls = ["reqwest/default-tls"] native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] +ring = ["dep:ring"] [dependencies] async-recursion = "1.1.1" @@ -34,7 +35,7 @@ derivative = "2.2.0" env_logger = "0.11.7" futures-util = "0.3.31" hex = "0.4.3" -hmac = "0.12.1" +hmac = { version = "0.12.1", optional = true } #home = "0.5.9" http = "1.2.0" hyper = { version = "1.6.0", features = ["full"] } @@ -45,9 +46,10 @@ multimap = "0.10.0" percent-encoding = "2.3.1" rand = { version = "0.8.5", features = ["small_rng"] } regex = "1.11.1" +ring = { version = "0.17.14", optional = true, default-features = false, features = ["alloc"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" -sha2 = "0.10.8" +sha2 = { version = "0.10.8", optional = true } tokio = { version = "1.44.0", features = ["full"] } tokio-stream = "0.1.17" tokio-util = { version = "0.7.13", features = ["io"] } diff --git a/src/s3/signer.rs b/src/s3/signer.rs index ffaef2e..6a62049 100644 --- a/src/s3/signer.rs +++ b/src/s3/signer.rs @@ -20,15 +20,28 @@ use crate::s3::utils::{ to_signer_date, }; use hex::encode as hexencode; +#[cfg(not(feature = "ring"))] use hmac::{Hmac, Mac}; use hyper::http::Method; +#[cfg(feature = "ring")] +use ring::hmac; +#[cfg(not(feature = "ring"))] use sha2::Sha256; /// Returns HMAC hash for given key and data pub fn hmac_hash(key: &[u8], data: &[u8]) -> Vec { - let mut hasher = Hmac::::new_from_slice(key).expect("HMAC can take key of any size"); - hasher.update(data); - hasher.finalize().into_bytes().to_vec() + #[cfg(feature = "ring")] + { + let key = hmac::Key::new(hmac::HMAC_SHA256, key); + hmac::sign(&key, data).as_ref().to_vec() + } + #[cfg(not(feature = "ring"))] + { + let mut hasher = + Hmac::::new_from_slice(key).expect("HMAC can take key of any size"); + hasher.update(data); + hasher.finalize().into_bytes().to_vec() + } } /// Returns hex encoded HMAC hash for given key and data diff --git a/src/s3/utils.rs b/src/s3/utils.rs index 5c50599..73a0bca 100644 --- a/src/s3/utils.rs +++ b/src/s3/utils.rs @@ -22,11 +22,15 @@ use base64::engine::general_purpose::STANDARD as BASE64; use byteorder::{BigEndian, ReadBytesExt}; use chrono::{DateTime, Datelike, NaiveDateTime, ParseError, Utc}; use crc::{CRC_32_ISO_HDLC, Crc}; +use hex::ToHex; use lazy_static::lazy_static; use md5::compute as md5compute; use multimap::MultiMap; use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, utf8_percent_encode}; use regex::Regex; +#[cfg(feature = "ring")] +use ring::digest::{Context, SHA256}; +#[cfg(not(feature = "ring"))] use sha2::{Digest, Sha256}; pub use urlencoding::decode as urldecode; pub use urlencoding::encode as urlencode; @@ -68,17 +72,35 @@ pub fn uint32(mut data: &[u8]) -> Result { /// Gets hex encoded SHA256 hash of given data pub fn sha256_hash(data: &[u8]) -> String { - let mut hasher = Sha256::new(); - hasher.update(data); - format!("{:x}", hasher.finalize()) + #[cfg(feature = "ring")] + { + ring::digest::digest(&SHA256, data).encode_hex() + } + #[cfg(not(feature = "ring"))] + { + let mut hasher = Sha256::new(); + hasher.update(data); + hasher.finalize().encode_hex() + } } pub fn sha256_hash_sb(sb: &SegmentedBytes) -> String { - let mut hasher = Sha256::new(); - for data in sb.iter() { - hasher.update(data); + #[cfg(feature = "ring")] + { + let mut context = Context::new(&SHA256); + for data in sb.iter() { + context.update(data.as_ref()); + } + context.finish().encode_hex() + } + #[cfg(not(feature = "ring"))] + { + let mut hasher = Sha256::new(); + for data in sb.iter() { + hasher.update(data); + } + hasher.finalize().encode_hex() } - format!("{:x}", hasher.finalize()) } /// Gets bas64 encoded MD5 hash of given data diff --git a/tests/test_upload_download_object.rs b/tests/test_upload_download_object.rs index 1c0f54b..435edc4 100644 --- a/tests/test_upload_download_object.rs +++ b/tests/test_upload_download_object.rs @@ -16,17 +16,35 @@ mod common; use crate::common::{RandReader, TestContext, create_bucket_helper, rand_object_name}; +use hex::ToHex; use minio::s3::response::PutObjectContentResponse; use minio::s3::types::S3Api; +#[cfg(feature = "ring")] +use ring::digest::{Context, SHA256}; +#[cfg(not(feature = "ring"))] use sha2::{Digest, Sha256}; +#[cfg(feature = "ring")] +use std::io::Read; use std::path::PathBuf; use std::{fs, io}; fn get_hash(filename: &String) -> String { - let mut hasher = Sha256::new(); - let mut file = fs::File::open(filename).unwrap(); - io::copy(&mut file, &mut hasher).unwrap(); - format!("{:x}", hasher.finalize()) + #[cfg(feature = "ring")] + { + let mut context = Context::new(&SHA256); + let mut file = fs::File::open(filename).unwrap(); + let mut buf = Vec::new(); + file.read_to_end(&mut buf).unwrap(); + context.update(&buf); + context.finish().encode_hex() + } + #[cfg(not(feature = "ring"))] + { + let mut hasher = Sha256::new(); + let mut file = fs::File::open(filename).unwrap(); + io::copy(&mut file, &mut hasher).unwrap(); + hasher.finalize().encode_hex() + } } #[tokio::test(flavor = "multi_thread", worker_threads = 10)]