x509_cert/
time.rs

1//! X.501 time types as defined in RFC 5280
2
3use core::fmt;
4use core::time::Duration;
5use der::asn1::{GeneralizedTime, UtcTime};
6use der::{Choice, DateTime, Sequence, ValueOrd};
7
8#[cfg(feature = "std")]
9use std::time::SystemTime;
10
11/// X.501 `Time` as defined in [RFC 5280 Section 4.1.2.5].
12///
13/// Schema definition from [RFC 5280 Appendix A]:
14///
15/// ```text
16/// Time ::= CHOICE {
17///      utcTime        UTCTime,
18///      generalTime    GeneralizedTime
19/// }
20/// ```
21///
22/// [RFC 5280 Section 4.1.2.5]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5
23/// [RFC 5280 Appendix A]: https://tools.ietf.org/html/rfc5280#page-117
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[derive(Choice, Copy, Clone, Debug, Eq, PartialEq, ValueOrd)]
26pub enum Time {
27    /// Legacy UTC time (has 2-digit year, valid from 1970 to 2049).
28    ///
29    /// Note: RFC 5280 specifies 1950-2049, however due to common operations working on
30    /// `UNIX_EPOCH` this implementation's lower bound is 1970.
31    #[asn1(type = "UTCTime")]
32    UtcTime(UtcTime),
33
34    /// Modern [`GeneralizedTime`] encoding with 4-digit year.
35    #[asn1(type = "GeneralizedTime")]
36    GeneralTime(GeneralizedTime),
37}
38
39impl Time {
40    /// Time used for Certificate who do not expire.
41    pub const INFINITY: Time =
42        Time::GeneralTime(GeneralizedTime::from_date_time(DateTime::INFINITY));
43
44    /// Get duration since `UNIX_EPOCH`.
45    pub fn to_unix_duration(self) -> Duration {
46        match self {
47            Time::UtcTime(t) => t.to_unix_duration(),
48            Time::GeneralTime(t) => t.to_unix_duration(),
49        }
50    }
51
52    /// Get Time as DateTime
53    pub fn to_date_time(&self) -> DateTime {
54        match self {
55            Time::UtcTime(t) => t.to_date_time(),
56            Time::GeneralTime(t) => t.to_date_time(),
57        }
58    }
59
60    /// Convert to [`SystemTime`].
61    #[cfg(feature = "std")]
62    pub fn to_system_time(&self) -> SystemTime {
63        match self {
64            Time::UtcTime(t) => t.to_system_time(),
65            Time::GeneralTime(t) => t.to_system_time(),
66        }
67    }
68
69    /// Convert time to UTCTime representation
70    /// As per RFC 5280: 4.1.2.5, date through 2049 should be expressed as UTC Time.
71    #[cfg(feature = "builder")]
72    pub(crate) fn rfc5280_adjust_utc_time(&mut self) -> der::Result<()> {
73        if let Time::GeneralTime(t) = self {
74            let date = t.to_date_time();
75            if date.year() <= UtcTime::MAX_YEAR {
76                *self = Time::UtcTime(UtcTime::from_date_time(date)?);
77            }
78        }
79
80        Ok(())
81    }
82}
83
84impl fmt::Display for Time {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        write!(f, "{}", self.to_date_time())
87    }
88}
89
90impl From<UtcTime> for Time {
91    fn from(time: UtcTime) -> Time {
92        Time::UtcTime(time)
93    }
94}
95
96impl From<GeneralizedTime> for Time {
97    fn from(time: GeneralizedTime) -> Time {
98        Time::GeneralTime(time)
99    }
100}
101
102#[cfg(feature = "std")]
103impl From<Time> for SystemTime {
104    fn from(time: Time) -> SystemTime {
105        time.to_system_time()
106    }
107}
108
109#[cfg(feature = "std")]
110impl From<&Time> for SystemTime {
111    fn from(time: &Time) -> SystemTime {
112        time.to_system_time()
113    }
114}
115
116#[cfg(feature = "std")]
117impl TryFrom<SystemTime> for Time {
118    type Error = der::Error;
119
120    fn try_from(time: SystemTime) -> der::Result<Time> {
121        Ok(GeneralizedTime::try_from(time)?.into())
122    }
123}
124
125/// X.501 `Validity` as defined in [RFC 5280 Section 4.1.2.5]
126///
127/// ```text
128/// Validity ::= SEQUENCE {
129///     notBefore      Time,
130///     notAfter       Time
131/// }
132/// ```
133/// [RFC 5280 Section 4.1.2.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5
134#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
135#[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
136pub struct Validity {
137    /// notBefore value
138    pub not_before: Time,
139
140    /// notAfter value
141    pub not_after: Time,
142}
143
144impl Validity {
145    /// Creates a `Validity` which starts now and lasts for `duration`.
146    #[cfg(feature = "std")]
147    pub fn from_now(duration: Duration) -> der::Result<Self> {
148        let now = SystemTime::now();
149        let then = now + duration;
150
151        Ok(Self {
152            not_before: Time::try_from(now)?,
153            not_after: Time::try_from(then)?,
154        })
155    }
156}