trz_gateway_common/x509/
time.rs

1use std::num::TryFromIntError;
2use std::sync::OnceLock;
3use std::time::Duration;
4use std::time::SystemTime;
5use std::time::SystemTimeError;
6use std::time::UNIX_EPOCH;
7
8use nameth::NamedEnumValues as _;
9use nameth::nameth;
10use openssl::asn1::Asn1Time;
11use openssl::asn1::Asn1TimeRef;
12use openssl::error::ErrorStack;
13
14pub fn system_to_asn1_time(system: SystemTime) -> Result<Asn1Time, SystemToAsn1TimeError> {
15    let unix_seconds = system.duration_since(UNIX_EPOCH)?.as_secs();
16    Ok(Asn1Time::from_unix(unix_seconds as i64)?)
17}
18
19#[nameth]
20#[derive(thiserror::Error, Debug)]
21pub enum SystemToAsn1TimeError {
22    #[error("[{n}] Can't convert time earlier than UNIX_EPOCH: {0}", n = self.name())]
23    SystemTimeError(#[from] SystemTimeError),
24
25    #[error("[{n}] Failed to convert UNIX seconds to Asn1Time: {0}", n = self.name())]
26    Asn1TimeError(#[from] ErrorStack),
27}
28
29pub fn asn1_to_system_time(asn1: &Asn1TimeRef) -> Result<SystemTime, Asn1ToSystemTimeError> {
30    static UNIX_EPOCH_ASN1TIME: OnceLock<Asn1Time> = OnceLock::new();
31    let epoch = UNIX_EPOCH_ASN1TIME
32        .get_or_init(|| system_to_asn1_time(UNIX_EPOCH).expect("UNIX_EPOCH as Asn1Time"));
33    let diff = epoch.diff(asn1)?;
34
35    let days = diff.days.try_into()?;
36    let secs = diff.secs.try_into()?;
37
38    const SECOND: Duration = Duration::from_secs(1);
39    Ok(SystemTime::UNIX_EPOCH + SECOND * 3600 * 24 * days + SECOND * secs)
40}
41
42#[nameth]
43#[derive(thiserror::Error, Debug)]
44pub enum Asn1ToSystemTimeError {
45    #[error("[{n}] Can't convert time earlier than UNIX_EPOCH: {0}", n = self.name())]
46    SystemTimeError(#[from] TryFromIntError),
47
48    #[error("[{n}] Failed to convert Asn1Time to UNIX seconds: {0}", n = self.name())]
49    Asn1TimeError(#[from] ErrorStack),
50}
51
52#[cfg(test)]
53mod tests {
54    use std::time::Duration;
55    use std::time::SystemTime;
56    use std::time::UNIX_EPOCH;
57
58    use super::asn1_to_system_time;
59    use super::system_to_asn1_time;
60
61    #[test]
62    fn convert() {
63        let system = SystemTime::now();
64        let system = system
65            - Duration::from_nanos(system.duration_since(UNIX_EPOCH).unwrap().subsec_nanos() as u64);
66        let asn1 = system_to_asn1_time(system).unwrap();
67        let system2 = asn1_to_system_time(&asn1).unwrap();
68        assert_eq!(system, system2);
69    }
70
71    #[test]
72    fn fifty_years() {
73        let fifty = SystemTime::now() + Duration::from_secs(1) * 3600 * 24 * 365 * 50;
74        let fifty = fifty
75            - Duration::from_nanos(fifty.duration_since(UNIX_EPOCH).unwrap().subsec_nanos() as u64);
76        let asn1 = system_to_asn1_time(fifty).unwrap();
77        let system = asn1_to_system_time(&asn1).unwrap();
78        assert_eq!(fifty, system);
79    }
80}