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("=", "_")
}