openmls/key_packages/
lifetime.rs1#[cfg(not(target_arch = "wasm32"))]
2use std::time::{SystemTime, UNIX_EPOCH};
3#[cfg(target_arch = "wasm32")]
4use web_time::{SystemTime, UNIX_EPOCH};
5
6use serde::{Deserialize, Serialize};
7use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize};
8
9use crate::{group::LeafNodeLifetimePolicy, treesync::errors::LifetimeError};
10
11const DEFAULT_KEY_PACKAGE_LIFETIME_SECONDS: u64 = 60 * 60 * 24 * 28 * 3;
14
15const DEFAULT_KEY_PACKAGE_LIFETIME_MARGIN_SECONDS: u64 = 60 * 60;
19
20const MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS: u64 =
23 DEFAULT_KEY_PACKAGE_LIFETIME_MARGIN_SECONDS + DEFAULT_KEY_PACKAGE_LIFETIME_SECONDS;
24
25#[derive(
43 PartialEq,
44 Eq,
45 Copy,
46 Clone,
47 Debug,
48 TlsSerialize,
49 TlsSize,
50 TlsDeserialize,
51 TlsDeserializeBytes,
52 Serialize,
53 Deserialize,
54)]
55pub struct Lifetime {
56 not_before: u64,
57 not_after: u64,
58}
59
60impl Lifetime {
61 pub fn new(t: u64) -> Self {
65 let lifetime_margin: u64 = DEFAULT_KEY_PACKAGE_LIFETIME_MARGIN_SECONDS;
66 let now = SystemTime::now()
67 .duration_since(UNIX_EPOCH)
68 .expect("SystemTime before UNIX EPOCH!")
69 .as_secs();
70 let not_before = now - lifetime_margin;
71 let not_after = now + t;
72 Self {
73 not_before,
74 not_after,
75 }
76 }
77
78 pub fn init(not_before: u64, not_after: u64) -> Self {
80 Self {
81 not_before,
82 not_after,
83 }
84 }
85
86 pub fn validate(&self) -> Result<(), LifetimeError> {
88 self.validate_with_time(SystemTime::now())
89 }
90
91 pub(crate) fn validate_with_policy(
94 &self,
95 policy: LeafNodeLifetimePolicy,
96 ) -> Result<(), LifetimeError> {
97 match policy {
98 LeafNodeLifetimePolicy::Verify => self.validate(),
99 LeafNodeLifetimePolicy::Skip => Ok(()),
100 LeafNodeLifetimePolicy::VerifyAt(unix_seconds) => self.validate_at(unix_seconds),
101 }
102 }
103
104 pub fn validate_with_time(&self, now: SystemTime) -> Result<(), LifetimeError> {
107 let duration_since_unix_epoch = now
108 .duration_since(UNIX_EPOCH)
109 .map_err(|_| LifetimeError::SystemTimeBeforeUnixEpoch)?
110 .as_secs();
111 self.validate_at(duration_since_unix_epoch)
112 }
113
114 fn validate_at(&self, now: u64) -> Result<(), LifetimeError> {
117 if self.not_after <= now {
118 Err(LifetimeError::Expired {
119 not_after: self.not_after,
120 now,
121 })
122 } else if self.not_before > now {
123 Err(LifetimeError::NotValidYet {
124 not_before: self.not_before,
125 now,
126 })
127 } else {
128 Ok(())
129 }
130 }
131
132 pub fn has_acceptable_range(&self) -> bool {
136 self.not_after.saturating_sub(self.not_before) <= MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS
137 }
138
139 pub fn not_before(&self) -> u64 {
141 self.not_before
142 }
143
144 pub fn not_after(&self) -> u64 {
146 self.not_after
147 }
148}
149
150impl Default for Lifetime {
151 fn default() -> Self {
152 Lifetime::new(DEFAULT_KEY_PACKAGE_LIFETIME_SECONDS)
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use core::time::Duration;
159 #[cfg(not(target_arch = "wasm32"))]
160 use std::time::SystemTime;
161 #[cfg(target_arch = "wasm32")]
162 use web_time::SystemTime;
163
164 use tls_codec::{Deserialize, Serialize};
165
166 use super::Lifetime;
167
168 #[test]
169 fn lifetime() {
170 let ext = Lifetime::default();
172 ext.validate().expect("Default Lifetime should be valid");
173
174 let ext = Lifetime::new(0);
176 let now_plus_1s = SystemTime::now() + Duration::from_secs(1);
177 let e = ext
178 .validate_with_time(now_plus_1s)
179 .expect_err("Lifetime should be expired");
180 assert!(matches!(e, super::LifetimeError::Expired { .. }));
181
182 let five_hours_before_now = SystemTime::now() - Duration::from_hours(5);
183 let e = ext
184 .validate_with_time(five_hours_before_now)
185 .expect_err("Lifetime should not be valid yet");
186 assert!(matches!(e, super::LifetimeError::NotValidYet { .. }));
187
188 let serialized = ext
190 .tls_serialize_detached()
191 .expect("error encoding life time extension");
192 let ext_deserialized = Lifetime::tls_deserialize(&mut serialized.as_slice())
193 .expect("Error deserializing lifetime");
194 ext_deserialized
195 .validate_with_time(now_plus_1s)
196 .expect_err("Lifetime should be expired");
197 }
198}