bitwark/
exp.rs

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// Tests --------------------------------------------------------------------------------------
123
124#[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}