rusty-oss 0.2.1

Simple pure Rust Aliyun OSS Client following a Sans-IO approach
Documentation
use hmac::{Hmac, Mac as _};
use sha2::Sha256;
use time::OffsetDateTime;
use zeroize::Zeroizing;

use crate::time_::YYYYMMDD;

type HmacSha256 = Hmac<Sha256>;

pub fn signature(
    date: &OffsetDateTime,
    secret: &str,
    region: &str,
    string_to_sign: &str,
) -> String {
    let yyyymmdd = date.format(&YYYYMMDD).expect("invalid format");

    let mut raw_date = String::with_capacity("aliyun_v4".len() + secret.len());
    raw_date.push_str("aliyun_v4");
    raw_date.push_str(secret);
    let raw_date = Zeroizing::new(raw_date);

    let mut mac =
        HmacSha256::new_from_slice(raw_date.as_bytes()).expect("HMAC can take keys of any size");
    mac.update(yyyymmdd.as_bytes());
    let date_key = mac.finalize().into_bytes();

    let mut mac = HmacSha256::new_from_slice(&date_key).expect("HMAC can take keys of any size");
    mac.update(region.as_bytes());
    let date_region_key = mac.finalize().into_bytes();

    let mut mac =
        HmacSha256::new_from_slice(&date_region_key).expect("HMAC can take keys of any size");
    mac.update(b"oss");
    let date_region_service_key = mac.finalize().into_bytes();

    let mut mac = HmacSha256::new_from_slice(&date_region_service_key)
        .expect("HMAC can take keys of any size");
    mac.update(b"aliyun_v4_request");
    let signing_key = mac.finalize().into_bytes();

    let mut mac = HmacSha256::new_from_slice(&signing_key).expect("HMAC can take keys of any size");
    mac.update(string_to_sign.as_bytes());

    format!("{:x}", mac.finalize().into_bytes())
}

#[cfg(test)]
mod tests {
    use pretty_assertions::assert_eq;
    use time::OffsetDateTime;

    use super::*;

    #[test]
    fn oss_example() {
        let date = OffsetDateTime::from_unix_timestamp(1738860711).unwrap();

        let secret = "accesskeysecret";
        let region = "cn-hangzhou";

        let expected = "0710a37f4a7f7dffa50b84b801c15e286562216be17256116038fde07eec38cf";

        let got = signature(&date, secret, region, create_string_to_sign());

        assert_eq!(got, expected);
    }

    fn create_string_to_sign() -> &'static str {
        concat!(
            "OSS4-HMAC-SHA256\n",
            "20250206T165151Z\n",
            "20250206/cn-hangzhou/oss/aliyun_v4_request\n",
            "2f3a0f126f58eb30d96c71213d54352a00698f578b8a6398817fce3de06b7ed2",
        )
    }
}