1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use crypto::{hmac::Hmac, mac::Mac, sha2::Sha256};
use flate2::{write::ZlibEncoder, Compression};
use serde::Serialize;
use std::{
    io::prelude::*,
    time::{Duration, SystemTime, UNIX_EPOCH},
};

use crate::error::Error;

#[derive(Debug, Serialize)]
struct SigDoc {
    #[serde(rename = "TLS.expire")]
    expire: u64,
    #[serde(rename = "TLS.ver")]
    ver: String,
    #[serde(rename = "TLS.sdkappid")]
    sdkappid: u64,
    #[serde(rename = "TLS.identifier")]
    identifier: String,
    #[serde(rename = "TLS.sig")]
    sig: String,
    #[serde(rename = "TLS.time")]
    time: u64,
    #[serde(rename = "TLS.userbuf", skip_serializing_if = "Option::is_none")]
    user_buf: Option<String>,
}

pub fn gen_usersig(
    sdk_appid: u64,
    key: &str,
    userid: &str,
    expire: Duration,
) -> Result<String, Error> {
    do_gen_usersig(sdk_appid, key, userid, expire, vec![].as_ref())
}

fn do_gen_usersig(
    sdk_appid: u64,
    key: &str,
    userid: &str,
    expire: Duration,
    user_buf: &[u8],
) -> Result<String, Error> {
    let curr_time = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("please setup system timestamp")
        .as_secs();
    let base64_userbuf = if !user_buf.is_empty() {
        Some(base64::encode(user_buf))
    } else {
        None
    };

    let sign = hmacsha256(
        sdk_appid,
        key,
        userid,
        curr_time,
        expire.as_secs(),
        base64_userbuf.as_ref().map(|t| t.as_str()),
    );

    let doc = SigDoc {
        ver: "2.0".to_owned(),
        identifier: userid.to_owned(),
        sdkappid: sdk_appid,
        expire: expire.as_secs(),
        time: curr_time,
        sig: sign,
        user_buf: base64_userbuf,
    };

    let mut deflater = ZlibEncoder::new(Vec::new(), Compression::default());
    deflater.write_all(serde_json::to_string(&doc)?.as_bytes())?;
    let ret_vec = deflater.finish()?;
    Ok(base64_url(ret_vec.as_slice()))
}

fn hmacsha256(
    sdk_appid: u64,
    key: &str,
    identifier: &str,
    curr_time: u64,
    expire: u64,
    user_buf: Option<&str>,
) -> String {
    let mut content = format!(
        "TLS.identifier:{}\nTLS.sdkappid:{}\nTLS.time:{}\nTLS.expire:{}\n",
        identifier, sdk_appid, curr_time, expire
    );

    if let Some(data) = user_buf {
        content += "TLS.userbuf:";
        content += data;
        content += "\n";
    }

    let mut hmac = Hmac::new(Sha256::new(), key.as_bytes());
    hmac.input(content.as_bytes());
    base64::encode(hmac.result().code())
}

fn base64_url(bytes: &[u8]) -> String {
    base64::encode(bytes)
        .replace("+", "*")
        .replace("/", "-")
        .replace("=", "_")
}