1use base62::base62;
2use base64;
3use digest::{Digest, Mac};
4use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
5use hmac::Hmac;
6use serde::de::DeserializeOwned;
7use serde::Serialize;
8use sha2::Sha256;
9use std::io::{Read, Write};
10pub use time::Duration; use time::OffsetDateTime;
12
13type HmacSha256 = Hmac<Sha256>;
14
15#[derive(Debug)]
16pub enum SignatureError {
17 MissingSeparator,
18 FormatError,
19 InvalidSignature,
20 MissingTimestamp,
21 TimestampFormatError,
22 SignatureExpired,
23 ObjectFormatError,
24}
25
26pub trait Signer {
27 fn sign(&self, value: String) -> String;
28
29 fn unsign(&self, signed_value: String) -> Result<String, SignatureError>;
30
31 fn sign_object<T>(&self, obj: T, compress: bool) -> String
32 where
33 T: Serialize;
34
35 fn unsign_object<T>(&self, signed_object: String) -> Result<T, SignatureError>
36 where
37 T: DeserializeOwned;
38}
39
40pub trait TimedSigner: Signer {
41 fn unsign_with_age(
42 &self,
43 signed_value: String,
44 max_age: Duration,
45 ) -> Result<String, SignatureError>;
46
47 fn unsign_object_with_age<T>(
48 &self,
49 signed_value: String,
50 max_age: Duration,
51 ) -> Result<T, SignatureError>
52 where
53 T: DeserializeOwned;
54}
55
56pub struct BaseSigner {
57 key: Vec<u8>,
58}
59
60impl BaseSigner {
61 pub fn new(key: &[u8], salt: &[u8]) -> Self {
62 let mut new_salt = Vec::with_capacity(salt.len() + 6);
64 new_salt.extend_from_slice(salt);
65 new_salt.extend(b"signer");
66
67 let mut inner_hasher = Sha256::new();
68 inner_hasher.update(&new_salt[..]);
69 inner_hasher.update(key);
70
71 Self {
72 key: inner_hasher.finalize().to_vec(),
73 }
74 }
75 fn get_mac_with_value(&self, value: &[u8]) -> HmacSha256 {
76 let mut mac = HmacSha256::new_from_slice(&self.key[..]).unwrap();
77 mac.update(value);
78 mac
79 }
80 fn encoded_signature(&self, value: &[u8]) -> String {
81 let mac = self.get_mac_with_value(value);
82 base64::encode_config(mac.finalize().into_bytes(), base64::URL_SAFE_NO_PAD)
83 }
84
85 fn decode_object<T>(&self, value: String) -> Result<T, SignatureError>
86 where
87 T: DeserializeOwned,
88 {
89 let (decompress, encoded_value) = match value.strip_prefix(".") {
90 Some(remainder) => (true, remainder.as_bytes()),
91 None => (false, value.as_bytes()),
92 };
93 let mut decoded_value =
94 base64::decode_config(encoded_value, base64::URL_SAFE_NO_PAD).unwrap();
95 if decompress {
96 let mut decoder = ZlibDecoder::new(&decoded_value[..]);
97 let mut unpacked = String::new();
98 decoder.read_to_string(&mut unpacked).unwrap();
99 decoded_value = unpacked.into();
100 }
101 match serde_json::from_slice(&decoded_value[..]) {
102 Ok(obj) => Ok(obj),
103 Err(_) => Err(SignatureError::ObjectFormatError),
104 }
105 }
106
107 fn encode_object<T>(&self, obj: T, compress: bool) -> String
108 where
109 T: Serialize,
110 {
111 let mut value = serde_json::to_vec(&obj).unwrap();
112 let mut is_compressed = false;
113 if compress {
114 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
115 encoder.write(&value[..]).unwrap();
116 let compressed = encoder.finish().unwrap();
117 if compressed.len() < value.len() - 1 {
118 value = compressed;
119 is_compressed = true;
120 }
121 }
122 let mut value = base64::encode_config(value, base64::URL_SAFE_NO_PAD);
123 if is_compressed {
124 value.insert(0, '.');
125 }
126 value
127 }
128}
129
130impl Signer for BaseSigner {
131 fn sign(&self, value: String) -> String {
132 format!("{}:{}", value, self.encoded_signature(value.as_bytes()))
133 }
134 fn unsign(&self, signed_value: String) -> Result<String, SignatureError> {
135 if let Some((value, sig)) = signed_value.rsplit_once(":") {
136 if let Ok(decoded_sig) = base64::decode_config(sig, base64::URL_SAFE_NO_PAD) {
137 let mac = self.get_mac_with_value(value.as_bytes());
138 if let Ok(_) = mac.verify_slice(&decoded_sig[..]) {
139 Ok(value.to_string())
140 } else {
141 Err(SignatureError::InvalidSignature)
142 }
143 } else {
144 Err(SignatureError::FormatError)
145 }
146 } else {
147 Err(SignatureError::MissingSeparator)
148 }
149 }
150
151 fn sign_object<T>(&self, obj: T, compress: bool) -> String
152 where
153 T: Serialize,
154 {
155 let value = self.encode_object(obj, compress);
156 self.sign(value)
157 }
158 fn unsign_object<T>(&self, signed_object: String) -> Result<T, SignatureError>
159 where
160 T: DeserializeOwned,
161 {
162 let unsigned = self.unsign(signed_object);
163 match unsigned {
164 Ok(value) => self.decode_object(value),
165 Err(e) => Err(e),
166 }
167 }
168}
169
170pub struct TimestampSigner {
171 inner: BaseSigner,
172}
173
174impl TimestampSigner {
175 pub fn new(key: &[u8], salt: &[u8]) -> Self {
176 Self {
177 inner: BaseSigner::new(key, salt),
178 }
179 }
180}
181
182impl Signer for TimestampSigner {
183 fn sign(&self, value: String) -> String {
184 let timestamp = OffsetDateTime::now_utc().unix_timestamp() as u64;
185 let value = format!("{}:{}", value, base62::encode(timestamp));
186 self.inner.sign(value)
187 }
188 fn unsign(&self, signed_value: String) -> Result<String, SignatureError> {
189 let unsigned = self.inner.unsign(signed_value);
190 match unsigned {
191 Err(e) => Err(e),
192 Ok(timestamped_value) => {
193 if let Some((value, _)) = timestamped_value.rsplit_once(":") {
194 Ok(value.to_string())
195 } else {
196 Err(SignatureError::MissingTimestamp)
197 }
198 }
199 }
200 }
201
202 fn sign_object<T>(&self, obj: T, compress: bool) -> String
203 where
204 T: Serialize,
205 {
206 let value = self.inner.encode_object(obj, compress);
207 self.sign(value)
208 }
209
210 fn unsign_object<T>(&self, signed_object: String) -> Result<T, SignatureError>
211 where
212 T: DeserializeOwned,
213 {
214 match self.unsign(signed_object) {
215 Ok(value) => self.inner.decode_object(value),
216 Err(e) => Err(e),
217 }
218 }
219}
220
221impl TimedSigner for TimestampSigner {
222 fn unsign_with_age(
223 &self,
224 signed_value: String,
225 max_age: Duration,
226 ) -> Result<String, SignatureError> {
227 let unsigned = self.inner.unsign(signed_value);
228 match unsigned {
229 Err(e) => Err(e),
230 Ok(timestamped_value) => {
231 if let Some((value, timestamp)) = timestamped_value.rsplit_once(":") {
232 if let Ok(timestamp) = base62::decode(timestamp) {
233 if let Ok(timestamp) = OffsetDateTime::from_unix_timestamp(timestamp as i64)
234 {
235 let distance = OffsetDateTime::now_utc() - timestamp;
236 if distance <= max_age {
237 Ok(value.to_string())
238 } else {
239 Err(SignatureError::SignatureExpired)
240 }
241 } else {
242 Err(SignatureError::TimestampFormatError)
243 }
244 } else {
245 Err(SignatureError::TimestampFormatError)
246 }
247 } else {
248 Err(SignatureError::MissingTimestamp)
249 }
250 }
251 }
252 }
253
254 fn unsign_object_with_age<T>(
255 &self,
256 signed_value: String,
257 max_age: Duration,
258 ) -> Result<T, SignatureError>
259 where
260 T: DeserializeOwned,
261 {
262 match self.unsign_with_age(signed_value, max_age) {
263 Ok(value) => self.inner.decode_object(value),
264 Err(e) => Err(e),
265 }
266 }
267}
268
269pub fn dumps<T>(obj: T, key: &[u8], salt: &[u8], compress: bool) -> String
270where
271 T: Serialize,
272{
273 let signer = TimestampSigner::new(key, salt);
274 signer.sign_object(obj, compress)
275}
276
277pub fn loads<T>(
278 signed_value: String,
279 key: &[u8],
280 salt: &[u8],
281 max_age: Duration,
282) -> Result<T, SignatureError>
283where
284 T: DeserializeOwned,
285{
286 let signer = TimestampSigner::new(key, salt);
287 signer.unsign_object_with_age(signed_value, max_age)
288}