Skip to main content

muxi_rust/
auth.rs

1use hmac::{Hmac, Mac};
2use sha2::Sha256;
3use base64::{Engine as _, engine::general_purpose::STANDARD};
4use std::time::{SystemTime, UNIX_EPOCH};
5
6type HmacSha256 = Hmac<Sha256>;
7
8pub struct Auth;
9
10impl Auth {
11    pub fn generate_hmac_signature(secret_key: &str, method: &str, path: &str) -> (String, String) {
12        let clean_path = path.split('?').next().unwrap_or(path);
13        let timestamp = SystemTime::now()
14            .duration_since(UNIX_EPOCH)
15            .unwrap()
16            .as_secs()
17            .to_string();
18        
19        let message = format!("{};{};{}", timestamp, method, clean_path);
20        
21        let mut mac = HmacSha256::new_from_slice(secret_key.as_bytes())
22            .expect("HMAC can take key of any size");
23        mac.update(message.as_bytes());
24        let result = mac.finalize();
25        let signature = STANDARD.encode(result.into_bytes());
26        
27        (signature, timestamp)
28    }
29    
30    pub fn build_auth_header(key_id: &str, secret_key: &str, method: &str, path: &str) -> String {
31        let (signature, timestamp) = Self::generate_hmac_signature(secret_key, method, path);
32        format!(
33            "MUXI-HMAC key={}, timestamp={}, signature={}",
34            key_id, timestamp, signature
35        )
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    
43    #[test]
44    fn test_generate_hmac_signature() {
45        let (sig, ts) = Auth::generate_hmac_signature("secret456", "GET", "/rpc/status");
46        assert!(!sig.is_empty());
47        assert!(!ts.is_empty());
48    }
49    
50    #[test]
51    fn test_build_auth_header() {
52        let header = Auth::build_auth_header("key123", "secret456", "GET", "/path");
53        assert!(header.contains("MUXI-HMAC key="));
54        assert!(header.contains("key123"));
55        assert!(header.contains("timestamp="));
56        assert!(header.contains("signature="));
57    }
58    
59    #[test]
60    fn test_signature_strips_query_params() {
61        let (sig1, _) = Auth::generate_hmac_signature("secret", "GET", "/path");
62        let (sig2, _) = Auth::generate_hmac_signature("secret", "GET", "/path?foo=bar");
63        assert!(!sig1.is_empty());
64        assert!(!sig2.is_empty());
65    }
66}