1 //! X.501 time types as defined in RFC 5280
2 
3 use core::fmt;
4 use core::time::Duration;
5 use der::asn1::{GeneralizedTime, UtcTime};
6 use der::{Choice, DateTime, Sequence, ValueOrd};
7 
8 #[cfg(feature = "std")]
9 use 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)]
26 pub 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 
39 impl 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`.
to_unix_duration(self) -> Duration45     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
to_date_time(&self) -> DateTime53     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")]
to_system_time(&self) -> SystemTime62     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")]
rfc5280_adjust_utc_time(&mut self) -> der::Result<()>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 
84 impl fmt::Display for Time {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result85     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86         write!(f, "{}", self.to_date_time())
87     }
88 }
89 
90 impl From<UtcTime> for Time {
from(time: UtcTime) -> Time91     fn from(time: UtcTime) -> Time {
92         Time::UtcTime(time)
93     }
94 }
95 
96 impl From<GeneralizedTime> for Time {
from(time: GeneralizedTime) -> Time97     fn from(time: GeneralizedTime) -> Time {
98         Time::GeneralTime(time)
99     }
100 }
101 
102 #[cfg(feature = "std")]
103 impl From<Time> for SystemTime {
from(time: Time) -> SystemTime104     fn from(time: Time) -> SystemTime {
105         time.to_system_time()
106     }
107 }
108 
109 #[cfg(feature = "std")]
110 impl From<&Time> for SystemTime {
from(time: &Time) -> SystemTime111     fn from(time: &Time) -> SystemTime {
112         time.to_system_time()
113     }
114 }
115 
116 #[cfg(feature = "std")]
117 impl TryFrom<SystemTime> for Time {
118     type Error = der::Error;
119 
try_from(time: SystemTime) -> der::Result<Time>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)]
136 pub struct Validity {
137     /// notBefore value
138     pub not_before: Time,
139 
140     /// notAfter value
141     pub not_after: Time,
142 }
143 
144 impl Validity {
145     /// Creates a `Validity` which starts now and lasts for `duration`.
146     #[cfg(feature = "std")]
from_now(duration: Duration) -> der::Result<Self>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 }
157