From 3422ec791a120427e6f5302a90e63fe8314a0c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E5=B0=8F=E9=B8=AD?= Date: Wed, 29 Jul 2026 22:58:24 +0800 Subject: [PATCH] fix(sse): validate copy source customer algorithm --- rustfs/src/storage/sse.rs | 56 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/rustfs/src/storage/sse.rs b/rustfs/src/storage/sse.rs index ec961c260..e06b9dea5 100644 --- a/rustfs/src/storage/sse.rs +++ b/rustfs/src/storage/sse.rs @@ -805,8 +805,21 @@ impl ObjectEncryptionResolver for SseObjectEncryptionResolver { request: ReadEncryptionRequest<'_>, ) -> Result, EncryptionResolutionError> { let metadata = normalize_encryption_metadata_case(request.metadata)?; - let (_, customer_key, customer_key_md5) = + let (customer_algorithm, customer_key, customer_key_md5) = extract_ssec_params_from_headers(request.headers).map_err(map_encryption_resolution_error)?; + if let Some(stored_algorithm) = metadata.get("x-amz-server-side-encryption-customer-algorithm") { + let request_algorithm = customer_algorithm.as_ref().ok_or_else(|| { + map_encryption_resolution_error(ssec_invalid_request( + "The object was stored using a form of Server Side Encryption. \ + The correct parameters must be provided to retrieve the object.", + )) + })?; + if stored_algorithm != request_algorithm.as_str() { + return Err(map_encryption_resolution_error(ssec_invalid_request( + "The provided encryption parameters did not match the ones used originally to encrypt the object.", + ))); + } + } let material = sse_decryption(DecryptionRequest { bucket: request.bucket, key: request.object, @@ -2883,6 +2896,47 @@ mod tests { assert_eq!(material.mode, ReadEncryptionMode::Direct { base_nonce: nonce }); } + #[tokio::test] + async fn object_encryption_resolver_rejects_missing_or_invalid_ssec_algorithm() { + let key = [0x31; 32]; + let key_b64 = BASE64_STANDARD.encode(key); + let key_md5 = md5_base64(key); + let metadata = HashMap::from([ + ("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string()), + ("x-amz-server-side-encryption-customer-key-md5".to_string(), key_md5.clone()), + ]); + + for algorithm in [None, Some("AES128")] { + let mut headers = HeaderMap::new(); + if let Some(algorithm) = algorithm { + headers.insert("x-amz-server-side-encryption-customer-algorithm", HeaderValue::from_static(algorithm)); + } + headers.insert( + "x-amz-server-side-encryption-customer-key", + HeaderValue::from_str(&key_b64).expect("base64 key is a valid header"), + ); + headers.insert( + "x-amz-server-side-encryption-customer-key-md5", + HeaderValue::from_str(&key_md5).expect("base64 MD5 is a valid header"), + ); + + let result = SseObjectEncryptionResolver + .resolve_read_material(ReadEncryptionRequest { + bucket: "bucket", + object: "object", + metadata: &metadata, + headers: &headers, + }) + .await; + let error = match result { + Err(error) => error, + Ok(_) => panic!("missing or invalid SSE-C algorithm must fail closed"), + }; + + assert_eq!(error.kind(), EncryptionResolutionErrorKind::InvalidRequest); + } + } + #[tokio::test] async fn object_encryption_resolver_classifies_missing_ssec_key_as_invalid_request() { let metadata = HashMap::from([("x-amz-server-side-encryption-customer-algorithm".to_string(), "AES256".to_string())]);