1use core::{fmt, marker::PhantomData, str::FromStr, time::Duration};
4use der::asn1::{GeneralizedTime, UtcTime};
5use der::{Choice, DateTime, DecodeValue, Encode, Header, Length, Reader, Sequence, ValueOrd};
6
7#[cfg(feature = "std")]
8use std::time::SystemTime;
9
10use crate::certificate::{Profile, Rfc5280};
11
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[derive(Choice, Copy, Clone, Debug, Eq, PartialEq, ValueOrd)]
27pub enum Time {
28 #[asn1(type = "UTCTime")]
33 UtcTime(UtcTime),
34
35 #[asn1(type = "GeneralizedTime")]
37 GeneralTime(GeneralizedTime),
38}
39
40impl Time {
41 pub const INFINITY: Time =
43 Time::GeneralTime(GeneralizedTime::from_date_time(DateTime::INFINITY));
44
45 pub fn to_unix_duration(self) -> Duration {
47 match self {
48 Time::UtcTime(t) => t.to_unix_duration(),
49 Time::GeneralTime(t) => t.to_unix_duration(),
50 }
51 }
52
53 pub fn to_date_time(&self) -> DateTime {
55 match self {
56 Time::UtcTime(t) => t.to_date_time(),
57 Time::GeneralTime(t) => t.to_date_time(),
58 }
59 }
60
61 #[cfg(feature = "std")]
63 pub fn to_system_time(&self) -> SystemTime {
64 match self {
65 Time::UtcTime(t) => t.to_system_time(),
66 Time::GeneralTime(t) => t.to_system_time(),
67 }
68 }
69
70 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 #[cfg(feature = "std")]
85 pub fn now() -> der::Result<Self> {
86 SystemTime::now().try_into()
87 }
88}
89
90impl fmt::Display for Time {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(f, "{}", self.to_date_time())
93 }
94}
95
96impl From<UtcTime> for Time {
97 fn from(time: UtcTime) -> Time {
98 Time::UtcTime(time)
99 }
100}
101
102impl From<GeneralizedTime> for Time {
103 fn from(time: GeneralizedTime) -> Time {
104 Time::GeneralTime(time)
105 }
106}
107
108impl From<DateTime> for Time {
109 fn from(time: DateTime) -> Time {
110 UtcTime::from_date_time(time)
111 .map(Self::UtcTime)
112 .unwrap_or_else(|_e| Self::GeneralTime(GeneralizedTime::from_date_time(time)))
113 }
114}
115
116impl FromStr for Time {
117 type Err = der::Error;
118
119 fn from_str(input: &str) -> der::Result<Self> {
120 let datetime = DateTime::from_str(input)?;
121
122 Ok(Self::from(datetime))
123 }
124}
125
126#[cfg(feature = "std")]
127impl From<Time> for SystemTime {
128 fn from(time: Time) -> SystemTime {
129 time.to_system_time()
130 }
131}
132
133#[cfg(feature = "std")]
134impl From<&Time> for SystemTime {
135 fn from(time: &Time) -> SystemTime {
136 time.to_system_time()
137 }
138}
139
140#[cfg(feature = "std")]
141impl TryFrom<SystemTime> for Time {
142 type Error = der::Error;
143
144 fn try_from(time: SystemTime) -> der::Result<Time> {
145 let datetime = DateTime::from_system_time(time)?;
146
147 Ok(datetime.into())
148 }
149}
150
151#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
161#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueOrd)]
162pub struct Validity<P: Profile = Rfc5280> {
163 pub not_before: Time,
165
166 pub not_after: Time,
168
169 _profile: PhantomData<P>,
170}
171
172impl<P> Validity<P>
173where
174 P: Profile,
175{
176 pub const fn new(not_before: Time, not_after: Time) -> Self {
178 Self {
179 not_before,
180 not_after,
181 _profile: PhantomData,
182 }
183 }
184
185 #[cfg(feature = "std")]
187 pub fn from_now(duration: Duration) -> der::Result<Self> {
188 let now = SystemTime::now();
189 let then = now + duration;
190
191 Ok(Self {
192 not_before: Time::try_from(now)?,
193 not_after: Time::try_from(then)?,
194 _profile: PhantomData,
195 })
196 }
197
198 #[cfg(all(feature = "std", feature = "hazmat"))]
200 pub fn infinity() -> der::Result<Self> {
201 let now = SystemTime::now();
202
203 Ok(Self {
204 not_before: Time::try_from(now)?,
205 not_after: Time::INFINITY,
206 _profile: PhantomData,
207 })
208 }
209}
210
211impl<'a, P: Profile> DecodeValue<'a> for Validity<P> {
212 type Error = der::Error;
213
214 fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
215 let not_before = reader.decode()?;
216 let not_after = reader.decode()?;
217 let out = Self {
218 not_before,
219 not_after,
220 _profile: PhantomData,
221 };
222
223 Ok(out)
224 }
225}
226
227impl<P: Profile> ::der::EncodeValue for Validity<P> {
228 fn value_len(&self) -> ::der::Result<::der::Length> {
229 [
230 P::time_encoding(self.not_before)?.encoded_len()?,
231 P::time_encoding(self.not_after)?.encoded_len()?,
232 ]
233 .into_iter()
234 .try_fold(Length::ZERO, |acc, len| acc + len)
235 }
236 fn encode_value(&self, writer: &mut impl ::der::Writer) -> ::der::Result<()> {
237 P::time_encoding(self.not_before)?.encode(writer)?;
238 P::time_encoding(self.not_after)?.encode(writer)?;
239 Ok(())
240 }
241}
242
243impl<P: Profile> Sequence<'_> for Validity<P> {}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn parse_time() {
251 let time = Time::from_str("1970-01-01T00:00:00Z").expect("parse date from string");
252 assert!(matches!(time, Time::UtcTime(_)));
253 assert_eq!(alloc::format!("{}", time), "1970-01-01T00:00:00Z");
254
255 let time = Time::from_str("2020-01-01T00:00:00Z").expect("parse date from string");
256 assert!(matches!(time, Time::UtcTime(_)));
257 assert_eq!(alloc::format!("{}", time), "2020-01-01T00:00:00Z");
258
259 let time = Time::from_str("2049-12-31T23:59:59Z").expect("parse date from string");
260 assert!(matches!(time, Time::UtcTime(_)));
261 assert_eq!(alloc::format!("{}", time), "2049-12-31T23:59:59Z");
262
263 let time = Time::from_str("2050-01-01T00:00:00Z").expect("parse date from string");
264 assert!(matches!(time, Time::GeneralTime(_)));
265 assert_eq!(alloc::format!("{}", time), "2050-01-01T00:00:00Z");
266 }
267}