use crate::s3::header_constants::*;
use crate::s3::multimap_ext::{Multimap, MultimapExt};
use crate::s3::types::Region;
use crate::s3::utils::{UtcTime, hex_encode, sha256_hash, to_amz_date, to_signer_date};
#[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;
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone)]
pub(crate) struct SigningKeyCache {
key: Arc<[u8]>,
date_str: String,
region: Region,
service: String,
}
impl Default for SigningKeyCache {
fn default() -> Self {
Self::new()
}
}
impl SigningKeyCache {
pub(crate) fn new() -> Self {
Self {
key: Arc::from(Vec::new()),
date_str: String::new(),
region: Region::new_empty(),
service: String::new(),
}
}
#[inline]
fn matches(&self, date_str: &str, region: &Region, service: &str) -> bool {
(self.date_str == date_str) && (&self.region == region) && (self.service == service)
}
#[inline]
fn get_key_if_matches(
&self,
date_str: &str,
region: &Region,
service: &str,
) -> Option<Arc<[u8]>> {
if self.matches(date_str, region, service) {
Some(Arc::clone(&self.key))
} else {
None
}
}
fn update(&mut self, key: Arc<[u8]>, date_str: String, region: Region, service: String) {
self.key = key;
self.date_str = date_str;
self.region = region;
self.service = service;
}
}
fn hmac_hash(key: &[u8], data: &[u8]) -> Vec<u8> {
#[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::<Sha256>::new_from_slice(key).expect("HMAC can take key of any size");
hasher.update(data);
hasher.finalize().into_bytes().to_vec()
}
}
fn hmac_hash_hex(key: &[u8], data: &[u8]) -> String {
hex_encode(hmac_hash(key, data).as_slice())
}
fn get_scope(date: UtcTime, region: &Region, service_name: &str) -> String {
format!(
"{}/{}/{service_name}/aws4_request",
to_signer_date(date),
region.as_str()
)
}
fn get_canonical_request_hash(
method: &Method,
uri: &str,
query_string: &str,
headers: &str,
signed_headers: &str,
content_sha256: &str,
) -> String {
let canonical_request = format!(
"{method}\n{uri}\n{query_string}\n{headers}\n\n{signed_headers}\n{content_sha256}",
);
sha256_hash(canonical_request.as_bytes())
}
fn get_string_to_sign(date: UtcTime, scope: &str, canonical_request_hash: &str) -> String {
format!(
"AWS4-HMAC-SHA256\n{}\n{scope}\n{canonical_request_hash}",
to_amz_date(date)
)
}
fn compute_signing_key(
secret_key: &str,
date_str: &str,
region: &Region,
service_name: &str,
) -> Vec<u8> {
let mut key: Vec<u8> = b"AWS4".to_vec();
key.extend(secret_key.as_bytes());
let date_key = hmac_hash(key.as_slice(), date_str.as_bytes());
let date_region_key = hmac_hash(date_key.as_slice(), region.as_str().as_bytes());
let date_region_service_key = hmac_hash(date_region_key.as_slice(), service_name.as_bytes());
hmac_hash(date_region_service_key.as_slice(), b"aws4_request")
}
fn get_signing_key(
cache: &RwLock<SigningKeyCache>,
secret_key: &str,
date: UtcTime,
region: &Region,
service_name: &str,
) -> Arc<[u8]> {
let date_str = to_signer_date(date);
if let Ok(cache_guard) = cache.read()
&& let Some(key) = cache_guard.get_key_if_matches(&date_str, region, service_name)
{
return key;
}
let signing_key = Arc::from(compute_signing_key(
secret_key,
&date_str,
region,
service_name,
));
if let Ok(mut cache_guard) = cache.write() {
cache_guard.update(
Arc::clone(&signing_key),
date_str,
region.clone(),
service_name.to_string(),
);
}
signing_key
}
fn get_signature(signing_key: &[u8], string_to_sign: &[u8]) -> String {
hmac_hash_hex(signing_key, string_to_sign)
}
fn get_authorization(
access_key: &str,
scope: &str,
signed_headers: &str,
signature: &str,
) -> String {
format!(
"AWS4-HMAC-SHA256 Credential={access_key}/{scope}, SignedHeaders={signed_headers}, Signature={signature}",
)
}
fn sign_v4(
cache: &RwLock<SigningKeyCache>,
service_name: &str,
method: &Method,
uri: &str,
region: &Region,
headers: &mut Multimap,
query_params: &Multimap,
access_key: &str,
secret_key: &str,
content_sha256: &str,
date: UtcTime,
) {
let scope = get_scope(date, region, service_name);
let (signed_headers, canonical_headers) = headers.get_canonical_headers();
let canonical_query_string = query_params.get_canonical_query_string();
let canonical_request_hash = get_canonical_request_hash(
method,
uri,
&canonical_query_string,
&canonical_headers,
&signed_headers,
content_sha256,
);
let string_to_sign = get_string_to_sign(date, &scope, &canonical_request_hash);
let signing_key = get_signing_key(cache, secret_key, date, region, service_name);
let signature = get_signature(&signing_key, string_to_sign.as_bytes());
let authorization = get_authorization(access_key, &scope, &signed_headers, &signature);
headers.add(AUTHORIZATION, authorization);
}
pub(crate) fn sign_v4_s3(
cache: &RwLock<SigningKeyCache>,
method: &Method,
uri: &str,
region: &Region,
headers: &mut Multimap,
query_params: &Multimap,
access_key: &str,
secret_key: &str,
content_sha256: &str,
date: UtcTime,
) {
sign_v4(
cache,
"s3",
method,
uri,
region,
headers,
query_params,
access_key,
secret_key,
content_sha256,
date,
)
}
pub(crate) fn presign_v4(
cache: &RwLock<SigningKeyCache>,
method: &Method,
host: &str,
uri: &str,
region: &Region,
query_params: &mut Multimap,
access_key: &str,
secret_key: &str,
date: UtcTime,
expires: u32,
) {
let scope = get_scope(date, region, "s3");
let canonical_headers = "host:".to_string() + host;
let signed_headers = "host";
query_params.add(X_AMZ_ALGORITHM, "AWS4-HMAC-SHA256");
query_params.add(X_AMZ_CREDENTIAL, access_key.to_string() + "/" + &scope);
query_params.add(X_AMZ_DATE, to_amz_date(date));
query_params.add(X_AMZ_EXPIRES, expires.to_string());
query_params.add(X_AMZ_SIGNED_HEADERS, signed_headers.to_string());
let canonical_query_string = query_params.get_canonical_query_string();
let canonical_request_hash = get_canonical_request_hash(
method,
uri,
&canonical_query_string,
&canonical_headers,
signed_headers,
"UNSIGNED-PAYLOAD",
);
let string_to_sign = get_string_to_sign(date, &scope, &canonical_request_hash);
let signing_key = get_signing_key(cache, secret_key, date, region, "s3");
let signature = get_signature(&signing_key, string_to_sign.as_bytes());
query_params.add(X_AMZ_SIGNATURE, signature);
}
pub(crate) fn post_presign_v4(
cache: &RwLock<SigningKeyCache>,
string_to_sign: &str,
secret_key: &str,
date: UtcTime,
region: &Region,
) -> String {
let signing_key = get_signing_key(cache, secret_key, date, region, "s3");
get_signature(&signing_key, string_to_sign.as_bytes())
}
#[derive(Debug, Clone)]
pub struct ChunkSigningContext {
pub signing_key: Arc<[u8]>,
pub date_time: String,
pub scope: String,
pub seed_signature: String,
}
pub fn sign_chunk(
signing_key: &[u8],
date_time: &str,
scope: &str,
previous_signature: &str,
chunk_hash: &str,
) -> String {
const EMPTY_SHA256: &str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let string_to_sign = format!(
"AWS4-HMAC-SHA256-PAYLOAD\n{}\n{}\n{}\n{}\n{}",
date_time, scope, previous_signature, EMPTY_SHA256, chunk_hash
);
hmac_hash_hex(signing_key, string_to_sign.as_bytes())
}
pub fn sign_trailer(
signing_key: &[u8],
date_time: &str,
scope: &str,
last_chunk_signature: &str,
canonical_trailers_hash: &str,
) -> String {
let string_to_sign = format!(
"AWS4-HMAC-SHA256-TRAILER\n{}\n{}\n{}\n{}",
date_time, scope, last_chunk_signature, canonical_trailers_hash
);
hmac_hash_hex(signing_key, string_to_sign.as_bytes())
}
pub(crate) fn sign_v4_s3_with_context(
cache: &RwLock<SigningKeyCache>,
method: &Method,
uri: &str,
region: &Region,
headers: &mut Multimap,
query_params: &Multimap,
access_key: &str,
secret_key: &str,
content_sha256: &str,
date: UtcTime,
) -> ChunkSigningContext {
let scope = get_scope(date, region, "s3");
let (signed_headers, canonical_headers) = headers.get_canonical_headers();
let canonical_query_string = query_params.get_canonical_query_string();
let canonical_request_hash = get_canonical_request_hash(
method,
uri,
&canonical_query_string,
&canonical_headers,
&signed_headers,
content_sha256,
);
let string_to_sign = get_string_to_sign(date, &scope, &canonical_request_hash);
let signing_key = get_signing_key(cache, secret_key, date, region, "s3");
let signature = get_signature(&signing_key, string_to_sign.as_bytes());
let authorization = get_authorization(access_key, &scope, &signed_headers, &signature);
headers.add(AUTHORIZATION, authorization);
ChunkSigningContext {
signing_key,
date_time: to_amz_date(date),
scope,
seed_signature: signature,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::s3::header_constants::{HOST, X_AMZ_CONTENT_SHA256, X_AMZ_DATE};
use crate::s3::multimap_ext::{Multimap, MultimapExt};
use chrono::{TimeZone, Utc};
use hyper::http::Method;
fn get_test_date() -> chrono::DateTime<Utc> {
Utc.with_ymd_and_hms(2013, 5, 24, 0, 0, 0).unwrap()
}
fn test_cache() -> RwLock<SigningKeyCache> {
RwLock::new(SigningKeyCache::new())
}
#[test]
fn test_sign_v4_s3_adds_authorization_header() {
let cache = test_cache();
let method = Method::GET;
let uri = "/bucket/key";
let region = Region::default();
let mut headers = Multimap::new();
let date = get_test_date();
let content_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let access_key = "AKIAIOSFODNN7EXAMPLE";
let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
headers.add(HOST, "s3.amazonaws.com");
headers.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers.add(X_AMZ_DATE, "20130524T000000Z");
let query_params = Multimap::new();
sign_v4_s3(
&cache,
&method,
uri,
®ion,
&mut headers,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
assert!(headers.contains_key("Authorization"));
let auth_header = headers.get("Authorization").unwrap();
assert!(!auth_header.is_empty());
assert!(auth_header.starts_with("AWS4-HMAC-SHA256"));
assert!(auth_header.contains(access_key));
}
#[test]
fn test_sign_v4_s3_deterministic() {
let cache = test_cache();
let method = Method::GET;
let uri = "/test";
let region = Region::default();
let access_key = "test_key";
let secret_key = "test_secret";
let content_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let date = get_test_date();
let query_params = Multimap::new();
let mut headers1 = Multimap::new();
headers1.add(HOST, "example.com");
headers1.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers1.add(X_AMZ_DATE, "20130524T000000Z");
let mut headers2 = Multimap::new();
headers2.add(HOST, "example.com");
headers2.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers2.add(X_AMZ_DATE, "20130524T000000Z");
sign_v4_s3(
&cache,
&method,
uri,
®ion,
&mut headers1,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
sign_v4_s3(
&cache,
&method,
uri,
®ion,
&mut headers2,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
assert_eq!(headers1.get("Authorization"), headers2.get("Authorization"));
}
#[test]
fn test_sign_v4_s3_different_methods() {
let cache = test_cache();
let region = Region::default();
let uri = "/test";
let access_key = "test";
let secret_key = "secret";
let content_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let date = get_test_date();
let query_params = Multimap::new();
let mut headers_get = Multimap::new();
headers_get.add(HOST, "example.com");
headers_get.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers_get.add(X_AMZ_DATE, "20130524T000000Z");
let mut headers_put = Multimap::new();
headers_put.add(HOST, "example.com");
headers_put.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers_put.add(X_AMZ_DATE, "20130524T000000Z");
sign_v4_s3(
&cache,
&Method::GET,
uri,
®ion,
&mut headers_get,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
sign_v4_s3(
&cache,
&Method::PUT,
uri,
®ion,
&mut headers_put,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
assert_ne!(
headers_get.get("Authorization"),
headers_put.get("Authorization")
);
}
#[test]
fn test_sign_v4_s3_with_special_characters() {
let cache = test_cache();
let method = Method::GET;
let uri = "/bucket/my file.txt"; let region = Region::default();
let mut headers = Multimap::new();
let date = get_test_date();
let content_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
headers.add(HOST, "s3.amazonaws.com");
headers.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers.add(X_AMZ_DATE, "20130524T000000Z");
let query_params = Multimap::new();
let access_key = "test";
let secret_key = "secret";
sign_v4_s3(
&cache,
&method,
uri,
®ion,
&mut headers,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
assert!(headers.contains_key("Authorization"));
}
#[test]
fn test_presign_v4_adds_query_params() {
let cache = test_cache();
let method = Method::GET;
let host = "s3.amazonaws.com";
let uri = "/bucket/key";
let region = Region::default();
let mut query_params = Multimap::new();
let access_key = "AKIAIOSFODNN7EXAMPLE";
let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
let date = get_test_date();
let expires = 3600;
presign_v4(
&cache,
&method,
host,
uri,
®ion,
&mut query_params,
access_key,
secret_key,
date,
expires,
);
assert!(query_params.contains_key("X-Amz-Algorithm"));
assert!(query_params.contains_key("X-Amz-Credential"));
assert!(query_params.contains_key("X-Amz-Date"));
assert!(query_params.contains_key("X-Amz-Expires"));
assert!(query_params.contains_key("X-Amz-SignedHeaders"));
assert!(query_params.contains_key("X-Amz-Signature"));
}
#[test]
fn test_presign_v4_algorithm_value() {
let cache = test_cache();
let method = Method::GET;
let host = "s3.amazonaws.com";
let uri = "/test";
let region = Region::default();
let mut query_params = Multimap::new();
let access_key = "test";
let secret_key = "secret";
let date = get_test_date();
let expires = 3600;
presign_v4(
&cache,
&method,
host,
uri,
®ion,
&mut query_params,
access_key,
secret_key,
date,
expires,
);
let algorithm = query_params.get("X-Amz-Algorithm").unwrap();
assert_eq!(algorithm, "AWS4-HMAC-SHA256");
}
#[test]
fn test_presign_v4_expires_value() {
let cache = test_cache();
let method = Method::GET;
let host = "s3.amazonaws.com";
let uri = "/test";
let region = Region::default();
let mut query_params = Multimap::new();
let access_key = "test";
let secret_key = "secret";
let date = get_test_date();
let expires = 7200;
presign_v4(
&cache,
&method,
host,
uri,
®ion,
&mut query_params,
access_key,
secret_key,
date,
expires,
);
let expires_value = query_params.get("X-Amz-Expires").unwrap();
assert_eq!(expires_value, "7200");
}
#[test]
fn test_presign_v4_credential_format() {
let cache = test_cache();
let method = Method::GET;
let host = "s3.amazonaws.com";
let uri = "/test";
let region = Region::default();
let mut query_params = Multimap::new();
let access_key = "AKIAIOSFODNN7EXAMPLE";
let secret_key = "secret";
let date = get_test_date();
let expires = 3600;
presign_v4(
&cache,
&method,
host,
uri,
®ion,
&mut query_params,
access_key,
secret_key,
date,
expires,
);
let credential = query_params.get("X-Amz-Credential").unwrap();
assert!(credential.starts_with(access_key));
assert!(credential.contains("/20130524/"));
assert!(credential.contains("/us-east-1/"));
assert!(credential.contains("/s3/"));
assert!(credential.contains("/aws4_request"));
}
#[test]
fn test_post_presign_v4() {
let cache = test_cache();
let string_to_sign = "test_string_to_sign";
let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signature = post_presign_v4(&cache, string_to_sign, secret_key, date, ®ion);
assert_eq!(signature.len(), 64);
assert!(signature.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_post_presign_v4_deterministic() {
let cache = test_cache();
let string_to_sign = "test_string";
let secret_key = "test_secret";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let sig1 = post_presign_v4(&cache, string_to_sign, secret_key, date, ®ion);
let sig2 = post_presign_v4(&cache, string_to_sign, secret_key, date, ®ion);
assert_eq!(sig1, sig2);
}
#[test]
fn test_sign_chunk_produces_valid_signature() {
let cache = test_cache();
let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signing_key = get_signing_key(&cache, secret_key, date, ®ion, "s3");
let date_time = "20130524T000000Z";
let scope = "20130524/us-east-1/s3/aws4_request";
let previous_signature = "4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9";
let chunk_hash = "bf718b6f653bebc184e1479f1935b8da974d701b893afcf49e701f3e2f9f9c5a";
let signature = sign_chunk(
&signing_key,
date_time,
scope,
previous_signature,
chunk_hash,
);
assert_eq!(signature.len(), 64);
assert!(signature.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_sign_chunk_deterministic() {
let cache = test_cache();
let secret_key = "test_secret";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signing_key = get_signing_key(&cache, secret_key, date, ®ion, "s3");
let date_time = "20130524T000000Z";
let scope = "20130524/us-east-1/s3/aws4_request";
let previous_signature = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234";
let chunk_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let sig1 = sign_chunk(
&signing_key,
date_time,
scope,
previous_signature,
chunk_hash,
);
let sig2 = sign_chunk(
&signing_key,
date_time,
scope,
previous_signature,
chunk_hash,
);
assert_eq!(sig1, sig2);
}
#[test]
fn test_sign_chunk_empty_data() {
let cache = test_cache();
let secret_key = "test_secret";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signing_key = get_signing_key(&cache, secret_key, date, ®ion, "s3");
let date_time = "20130524T000000Z";
let scope = "20130524/us-east-1/s3/aws4_request";
let previous_signature = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234";
let chunk_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let signature = sign_chunk(
&signing_key,
date_time,
scope,
previous_signature,
chunk_hash,
);
assert_eq!(signature.len(), 64);
assert!(signature.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_sign_chunk_chaining() {
let cache = test_cache();
let secret_key = "test_secret";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signing_key = get_signing_key(&cache, secret_key, date, ®ion, "s3");
let date_time = "20130524T000000Z";
let scope = "20130524/us-east-1/s3/aws4_request";
let seed_signature = "seed1234seed1234seed1234seed1234seed1234seed1234seed1234seed1234";
let chunk1_hash = "aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111";
let chunk2_hash = "bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222";
let chunk1_sig = sign_chunk(&signing_key, date_time, scope, seed_signature, chunk1_hash);
let chunk2_sig = sign_chunk(&signing_key, date_time, scope, &chunk1_sig, chunk2_hash);
assert_ne!(chunk1_sig, chunk2_sig);
assert_eq!(chunk1_sig.len(), 64);
assert_eq!(chunk2_sig.len(), 64);
}
#[test]
fn test_sign_trailer_produces_valid_signature() {
let cache = test_cache();
let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signing_key = get_signing_key(&cache, secret_key, date, ®ion, "s3");
let date_time = "20130524T000000Z";
let scope = "20130524/us-east-1/s3/aws4_request";
let last_chunk_signature =
"b6c6ea8a5354eaf15b3cb7646744f4275b71ea724fed81ceb9323e279d449df9";
let canonical_trailers_hash =
"1e376db7e1a34a8ef1c4bcee131a2d60a1cb62503747488624e10995f448d774";
let signature = sign_trailer(
&signing_key,
date_time,
scope,
last_chunk_signature,
canonical_trailers_hash,
);
assert_eq!(signature.len(), 64);
assert!(signature.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_sign_trailer_deterministic() {
let cache = test_cache();
let secret_key = "test_secret";
let date = get_test_date();
let region = Region::new("us-east-1").unwrap();
let signing_key = get_signing_key(&cache, secret_key, date, ®ion, "s3");
let date_time = "20130524T000000Z";
let scope = "20130524/us-east-1/s3/aws4_request";
let last_chunk_signature =
"abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234";
let canonical_trailers_hash =
"1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd";
let sig1 = sign_trailer(
&signing_key,
date_time,
scope,
last_chunk_signature,
canonical_trailers_hash,
);
let sig2 = sign_trailer(
&signing_key,
date_time,
scope,
last_chunk_signature,
canonical_trailers_hash,
);
assert_eq!(sig1, sig2);
}
#[test]
fn test_sign_v4_s3_with_context_returns_valid_context() {
let cache = test_cache();
let method = Method::PUT;
let uri = "/examplebucket/chunkObject.txt";
let region = Region::new("us-east-1").unwrap();
let mut headers = Multimap::new();
let date = get_test_date();
let content_sha256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER";
let access_key = "AKIAIOSFODNN7EXAMPLE";
let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
headers.add(HOST, "s3.amazonaws.com");
headers.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers.add(X_AMZ_DATE, "20130524T000000Z");
headers.add("Content-Encoding", "aws-chunked");
headers.add("x-amz-decoded-content-length", "66560");
headers.add("x-amz-trailer", "x-amz-checksum-crc32c");
let query_params = Multimap::new();
let context = sign_v4_s3_with_context(
&cache,
&method,
uri,
®ion,
&mut headers,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
assert!(headers.contains_key("Authorization"));
let auth_header = headers.get("Authorization").unwrap();
assert!(auth_header.starts_with("AWS4-HMAC-SHA256"));
assert!(!context.signing_key.is_empty());
assert_eq!(context.date_time, "20130524T000000Z");
assert_eq!(context.scope, "20130524/us-east-1/s3/aws4_request");
assert_eq!(context.seed_signature.len(), 64);
assert!(
context
.seed_signature
.chars()
.all(|c| c.is_ascii_hexdigit())
);
}
#[test]
fn test_chunk_signing_context_can_be_cloned() {
let cache = test_cache();
let method = Method::PUT;
let uri = "/test";
let region = Region::new("us-east-1").unwrap();
let mut headers = Multimap::new();
let date = get_test_date();
let content_sha256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER";
let access_key = "test";
let secret_key = "secret";
headers.add(HOST, "s3.amazonaws.com");
headers.add(X_AMZ_CONTENT_SHA256, content_sha256);
headers.add(X_AMZ_DATE, "20130524T000000Z");
let query_params = Multimap::new();
let context = sign_v4_s3_with_context(
&cache,
&method,
uri,
®ion,
&mut headers,
&query_params,
access_key,
secret_key,
content_sha256,
date,
);
let cloned = context.clone();
assert_eq!(context.date_time, cloned.date_time);
assert_eq!(context.scope, cloned.scope);
assert_eq!(context.seed_signature, cloned.seed_signature);
}
}