1use std::ops::Deref;
2
3use chrono::Utc;
4use serde::{Deserialize, Serialize};
5
6use crate::error::BwError;
7use crate::Generator;
8use crate::Rotation;
9
10#[derive(Serialize, Deserialize)]
11pub struct Expiring<K> {
12 init_exp: i64,
13 exp: i64,
14 object: K,
15}
16
17impl<K> Expiring<K> {
18 pub fn new(exp: chrono::Duration, object: K) -> Result<Self, BwError> {
19 let expiration = Utc::now()
20 .checked_add_signed(exp)
21 .ok_or(BwError::IncorrectTimestamp)?
22 .timestamp();
23 Ok(Self {
24 init_exp: exp.num_milliseconds(),
25 exp: expiration,
26 object,
27 })
28 }
29
30 #[inline]
31 pub fn has_expired(&self) -> bool {
32 Utc::now().timestamp() > self.exp
33 }
34}
35
36impl<K> Deref for Expiring<K> {
37 type Target = K;
38
39 fn deref(&self) -> &Self::Target {
40 &self.object
41 }
42}
43
44impl<K: Clone> Clone for Expiring<K> {
45 fn clone(&self) -> Self {
46 Self {
47 init_exp: self.init_exp,
48 exp: self.exp,
49 object: self.object.clone(),
50 }
51 }
52}
53
54#[derive(Serialize, Deserialize)]
55pub struct AutoExpiring<K: Generator> {
56 expiring: Expiring<K>,
57}
58
59impl<K: Generator> Deref for AutoExpiring<K> {
60 type Target = K;
61
62 fn deref(&self) -> &Self::Target {
63 &self.expiring.object
64 }
65}
66
67impl<K: Clone + Generator> Clone for AutoExpiring<K> {
68 fn clone(&self) -> Self {
69 Self {
70 expiring: self.expiring.clone(),
71 }
72 }
73}
74
75impl<K: Generator> AutoExpiring<K> {
76 #[inline]
77 pub fn generate(exp: chrono::Duration) -> Result<Self, BwError> {
78 Self::new(exp, K::generate()?)
79 }
80
81 #[inline]
82 pub fn new(exp: chrono::Duration, object: K) -> Result<Self, BwError> {
83 Ok(Self {
84 expiring: Expiring::new(exp, object)?,
85 })
86 }
87
88 #[inline]
89 pub fn is_expired(&self) -> bool {
90 self.expiring.has_expired()
91 }
92}
93
94impl<K: Generator> Rotation for AutoExpiring<K> {
95 #[inline]
96 fn rotate(&mut self) -> Result<(), BwError> {
97 self.expiring.object = K::generate()?;
98 let duration = chrono::Duration::try_milliseconds(self.expiring.init_exp)
99 .ok_or(BwError::IncorrectTimestamp)?;
100 self.expiring.exp = Utc::now()
101 .checked_add_signed(duration)
102 .ok_or(BwError::IncorrectTimestamp)?
103 .timestamp();
104 Ok(())
105 }
106}
107
108impl<K: PartialEq> PartialEq for Expiring<K> {
109 fn eq(&self, other: &Self) -> bool {
110 self.exp == other.exp && self.init_exp == other.init_exp && self.object.eq(&other.object)
111 }
112}
113
114impl<K: PartialEq + Generator> PartialEq for AutoExpiring<K> {
115 fn eq(&self, other: &Self) -> bool {
116 self.expiring.exp == other.expiring.exp
117 && self.expiring.init_exp == other.expiring.init_exp
118 && self.expiring.object.eq(&other.expiring.object)
119 }
120}
121
122#[cfg(test)]
125mod tests {
126 use crate::exp::{AutoExpiring, Expiring};
127 use crate::keys::ed::EdDsaKey;
128 use crate::keys::{BwSigner, BwVerifier};
129 use crate::{Generator, Rotation};
130
131 #[test]
132 #[cfg_attr(miri, ignore)]
133 fn test_generate() {
134 let key = AutoExpiring::<EdDsaKey>::generate(chrono::Duration::try_seconds(60).unwrap());
135 assert!(key.is_ok());
136 }
137 #[test]
138 #[cfg_attr(miri, ignore)]
139 fn test_update_key_verify_failed() {
140 let mut key =
141 AutoExpiring::<EdDsaKey>::generate(chrono::Duration::try_seconds(60).unwrap()).unwrap();
142 let message = b"Hello world!";
143 let signature_bytes = key.sign(&message[..]).unwrap();
144
145 assert!(key.rotate().is_ok(), "failed to roll key");
146 let result = key.verify(message.as_slice(), &signature_bytes);
147 assert!(result.is_err(), "Failed to regenerate properly");
148 }
149
150 #[test]
151 #[cfg_attr(miri, ignore)]
152 fn test_sign_with_same_signature() {
153 let key =
154 AutoExpiring::<EdDsaKey>::generate(chrono::Duration::try_seconds(60).unwrap()).unwrap();
155 let message = b"Hello world!";
156 let signature_bytes_1 = key.sign(&message[..]).unwrap();
157 let signature_bytes_2 = key.sign(&message[..]).unwrap();
158 assert_eq!(
159 signature_bytes_1, signature_bytes_2,
160 "Failed to sign properly"
161 );
162 }
163
164 #[test]
165 #[cfg_attr(miri, ignore)]
166 fn test_update_key_sign_with_different_signature() {
167 let mut key =
168 AutoExpiring::<EdDsaKey>::generate(chrono::Duration::try_seconds(60).unwrap()).unwrap();
169 let message = b"Hello world!";
170 let signature_bytes_1 = key.sign(&message[..]).unwrap();
171 key.rotate().expect("Must roll correctly");
172
173 let signature_bytes_2 = key.sign(&message[..]).unwrap();
174 assert_ne!(
175 signature_bytes_1, signature_bytes_2,
176 "Failed to regenerate properly"
177 );
178 }
179
180 #[test]
181 #[cfg_attr(miri, ignore)]
182 fn test_expired_expiring() {
183 let key = Expiring::<EdDsaKey>::new(
184 chrono::Duration::try_seconds(-60).unwrap(),
185 EdDsaKey::generate().unwrap(),
186 )
187 .unwrap();
188 assert!(key.has_expired(), "AutoExpiring must be expired");
189 }
190
191 #[test]
192 #[cfg_attr(miri, ignore)]
193 fn test_expired_auth_expiring() {
194 let key = AutoExpiring::<EdDsaKey>::generate(chrono::Duration::try_seconds(-60).unwrap())
195 .unwrap();
196 assert!(key.is_expired(), "AutoExpiring must be expired");
197 }
198
199 #[test]
200 #[cfg_attr(miri, ignore)]
201 fn test_serialize_expiring_secure_key() {
202 let secret_key =
203 AutoExpiring::<EdDsaKey>::generate(chrono::Duration::try_seconds(60).unwrap()).unwrap();
204
205 let message = b"Hello world!";
206 let signature_bytes = secret_key.sign(&message[..]).unwrap();
207 let result = secret_key.verify(message.as_slice(), &signature_bytes);
208 assert!(result.is_ok(), "Failed to verify signature");
209
210 let secret_key_bytes = bincode::serialize(&secret_key).unwrap();
211 let new_secret_key =
212 bincode::deserialize::<AutoExpiring<EdDsaKey>>(&secret_key_bytes).unwrap();
213 assert!(!new_secret_key.is_expired(), "Key must be not expired");
214
215 let result = new_secret_key.verify(message.as_slice(), &signature_bytes);
216 assert!(result.is_ok(), "Failed to verify signature");
217 }
218}