garage_api/signature/
mod.rs1use chrono::{DateTime, Utc};
2use hmac::{Hmac, Mac};
3use sha2::Sha256;
4
5use hyper::{body::Incoming as IncomingBody, Request};
6
7use garage_model::garage::Garage;
8use garage_model::key_table::Key;
9use garage_util::data::{sha256sum, Hash};
10
11use error::*;
12
13pub mod error;
14pub mod payload;
15pub mod streaming;
16
17pub const SHORT_DATE: &str = "%Y%m%d";
18pub const LONG_DATETIME: &str = "%Y%m%dT%H%M%SZ";
19
20type HmacSha256 = Hmac<Sha256>;
21
22pub async fn verify_request(
23 garage: &Garage,
24 mut req: Request<IncomingBody>,
25 service: &'static str,
26) -> Result<(Request<streaming::ReqBody>, Key, Option<Hash>), Error> {
27 let (api_key, mut content_sha256) =
28 payload::check_payload_signature(&garage, &mut req, service).await?;
29 let api_key =
30 api_key.ok_or_else(|| Error::forbidden("Garage does not support anonymous access yet"))?;
31
32 let req = streaming::parse_streaming_body(
33 &api_key,
34 req,
35 &mut content_sha256,
36 &garage.config.s3_api.s3_region,
37 service,
38 )?;
39
40 Ok((req, api_key, content_sha256))
41}
42
43pub fn verify_signed_content(expected_sha256: Hash, body: &[u8]) -> Result<(), Error> {
44 if expected_sha256 != sha256sum(body) {
45 return Err(Error::bad_request(
46 "Request content hash does not match signed hash".to_string(),
47 ));
48 }
49 Ok(())
50}
51
52pub fn signing_hmac(
53 datetime: &DateTime<Utc>,
54 secret_key: &str,
55 region: &str,
56 service: &str,
57) -> Result<HmacSha256, crypto_common::InvalidLength> {
58 let secret = String::from("AWS4") + secret_key;
59 let mut date_hmac = HmacSha256::new_from_slice(secret.as_bytes())?;
60 date_hmac.update(datetime.format(SHORT_DATE).to_string().as_bytes());
61 let mut region_hmac = HmacSha256::new_from_slice(&date_hmac.finalize().into_bytes())?;
62 region_hmac.update(region.as_bytes());
63 let mut service_hmac = HmacSha256::new_from_slice(®ion_hmac.finalize().into_bytes())?;
64 service_hmac.update(service.as_bytes());
65 let mut signing_hmac = HmacSha256::new_from_slice(&service_hmac.finalize().into_bytes())?;
66 signing_hmac.update(b"aws4_request");
67 let hmac = HmacSha256::new_from_slice(&signing_hmac.finalize().into_bytes())?;
68 Ok(hmac)
69}
70
71pub fn compute_scope(datetime: &DateTime<Utc>, region: &str, service: &str) -> String {
72 format!(
73 "{}/{}/{}/aws4_request",
74 datetime.format(SHORT_DATE),
75 region,
76 service
77 )
78}