diff options
author | Alex <lx@deuxfleurs.fr> | 2025-02-01 17:48:25 +0000 |
---|---|---|
committer | Alex <lx@deuxfleurs.fr> | 2025-02-01 17:48:25 +0000 |
commit | d601f311865b8159a7bf1801dd8f43021d0b443b (patch) | |
tree | 07e4268d09a6bead4c4e5a2a4f122471a7a5d23b /src/api/common/signature/mod.rs | |
parent | 9330fd79d3466051394f6d419a247d46da8f5151 (diff) | |
parent | e4de7bdfd5e6acc05309f59d7f77755f0807e8e4 (diff) | |
download | garage-d601f311865b8159a7bf1801dd8f43021d0b443b.tar.gz garage-d601f311865b8159a7bf1801dd8f43021d0b443b.zip |
Merge pull request 'split garage_api in garage_api_{common,s3,k2v,admin}' (#947) from split-garage-api into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/947
Diffstat (limited to 'src/api/common/signature/mod.rs')
-rw-r--r-- | src/api/common/signature/mod.rs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/api/common/signature/mod.rs b/src/api/common/signature/mod.rs new file mode 100644 index 00000000..6514da43 --- /dev/null +++ b/src/api/common/signature/mod.rs @@ -0,0 +1,78 @@ +use chrono::{DateTime, Utc}; +use hmac::{Hmac, Mac}; +use sha2::Sha256; + +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 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"; + +type HmacSha256 = Hmac<Sha256>; + +pub async fn verify_request( + garage: &Garage, + mut req: Request<IncomingBody>, + service: &'static str, +) -> Result<(Request<streaming::ReqBody>, Key, Option<Hash>), Error> { + let (api_key, mut content_sha256) = + payload::check_payload_signature(&garage, &mut req, service).await?; + let api_key = + api_key.ok_or_else(|| Error::forbidden("Garage does not support anonymous access yet"))?; + + let req = streaming::parse_streaming_body( + &api_key, + req, + &mut content_sha256, + &garage.config.s3_api.s3_region, + service, + )?; + + Ok((req, api_key, content_sha256)) +} + +pub fn verify_signed_content(expected_sha256: Hash, body: &[u8]) -> Result<(), Error> { + if expected_sha256 != sha256sum(body) { + return Err(Error::bad_request( + "Request content hash does not match signed hash".to_string(), + )); + } + Ok(()) +} + +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 + ) +} |