118 lines
3.8 KiB
Rust
118 lines
3.8 KiB
Rust
use chrono::{DateTime, Utc};
|
|
use hmac::{Hmac, Mac};
|
|
use sha2::Sha256;
|
|
|
|
use hyper::header::HeaderName;
|
|
use hyper::{body::Incoming as IncomingBody, Request};
|
|
|
|
use garage_model::garage::Garage;
|
|
use garage_model::key_table::Key;
|
|
use garage_util::data::{sha256sum, Hash};
|
|
|
|
use error::*;
|
|
|
|
pub mod body;
|
|
pub mod checksum;
|
|
pub mod error;
|
|
pub mod payload;
|
|
pub mod streaming;
|
|
|
|
pub const SHORT_DATE: &str = "%Y%m%d";
|
|
pub const LONG_DATETIME: &str = "%Y%m%dT%H%M%SZ";
|
|
|
|
// ---- Constants used in AWSv4 signatures ----
|
|
|
|
pub const X_AMZ_ALGORITHM: HeaderName = HeaderName::from_static("x-amz-algorithm");
|
|
pub const X_AMZ_CREDENTIAL: HeaderName = HeaderName::from_static("x-amz-credential");
|
|
pub const X_AMZ_DATE: HeaderName = HeaderName::from_static("x-amz-date");
|
|
pub const X_AMZ_EXPIRES: HeaderName = HeaderName::from_static("x-amz-expires");
|
|
pub const X_AMZ_SIGNEDHEADERS: HeaderName = HeaderName::from_static("x-amz-signedheaders");
|
|
pub const X_AMZ_SIGNATURE: HeaderName = HeaderName::from_static("x-amz-signature");
|
|
pub const X_AMZ_CONTENT_SHA256: HeaderName = HeaderName::from_static("x-amz-content-sha256");
|
|
pub const X_AMZ_TRAILER: HeaderName = HeaderName::from_static("x-amz-trailer");
|
|
|
|
/// Result of `sha256("")`
|
|
pub(crate) const EMPTY_STRING_HEX_DIGEST: &str =
|
|
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
|
|
// Signature calculation algorithm
|
|
pub const AWS4_HMAC_SHA256: &str = "AWS4-HMAC-SHA256";
|
|
type HmacSha256 = Hmac<Sha256>;
|
|
|
|
// Possible values for x-amz-content-sha256, in addition to the actual sha256
|
|
pub const UNSIGNED_PAYLOAD: &str = "UNSIGNED-PAYLOAD";
|
|
pub const STREAMING_UNSIGNED_PAYLOAD_TRAILER: &str = "STREAMING-UNSIGNED-PAYLOAD-TRAILER";
|
|
pub const STREAMING_AWS4_HMAC_SHA256_PAYLOAD: &str = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
|
|
|
|
// Used in the computation of StringToSign
|
|
pub const AWS4_HMAC_SHA256_PAYLOAD: &str = "AWS4-HMAC-SHA256-PAYLOAD";
|
|
|
|
// ---- enums to describe stuff going on in signature calculation ----
|
|
|
|
#[derive(Debug)]
|
|
pub enum ContentSha256Header {
|
|
UnsignedPayload,
|
|
Sha256Checksum(Hash),
|
|
StreamingPayload { trailer: bool, signed: bool },
|
|
}
|
|
|
|
// ---- top-level functions ----
|
|
|
|
pub struct VerifiedRequest {
|
|
pub request: Request<streaming::ReqBody>,
|
|
pub access_key: Key,
|
|
pub content_sha256_header: ContentSha256Header,
|
|
}
|
|
|
|
pub async fn verify_request(
|
|
garage: &Garage,
|
|
mut req: Request<IncomingBody>,
|
|
service: &'static str,
|
|
) -> Result<VerifiedRequest, Error> {
|
|
let checked_signature = payload::check_payload_signature(&garage, &mut req, service).await?;
|
|
|
|
let request = streaming::parse_streaming_body(
|
|
req,
|
|
&checked_signature,
|
|
&garage.config.s3_api.s3_region,
|
|
service,
|
|
)?;
|
|
|
|
let access_key = checked_signature
|
|
.key
|
|
.ok_or_else(|| Error::forbidden("Garage does not support anonymous access yet"))?;
|
|
|
|
Ok(VerifiedRequest {
|
|
request,
|
|
access_key,
|
|
content_sha256_header: checked_signature.content_sha256_header,
|
|
})
|
|
}
|
|
|
|
pub fn signing_hmac(
|
|
datetime: &DateTime<Utc>,
|
|
secret_key: &str,
|
|
region: &str,
|
|
service: &str,
|
|
) -> Result<HmacSha256, crypto_common::InvalidLength> {
|
|
let secret = String::from("AWS4") + secret_key;
|
|
let mut date_hmac = HmacSha256::new_from_slice(secret.as_bytes())?;
|
|
date_hmac.update(datetime.format(SHORT_DATE).to_string().as_bytes());
|
|
let mut region_hmac = HmacSha256::new_from_slice(&date_hmac.finalize().into_bytes())?;
|
|
region_hmac.update(region.as_bytes());
|
|
let mut service_hmac = HmacSha256::new_from_slice(®ion_hmac.finalize().into_bytes())?;
|
|
service_hmac.update(service.as_bytes());
|
|
let mut signing_hmac = HmacSha256::new_from_slice(&service_hmac.finalize().into_bytes())?;
|
|
signing_hmac.update(b"aws4_request");
|
|
let hmac = HmacSha256::new_from_slice(&signing_hmac.finalize().into_bytes())?;
|
|
Ok(hmac)
|
|
}
|
|
|
|
pub fn compute_scope(datetime: &DateTime<Utc>, region: &str, service: &str) -> String {
|
|
format!(
|
|
"{}/{}/{}/aws4_request",
|
|
datetime.format(SHORT_DATE),
|
|
region,
|
|
service
|
|
)
|
|
}
|