diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/der/src/asn1/utc_time.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/der/src/asn1/utc_time.rs')
-rw-r--r-- | vendor/der/src/asn1/utc_time.rs | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/vendor/der/src/asn1/utc_time.rs b/vendor/der/src/asn1/utc_time.rs new file mode 100644 index 000000000..7c2381155 --- /dev/null +++ b/vendor/der/src/asn1/utc_time.rs @@ -0,0 +1,215 @@ +//! ASN.1 `UTCTime` support. + +use crate::{ + asn1::AnyRef, + datetime::{self, DateTime}, + ord::OrdIsValueOrd, + DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, + Writer, +}; +use core::time::Duration; + +#[cfg(feature = "std")] +use std::time::SystemTime; + +/// Maximum year that can be represented as a `UTCTime`. +pub const MAX_YEAR: u16 = 2049; + +/// ASN.1 `UTCTime` type. +/// +/// This type implements the validity requirements specified in +/// [RFC 5280 Section 4.1.2.5.1][1], namely: +/// +/// > For the purposes of this profile, UTCTime values MUST be expressed in +/// > Greenwich Mean Time (Zulu) and MUST include seconds (i.e., times are +/// > `YYMMDDHHMMSSZ`), even where the number of seconds is zero. Conforming +/// > systems MUST interpret the year field (`YY`) as follows: +/// > +/// > - Where `YY` is greater than or equal to 50, the year SHALL be +/// > interpreted as `19YY`; and +/// > - Where `YY` is less than 50, the year SHALL be interpreted as `20YY`. +/// +/// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct UtcTime(DateTime); + +impl UtcTime { + /// Length of an RFC 5280-flavored ASN.1 DER-encoded [`UtcTime`]. + pub const LENGTH: usize = 13; + + /// Create a [`UtcTime`] from a [`DateTime`]. + pub fn from_date_time(datetime: DateTime) -> Result<Self> { + if datetime.year() <= MAX_YEAR { + Ok(Self(datetime)) + } else { + Err(Self::TAG.value_error()) + } + } + + /// Convert this [`UtcTime`] into a [`DateTime`]. + pub fn to_date_time(&self) -> DateTime { + self.0 + } + + /// Create a new [`UtcTime`] given a [`Duration`] since `UNIX_EPOCH` + /// (a.k.a. "Unix time") + pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> { + DateTime::from_unix_duration(unix_duration)?.try_into() + } + + /// Get the duration of this timestamp since `UNIX_EPOCH`. + pub fn to_unix_duration(&self) -> Duration { + self.0.unix_duration() + } + + /// Instantiate from [`SystemTime`]. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub fn from_system_time(time: SystemTime) -> Result<Self> { + DateTime::try_from(time) + .map_err(|_| Self::TAG.value_error())? + .try_into() + } + + /// Convert to [`SystemTime`]. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub fn to_system_time(&self) -> SystemTime { + self.0.to_system_time() + } +} + +impl<'a> DecodeValue<'a> for UtcTime { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + if Self::LENGTH != usize::try_from(header.length)? { + return Err(Self::TAG.value_error()); + } + + let mut bytes = [0u8; Self::LENGTH]; + reader.read_into(&mut bytes)?; + + match bytes { + // RFC 5280 requires mandatory seconds and Z-normalized time zone + [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => { + let year = u16::from(datetime::decode_decimal(Self::TAG, year1, year2)?); + let month = datetime::decode_decimal(Self::TAG, mon1, mon2)?; + let day = datetime::decode_decimal(Self::TAG, day1, day2)?; + let hour = datetime::decode_decimal(Self::TAG, hour1, hour2)?; + let minute = datetime::decode_decimal(Self::TAG, min1, min2)?; + let second = datetime::decode_decimal(Self::TAG, sec1, sec2)?; + + // RFC 5280 rules for interpreting the year + let year = if year >= 50 { + year.checked_add(1900) + } else { + year.checked_add(2000) + } + .ok_or(ErrorKind::DateTime)?; + + DateTime::new(year, month, day, hour, minute, second) + .map_err(|_| Self::TAG.value_error()) + .and_then(|dt| Self::from_unix_duration(dt.unix_duration())) + } + _ => Err(Self::TAG.value_error()), + } + } +} + +impl EncodeValue for UtcTime { + fn value_len(&self) -> Result<Length> { + Self::LENGTH.try_into() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + let year = match self.0.year() { + y @ 1950..=1999 => y.checked_sub(1900), + y @ 2000..=2049 => y.checked_sub(2000), + _ => return Err(Self::TAG.value_error()), + } + .and_then(|y| u8::try_from(y).ok()) + .ok_or(ErrorKind::DateTime)?; + + datetime::encode_decimal(writer, Self::TAG, year)?; + datetime::encode_decimal(writer, Self::TAG, self.0.month())?; + datetime::encode_decimal(writer, Self::TAG, self.0.day())?; + datetime::encode_decimal(writer, Self::TAG, self.0.hour())?; + datetime::encode_decimal(writer, Self::TAG, self.0.minutes())?; + datetime::encode_decimal(writer, Self::TAG, self.0.seconds())?; + writer.write_byte(b'Z') + } +} + +impl FixedTag for UtcTime { + const TAG: Tag = Tag::UtcTime; +} + +impl OrdIsValueOrd for UtcTime {} + +impl From<&UtcTime> for UtcTime { + fn from(value: &UtcTime) -> UtcTime { + *value + } +} + +impl From<UtcTime> for DateTime { + fn from(utc_time: UtcTime) -> DateTime { + utc_time.0 + } +} + +impl From<&UtcTime> for DateTime { + fn from(utc_time: &UtcTime) -> DateTime { + utc_time.0 + } +} + +impl TryFrom<DateTime> for UtcTime { + type Error = Error; + + fn try_from(datetime: DateTime) -> Result<Self> { + Self::from_date_time(datetime) + } +} + +impl TryFrom<&DateTime> for UtcTime { + type Error = Error; + + fn try_from(datetime: &DateTime) -> Result<Self> { + Self::from_date_time(*datetime) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From<UtcTime> for SystemTime { + fn from(utc_time: UtcTime) -> SystemTime { + utc_time.to_system_time() + } +} + +impl TryFrom<AnyRef<'_>> for UtcTime { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<UtcTime> { + any.decode_into() + } +} + +#[cfg(test)] +mod tests { + use super::UtcTime; + use crate::{Decode, Encode, SliceWriter}; + use hex_literal::hex; + + #[test] + fn round_trip_vector() { + let example_bytes = hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a"); + let utc_time = UtcTime::from_der(&example_bytes).unwrap(); + assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540); + + let mut buf = [0u8; 128]; + let mut encoder = SliceWriter::new(&mut buf); + utc_time.encode(&mut encoder).unwrap(); + assert_eq!(example_bytes, encoder.finish().unwrap()); + } +} |