From 2aa049c781be87ae77e334996558fd82a2409506 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 27 Feb 2024 19:13:24 +0100 Subject: [fix-presigned] fix tests --- src/api/signature/payload.rs | 60 ++++++++++++++---------------- src/garage/tests/s3/streaming_signature.rs | 2 +- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/api/signature/payload.rs b/src/api/signature/payload.rs index e2334ce7..29ed7081 100644 --- a/src/api/signature/payload.rs +++ b/src/api/signature/payload.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use chrono::{DateTime, Duration, NaiveDateTime, TimeZone, Utc}; use hmac::Mac; -use hyper::header::{HeaderMap, HeaderName, HeaderValue, AUTHORIZATION, CONTENT_TYPE, HOST}; +use hyper::header::{HeaderMap, HeaderName, AUTHORIZATION, CONTENT_TYPE, HOST}; use hyper::{body::Incoming as IncomingBody, Method, Request}; use sha2::{Digest, Sha256}; @@ -29,6 +29,9 @@ pub const X_AMZ_CONTENT_SH256: HeaderName = HeaderName::from_static("x-amz-conte pub const AWS4_HMAC_SHA256: &str = "AWS4-HMAC-SHA256"; pub const UNSIGNED_PAYLOAD: &str = "UNSIGNED-PAYLOAD"; +pub const STREAMING_AWS4_HMAC_SHA256_PAYLOAD: &str = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; + +pub type QueryMap = HashMap; pub async fn check_payload_signature( garage: &Garage, @@ -37,7 +40,7 @@ pub async fn check_payload_signature( ) -> Result<(Option, Option), Error> { let query = parse_query_map(request.uri())?; - let res = if query.contains_key(X_AMZ_ALGORITHM) { + let res = if query.contains_key(X_AMZ_ALGORITHM.as_str()) { check_presigned_signature(garage, service, request, query).await } else { check_standard_signature(garage, service, request, query).await @@ -54,7 +57,7 @@ async fn check_standard_signature( garage: &Garage, service: &'static str, request: &Request, - query: HeaderMap, + query: QueryMap, ) -> Result<(Option, Option), Error> { let authorization = Authorization::parse(request.headers())?; @@ -96,7 +99,7 @@ async fn check_standard_signature( let content_sha256 = if authorization.content_sha256 == UNSIGNED_PAYLOAD { None - } else if authorization.content_sha256 == "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" { + } else if authorization.content_sha256 == STREAMING_AWS4_HMAC_SHA256_PAYLOAD { let bytes = hex::decode(authorization.signature).ok_or_bad_request("Invalid signature")?; Some(Hash::try_from(&bytes).ok_or_bad_request("Invalid signature")?) } else { @@ -112,9 +115,9 @@ async fn check_presigned_signature( garage: &Garage, service: &'static str, request: &Request, - mut query: HeaderMap, + mut query: QueryMap, ) -> Result<(Option, Option), Error> { - let algorithm = query.get(X_AMZ_ALGORITHM).unwrap().to_str()?; + let algorithm = query.get(X_AMZ_ALGORITHM.as_str()).unwrap(); let authorization = Authorization::parse_presigned(algorithm, &query)?; // Check that all mandatory signed headers are included @@ -133,7 +136,7 @@ async fn check_presigned_signature( return Err(Error::bad_request("Header `Host` should be signed")); } - query.remove(X_AMZ_SIGNATURE); + query.remove(X_AMZ_SIGNATURE.as_str()); let canonical_request = canonical_request( service, request.method(), @@ -157,21 +160,15 @@ async fn check_presigned_signature( Ok((Some(key), None)) } -pub fn parse_query_map(uri: &http::uri::Uri) -> Result { - let mut query = HeaderMap::new(); +pub fn parse_query_map(uri: &http::uri::Uri) -> Result { + let mut query = QueryMap::new(); if let Some(query_str) = uri.query() { let query_pairs = url::form_urlencoded::parse(query_str.as_bytes()); for (key, val) in query_pairs { - let name: HeaderName = key - .to_lowercase() - .try_into() - .ok_or_bad_request("invalid query parameter name")?; - let value = HeaderValue::from_bytes(val.as_bytes()) - .ok_or_bad_request("invalid query parameter value")?; - if query.insert(name.clone(), value).is_some() { + if query.insert(key.to_string(), val.into_owned()).is_some() { return Err(Error::bad_request(format!( "duplicate query parameter: `{}`", - name + key ))); } } @@ -205,7 +202,7 @@ pub fn canonical_request( service: &'static str, method: &Method, canonical_uri: &str, - query: &HeaderMap, + query: &QueryMap, headers: &HeaderMap, mut signed_headers: Vec, content_sha256: &str, @@ -249,8 +246,7 @@ pub fn canonical_request( let canonical_query_string = { let mut items = Vec::with_capacity(query.len()); for (key, value) in query.iter() { - let value = std::str::from_utf8(value.as_bytes())?; - items.push(uri_encode(key.as_str(), true) + "=" + &uri_encode(value, true)); + items.push(uri_encode(&key, true) + "=" + &uri_encode(&value, true)); } items.sort(); items.join("&") @@ -403,7 +399,7 @@ impl Authorization { Ok(auth) } - fn parse_presigned(algorithm: &str, query: &HeaderMap) -> Result { + fn parse_presigned(algorithm: &str, query: &QueryMap) -> Result { if algorithm != AWS4_HMAC_SHA256 { return Err(Error::bad_request( "Unsupported authorization method".to_string(), @@ -411,19 +407,18 @@ impl Authorization { } let cred = query - .get(X_AMZ_CREDENTIAL) + .get(X_AMZ_CREDENTIAL.as_str()) .ok_or_bad_request("X-Amz-Credential not found in query parameters")?; let signed_headers = query - .get(X_AMZ_SIGNEDHEADERS) + .get(X_AMZ_SIGNEDHEADERS.as_str()) .ok_or_bad_request("X-Amz-SignedHeaders not found in query parameters")?; let signature = query - .get(X_AMZ_SIGNATURE) + .get(X_AMZ_SIGNATURE.as_str()) .ok_or_bad_request("X-Amz-Signature not found in query parameters")?; let duration = query - .get(X_AMZ_EXPIRES) + .get(X_AMZ_EXPIRES.as_str()) .ok_or_bad_request("X-Amz-Expires not found in query parameters")? - .to_str()? .parse() .map_err(|_| Error::bad_request("X-Amz-Expires is not a number".to_string()))?; @@ -434,21 +429,20 @@ impl Authorization { } let date = query - .get(X_AMZ_DATE) - .ok_or_bad_request("Missing X-Amz-Date field")? - .to_str()?; + .get(X_AMZ_DATE.as_str()) + .ok_or_bad_request("Missing X-Amz-Date field")?; let date = parse_date(date)?; if Utc::now() - date > Duration::seconds(duration) { return Err(Error::bad_request("Date is too old".to_string())); } - let (key_id, scope) = parse_credential(cred.to_str()?)?; + let (key_id, scope) = parse_credential(cred)?; Ok(Authorization { key_id, scope, - signed_headers: signed_headers.to_str()?.to_string(), - signature: signature.to_str()?.to_string(), + signed_headers: signed_headers.to_string(), + signature: signature.to_string(), content_sha256: UNSIGNED_PAYLOAD.to_string(), date, }) diff --git a/src/garage/tests/s3/streaming_signature.rs b/src/garage/tests/s3/streaming_signature.rs index 224b9ed5..351aa422 100644 --- a/src/garage/tests/s3/streaming_signature.rs +++ b/src/garage/tests/s3/streaming_signature.rs @@ -26,7 +26,7 @@ async fn test_putobject_streaming() { .builder(bucket.clone()) .method(Method::PUT) .path(STD_KEY.to_owned()) - .unsigned_headers(headers) + .signed_headers(headers) .vhost_style(true) .body(vec![]) .body_signature(BodySignature::Streaming(10)) -- cgit v1.2.3