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
use hmac::{Hmac, Mac};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use thiserror::Error;
use time::{error::Format, format_description::well_known::Rfc3339, OffsetDateTime};
type HmacSha256 = Hmac<Sha256>;
#[derive(Debug, Error)]
pub enum SignError {
#[error("format timestamp error: {0}")]
FormatTimestamp(#[from] Format),
#[error("convert timestamp error: {0}")]
ConvertTimestamp(#[from] time::error::ComponentRange),
#[error("secretkey length error")]
SecretKeyLength,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Key {
pub apikey: String,
pub secretkey: String,
pub passphrase: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Signature {
#[serde(rename = "sign")]
pub signature: String,
pub timestamp: String,
}
impl Key {
pub fn new(apikey: &str, secretkey: &str, passphrase: &str) -> Self {
Self {
apikey: apikey.to_string(),
secretkey: secretkey.to_string(),
passphrase: passphrase.to_string(),
}
}
pub fn sign(
&self,
method: &str,
uri: &str,
timestamp: OffsetDateTime,
use_unix_timestamp: bool,
) -> Result<Signature, SignError> {
let secret = self.secretkey.as_str();
let timestamp = timestamp.replace_millisecond(timestamp.millisecond())?;
let timestamp = if use_unix_timestamp {
timestamp.unix_timestamp().to_string()
} else {
timestamp.format(&Rfc3339)?
};
let raw_sign = timestamp.clone() + method + uri;
tracing::debug!("message to sign: {}", raw_sign);
let mut mac = HmacSha256::new_from_slice(secret.as_bytes())
.map_err(|_| SignError::SecretKeyLength)?;
mac.update(raw_sign.as_bytes());
Ok(Signature {
signature: base64::encode(&mac.finalize().into_bytes()),
timestamp,
})
}
pub fn sign_now(
&self,
method: &str,
uri: &str,
use_unix_timestamp: bool,
) -> Result<Signature, SignError> {
self.sign(method, uri, OffsetDateTime::now_utc(), use_unix_timestamp)
}
}