1 //! ASN.1 `GeneralizedTime` support.
2 #![cfg_attr(feature = "arbitrary", allow(clippy::integer_arithmetic))]
3 
4 use crate::{
5     datetime::{self, DateTime},
6     ord::OrdIsValueOrd,
7     DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, Writer,
8 };
9 use core::time::Duration;
10 
11 #[cfg(feature = "std")]
12 use {
13     crate::{asn1::AnyRef, Error},
14     std::time::SystemTime,
15 };
16 
17 #[cfg(feature = "time")]
18 use time::PrimitiveDateTime;
19 
20 /// ASN.1 `GeneralizedTime` type.
21 ///
22 /// This type implements the validity requirements specified in
23 /// [RFC 5280 Section 4.1.2.5.2][1], namely:
24 ///
25 /// > For the purposes of this profile, GeneralizedTime values MUST be
26 /// > expressed in Greenwich Mean Time (Zulu) and MUST include seconds
27 /// > (i.e., times are `YYYYMMDDHHMMSSZ`), even where the number of seconds
28 /// > is zero.  GeneralizedTime values MUST NOT include fractional seconds.
29 ///
30 /// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
31 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
32 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
33 pub struct GeneralizedTime(DateTime);
34 
35 impl GeneralizedTime {
36     /// Length of an RFC 5280-flavored ASN.1 DER-encoded [`GeneralizedTime`].
37     const LENGTH: usize = 15;
38 
39     /// Create a [`GeneralizedTime`] from a [`DateTime`].
from_date_time(datetime: DateTime) -> Self40     pub const fn from_date_time(datetime: DateTime) -> Self {
41         Self(datetime)
42     }
43 
44     /// Convert this [`GeneralizedTime`] into a [`DateTime`].
to_date_time(&self) -> DateTime45     pub fn to_date_time(&self) -> DateTime {
46         self.0
47     }
48 
49     /// Create a new [`GeneralizedTime`] given a [`Duration`] since `UNIX_EPOCH`
50     /// (a.k.a. "Unix time")
from_unix_duration(unix_duration: Duration) -> Result<Self>51     pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> {
52         DateTime::from_unix_duration(unix_duration)
53             .map(Into::into)
54             .map_err(|_| Self::TAG.value_error())
55     }
56 
57     /// Get the duration of this timestamp since `UNIX_EPOCH`.
to_unix_duration(&self) -> Duration58     pub fn to_unix_duration(&self) -> Duration {
59         self.0.unix_duration()
60     }
61 
62     /// Instantiate from [`SystemTime`].
63     #[cfg(feature = "std")]
from_system_time(time: SystemTime) -> Result<Self>64     pub fn from_system_time(time: SystemTime) -> Result<Self> {
65         DateTime::try_from(time)
66             .map(Into::into)
67             .map_err(|_| Self::TAG.value_error())
68     }
69 
70     /// Convert to [`SystemTime`].
71     #[cfg(feature = "std")]
to_system_time(&self) -> SystemTime72     pub fn to_system_time(&self) -> SystemTime {
73         self.0.to_system_time()
74     }
75 }
76 
77 impl_any_conversions!(GeneralizedTime);
78 
79 impl<'a> DecodeValue<'a> for GeneralizedTime {
decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>80     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
81         if Self::LENGTH != usize::try_from(header.length)? {
82             return Err(Self::TAG.value_error());
83         }
84 
85         let mut bytes = [0u8; Self::LENGTH];
86         reader.read_into(&mut bytes)?;
87 
88         match bytes {
89             // RFC 5280 requires mandatory seconds and Z-normalized time zone
90             [y1, y2, y3, y4, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
91                 let year = u16::from(datetime::decode_decimal(Self::TAG, y1, y2)?)
92                     .checked_mul(100)
93                     .and_then(|y| {
94                         y.checked_add(datetime::decode_decimal(Self::TAG, y3, y4).ok()?.into())
95                     })
96                     .ok_or(ErrorKind::DateTime)?;
97                 let month = datetime::decode_decimal(Self::TAG, mon1, mon2)?;
98                 let day = datetime::decode_decimal(Self::TAG, day1, day2)?;
99                 let hour = datetime::decode_decimal(Self::TAG, hour1, hour2)?;
100                 let minute = datetime::decode_decimal(Self::TAG, min1, min2)?;
101                 let second = datetime::decode_decimal(Self::TAG, sec1, sec2)?;
102 
103                 DateTime::new(year, month, day, hour, minute, second)
104                     .map_err(|_| Self::TAG.value_error())
105                     .and_then(|dt| Self::from_unix_duration(dt.unix_duration()))
106             }
107             _ => Err(Self::TAG.value_error()),
108         }
109     }
110 }
111 
112 impl EncodeValue for GeneralizedTime {
value_len(&self) -> Result<Length>113     fn value_len(&self) -> Result<Length> {
114         Self::LENGTH.try_into()
115     }
116 
encode_value(&self, writer: &mut impl Writer) -> Result<()>117     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
118         let year_hi = u8::try_from(self.0.year() / 100)?;
119         let year_lo = u8::try_from(self.0.year() % 100)?;
120 
121         datetime::encode_decimal(writer, Self::TAG, year_hi)?;
122         datetime::encode_decimal(writer, Self::TAG, year_lo)?;
123         datetime::encode_decimal(writer, Self::TAG, self.0.month())?;
124         datetime::encode_decimal(writer, Self::TAG, self.0.day())?;
125         datetime::encode_decimal(writer, Self::TAG, self.0.hour())?;
126         datetime::encode_decimal(writer, Self::TAG, self.0.minutes())?;
127         datetime::encode_decimal(writer, Self::TAG, self.0.seconds())?;
128         writer.write_byte(b'Z')
129     }
130 }
131 
132 impl FixedTag for GeneralizedTime {
133     const TAG: Tag = Tag::GeneralizedTime;
134 }
135 
136 impl OrdIsValueOrd for GeneralizedTime {}
137 
138 impl From<&GeneralizedTime> for GeneralizedTime {
from(value: &GeneralizedTime) -> GeneralizedTime139     fn from(value: &GeneralizedTime) -> GeneralizedTime {
140         *value
141     }
142 }
143 
144 impl From<GeneralizedTime> for DateTime {
from(utc_time: GeneralizedTime) -> DateTime145     fn from(utc_time: GeneralizedTime) -> DateTime {
146         utc_time.0
147     }
148 }
149 
150 impl From<&GeneralizedTime> for DateTime {
from(utc_time: &GeneralizedTime) -> DateTime151     fn from(utc_time: &GeneralizedTime) -> DateTime {
152         utc_time.0
153     }
154 }
155 
156 impl From<DateTime> for GeneralizedTime {
from(datetime: DateTime) -> Self157     fn from(datetime: DateTime) -> Self {
158         Self::from_date_time(datetime)
159     }
160 }
161 
162 impl From<&DateTime> for GeneralizedTime {
from(datetime: &DateTime) -> Self163     fn from(datetime: &DateTime) -> Self {
164         Self::from_date_time(*datetime)
165     }
166 }
167 
168 impl<'a> DecodeValue<'a> for DateTime {
decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>169     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
170         Ok(GeneralizedTime::decode_value(reader, header)?.into())
171     }
172 }
173 
174 impl EncodeValue for DateTime {
value_len(&self) -> Result<Length>175     fn value_len(&self) -> Result<Length> {
176         GeneralizedTime::from(self).value_len()
177     }
178 
encode_value(&self, writer: &mut impl Writer) -> Result<()>179     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
180         GeneralizedTime::from(self).encode_value(writer)
181     }
182 }
183 
184 impl FixedTag for DateTime {
185     const TAG: Tag = Tag::GeneralizedTime;
186 }
187 
188 impl OrdIsValueOrd for DateTime {}
189 
190 #[cfg(feature = "std")]
191 impl<'a> DecodeValue<'a> for SystemTime {
decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>192     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
193         Ok(GeneralizedTime::decode_value(reader, header)?.into())
194     }
195 }
196 
197 #[cfg(feature = "std")]
198 impl EncodeValue for SystemTime {
value_len(&self) -> Result<Length>199     fn value_len(&self) -> Result<Length> {
200         GeneralizedTime::try_from(self)?.value_len()
201     }
202 
encode_value(&self, writer: &mut impl Writer) -> Result<()>203     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
204         GeneralizedTime::try_from(self)?.encode_value(writer)
205     }
206 }
207 
208 #[cfg(feature = "std")]
209 impl From<GeneralizedTime> for SystemTime {
from(time: GeneralizedTime) -> SystemTime210     fn from(time: GeneralizedTime) -> SystemTime {
211         time.to_system_time()
212     }
213 }
214 
215 #[cfg(feature = "std")]
216 impl From<&GeneralizedTime> for SystemTime {
from(time: &GeneralizedTime) -> SystemTime217     fn from(time: &GeneralizedTime) -> SystemTime {
218         time.to_system_time()
219     }
220 }
221 
222 #[cfg(feature = "std")]
223 impl TryFrom<SystemTime> for GeneralizedTime {
224     type Error = Error;
225 
try_from(time: SystemTime) -> Result<GeneralizedTime>226     fn try_from(time: SystemTime) -> Result<GeneralizedTime> {
227         GeneralizedTime::from_system_time(time)
228     }
229 }
230 
231 #[cfg(feature = "std")]
232 impl TryFrom<&SystemTime> for GeneralizedTime {
233     type Error = Error;
234 
try_from(time: &SystemTime) -> Result<GeneralizedTime>235     fn try_from(time: &SystemTime) -> Result<GeneralizedTime> {
236         GeneralizedTime::from_system_time(*time)
237     }
238 }
239 
240 #[cfg(feature = "std")]
241 impl<'a> TryFrom<AnyRef<'a>> for SystemTime {
242     type Error = Error;
243 
try_from(any: AnyRef<'a>) -> Result<SystemTime>244     fn try_from(any: AnyRef<'a>) -> Result<SystemTime> {
245         GeneralizedTime::try_from(any).map(|s| s.to_system_time())
246     }
247 }
248 
249 #[cfg(feature = "std")]
250 impl FixedTag for SystemTime {
251     const TAG: Tag = Tag::GeneralizedTime;
252 }
253 
254 #[cfg(feature = "std")]
255 impl OrdIsValueOrd for SystemTime {}
256 
257 #[cfg(feature = "time")]
258 impl<'a> DecodeValue<'a> for PrimitiveDateTime {
decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>259     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
260         GeneralizedTime::decode_value(reader, header)?.try_into()
261     }
262 }
263 
264 #[cfg(feature = "time")]
265 impl EncodeValue for PrimitiveDateTime {
value_len(&self) -> Result<Length>266     fn value_len(&self) -> Result<Length> {
267         GeneralizedTime::try_from(self)?.value_len()
268     }
269 
encode_value(&self, writer: &mut impl Writer) -> Result<()>270     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
271         GeneralizedTime::try_from(self)?.encode_value(writer)
272     }
273 }
274 
275 #[cfg(feature = "time")]
276 impl FixedTag for PrimitiveDateTime {
277     const TAG: Tag = Tag::GeneralizedTime;
278 }
279 
280 #[cfg(feature = "time")]
281 impl OrdIsValueOrd for PrimitiveDateTime {}
282 
283 #[cfg(feature = "time")]
284 impl TryFrom<PrimitiveDateTime> for GeneralizedTime {
285     type Error = Error;
286 
try_from(time: PrimitiveDateTime) -> Result<GeneralizedTime>287     fn try_from(time: PrimitiveDateTime) -> Result<GeneralizedTime> {
288         Ok(GeneralizedTime::from_date_time(DateTime::try_from(time)?))
289     }
290 }
291 
292 #[cfg(feature = "time")]
293 impl TryFrom<&PrimitiveDateTime> for GeneralizedTime {
294     type Error = Error;
295 
try_from(time: &PrimitiveDateTime) -> Result<GeneralizedTime>296     fn try_from(time: &PrimitiveDateTime) -> Result<GeneralizedTime> {
297         Self::try_from(*time)
298     }
299 }
300 
301 #[cfg(feature = "time")]
302 impl TryFrom<GeneralizedTime> for PrimitiveDateTime {
303     type Error = Error;
304 
try_from(time: GeneralizedTime) -> Result<PrimitiveDateTime>305     fn try_from(time: GeneralizedTime) -> Result<PrimitiveDateTime> {
306         time.to_date_time().try_into()
307     }
308 }
309 
310 #[cfg(test)]
311 mod tests {
312     use super::GeneralizedTime;
313     use crate::{Decode, Encode, SliceWriter};
314     use hex_literal::hex;
315 
316     #[test]
round_trip()317     fn round_trip() {
318         let example_bytes = hex!("18 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a");
319         let utc_time = GeneralizedTime::from_der(&example_bytes).unwrap();
320         assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
321 
322         let mut buf = [0u8; 128];
323         let mut encoder = SliceWriter::new(&mut buf);
324         utc_time.encode(&mut encoder).unwrap();
325         assert_eq!(example_bytes, encoder.finish().unwrap());
326     }
327 }
328