1 //! ASN.1 `UTCTime` support.
2 
3 use crate::{
4     datetime::{self, DateTime},
5     ord::OrdIsValueOrd,
6     DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag,
7     Writer,
8 };
9 use core::time::Duration;
10 
11 #[cfg(feature = "std")]
12 use std::time::SystemTime;
13 
14 /// ASN.1 `UTCTime` type.
15 ///
16 /// This type implements the validity requirements specified in
17 /// [RFC 5280 Section 4.1.2.5.1][1], namely:
18 ///
19 /// > For the purposes of this profile, UTCTime values MUST be expressed in
20 /// > Greenwich Mean Time (Zulu) and MUST include seconds (i.e., times are
21 /// > `YYMMDDHHMMSSZ`), even where the number of seconds is zero.  Conforming
22 /// > systems MUST interpret the year field (`YY`) as follows:
23 /// >
24 /// > - Where `YY` is greater than or equal to 50, the year SHALL be
25 /// >   interpreted as `19YY`; and
26 /// > - Where `YY` is less than 50, the year SHALL be interpreted as `20YY`.
27 ///
28 /// Note: Due to common operations working on `UNIX_EPOCH` [`UtcTime`]s are
29 /// only supported for the years 1970-2049.
30 ///
31 /// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
32 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
33 pub struct UtcTime(DateTime);
34 
35 impl UtcTime {
36     /// Length of an RFC 5280-flavored ASN.1 DER-encoded [`UtcTime`].
37     pub const LENGTH: usize = 13;
38 
39     /// Maximum year that can be represented as a `UTCTime`.
40     pub const MAX_YEAR: u16 = 2049;
41 
42     /// Create a [`UtcTime`] from a [`DateTime`].
from_date_time(datetime: DateTime) -> Result<Self>43     pub fn from_date_time(datetime: DateTime) -> Result<Self> {
44         if datetime.year() <= UtcTime::MAX_YEAR {
45             Ok(Self(datetime))
46         } else {
47             Err(Self::TAG.value_error())
48         }
49     }
50 
51     /// Convert this [`UtcTime`] into a [`DateTime`].
to_date_time(&self) -> DateTime52     pub fn to_date_time(&self) -> DateTime {
53         self.0
54     }
55 
56     /// Create a new [`UtcTime`] given a [`Duration`] since `UNIX_EPOCH`
57     /// (a.k.a. "Unix time")
from_unix_duration(unix_duration: Duration) -> Result<Self>58     pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> {
59         DateTime::from_unix_duration(unix_duration)?.try_into()
60     }
61 
62     /// Get the duration of this timestamp since `UNIX_EPOCH`.
to_unix_duration(&self) -> Duration63     pub fn to_unix_duration(&self) -> Duration {
64         self.0.unix_duration()
65     }
66 
67     /// Instantiate from [`SystemTime`].
68     #[cfg(feature = "std")]
from_system_time(time: SystemTime) -> Result<Self>69     pub fn from_system_time(time: SystemTime) -> Result<Self> {
70         DateTime::try_from(time)
71             .map_err(|_| Self::TAG.value_error())?
72             .try_into()
73     }
74 
75     /// Convert to [`SystemTime`].
76     #[cfg(feature = "std")]
to_system_time(&self) -> SystemTime77     pub fn to_system_time(&self) -> SystemTime {
78         self.0.to_system_time()
79     }
80 }
81 
82 impl_any_conversions!(UtcTime);
83 
84 impl<'a> DecodeValue<'a> for UtcTime {
decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>85     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
86         if Self::LENGTH != usize::try_from(header.length)? {
87             return Err(Self::TAG.value_error());
88         }
89 
90         let mut bytes = [0u8; Self::LENGTH];
91         reader.read_into(&mut bytes)?;
92 
93         match bytes {
94             // RFC 5280 requires mandatory seconds and Z-normalized time zone
95             [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
96                 let year = u16::from(datetime::decode_decimal(Self::TAG, year1, year2)?);
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                 // RFC 5280 rules for interpreting the year
104                 let year = if year >= 50 {
105                     year.checked_add(1900)
106                 } else {
107                     year.checked_add(2000)
108                 }
109                 .ok_or(ErrorKind::DateTime)?;
110 
111                 DateTime::new(year, month, day, hour, minute, second)
112                     .map_err(|_| Self::TAG.value_error())
113                     .and_then(|dt| Self::from_unix_duration(dt.unix_duration()))
114             }
115             _ => Err(Self::TAG.value_error()),
116         }
117     }
118 }
119 
120 impl EncodeValue for UtcTime {
value_len(&self) -> Result<Length>121     fn value_len(&self) -> Result<Length> {
122         Self::LENGTH.try_into()
123     }
124 
encode_value(&self, writer: &mut impl Writer) -> Result<()>125     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
126         let year = match self.0.year() {
127             y @ 1950..=1999 => y.checked_sub(1900),
128             y @ 2000..=2049 => y.checked_sub(2000),
129             _ => return Err(Self::TAG.value_error()),
130         }
131         .and_then(|y| u8::try_from(y).ok())
132         .ok_or(ErrorKind::DateTime)?;
133 
134         datetime::encode_decimal(writer, Self::TAG, year)?;
135         datetime::encode_decimal(writer, Self::TAG, self.0.month())?;
136         datetime::encode_decimal(writer, Self::TAG, self.0.day())?;
137         datetime::encode_decimal(writer, Self::TAG, self.0.hour())?;
138         datetime::encode_decimal(writer, Self::TAG, self.0.minutes())?;
139         datetime::encode_decimal(writer, Self::TAG, self.0.seconds())?;
140         writer.write_byte(b'Z')
141     }
142 }
143 
144 impl FixedTag for UtcTime {
145     const TAG: Tag = Tag::UtcTime;
146 }
147 
148 impl OrdIsValueOrd for UtcTime {}
149 
150 impl From<&UtcTime> for UtcTime {
from(value: &UtcTime) -> UtcTime151     fn from(value: &UtcTime) -> UtcTime {
152         *value
153     }
154 }
155 
156 impl From<UtcTime> for DateTime {
from(utc_time: UtcTime) -> DateTime157     fn from(utc_time: UtcTime) -> DateTime {
158         utc_time.0
159     }
160 }
161 
162 impl From<&UtcTime> for DateTime {
from(utc_time: &UtcTime) -> DateTime163     fn from(utc_time: &UtcTime) -> DateTime {
164         utc_time.0
165     }
166 }
167 
168 impl TryFrom<DateTime> for UtcTime {
169     type Error = Error;
170 
try_from(datetime: DateTime) -> Result<Self>171     fn try_from(datetime: DateTime) -> Result<Self> {
172         Self::from_date_time(datetime)
173     }
174 }
175 
176 impl TryFrom<&DateTime> for UtcTime {
177     type Error = Error;
178 
try_from(datetime: &DateTime) -> Result<Self>179     fn try_from(datetime: &DateTime) -> Result<Self> {
180         Self::from_date_time(*datetime)
181     }
182 }
183 
184 #[cfg(feature = "std")]
185 impl From<UtcTime> for SystemTime {
from(utc_time: UtcTime) -> SystemTime186     fn from(utc_time: UtcTime) -> SystemTime {
187         utc_time.to_system_time()
188     }
189 }
190 
191 // Implement by hand because the derive would create invalid values.
192 // Use the conversion from DateTime to create a valid value.
193 // The DateTime type has a way bigger range of valid years than UtcTime,
194 // so the DateTime year is mapped into a valid range to throw away less inputs.
195 #[cfg(feature = "arbitrary")]
196 impl<'a> arbitrary::Arbitrary<'a> for UtcTime {
arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self>197     fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
198         const MIN_YEAR: u16 = 1970;
199         const VALID_YEAR_COUNT: u16 = UtcTime::MAX_YEAR - MIN_YEAR + 1;
200         const AVERAGE_SECONDS_IN_YEAR: u64 = 31_556_952;
201 
202         let datetime = DateTime::arbitrary(u)?;
203         let year = datetime.year();
204         let duration = datetime.unix_duration();
205 
206         // Clamp the year into a valid range to not throw away too much input
207         let valid_year = (year.saturating_sub(MIN_YEAR))
208             .rem_euclid(VALID_YEAR_COUNT)
209             .saturating_add(MIN_YEAR);
210         let year_to_remove = year.saturating_sub(valid_year);
211         let valid_duration = duration
212             - Duration::from_secs(
213                 u64::from(year_to_remove).saturating_mul(AVERAGE_SECONDS_IN_YEAR),
214             );
215 
216         Self::from_date_time(DateTime::from_unix_duration(valid_duration).expect("supported range"))
217             .map_err(|_| arbitrary::Error::IncorrectFormat)
218     }
219 
size_hint(depth: usize) -> (usize, Option<usize>)220     fn size_hint(depth: usize) -> (usize, Option<usize>) {
221         DateTime::size_hint(depth)
222     }
223 }
224 
225 #[cfg(test)]
226 mod tests {
227     use super::UtcTime;
228     use crate::{Decode, Encode, SliceWriter};
229     use hex_literal::hex;
230 
231     #[test]
round_trip_vector()232     fn round_trip_vector() {
233         let example_bytes = hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a");
234         let utc_time = UtcTime::from_der(&example_bytes).unwrap();
235         assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
236 
237         let mut buf = [0u8; 128];
238         let mut encoder = SliceWriter::new(&mut buf);
239         utc_time.encode(&mut encoder).unwrap();
240         assert_eq!(example_bytes, encoder.finish().unwrap());
241     }
242 }
243