refactor(deps): replace md5 crate with md-5 (#5432)

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-07-29 19:29:37 +08:00
committed by GitHub
parent f329d330df
commit f7c1b13c0f
16 changed files with 119 additions and 64 deletions
Generated
+6 -4
View File
@@ -3671,12 +3671,13 @@ dependencies = [
"flatbuffers",
"flate2",
"futures",
"hex",
"http 1.4.2",
"http-body-util",
"hyper",
"hyper-util",
"local-ip-address",
"md5",
"md-5 0.11.0",
"opentelemetry-proto",
"prost 0.14.4",
"rand 0.10.2",
@@ -8890,7 +8891,7 @@ dependencies = [
"libmimalloc-sys",
"libsystemd",
"matchit 0.9.2",
"md5",
"md-5 0.11.0",
"metrics",
"metrics-util",
"mimalloc",
@@ -9449,9 +9450,10 @@ dependencies = [
"async-trait",
"base64 0.23.0",
"chacha20poly1305",
"hex",
"insta",
"jiff",
"md5",
"md-5 0.11.0",
"moka",
"rand 0.10.2",
"reqwest",
@@ -9718,7 +9720,7 @@ dependencies = [
"hyper-util",
"ipnetwork",
"libunftp",
"md5",
"md-5 0.11.0",
"percent-encoding",
"proptest",
"quick-xml",
-1
View File
@@ -265,7 +265,6 @@ memmap2 = "0.9.11"
lz4 = "1.28.1"
matchit = "0.9.2"
md-5 = "0.11.0"
md5 = "0.8.1"
mime_guess = "2.0.5"
moka = { version = "0.12.15" }
netif = "0.1.6"
+2 -1
View File
@@ -70,7 +70,8 @@ walkdir.workspace = true
base64 = { workspace = true }
rand = { workspace = true, features = ["serde"] }
chrono = { workspace = true, features = ["serde"] }
md5 = { workspace = true }
hex = { workspace = true }
md-5 = { workspace = true }
opentelemetry-proto = { workspace = true }
prost.workspace = true
sha2 = { workspace = true }
+5 -2
View File
@@ -24,9 +24,10 @@ mod tests {
use aws_sdk_s3::types::{ChecksumAlgorithm, ChecksumMode, CompletedMultipartUpload, CompletedPart};
use aws_smithy_http_client::Builder as SmithyHttpClientBuilder;
use base64::Engine;
use md5::{Digest as Md5Digest, Md5};
use rustfs_rio::{Checksum, ChecksumType as RioChecksumType};
use serial_test::serial;
use sha2::{Digest, Sha256};
use sha2::Sha256;
use tracing::info;
fn create_s3_client(env: &RustFSTestEnvironment) -> Client {
@@ -70,7 +71,9 @@ mod tests {
}
fn content_md5_base64(body: &[u8]) -> String {
let digest = md5::compute(body);
let mut hasher = Md5::new();
hasher.update(body);
let digest = hasher.finalize();
base64::engine::general_purpose::STANDARD.encode(digest.as_slice())
}
+18 -5
View File
@@ -25,6 +25,7 @@ use hyper::body::Incoming;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper_util::rt::{TokioIo, TokioTimer};
use md5::{Digest as Md5Digest, Md5};
use s3s::access::{S3Access, S3AccessContext};
use s3s::auth::SimpleAuth;
use s3s::dto::{
@@ -827,13 +828,25 @@ fn ensure_body_growth(current: usize, added: usize) -> S3Result {
async fn md5_digest(body: Bytes, permit: OwnedSemaphorePermit) -> S3Result<([u8; 16], OwnedSemaphorePermit)> {
if body.len() < 1024 * 1024 {
return Ok((md5::compute(body).0, permit));
return Ok((md5_bytes(body), permit));
}
tokio::task::spawn_blocking(move || (md5::compute(body).0, permit))
tokio::task::spawn_blocking(move || (md5_bytes(body), permit))
.await
.map_err(|error| s3s::s3_error!(InternalError, "MD5 worker failed: {error}"))
}
fn md5_bytes(input: impl AsRef<[u8]>) -> [u8; 16] {
let mut hasher = Md5::new();
hasher.update(input.as_ref());
hasher.finalize().into()
}
fn md5_hex(input: impl AsRef<[u8]>) -> String {
let mut hasher = Md5::new();
hasher.update(input.as_ref());
hex::encode(hasher.finalize())
}
fn ensure_store_budget(state: &StoreState, removed_bytes: usize, added_bytes: usize, adds_version: bool) -> S3Result {
let total_bytes = state
.total_bytes
@@ -1005,7 +1018,7 @@ impl S3 for FakeBackend {
Some(value) => value,
None => {
let (digest, _body_permit) = md5_digest(body.clone(), _body_permit).await?;
format!("{:x}", md5::Digest(digest))
hex::encode(digest)
}
};
let version = ObjectVersion {
@@ -1208,7 +1221,7 @@ impl S3 for FakeBackend {
}
let body = collect_stream(input.body, input.content_length, fault.as_ref(), &self.control).await?;
let (digest, _body_permit) = md5_digest(body.clone(), _body_permit).await?;
let e_tag = format!("{:x}", md5::Digest(digest));
let e_tag = hex::encode(digest);
let mut state = lock(&self.store);
let existing_bytes = state
.uploads
@@ -1336,7 +1349,7 @@ impl S3 for FakeBackend {
.collect();
let (body, digests, _body_permits) = assemble_multipart(assembly_parts, total_len, _body_permits).await?;
let part_count = requested.len();
let e_tag = source_etag(&headers)?.unwrap_or_else(|| format!("{:x}-{part_count}", md5::compute(digests)));
let e_tag = source_etag(&headers)?.unwrap_or_else(|| format!("{}-{part_count}", md5_hex(digests)));
let version = ObjectVersion {
version_id: upload.version_id.clone(),
body,
+4 -1
View File
@@ -30,6 +30,7 @@ use aws_sdk_s3::primitives::ByteStream;
use aws_sdk_s3::types::ServerSideEncryption;
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use http::header::{CONTENT_TYPE, HOST};
use md5::{Digest as Md5Digest, Md5};
use rustfs_signer::constants::UNSIGNED_PAYLOAD;
use rustfs_signer::sign_v4;
use s3s::Body;
@@ -68,7 +69,9 @@ pub fn skip_if_kms_admin_tool_unavailable(test_name: &str) -> bool {
}
pub fn sse_customer_key_md5_base64(key: &str) -> String {
BASE64.encode(md5::compute(key).0)
let mut hasher = Md5::new();
hasher.update(key.as_bytes());
BASE64.encode(hasher.finalize())
}
pub async fn kms_admin_request(
@@ -25,12 +25,18 @@ use super::common::{LocalKMSTestEnvironment, sse_customer_key_md5_base64};
use crate::common::{TEST_BUCKET, init_logging};
use aws_sdk_s3::types::ServerSideEncryption;
use base64::Engine;
use md5::compute;
use md5::{Digest as Md5Digest, Md5};
use serial_test::serial;
use std::sync::Arc;
use tokio::sync::Semaphore;
use tracing::{info, warn};
fn md5_hex(input: impl AsRef<[u8]>) -> String {
let mut hasher = Md5::new();
hasher.update(input.as_ref());
hex::encode(hasher.finalize())
}
/// Test encryption of zero-byte files (empty files)
#[tokio::test]
#[serial]
@@ -294,7 +300,7 @@ async fn test_kms_invalid_key_scenarios() -> Result<(), Box<dyn std::error::Erro
info!("🔍 Testing invalid SSE-C key length");
let invalid_short_key = "short"; // Too short
let invalid_key_b64 = base64::engine::general_purpose::STANDARD.encode(invalid_short_key);
let invalid_key_md5 = format!("{:x}", compute(invalid_short_key));
let invalid_key_md5 = md5_hex(invalid_short_key);
let invalid_key_result = s3_client
.put_object()
+11 -2
View File
@@ -26,6 +26,7 @@ use chrono::{Duration as ChronoDuration, Utc};
use flate2::{Compression, write::GzEncoder};
use http::HeaderValue;
use http::header::{CONTENT_TYPE, HOST};
use md5::{Digest as Md5Digest, Md5};
use rustfs_signer::constants::UNSIGNED_PAYLOAD;
use rustfs_signer::sign_v4;
use s3s::Body;
@@ -50,7 +51,15 @@ fn encode_post_policy(conditions: Vec<serde_json::Value>) -> String {
}
fn sse_customer_key_md5_base64(key: &str) -> String {
base64::engine::general_purpose::STANDARD.encode(md5::compute(key).0)
let mut hasher = Md5::new();
hasher.update(key.as_bytes());
base64::engine::general_purpose::STANDARD.encode(hasher.finalize())
}
fn md5_hex(input: impl AsRef<[u8]>) -> String {
let mut hasher = Md5::new();
hasher.update(input.as_ref());
hex::encode(hasher.finalize())
}
/// Env var consumed by the local SSE-S3 DEK provider when KMS is not configured.
@@ -5664,7 +5673,7 @@ async fn test_signed_put_object_extract_returns_archive_etag() -> Result<(), Box
client.create_bucket().bucket(bucket).send().await?;
let archive = make_tar(&[("alpha.txt", b"alpha-body")], &[]).await;
let expected_etag = format!("\"{:x}\"", md5::compute(&archive));
let expected_etag = format!("\"{}\"", md5_hex(&archive));
let response = client
.put_object()
+4 -1
View File
@@ -22,6 +22,7 @@ use aws_sdk_s3::primitives::ByteStream;
use aws_sdk_s3::types::{BucketVersioningStatus, CompletedMultipartUpload, CompletedPart, VersioningConfiguration};
use aws_smithy_http_client::Builder as SmithyHttpClientBuilder;
use base64::Engine;
use md5::{Digest as Md5Digest, Md5};
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
@@ -102,10 +103,12 @@ impl Intercept for ResponseHeaderCapture {
fn customer_key(byte: u8) -> CustomerKey {
let raw = [byte; 32];
let mut hasher = Md5::new();
hasher.update(raw);
CustomerKey {
raw: String::from_utf8_lossy(&raw).into_owned(),
encoded: base64::engine::general_purpose::STANDARD.encode(raw),
md5: base64::engine::general_purpose::STANDARD.encode(md5::compute(raw).0),
md5: base64::engine::general_purpose::STANDARD.encode(hasher.finalize()),
}
}
+2 -1
View File
@@ -44,6 +44,7 @@ argon2 = { workspace = true }
chacha20poly1305 = { workspace = true }
rand = { workspace = true, features = ["serde"] }
base64 = { workspace = true }
hex = { workspace = true }
sha2 = { workspace = true }
subtle = { workspace = true }
zeroize = { workspace = true, features = ["derive"] }
@@ -56,7 +57,7 @@ tempfile = { workspace = true }
moka = { workspace = true, features = ["future"] }
# Additional dependencies
md5 = { workspace = true }
md-5 = { workspace = true }
arc-swap = { workspace = true }
rustfs-utils = { workspace = true }
rustfs-security-governance = { workspace = true }
+8 -2
View File
@@ -20,6 +20,7 @@ use crate::manager::KmsManager;
use crate::types::*;
use base64::Engine;
use jiff::Zoned;
use md5::{Digest as Md5Digest, Md5};
use rand::random;
use std::collections::HashMap;
use std::io::Cursor;
@@ -27,6 +28,12 @@ use tokio::io::{AsyncRead, AsyncReadExt};
use tracing::debug;
use zeroize::Zeroize;
fn md5_hex(input: impl AsRef<[u8]>) -> String {
let mut hasher = Md5::new();
hasher.update(input.as_ref());
hex::encode(hasher.finalize())
}
/// Data key for object encryption
/// SECURITY: This struct automatically zeros sensitive key material when dropped
#[derive(Debug, Clone)]
@@ -486,8 +493,7 @@ impl ObjectEncryptionService {
// Validate key MD5 if provided
if let Some(expected_md5) = customer_key_md5 {
let actual_md5 = md5::compute(customer_key);
let actual_md5_hex = format!("{actual_md5:x}");
let actual_md5_hex = md5_hex(customer_key);
if actual_md5_hex != expected_md5.to_lowercase() {
return Err(KmsError::validation_error("Customer key MD5 mismatch"));
}
+2 -2
View File
@@ -44,7 +44,7 @@ swift = [
"dep:tokio-util",
"dep:serde",
"dep:urlencoding",
"dep:md5",
"dep:md-5",
"dep:quick-xml",
"dep:hmac",
"dep:sha1",
@@ -108,7 +108,7 @@ http-body-util = { workspace = true, optional = true }
tokio-util = { workspace = true, optional = true, features = ["rt", "io", "compat"] }
serde = { workspace = true, optional = true, features = ["derive"] }
urlencoding = { workspace = true, optional = true }
md5 = { workspace = true, optional = true }
md-5 = { workspace = true, optional = true }
quick-xml = { workspace = true, optional = true, features = ["serialize"] }
hmac = { workspace = true, optional = true }
sha1 = { workspace = true, optional = true }
+4 -3
View File
@@ -21,6 +21,7 @@
use super::storage_api::large_object::HTTPRangeSpec;
use super::{SwiftError, object};
use axum::http::{HeaderMap, Response, StatusCode};
use md5::{Digest as Md5Digest, Md5};
use rustfs_credentials::Credentials;
use s3s::Body;
use serde::{Deserialize, Serialize};
@@ -81,9 +82,9 @@ impl SLOManifest {
etag_concat.push_str(etag);
}
// Calculate MD5 hash
let hash = md5::compute(etag_concat.as_bytes());
format!("\"{:x}-{}\"", hash, self.segments.len())
let mut hasher = Md5::new();
hasher.update(etag_concat.as_bytes());
format!("\"{}-{}\"", hex::encode(hasher.finalize()), self.segments.len())
}
/// Validate manifest against actual segments
+1 -1
View File
@@ -164,7 +164,7 @@ const-str = { workspace = true, features = ["std", "proc"] }
datafusion = { workspace = true, default-features = false, features = ["parquet", "recursive_protection", "sql"] }
hex-simd.workspace = true
matchit = { workspace = true }
md5.workspace = true
md-5 = { workspace = true }
mime_guess = { workspace = true }
percent-encoding = { workspace = true }
pin-project-lite.workspace = true
+5 -5
View File
@@ -116,7 +116,7 @@ use crate::table_catalog;
use bytes::Bytes;
use futures::{Stream, StreamExt};
use http::{HeaderMap, HeaderValue, StatusCode};
use md5::Context as Md5Context;
use md5::{Digest as Md5Digest, Md5};
use metrics::{counter, histogram};
use pin_project_lite::pin_project;
use rustfs_concurrency::GetObjectQueueSnapshot;
@@ -801,7 +801,7 @@ pin_project! {
struct ExtractArchiveEtagReader<R> {
#[pin]
inner: R,
md5: Md5Context,
md5: Md5,
finished: bool,
etag: Arc<Mutex<Option<String>>>,
}
@@ -1854,7 +1854,7 @@ impl<R> ExtractArchiveEtagReader<R> {
fn new(inner: R, etag: Arc<Mutex<Option<String>>>) -> Self {
Self {
inner,
md5: Md5Context::new(),
md5: Md5::new(),
finished: false,
etag,
}
@@ -1870,11 +1870,11 @@ impl<R: AsyncRead> AsyncRead for ExtractArchiveEtagReader<R> {
Poll::Ready(Ok(())) => {
let filled = &buf.filled()[before..];
if !filled.is_empty() {
this.md5.consume(filled);
this.md5.update(filled);
} else if !*this.finished {
*this.finished = true;
if let Ok(mut etag) = this.etag.lock() {
*etag = Some(format!("{:x}", this.md5.clone().finalize()));
*etag = Some(hex_simd::encode_to_string(this.md5.clone().finalize(), hex_simd::AsciiCase::Lower));
}
}
Poll::Ready(Ok(()))
+39 -31
View File
@@ -84,6 +84,7 @@ use chacha20poly1305::ChaCha20Poly1305;
#[cfg(feature = "rio-v2")]
use hmac::{Hmac, Mac};
use http::{HeaderMap, HeaderValue};
use md5::{Digest as Md5Digest, Md5};
use rand::Rng;
#[cfg(feature = "rio-v2")]
use rand::RngExt;
@@ -135,6 +136,16 @@ const SEALED_KEY_SIZE: usize = DARE_HEADER_SIZE + 32 + DARE_TAG_SIZE;
#[cfg(feature = "rio-v2")]
const OBJECT_KEY_DERIVATION_CONTEXT: &[u8] = b"object-encryption-key generation";
fn md5_bytes(input: impl AsRef<[u8]>) -> [u8; 16] {
let mut hasher = Md5::new();
hasher.update(input.as_ref());
hasher.finalize().into()
}
fn md5_base64(input: impl AsRef<[u8]>) -> String {
BASE64_STANDARD.encode(md5_bytes(input))
}
use super::Error;
use super::get_bucket_sse_config;
use crate::error::ApiError;
@@ -2527,7 +2538,7 @@ pub fn validate_ssec_params(params: SsecParams) -> Result<ValidatedSsecParams, A
)));
}
let computed_md5 = BASE64_STANDARD.encode(md5::compute(&key_bytes).0);
let computed_md5 = md5_base64(&key_bytes);
if computed_md5 != params.key_md5 {
return Err(ssec_invalid_request(
"The calculated MD5 hash of the key did not match the hash that was provided.",
@@ -2552,9 +2563,9 @@ pub fn validate_ssec_params(params: SsecParams) -> Result<ValidatedSsecParams, A
/// 2. Different objects get different nonces
pub fn generate_ssec_nonce(bucket: &str, key: &str) -> [u8; 12] {
let nonce_source = format!("{bucket}-{key}");
let nonce_hash = md5::compute(nonce_source.as_bytes());
let nonce_hash = md5_bytes(nonce_source.as_bytes());
let mut nonce = [0u8; 12];
nonce.copy_from_slice(&nonce_hash.0[..12]);
nonce.copy_from_slice(&nonce_hash[..12]);
nonce
}
@@ -2640,10 +2651,10 @@ mod tests {
SseDekProvider, SsecParams, StorageError, TestSseDekProvider, apply_managed_decryption_material,
apply_managed_encryption_material, encryption_material_to_metadata, extract_server_side_encryption_from_headers,
extract_ssec_params_from_headers, extract_ssekms_context_from_headers, generate_ssec_nonce, is_managed_sse,
kms_operation_error, map_get_object_reader_error, mark_encrypted_multipart_metadata, normalize_managed_metadata,
reset_sse_dek_provider, resolve_effective_kms_key_id, sse_decryption, sse_encryption, sse_prepare_encryption,
strip_managed_encryption_metadata, validate_sse_headers_for_read, validate_sse_headers_for_write, validate_ssec_for_read,
validate_ssec_params, verify_ssec_key_match,
kms_operation_error, map_get_object_reader_error, mark_encrypted_multipart_metadata, md5_base64,
normalize_managed_metadata, reset_sse_dek_provider, resolve_effective_kms_key_id, sse_decryption, sse_encryption,
sse_prepare_encryption, strip_managed_encryption_metadata, validate_sse_headers_for_read, validate_sse_headers_for_write,
validate_ssec_for_read, validate_ssec_params, verify_ssec_key_match,
};
#[cfg(feature = "rio-v2")]
use super::{
@@ -2882,7 +2893,7 @@ mod tests {
#[test]
fn test_validate_ssec_params_success() {
let key = BASE64_STANDARD.encode([42u8; 32]);
let key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let key_md5 = md5_base64([42u8; 32]);
let params = SsecParams {
algorithm: "AES256".to_string(),
@@ -2899,7 +2910,7 @@ mod tests {
#[test]
fn test_validate_ssec_params_wrong_algorithm() {
let key = BASE64_STANDARD.encode([42u8; 32]);
let key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let key_md5 = md5_base64([42u8; 32]);
let params = SsecParams {
algorithm: "AES128".to_string(), // Wrong algorithm
@@ -2914,7 +2925,7 @@ mod tests {
#[test]
fn test_validate_ssec_params_wrong_key_length() {
let key = BASE64_STANDARD.encode([42u8; 16]); // Only 16 bytes
let key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 16]).0);
let key_md5 = md5_base64([42u8; 16]);
let params = SsecParams {
algorithm: "AES256".to_string(),
@@ -2946,7 +2957,7 @@ mod tests {
let bucket = "test-bucket";
let key = "test-key";
let sse_key = BASE64_STANDARD.encode([42u8; 32]);
let sse_key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let sse_key_md5 = md5_base64([42u8; 32]);
let content_size = 1024;
let request_missing_md5 = EncryptionRequest {
@@ -2999,7 +3010,7 @@ mod tests {
async fn test_sse_prepare_encryption_rejects_partial_ssec_headers() {
let bucket = "test-bucket";
let key = "test-key";
let sse_key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let sse_key_md5 = md5_base64([42u8; 32]);
let request_missing_algorithm = PrepareEncryptionRequest {
bucket,
@@ -3029,7 +3040,7 @@ mod tests {
async fn test_sse_prepare_encryption_rejects_ssec_headers_without_customer_key() {
let bucket = "test-bucket";
let key = "test-key";
let sse_key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let sse_key_md5 = md5_base64([42u8; 32]);
let request = PrepareEncryptionRequest {
bucket,
@@ -3087,7 +3098,7 @@ mod tests {
let key = "object";
let customer_key_bytes = [0x24u8; 32];
let customer_key = BASE64_STANDARD.encode(customer_key_bytes);
let customer_key_md5 = BASE64_STANDARD.encode(md5::compute(customer_key_bytes).0);
let customer_key_md5 = md5_base64(customer_key_bytes);
let metadata_one = ssec_direct_put_metadata(bucket, key, &customer_key, &customer_key_md5).await;
let metadata_two = ssec_direct_put_metadata(bucket, key, &customer_key, &customer_key_md5).await;
@@ -3128,7 +3139,7 @@ mod tests {
let key = "object";
let customer_key_bytes = [0x24u8; 32];
let customer_key = BASE64_STANDARD.encode(customer_key_bytes);
let customer_key_md5 = BASE64_STANDARD.encode(md5::compute(customer_key_bytes).0);
let customer_key_md5 = md5_base64(customer_key_bytes);
let mut metadata = HashMap::new();
metadata.insert("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string());
@@ -3160,7 +3171,7 @@ mod tests {
let key = "object";
let customer_key_bytes = [0x51u8; 32];
let customer_key = BASE64_STANDARD.encode(customer_key_bytes);
let customer_key_md5 = BASE64_STANDARD.encode(md5::compute(customer_key_bytes).0);
let customer_key_md5 = md5_base64(customer_key_bytes);
let plaintext = b"attack at dawn - sse-c round trip".to_vec();
let metadata = ssec_direct_put_metadata(bucket, key, &customer_key, &customer_key_md5).await;
@@ -3199,7 +3210,7 @@ mod tests {
let key = "object";
let customer_key_bytes = [0x33u8; 32];
let customer_key = BASE64_STANDARD.encode(customer_key_bytes);
let customer_key_md5 = BASE64_STANDARD.encode(md5::compute(customer_key_bytes).0);
let customer_key_md5 = md5_base64(customer_key_bytes);
let material = sse_prepare_encryption(PrepareEncryptionRequest {
bucket,
@@ -3261,7 +3272,7 @@ mod tests {
let key = "test-key";
let customer_key_bytes = [0x24u8; 32];
let customer_key = BASE64_STANDARD.encode(customer_key_bytes);
let sse_key_md5 = BASE64_STANDARD.encode(md5::compute(customer_key_bytes).0);
let sse_key_md5 = md5_base64(customer_key_bytes);
let request = PrepareEncryptionRequest {
bucket,
@@ -3356,7 +3367,7 @@ mod tests {
let key = "test-key";
let content_size = 1024;
let sse_key = BASE64_STANDARD.encode([42u8; 32]);
let sse_key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let sse_key_md5 = md5_base64([42u8; 32]);
let request = EncryptionRequest {
bucket,
@@ -3745,7 +3756,7 @@ mod tests {
async fn test_ssec_rio_v2_uses_sealed_object_key_metadata_roundtrip() {
let customer_key_bytes = [0x42u8; 32];
let customer_key = BASE64_STANDARD.encode(customer_key_bytes);
let customer_key_md5 = BASE64_STANDARD.encode(md5::compute(customer_key_bytes).0);
let customer_key_md5 = md5_base64(customer_key_bytes);
let material = sse_encryption(EncryptionRequest {
bucket: "bucket",
@@ -3825,7 +3836,7 @@ mod tests {
ssekms_context: None,
sse_customer_algorithm: Some("AES256".to_string()),
sse_customer_key: Some(BASE64_STANDARD.encode(key_bytes)),
sse_customer_key_md5: Some(BASE64_STANDARD.encode(md5::compute(key_bytes).0)),
sse_customer_key_md5: Some(md5_base64(key_bytes)),
content_size: 1,
}
}
@@ -3834,10 +3845,7 @@ mod tests {
let key_bytes = [key_byte; 32];
HashMap::from([
("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string()),
(
"x-amz-server-side-encryption-customer-key-md5".to_string(),
BASE64_STANDARD.encode(md5::compute(key_bytes).0),
),
("x-amz-server-side-encryption-customer-key-md5".to_string(), md5_base64(key_bytes)),
])
}
@@ -4547,7 +4555,7 @@ mod tests {
fn test_validate_ssec_for_read_wrong_key() {
// Key A is used to "encrypt" the object (stored MD5 is from key A).
let key_a = [42u8; 32];
let stored_md5 = BASE64_STANDARD.encode(md5::compute(key_a).0);
let stored_md5 = md5_base64(key_a);
let mut metadata = HashMap::new();
metadata.insert("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string());
@@ -4556,7 +4564,7 @@ mod tests {
// Key B is a different key; its MD5 won't match stored MD5.
let key_b = [99u8; 32];
let key_b_b64 = BASE64_STANDARD.encode(key_b);
let key_b_md5 = BASE64_STANDARD.encode(md5::compute(key_b).0);
let key_b_md5 = md5_base64(key_b);
let err = validate_ssec_for_read(&metadata, Some(&key_b_b64), Some(&key_b_md5)).unwrap_err();
assert_eq!(err.code, S3ErrorCode::InvalidRequest);
@@ -4566,7 +4574,7 @@ mod tests {
fn test_validate_ssec_for_read_correct_key() {
let key_bytes = [42u8; 32];
let key_b64 = BASE64_STANDARD.encode(key_bytes);
let key_md5 = BASE64_STANDARD.encode(md5::compute(key_bytes).0);
let key_md5 = md5_base64(key_bytes);
let mut metadata = HashMap::new();
metadata.insert("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string());
@@ -4582,7 +4590,7 @@ mod tests {
// DIFFERENT key. The server must recompute MD5 from the key bytes and
// reject the request because the recomputed MD5 won't match the header.
let real_key = [42u8; 32];
let stored_md5 = BASE64_STANDARD.encode(md5::compute(real_key).0);
let stored_md5 = md5_base64(real_key);
let mut metadata = HashMap::new();
metadata.insert("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string());
@@ -4694,7 +4702,7 @@ mod tests {
#[test]
fn test_validate_ssec_params_returns_invalid_request_on_bad_algorithm() {
let key = BASE64_STANDARD.encode([42u8; 32]);
let key_md5 = BASE64_STANDARD.encode(md5::compute([42u8; 32]).0);
let key_md5 = md5_base64([42u8; 32]);
let params = SsecParams {
algorithm: "AES128".to_string(),
key,
@@ -4780,7 +4788,7 @@ mod tests {
ssekms_context: None,
sse_customer_algorithm: Some("unsupported-algo".to_string()),
sse_customer_key: Some(sse_key),
sse_customer_key_md5: Some(BASE64_STANDARD.encode(md5::compute([42u8; 32]).0)),
sse_customer_key_md5: Some(md5_base64([42u8; 32])),
content_size: 1024,
};
let err = sse_encryption(request_unsupported_algorithm).await.unwrap_err();