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