diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:35 +0000 |
commit | 7e5d7eea9c580ef4b41a765bde624af431942b96 (patch) | |
tree | 2c0d9ca12878fc4525650aa4e54d77a81a07cc09 /vendor/der/src/asn1/generalized_time.rs | |
parent | Adding debian version 1.70.0+dfsg1-9. (diff) | |
download | rustc-7e5d7eea9c580ef4b41a765bde624af431942b96.tar.xz rustc-7e5d7eea9c580ef4b41a765bde624af431942b96.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/generalized_time.rs')
-rw-r--r-- | vendor/der/src/asn1/generalized_time.rs | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/vendor/der/src/asn1/generalized_time.rs b/vendor/der/src/asn1/generalized_time.rs new file mode 100644 index 000000000..9950e368e --- /dev/null +++ b/vendor/der/src/asn1/generalized_time.rs @@ -0,0 +1,348 @@ +//! ASN.1 `GeneralizedTime` 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; + +#[cfg(feature = "time")] +use time::PrimitiveDateTime; + +/// ASN.1 `GeneralizedTime` type. +/// +/// This type implements the validity requirements specified in +/// [RFC 5280 Section 4.1.2.5.2][1], namely: +/// +/// > For the purposes of this profile, GeneralizedTime values MUST be +/// > expressed in Greenwich Mean Time (Zulu) and MUST include seconds +/// > (i.e., times are `YYYYMMDDHHMMSSZ`), even where the number of seconds +/// > is zero. GeneralizedTime values MUST NOT include fractional seconds. +/// +/// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct GeneralizedTime(DateTime); + +impl GeneralizedTime { + /// Length of an RFC 5280-flavored ASN.1 DER-encoded [`GeneralizedTime`]. + const LENGTH: usize = 15; + + /// Create a [`GeneralizedTime`] from a [`DateTime`]. + pub fn from_date_time(datetime: DateTime) -> Self { + Self(datetime) + } + + /// Convert this [`GeneralizedTime`] into a [`DateTime`]. + pub fn to_date_time(&self) -> DateTime { + self.0 + } + + /// Create a new [`GeneralizedTime`] 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) + .map(Into::into) + .map_err(|_| Self::TAG.value_error()) + } + + /// 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(Into::into) + .map_err(|_| Self::TAG.value_error()) + } + + /// 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 GeneralizedTime { + 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 + [y1, y2, y3, y4, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => { + let year = u16::from(datetime::decode_decimal(Self::TAG, y1, y2)?) + .checked_mul(100) + .and_then(|y| { + y.checked_add(datetime::decode_decimal(Self::TAG, y3, y4).ok()?.into()) + }) + .ok_or(ErrorKind::DateTime)?; + 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)?; + + 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 GeneralizedTime { + fn value_len(&self) -> Result<Length> { + Self::LENGTH.try_into() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + let year_hi = u8::try_from(self.0.year() / 100)?; + let year_lo = u8::try_from(self.0.year() % 100)?; + + datetime::encode_decimal(writer, Self::TAG, year_hi)?; + datetime::encode_decimal(writer, Self::TAG, year_lo)?; + 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 GeneralizedTime { + const TAG: Tag = Tag::GeneralizedTime; +} + +impl OrdIsValueOrd for GeneralizedTime {} + +impl From<&GeneralizedTime> for GeneralizedTime { + fn from(value: &GeneralizedTime) -> GeneralizedTime { + *value + } +} + +impl From<GeneralizedTime> for DateTime { + fn from(utc_time: GeneralizedTime) -> DateTime { + utc_time.0 + } +} + +impl From<&GeneralizedTime> for DateTime { + fn from(utc_time: &GeneralizedTime) -> DateTime { + utc_time.0 + } +} + +impl From<DateTime> for GeneralizedTime { + fn from(datetime: DateTime) -> Self { + Self::from_date_time(datetime) + } +} + +impl From<&DateTime> for GeneralizedTime { + fn from(datetime: &DateTime) -> Self { + Self::from_date_time(*datetime) + } +} + +impl TryFrom<AnyRef<'_>> for GeneralizedTime { + type Error = Error; + + fn try_from(any: AnyRef<'_>) -> Result<GeneralizedTime> { + any.decode_into() + } +} + +impl<'a> DecodeValue<'a> for DateTime { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Ok(GeneralizedTime::decode_value(reader, header)?.into()) + } +} + +impl EncodeValue for DateTime { + fn value_len(&self) -> Result<Length> { + GeneralizedTime::from(self).value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + GeneralizedTime::from(self).encode_value(writer) + } +} + +impl FixedTag for DateTime { + const TAG: Tag = Tag::GeneralizedTime; +} + +impl OrdIsValueOrd for DateTime {} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl<'a> DecodeValue<'a> for SystemTime { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + Ok(GeneralizedTime::decode_value(reader, header)?.into()) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl EncodeValue for SystemTime { + fn value_len(&self) -> Result<Length> { + GeneralizedTime::try_from(self)?.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + GeneralizedTime::try_from(self)?.encode_value(writer) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From<GeneralizedTime> for SystemTime { + fn from(time: GeneralizedTime) -> SystemTime { + time.to_system_time() + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From<&GeneralizedTime> for SystemTime { + fn from(time: &GeneralizedTime) -> SystemTime { + time.to_system_time() + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl TryFrom<SystemTime> for GeneralizedTime { + type Error = Error; + + fn try_from(time: SystemTime) -> Result<GeneralizedTime> { + GeneralizedTime::from_system_time(time) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl TryFrom<&SystemTime> for GeneralizedTime { + type Error = Error; + + fn try_from(time: &SystemTime) -> Result<GeneralizedTime> { + GeneralizedTime::from_system_time(*time) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl<'a> TryFrom<AnyRef<'a>> for SystemTime { + type Error = Error; + + fn try_from(any: AnyRef<'a>) -> Result<SystemTime> { + GeneralizedTime::try_from(any).map(|s| s.to_system_time()) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl FixedTag for SystemTime { + const TAG: Tag = Tag::GeneralizedTime; +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl OrdIsValueOrd for SystemTime {} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl<'a> DecodeValue<'a> for PrimitiveDateTime { + fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { + GeneralizedTime::decode_value(reader, header)?.try_into() + } +} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl EncodeValue for PrimitiveDateTime { + fn value_len(&self) -> Result<Length> { + GeneralizedTime::try_from(self)?.value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> { + GeneralizedTime::try_from(self)?.encode_value(writer) + } +} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl FixedTag for PrimitiveDateTime { + const TAG: Tag = Tag::GeneralizedTime; +} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl OrdIsValueOrd for PrimitiveDateTime {} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl TryFrom<PrimitiveDateTime> for GeneralizedTime { + type Error = Error; + + fn try_from(time: PrimitiveDateTime) -> Result<GeneralizedTime> { + Ok(GeneralizedTime::from_date_time(DateTime::try_from(time)?)) + } +} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl TryFrom<&PrimitiveDateTime> for GeneralizedTime { + type Error = Error; + + fn try_from(time: &PrimitiveDateTime) -> Result<GeneralizedTime> { + Self::try_from(*time) + } +} + +#[cfg(feature = "time")] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] +impl TryFrom<GeneralizedTime> for PrimitiveDateTime { + type Error = Error; + + fn try_from(time: GeneralizedTime) -> Result<PrimitiveDateTime> { + time.to_date_time().try_into() + } +} + +#[cfg(test)] +mod tests { + use super::GeneralizedTime; + use crate::{Decode, Encode, SliceWriter}; + use hex_literal::hex; + + #[test] + fn round_trip() { + let example_bytes = hex!("18 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a"); + let utc_time = GeneralizedTime::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()); + } +} |