openmls/key_packages/
lifetime.rs

1#[cfg(target_arch = "wasm32")]
2use fluvio_wasm_timer::{SystemTime, UNIX_EPOCH};
3#[cfg(not(target_arch = "wasm32"))]
4use std::time::{SystemTime, UNIX_EPOCH};
5
6use serde::{Deserialize, Serialize};
7use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize};
8
9/// This value is used as the default lifetime if no default  lifetime is configured.
10/// The value is in seconds and amounts to 3 * 28 Days, i.e. about 3 months.
11const DEFAULT_KEY_PACKAGE_LIFETIME_SECONDS: u64 = 60 * 60 * 24 * 28 * 3;
12
13/// This value is used as the default amount of time (in seconds) the lifetime
14/// of a `KeyPackage` is extended into the past to allow for skewed clocks. The
15/// value is in seconds and amounts to 1h.
16const DEFAULT_KEY_PACKAGE_LIFETIME_MARGIN_SECONDS: u64 = 60 * 60;
17
18/// The maximum total lifetime range that is acceptable for a leaf node.
19/// The value is in seconds and amounts to 3 * 28 Days, i.e., about 3 months.
20const MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS: u64 =
21    DEFAULT_KEY_PACKAGE_LIFETIME_MARGIN_SECONDS + DEFAULT_KEY_PACKAGE_LIFETIME_SECONDS;
22
23/// The lifetime represents the times between which clients will
24/// consider a KeyPackage valid. This time is represented as an absolute time,
25/// measured in seconds since the Unix epoch (1970-01-01T00:00:00Z).
26/// A client MUST NOT use the data in a KeyPackage for any processing before
27/// the not_before date, or after the not_after date.
28///
29/// Applications MUST define a maximum total lifetime that is acceptable for a
30/// KeyPackage, and reject any KeyPackage where the total lifetime is longer
31/// than this duration.This extension MUST always be present in a KeyPackage.
32///
33/// ```c
34/// // draft-ietf-mls-protocol-16
35/// struct {
36///     uint64 not_before;
37///     uint64 not_after;
38/// } Lifetime;
39/// ```
40#[derive(
41    PartialEq,
42    Eq,
43    Copy,
44    Clone,
45    Debug,
46    TlsSerialize,
47    TlsSize,
48    TlsDeserialize,
49    TlsDeserializeBytes,
50    Serialize,
51    Deserialize,
52)]
53pub struct Lifetime {
54    not_before: u64,
55    not_after: u64,
56}
57
58impl Lifetime {
59    /// Create a new lifetime with lifetime `t` (in seconds).
60    /// Note that the lifetime is extended 1h into the past to adapt to skewed
61    /// clocks, i.e. `not_before` is set to now - 1h.
62    pub fn new(t: u64) -> Self {
63        let lifetime_margin: u64 = DEFAULT_KEY_PACKAGE_LIFETIME_MARGIN_SECONDS;
64        let now = SystemTime::now()
65            .duration_since(UNIX_EPOCH)
66            .expect("SystemTime before UNIX EPOCH!")
67            .as_secs();
68        let not_before = now - lifetime_margin;
69        let not_after = now + t;
70        Self {
71            not_before,
72            not_after,
73        }
74    }
75
76    /// Returns true if this lifetime is valid.
77    pub fn is_valid(&self) -> bool {
78        match SystemTime::now()
79            .duration_since(UNIX_EPOCH)
80            .map(|duration| duration.as_secs())
81        {
82            Ok(elapsed) => self.not_before < elapsed && elapsed < self.not_after,
83            Err(_) => {
84                log::error!("SystemTime before UNIX EPOCH.");
85                false
86            }
87        }
88    }
89
90    /// ValSem(openmls/annotations#32):
91    /// Applications MUST define a maximum total lifetime that is acceptable for a LeafNode,
92    /// and reject any LeafNode where the total lifetime is longer than this duration.
93    pub fn has_acceptable_range(&self) -> bool {
94        self.not_after.saturating_sub(self.not_before) <= MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS
95    }
96
97    /// Returns the "not before" timestamp of the KeyPackage.
98    pub fn not_before(&self) -> u64 {
99        self.not_before
100    }
101
102    /// Returns the "not after" timestamp of the KeyPackage.
103    pub fn not_after(&self) -> u64 {
104        self.not_after
105    }
106}
107
108impl Default for Lifetime {
109    fn default() -> Self {
110        Lifetime::new(DEFAULT_KEY_PACKAGE_LIFETIME_SECONDS)
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use tls_codec::{Deserialize, Serialize};
117
118    use super::Lifetime;
119
120    #[test]
121    fn lifetime() {
122        // A freshly created extensions must be valid.
123        let ext = Lifetime::default();
124        assert!(ext.is_valid());
125
126        // An extension without lifetime is invalid (waiting for 1 second).
127        let ext = Lifetime::new(0);
128        std::thread::sleep(std::time::Duration::from_secs(1));
129        assert!(!ext.is_valid());
130
131        // Test (de)serializing invalid extension
132        let serialized = ext
133            .tls_serialize_detached()
134            .expect("error encoding life time extension");
135        let ext_deserialized = Lifetime::tls_deserialize(&mut serialized.as_slice())
136            .expect("Error deserializing lifetime");
137        assert!(!ext_deserialized.is_valid());
138    }
139}