init_data_rust/
sign.rs

1use hmac::{Hmac, Mac};
2use sha2::Sha256;
3
4use crate::errors::SignError;
5use std::{
6    collections::HashMap,
7    time::{SystemTime, UNIX_EPOCH},
8};
9
10type HmacSha256 = Hmac<Sha256>;
11
12/// Signs the payload using the specified key.
13pub fn sign(
14    payload: &HashMap<String, String>,
15    bot_token: &str,
16    auth_time: SystemTime,
17) -> Result<String, SignError> {
18    let auth_date = auth_time.duration_since(UNIX_EPOCH)?;
19
20    // Collect and filter pairs
21    let mut pairs: Vec<(String, String)> = payload
22        .iter()
23        .filter(|&(k, _)| k != "hash" && k != "auth_date")
24        .map(|(k, v)| (k.clone(), v.clone()))
25        .collect();
26
27    // Add the auth_date
28    pairs.push(("auth_date".to_string(), auth_date.as_secs().to_string()));
29
30    // Sort pairs by key
31    pairs.sort_by(|a, b| a.0.cmp(&b.0));
32
33    // Build the payload
34    let payload_string = pairs
35        .iter()
36        .map(|(k, v)| format!("{}={}", k, v))
37        .collect::<Vec<String>>()
38        .join("\n");
39
40    // First HMAC: Create secret key using "WebAppData"
41    let mut sk_hmac = HmacSha256::new_from_slice(b"WebAppData")
42        .map_err(|_| SignError::CouldNotProcessSignature)?;
43    sk_hmac.update(bot_token.as_bytes());
44    let secret_key = sk_hmac.finalize().into_bytes();
45
46    // Second HMAC: Sign the payload using the secret key
47    let mut imp_hmac =
48        HmacSha256::new_from_slice(&secret_key).map_err(|_| SignError::CouldNotProcessSignature)?;
49    imp_hmac.update(payload_string.as_bytes());
50
51    // Get result and convert to hex string
52    let result = imp_hmac.finalize().into_bytes();
53
54    Ok(hex::encode(result))
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use std::time::{Duration, UNIX_EPOCH};
61
62    #[test]
63    fn test_sign() {
64        let mut payload: HashMap<String, String> = HashMap::new();
65        payload.insert(
66            "query_id".to_string(),
67            "AAHdF6IQAAAAAN0XohDhrOrc".to_string(),
68        );
69        payload.insert(
70            "user".to_string(),
71            r#"{"id":279058397,"first_name":"Vladislav","last_name":"Kibenko","username":"vdkfrost","language_code":"ru","is_premium":true}"#.to_string(),
72        );
73        let bot_token = "5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8";
74        let auth_time = UNIX_EPOCH + Duration::from_secs(1662771648);
75
76        let expected_hash =
77            "c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2".to_string();
78        let result = sign(&payload, bot_token, auth_time).unwrap();
79        assert_eq!(result, expected_hash);
80    }
81}