summaryrefslogtreecommitdiffstats
path: root/vendor/der/src/asn1/generalized_time.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:35 +0000
commit7e5d7eea9c580ef4b41a765bde624af431942b96 (patch)
tree2c0d9ca12878fc4525650aa4e54d77a81a07cc09 /vendor/der/src/asn1/generalized_time.rs
parentAdding debian version 1.70.0+dfsg1-9. (diff)
downloadrustc-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.rs348
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());
+ }
+}