aboutsummaryrefslogtreecommitdiff
path: root/src/api/signature/mod.rs
blob: 6514da43b872054a2c246504032df6bc6b96c395 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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(&region_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
	)
}