tencentcloud_cls_sdk_rust/
sign.rs

1extern crate chrono;
2extern crate rustc_serialize;
3extern crate sha1;
4extern crate url;
5
6use std::collections::HashMap;
7
8use rustc_serialize::hex::ToHex;
9
10pub const SHA1_DIGEST_BYTES: usize = 20;
11const SHA1_KEY_BYTES: usize = 64;
12
13pub fn cal_sha1_sum(msg: &str) -> String {
14    sha1::Sha1::from(msg).digest().to_string()
15}
16
17pub fn cal_sha1_hmac_digest(key: &[u8], message: &[u8]) -> [u8; SHA1_DIGEST_BYTES] {
18    let inner_pad_byte: u8 = 0x36;
19    let outer_pad_byte: u8 = 0x5c;
20    let key_pad_byte: u8 = 0x00;
21
22    let mut sha1_ctx = sha1::Sha1::new();
23    let auth_key: &mut [u8; SHA1_KEY_BYTES] = &mut [key_pad_byte; SHA1_KEY_BYTES];
24
25    if key.len() > SHA1_KEY_BYTES {
26        sha1_ctx.update(key);
27        auth_key[..SHA1_DIGEST_BYTES].copy_from_slice(&(sha1_ctx.digest().bytes()));
28        sha1_ctx.reset();
29    } else {
30        auth_key[..key.len()].copy_from_slice(key);
31    }
32
33    let mut inner_padding: [u8; SHA1_KEY_BYTES] = [inner_pad_byte; SHA1_KEY_BYTES];
34    let mut outer_padding: [u8; SHA1_KEY_BYTES] = [outer_pad_byte; SHA1_KEY_BYTES];
35
36    for offset in 0..auth_key.len() {
37        inner_padding[offset] ^= auth_key[offset];
38        outer_padding[offset] ^= auth_key[offset];
39    }
40    sha1_ctx.update(&inner_padding);
41    sha1_ctx.update(message);
42    let inner_hash = sha1_ctx.digest().bytes();
43    sha1_ctx.reset();
44    sha1_ctx.update(&outer_padding);
45    sha1_ctx.update(&inner_hash);
46    sha1_ctx.digest().bytes()
47}
48
49pub fn signature(
50    secret_id: &str,
51    secret_key: &str,
52    method: &str,
53    path: &str,
54    params: &HashMap<String, String>,
55    headers: &HashMap<String, String>,
56    expire: i64,
57) -> String {
58    let mut signed_header_list: Vec<String> = vec![];
59    let mut signed_parameter_list: Vec<String> = vec![];
60
61    let mut hs = url::form_urlencoded::Serializer::new(String::new());
62    for (key, val) in headers {
63        let lower_key = key.to_lowercase();
64        if lower_key == "content-type"
65            || lower_key == "content-md5"
66            || lower_key == "host"
67            || lower_key.starts_with('x')
68        {
69            hs.append_pair(lower_key.as_str(), val.as_str());
70            signed_header_list.push(lower_key.clone());
71        }
72    }
73    signed_header_list.sort();
74    let format_headers = hs.finish().as_str().to_string();
75
76    let mut ps = url::form_urlencoded::Serializer::new(String::new());
77    for (key, val) in params {
78        let lower_key = key.to_lowercase();
79        ps.append_pair(lower_key.as_str(), val.as_str());
80        signed_parameter_list.push(lower_key.clone());
81    }
82    let format_parameters = ps.finish().as_str().to_string();
83    signed_parameter_list.sort();
84    let format_string = format!(
85        "{}\n{}\n{}\n{}\n",
86        method.to_lowercase(),
87        path,
88        format_parameters,
89        format_headers
90    );
91
92    let sign_time = format!(
93        "{};{}",
94        chrono::Local::now().timestamp() - 60,
95        chrono::Local::now().timestamp() + expire
96    );
97    let string_to_sign = format!(
98        "sha1\n{}\n{}\n",
99        sign_time.clone(),
100        cal_sha1_sum(format_string.as_str())
101    );
102
103    let sign_key = cal_sha1_hmac_digest(secret_key.as_bytes(), sign_time.as_bytes()).to_hex();
104    let signature = cal_sha1_hmac_digest(sign_key.as_bytes(), string_to_sign.as_bytes()).to_hex();
105
106    vec![
107        "q-sign-algorithm=sha1".to_string(),
108        format!("q-ak={}", secret_id),
109        format!("q-sign-time={}", sign_time),
110        format!("q-key-time={}", sign_time),
111        format!("q-header-list={}", signed_header_list.join(";")),
112        format!("q-url-param-list={}", signed_parameter_list.join(";")),
113        format!("q-signature={}", signature),
114    ]
115    .join("&")
116}
117
118#[cfg(test)]
119mod tests {
120    use std::collections::HashMap;
121    
122    use crate::sign::signature;
123    
124    #[test]
125    fn test_url_encode() {
126        let mut headers: HashMap<String, String> = HashMap::new();
127        let params: HashMap<String, String> = HashMap::new();
128        headers.insert(
129            "Host".to_string(),
130            "ap-shanghai.cls.myqcloud.com".to_string(),
131        );
132        headers.insert("User-Agent".to_string(), "AuthSDK".to_string());
133        headers.insert("Content-Type".to_string(), "application/json".to_string());
134        headers.insert(
135            "Content-MD5".to_string(),
136            "f9c7fc33c7eab68dfa8a52508d1f4659".to_string(),
137        );
138
139        // params.insert(
140        //     "logset_id".to_string(),
141        //     "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".to_string(),
142        // );
143
144        let data = signature(
145            "SecretIdExample_XXXXXXXXXXXXXXXXXXXXX",
146            "SecretKeyExample_XXXXXXXXXXXXXXXX",
147            "GET",
148            "/logset",
149            &params,
150            &headers,
151            300,
152        );
153        println!("{}", data)
154    }
155}