coven_crypto/
s3_signing.rs1use hmac::{Hmac, KeyInit, Mac};
5use sha2::{Digest, Sha256};
6use zeroize::Zeroizing;
7
8pub fn s3_request_hash(bytes: &[u8]) -> [u8; 32] {
10 Sha256::digest(bytes).into()
11}
12
13pub fn gcs_request_signature(secret: &str, date: &str, region: &str, message: &[u8]) -> [u8; 32] {
17 v4_signature(("GOOG4", "goog4_request"), secret, [date, region, "storage"], message)
18}
19
20pub fn aws_request_signature(
24 secret: &str,
25 date: &str,
26 region: &str,
27 service: &str,
28 message: &[u8],
29) -> [u8; 32] {
30 v4_signature(("AWS4", "aws4_request"), secret, [date, region, service], message)
31}
32
33fn v4_signature(
37 (prefix, terminator): (&str, &str),
38 secret: &str,
39 scope: [&str; 3],
40 message: &[u8],
41) -> [u8; 32] {
42 let secret = Zeroizing::new(format!("{prefix}{secret}"));
43 let mut key = hmac(secret.as_bytes(), scope[0].as_bytes());
44 for part in [scope[1], scope[2], terminator] {
45 key = hmac(key.as_ref(), part.as_bytes());
46 }
47 *hmac(key.as_ref(), message)
48}
49
50fn hmac(key: &[u8], bytes: &[u8]) -> Zeroizing<[u8; 32]> {
51 let mut mac = Hmac::<Sha256>::new_from_slice(key).expect("HMAC accepts any key length");
52 mac.update(bytes);
53 Zeroizing::new(mac.finalize().into_bytes().into())
54}
55
56#[cfg(test)]
57#[path = "s3_signing_tests.rs"]
58mod tests;