summaryrefslogtreecommitdiffstats
path: root/vendor/der/src/asn1
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
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')
-rw-r--r--vendor/der/src/asn1/any.rs284
-rw-r--r--vendor/der/src/asn1/bit_string.rs504
-rw-r--r--vendor/der/src/asn1/boolean.rs93
-rw-r--r--vendor/der/src/asn1/choice.rs26
-rw-r--r--vendor/der/src/asn1/context_specific.rs354
-rw-r--r--vendor/der/src/asn1/generalized_time.rs348
-rw-r--r--vendor/der/src/asn1/ia5_string.rs132
-rw-r--r--vendor/der/src/asn1/integer.rs276
-rw-r--r--vendor/der/src/asn1/integer/bigint.rs152
-rw-r--r--vendor/der/src/asn1/integer/int.rs55
-rw-r--r--vendor/der/src/asn1/integer/uint.rs116
-rw-r--r--vendor/der/src/asn1/null.rs108
-rw-r--r--vendor/der/src/asn1/octet_string.rs182
-rw-r--r--vendor/der/src/asn1/oid.rs90
-rw-r--r--vendor/der/src/asn1/optional.rs66
-rw-r--r--vendor/der/src/asn1/printable_string.rs168
-rw-r--r--vendor/der/src/asn1/real.rs993
-rw-r--r--vendor/der/src/asn1/sequence.rs84
-rw-r--r--vendor/der/src/asn1/sequence_of.rs234
-rw-r--r--vendor/der/src/asn1/set_of.rs451
-rw-r--r--vendor/der/src/asn1/teletex_string.rs144
-rw-r--r--vendor/der/src/asn1/utc_time.rs215
-rw-r--r--vendor/der/src/asn1/utf8_string.rs209
-rw-r--r--vendor/der/src/asn1/videotex_string.rs143
24 files changed, 5427 insertions, 0 deletions
diff --git a/vendor/der/src/asn1/any.rs b/vendor/der/src/asn1/any.rs
new file mode 100644
index 000000000..cb65f2391
--- /dev/null
+++ b/vendor/der/src/asn1/any.rs
@@ -0,0 +1,284 @@
+//! ASN.1 `ANY` type.
+
+use crate::{
+ asn1::*, ByteSlice, Choice, Decode, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind,
+ FixedTag, Header, Length, Reader, Result, SliceReader, Tag, Tagged, ValueOrd, Writer,
+};
+use core::cmp::Ordering;
+
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+#[cfg(feature = "oid")]
+use crate::asn1::ObjectIdentifier;
+
+/// ASN.1 `ANY`: represents any explicitly tagged ASN.1 value.
+///
+/// This is a zero-copy reference type which borrows from the input data.
+///
+/// Technically `ANY` hasn't been a recommended part of ASN.1 since the X.209
+/// revision from 1988. It was deprecated and replaced by Information Object
+/// Classes in X.680 in 1994, and X.690 no longer refers to it whatsoever.
+///
+/// Nevertheless, this crate defines an `ANY` type as it remains a familiar
+/// and useful concept which is still extensively used in things like
+/// PKI-related RFCs.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct AnyRef<'a> {
+ /// Tag representing the type of the encoded value.
+ tag: Tag,
+
+ /// Inner value encoded as bytes.
+ value: ByteSlice<'a>,
+}
+
+impl<'a> AnyRef<'a> {
+ /// [`AnyRef`] representation of the ASN.1 `NULL` type.
+ pub const NULL: Self = Self {
+ tag: Tag::Null,
+ value: ByteSlice::EMPTY,
+ };
+
+ /// Create a new [`AnyRef`] from the provided [`Tag`] and DER bytes.
+ pub fn new(tag: Tag, bytes: &'a [u8]) -> Result<Self> {
+ let value = ByteSlice::new(bytes).map_err(|_| ErrorKind::Length { tag })?;
+ Ok(Self { tag, value })
+ }
+
+ /// Infallible creation of an [`AnyRef`] from a [`ByteSlice`].
+ pub(crate) fn from_tag_and_value(tag: Tag, value: ByteSlice<'a>) -> Self {
+ Self { tag, value }
+ }
+
+ /// Get the raw value for this [`AnyRef`] type as a byte slice.
+ pub fn value(self) -> &'a [u8] {
+ self.value.as_slice()
+ }
+
+ /// Attempt to decode this [`AnyRef`] type into the inner value.
+ pub fn decode_into<T>(self) -> Result<T>
+ where
+ T: DecodeValue<'a> + FixedTag,
+ {
+ self.tag.assert_eq(T::TAG)?;
+ let header = Header {
+ tag: self.tag,
+ length: self.value.len(),
+ };
+
+ let mut decoder = SliceReader::new(self.value())?;
+ let result = T::decode_value(&mut decoder, header)?;
+ decoder.finish(result)
+ }
+
+ /// Is this value an ASN.1 `NULL` value?
+ pub fn is_null(self) -> bool {
+ self == Self::NULL
+ }
+
+ /// Attempt to decode an ASN.1 `BIT STRING`.
+ pub fn bit_string(self) -> Result<BitStringRef<'a>> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `CONTEXT-SPECIFIC` field.
+ pub fn context_specific<T>(self) -> Result<ContextSpecific<T>>
+ where
+ T: Decode<'a>,
+ {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `GeneralizedTime`.
+ pub fn generalized_time(self) -> Result<GeneralizedTime> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `IA5String`.
+ pub fn ia5_string(self) -> Result<Ia5StringRef<'a>> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `OCTET STRING`.
+ pub fn octet_string(self) -> Result<OctetStringRef<'a>> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `OBJECT IDENTIFIER`.
+ #[cfg(feature = "oid")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
+ pub fn oid(self) -> Result<ObjectIdentifier> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `OPTIONAL` value.
+ pub fn optional<T>(self) -> Result<Option<T>>
+ where
+ T: Choice<'a> + TryFrom<Self, Error = Error>,
+ {
+ if T::can_decode(self.tag) {
+ T::try_from(self).map(Some)
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Attempt to decode an ASN.1 `PrintableString`.
+ pub fn printable_string(self) -> Result<PrintableStringRef<'a>> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `TeletexString`.
+ pub fn teletex_string(self) -> Result<TeletexStringRef<'a>> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `VideotexString`.
+ pub fn videotex_string(self) -> Result<VideotexStringRef<'a>> {
+ self.try_into()
+ }
+
+ /// Attempt to decode this value an ASN.1 `SEQUENCE`, creating a new
+ /// nested reader and calling the provided argument with it.
+ pub fn sequence<F, T>(self, f: F) -> Result<T>
+ where
+ F: FnOnce(&mut SliceReader<'a>) -> Result<T>,
+ {
+ self.tag.assert_eq(Tag::Sequence)?;
+ let mut reader = SliceReader::new(self.value.as_slice())?;
+ let result = f(&mut reader)?;
+ reader.finish(result)
+ }
+
+ /// Attempt to decode an ASN.1 `UTCTime`.
+ pub fn utc_time(self) -> Result<UtcTime> {
+ self.try_into()
+ }
+
+ /// Attempt to decode an ASN.1 `UTF8String`.
+ pub fn utf8_string(self) -> Result<Utf8StringRef<'a>> {
+ self.try_into()
+ }
+}
+
+impl<'a> Choice<'a> for AnyRef<'a> {
+ fn can_decode(_: Tag) -> bool {
+ true
+ }
+}
+
+impl<'a> Decode<'a> for AnyRef<'a> {
+ fn decode<R: Reader<'a>>(reader: &mut R) -> Result<AnyRef<'a>> {
+ let header = Header::decode(reader)?;
+
+ Ok(Self {
+ tag: header.tag,
+ value: ByteSlice::decode_value(reader, header)?,
+ })
+ }
+}
+
+impl EncodeValue for AnyRef<'_> {
+ fn value_len(&self) -> Result<Length> {
+ Ok(self.value.len())
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write(self.value())
+ }
+}
+
+impl Tagged for AnyRef<'_> {
+ fn tag(&self) -> Tag {
+ self.tag
+ }
+}
+
+impl ValueOrd for AnyRef<'_> {
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ self.value.der_cmp(&other.value)
+ }
+}
+
+impl<'a> From<AnyRef<'a>> for ByteSlice<'a> {
+ fn from(any: AnyRef<'a>) -> ByteSlice<'a> {
+ any.value
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for AnyRef<'a> {
+ type Error = Error;
+
+ fn try_from(bytes: &'a [u8]) -> Result<AnyRef<'a>> {
+ AnyRef::from_der(bytes)
+ }
+}
+
+/// ASN.1 `ANY`: represents any explicitly tagged ASN.1 value.
+///
+/// This type provides the same functionality as [`AnyRef`] but owns the
+/// backing data.
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct Any {
+ /// Tag representing the type of the encoded value.
+ tag: Tag,
+
+ /// Inner value encoded as bytes.
+ value: Vec<u8>,
+}
+
+#[cfg(feature = "alloc")]
+impl Any {
+ /// Create a new [`Any`] from the provided [`Tag`] and DER bytes.
+ pub fn new(tag: Tag, bytes: impl Into<Vec<u8>>) -> Result<Self> {
+ let value = bytes.into();
+
+ // Ensure the tag and value are a valid `AnyRef`.
+ AnyRef::new(tag, &value)?;
+ Ok(Self { tag, value })
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl Choice<'_> for Any {
+ fn can_decode(_: Tag) -> bool {
+ true
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> Decode<'a> for Any {
+ fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
+ let header = Header::decode(reader)?;
+ let value = reader.read_vec(header.length)?;
+ Self::new(header.tag, value)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl EncodeValue for Any {
+ fn value_len(&self) -> Result<Length> {
+ self.value.len().try_into()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write(&self.value)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> From<&'a Any> for AnyRef<'a> {
+ fn from(any: &'a Any) -> AnyRef<'a> {
+ // Ensured to parse successfully in constructor
+ AnyRef::new(any.tag, &any.value).expect("invalid ANY")
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl Tagged for Any {
+ fn tag(&self) -> Tag {
+ self.tag
+ }
+}
diff --git a/vendor/der/src/asn1/bit_string.rs b/vendor/der/src/asn1/bit_string.rs
new file mode 100644
index 000000000..eed14e456
--- /dev/null
+++ b/vendor/der/src/asn1/bit_string.rs
@@ -0,0 +1,504 @@
+//! ASN.1 `BIT STRING` support.
+
+use crate::{
+ asn1::AnyRef, ByteSlice, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, FixedTag, Header,
+ Length, Reader, Result, Tag, ValueOrd, Writer,
+};
+use core::{cmp::Ordering, iter::FusedIterator};
+
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// ASN.1 `BIT STRING` type.
+///
+/// This type contains a sequence of any number of bits, modeled internally as
+/// a sequence of bytes with a known number of "unused bits".
+///
+/// This is a zero-copy reference type which borrows from the input data.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct BitStringRef<'a> {
+ /// Number of unused bits in the final octet.
+ unused_bits: u8,
+
+ /// Length of this `BIT STRING` in bits.
+ bit_length: usize,
+
+ /// Bitstring represented as a slice of bytes.
+ inner: ByteSlice<'a>,
+}
+
+impl<'a> BitStringRef<'a> {
+ /// Maximum number of unused bits allowed.
+ pub const MAX_UNUSED_BITS: u8 = 7;
+
+ /// Create a new ASN.1 `BIT STRING` from a byte slice.
+ ///
+ /// Accepts an optional number of "unused bits" (0-7) which are omitted
+ /// from the final octet. This number is 0 if the value is octet-aligned.
+ pub fn new(unused_bits: u8, bytes: &'a [u8]) -> Result<Self> {
+ if (unused_bits > Self::MAX_UNUSED_BITS) || (unused_bits != 0 && bytes.is_empty()) {
+ return Err(Self::TAG.value_error());
+ }
+
+ let inner = ByteSlice::new(bytes).map_err(|_| Self::TAG.length_error())?;
+
+ let bit_length = usize::try_from(inner.len())?
+ .checked_mul(8)
+ .and_then(|n| n.checked_sub(usize::from(unused_bits)))
+ .ok_or(ErrorKind::Overflow)?;
+
+ Ok(Self {
+ unused_bits,
+ bit_length,
+ inner,
+ })
+ }
+
+ /// Create a new ASN.1 `BIT STRING` from the given bytes.
+ ///
+ /// The "unused bits" are set to 0.
+ pub fn from_bytes(bytes: &'a [u8]) -> Result<Self> {
+ Self::new(0, bytes)
+ }
+
+ /// Get the number of unused bits in this byte slice.
+ pub fn unused_bits(&self) -> u8 {
+ self.unused_bits
+ }
+
+ /// Is the number of unused bits a value other than 0?
+ pub fn has_unused_bits(&self) -> bool {
+ self.unused_bits != 0
+ }
+
+ /// Get the length of this `BIT STRING` in bits.
+ pub fn bit_len(&self) -> usize {
+ self.bit_length
+ }
+
+ /// Get the number of bytes/octets needed to represent this `BIT STRING`
+ /// when serialized in an octet-aligned manner.
+ pub fn byte_len(&self) -> Length {
+ self.inner.len()
+ }
+
+ /// Is the inner byte slice empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ /// Borrow the inner byte slice.
+ ///
+ /// Returns `None` if the number of unused bits is *not* equal to zero,
+ /// i.e. if the `BIT STRING` is not octet aligned.
+ ///
+ /// Use [`BitString::raw_bytes`] to obtain access to the raw value
+ /// regardless of the presence of unused bits.
+ pub fn as_bytes(&self) -> Option<&'a [u8]> {
+ if self.has_unused_bits() {
+ None
+ } else {
+ Some(self.raw_bytes())
+ }
+ }
+
+ /// Borrow the raw bytes of this `BIT STRING`.
+ ///
+ /// Note that the byte string may contain extra unused bits in the final
+ /// octet. If the number of unused bits is expected to be 0, the
+ /// [`BitStringRef::as_bytes`] function can be used instead.
+ pub fn raw_bytes(&self) -> &'a [u8] {
+ self.inner.as_slice()
+ }
+
+ /// Iterator over the bits of this `BIT STRING`.
+ pub fn bits(self) -> BitStringIter<'a> {
+ BitStringIter {
+ bit_string: self,
+ position: 0,
+ }
+ }
+}
+
+impl<'a> DecodeValue<'a> for BitStringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let header = Header {
+ tag: header.tag,
+ length: (header.length - Length::ONE)?,
+ };
+
+ let unused_bits = reader.read_byte()?;
+ let inner = ByteSlice::decode_value(reader, header)?;
+ Self::new(unused_bits, inner.as_slice())
+ }
+}
+
+impl EncodeValue for BitStringRef<'_> {
+ fn value_len(&self) -> Result<Length> {
+ self.byte_len() + Length::ONE
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write_byte(self.unused_bits)?;
+ writer.write(self.raw_bytes())
+ }
+}
+
+impl ValueOrd for BitStringRef<'_> {
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ match self.unused_bits.cmp(&other.unused_bits) {
+ Ordering::Equal => self.inner.der_cmp(&other.inner),
+ ordering => Ok(ordering),
+ }
+ }
+}
+
+impl<'a> From<&BitStringRef<'a>> for BitStringRef<'a> {
+ fn from(value: &BitStringRef<'a>) -> BitStringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for BitStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<BitStringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for BitStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(bytes: &'a [u8]) -> Result<BitStringRef<'a>> {
+ BitStringRef::from_bytes(bytes)
+ }
+}
+
+/// Hack for simplifying the custom derive use case.
+impl<'a> TryFrom<&&'a [u8]> for BitStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(bytes: &&'a [u8]) -> Result<BitStringRef<'a>> {
+ BitStringRef::from_bytes(*bytes)
+ }
+}
+
+impl<'a> TryFrom<BitStringRef<'a>> for &'a [u8] {
+ type Error = Error;
+
+ fn try_from(bit_string: BitStringRef<'a>) -> Result<&'a [u8]> {
+ bit_string
+ .as_bytes()
+ .ok_or_else(|| Tag::BitString.value_error())
+ }
+}
+
+impl<'a> FixedTag for BitStringRef<'a> {
+ const TAG: Tag = Tag::BitString;
+}
+
+/// Owned form of ASN.1 `BIT STRING` type.
+///
+/// This type provides the same functionality as [`BitStringRef`] but owns the
+/// backing data.
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct BitString {
+ /// Number of unused bits in the final octet.
+ unused_bits: u8,
+
+ /// Length of this `BIT STRING` in bits.
+ bit_length: usize,
+
+ /// Bitstring represented as a slice of bytes.
+ inner: Vec<u8>,
+}
+
+#[cfg(feature = "alloc")]
+impl BitString {
+ /// Maximum number of unused bits allowed.
+ pub const MAX_UNUSED_BITS: u8 = 7;
+
+ /// Create a new ASN.1 `BIT STRING` from a byte slice.
+ ///
+ /// Accepts an optional number of "unused bits" (0-7) which are omitted
+ /// from the final octet. This number is 0 if the value is octet-aligned.
+ pub fn new(unused_bits: u8, bytes: impl Into<Vec<u8>>) -> Result<Self> {
+ let inner = bytes.into();
+
+ // Ensure parameters parse successfully as a `BitStringRef`.
+ let bit_length = BitStringRef::new(unused_bits, &inner)?.bit_length;
+
+ Ok(BitString {
+ unused_bits,
+ bit_length,
+ inner,
+ })
+ }
+
+ /// Create a new ASN.1 `BIT STRING` from the given bytes.
+ ///
+ /// The "unused bits" are set to 0.
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
+ Self::new(0, bytes)
+ }
+
+ /// Get the number of unused bits in the octet serialization of this
+ /// `BIT STRING`.
+ pub fn unused_bits(&self) -> u8 {
+ self.unused_bits
+ }
+
+ /// Is the number of unused bits a value other than 0?
+ pub fn has_unused_bits(&self) -> bool {
+ self.unused_bits != 0
+ }
+
+ /// Get the length of this `BIT STRING` in bits.
+ pub fn bit_len(&self) -> usize {
+ self.bit_length
+ }
+
+ /// Is the inner byte slice empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ /// Borrow the inner byte slice.
+ ///
+ /// Returns `None` if the number of unused bits is *not* equal to zero,
+ /// i.e. if the `BIT STRING` is not octet aligned.
+ ///
+ /// Use [`BitString::raw_bytes`] to obtain access to the raw value
+ /// regardless of the presence of unused bits.
+ pub fn as_bytes(&self) -> Option<&[u8]> {
+ if self.has_unused_bits() {
+ None
+ } else {
+ Some(self.raw_bytes())
+ }
+ }
+
+ /// Borrow the raw bytes of this `BIT STRING`.
+ pub fn raw_bytes(&self) -> &[u8] {
+ self.inner.as_slice()
+ }
+
+ /// Iterator over the bits of this `BIT STRING`.
+ pub fn bits(&self) -> BitStringIter<'_> {
+ BitStringRef::from(self).bits()
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> DecodeValue<'a> for BitString {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let inner_len = (header.length - Length::ONE)?;
+ let unused_bits = reader.read_byte()?;
+ let inner = reader.read_vec(inner_len)?;
+ Self::new(unused_bits, inner)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl EncodeValue for BitString {
+ fn value_len(&self) -> Result<Length> {
+ Length::ONE + Length::try_from(self.inner.len())?
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write_byte(self.unused_bits)?;
+ writer.write(&self.inner)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl FixedTag for BitString {
+ const TAG: Tag = Tag::BitString;
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> From<&'a BitString> for BitStringRef<'a> {
+ fn from(bit_string: &'a BitString) -> BitStringRef<'a> {
+ // Ensured to parse successfully in constructor
+ BitStringRef::new(bit_string.unused_bits, &bit_string.inner).expect("invalid BIT STRING")
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl ValueOrd for BitString {
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ match self.unused_bits.cmp(&other.unused_bits) {
+ Ordering::Equal => self.inner.der_cmp(&other.inner),
+ ordering => Ok(ordering),
+ }
+ }
+}
+
+/// Iterator over the bits of a [`BitString`].
+pub struct BitStringIter<'a> {
+ /// [`BitString`] being iterated over.
+ bit_string: BitStringRef<'a>,
+
+ /// Current bit position within the iterator.
+ position: usize,
+}
+
+impl<'a> Iterator for BitStringIter<'a> {
+ type Item = bool;
+
+ #[allow(clippy::integer_arithmetic)]
+ fn next(&mut self) -> Option<bool> {
+ if self.position >= self.bit_string.bit_len() {
+ return None;
+ }
+
+ let byte = self.bit_string.raw_bytes().get(self.position / 8)?;
+ let bit = 1u8 << (7 - (self.position % 8));
+ self.position = self.position.checked_add(1)?;
+ Some(byte & bit != 0)
+ }
+}
+
+impl<'a> ExactSizeIterator for BitStringIter<'a> {
+ fn len(&self) -> usize {
+ self.bit_string.bit_len()
+ }
+}
+
+impl<'a> FusedIterator for BitStringIter<'a> {}
+
+#[cfg(feature = "flagset")]
+impl<T: flagset::Flags> FixedTag for flagset::FlagSet<T> {
+ const TAG: Tag = BitStringRef::TAG;
+}
+
+#[cfg(feature = "flagset")]
+impl<T> ValueOrd for flagset::FlagSet<T>
+where
+ T: flagset::Flags,
+ T::Type: Ord,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ Ok(self.bits().cmp(&other.bits()))
+ }
+}
+
+#[cfg(feature = "flagset")]
+#[allow(clippy::integer_arithmetic)]
+impl<'a, T> DecodeValue<'a> for flagset::FlagSet<T>
+where
+ T: flagset::Flags,
+ T::Type: From<bool>,
+ T::Type: core::ops::Shl<usize, Output = T::Type>,
+{
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let position = reader.position();
+ let bits = BitStringRef::decode_value(reader, header)?;
+
+ let mut flags = T::none().bits();
+
+ if bits.bit_len() > core::mem::size_of_val(&flags) * 8 {
+ return Err(Error::new(ErrorKind::Overlength, position));
+ }
+
+ for (i, bit) in bits.bits().enumerate() {
+ flags |= T::Type::from(bit) << i;
+ }
+
+ Ok(Self::new_truncated(flags))
+ }
+}
+
+#[cfg(feature = "flagset")]
+#[allow(clippy::integer_arithmetic)]
+#[inline(always)]
+fn encode_flagset<T>(set: &flagset::FlagSet<T>) -> (usize, [u8; 16])
+where
+ T: flagset::Flags,
+ u128: From<T::Type>,
+{
+ let bits: u128 = set.bits().into();
+ let mut swap = 0u128;
+
+ for i in 0..128 {
+ let on = bits & (1 << i);
+ swap |= on >> i << (128 - i - 1);
+ }
+
+ (bits.leading_zeros() as usize, swap.to_be_bytes())
+}
+
+#[cfg(feature = "flagset")]
+#[allow(clippy::cast_possible_truncation, clippy::integer_arithmetic)]
+impl<T: flagset::Flags> EncodeValue for flagset::FlagSet<T>
+where
+ T::Type: From<bool>,
+ T::Type: core::ops::Shl<usize, Output = T::Type>,
+ u128: From<T::Type>,
+{
+ fn value_len(&self) -> Result<Length> {
+ let (lead, buff) = encode_flagset(self);
+ let buff = &buff[..buff.len() - lead / 8];
+ BitStringRef::new((lead % 8) as u8, buff)?.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ let (lead, buff) = encode_flagset(self);
+ let buff = &buff[..buff.len() - lead / 8];
+ BitStringRef::new((lead % 8) as u8, buff)?.encode_value(writer)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{BitStringRef, Result, Tag};
+ use crate::asn1::AnyRef;
+ use hex_literal::hex;
+
+ /// Parse a `BitString` from an ASN.1 `Any` value to test decoding behaviors.
+ fn parse_bitstring(bytes: &[u8]) -> Result<BitStringRef<'_>> {
+ AnyRef::new(Tag::BitString, bytes)?.try_into()
+ }
+
+ #[test]
+ fn decode_empty_bitstring() {
+ let bs = parse_bitstring(&hex!("00")).unwrap();
+ assert_eq!(bs.as_bytes().unwrap(), &[]);
+ }
+
+ #[test]
+ fn decode_non_empty_bitstring() {
+ let bs = parse_bitstring(&hex!("00010203")).unwrap();
+ assert_eq!(bs.as_bytes().unwrap(), &[0x01, 0x02, 0x03]);
+ }
+
+ #[test]
+ fn decode_bitstring_with_unused_bits() {
+ let bs = parse_bitstring(&hex!("066e5dc0")).unwrap();
+ assert_eq!(bs.unused_bits(), 6);
+ assert_eq!(bs.raw_bytes(), &hex!("6e5dc0"));
+
+ // Expected: 011011100101110111
+ let mut bits = bs.bits();
+ assert_eq!(bits.len(), 18);
+
+ for bit in [0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1] {
+ assert_eq!(bits.next().unwrap() as u8, bit)
+ }
+
+ // Ensure `None` is returned on successive calls
+ assert_eq!(bits.next(), None);
+ assert_eq!(bits.next(), None);
+ }
+
+ #[test]
+ fn reject_unused_bits_in_empty_string() {
+ assert_eq!(
+ parse_bitstring(&[0x03]).err().unwrap().kind(),
+ Tag::BitString.value_error().kind()
+ )
+ }
+}
diff --git a/vendor/der/src/asn1/boolean.rs b/vendor/der/src/asn1/boolean.rs
new file mode 100644
index 000000000..e03218120
--- /dev/null
+++ b/vendor/der/src/asn1/boolean.rs
@@ -0,0 +1,93 @@
+//! ASN.1 `BOOLEAN` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind,
+ FixedTag, Header, Length, Reader, Result, Tag, Writer,
+};
+
+/// Byte used to encode `true` in ASN.1 DER. From X.690 Section 11.1:
+///
+/// > If the encoding represents the boolean value TRUE, its single contents
+/// > octet shall have all eight bits set to one.
+const TRUE_OCTET: u8 = 0b11111111;
+
+/// Byte used to encode `false` in ASN.1 DER.
+const FALSE_OCTET: u8 = 0b00000000;
+
+impl<'a> DecodeValue<'a> for bool {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ if header.length != Length::ONE {
+ return Err(reader.error(ErrorKind::Length { tag: Self::TAG }));
+ }
+
+ match reader.read_byte()? {
+ FALSE_OCTET => Ok(false),
+ TRUE_OCTET => Ok(true),
+ _ => Err(Self::TAG.non_canonical_error()),
+ }
+ }
+}
+
+impl EncodeValue for bool {
+ fn value_len(&self) -> Result<Length> {
+ Ok(Length::ONE)
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write_byte(if *self { TRUE_OCTET } else { FALSE_OCTET })
+ }
+}
+
+impl FixedTag for bool {
+ const TAG: Tag = Tag::Boolean;
+}
+
+impl OrdIsValueOrd for bool {}
+
+impl From<bool> for AnyRef<'static> {
+ fn from(value: bool) -> AnyRef<'static> {
+ let value = ByteSlice::from(match value {
+ false => &[FALSE_OCTET],
+ true => &[TRUE_OCTET],
+ });
+
+ AnyRef::from_tag_and_value(Tag::Boolean, value)
+ }
+}
+
+impl TryFrom<AnyRef<'_>> for bool {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'_>) -> Result<bool> {
+ any.try_into()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{Decode, Encode};
+
+ #[test]
+ fn decode() {
+ assert_eq!(true, bool::from_der(&[0x01, 0x01, 0xFF]).unwrap());
+ assert_eq!(false, bool::from_der(&[0x01, 0x01, 0x00]).unwrap());
+ }
+
+ #[test]
+ fn encode() {
+ let mut buffer = [0u8; 3];
+ assert_eq!(
+ &[0x01, 0x01, 0xFF],
+ true.encode_to_slice(&mut buffer).unwrap()
+ );
+ assert_eq!(
+ &[0x01, 0x01, 0x00],
+ false.encode_to_slice(&mut buffer).unwrap()
+ );
+ }
+
+ #[test]
+ fn reject_non_canonical() {
+ assert!(bool::from_der(&[0x01, 0x01, 0x01]).is_err());
+ }
+}
diff --git a/vendor/der/src/asn1/choice.rs b/vendor/der/src/asn1/choice.rs
new file mode 100644
index 000000000..40c7720ca
--- /dev/null
+++ b/vendor/der/src/asn1/choice.rs
@@ -0,0 +1,26 @@
+//! ASN.1 `CHOICE` support.
+
+use crate::{Decode, FixedTag, Tag, Tagged};
+
+/// ASN.1 `CHOICE` denotes a union of one or more possible alternatives.
+///
+/// The types MUST have distinct tags.
+///
+/// This crate models choice as a trait, with a blanket impl for all types
+/// which impl `Decode + FixedTag` (i.e. they are modeled as a `CHOICE`
+/// with only one possible variant)
+pub trait Choice<'a>: Decode<'a> + Tagged {
+ /// Is the provided [`Tag`] decodable as a variant of this `CHOICE`?
+ fn can_decode(tag: Tag) -> bool;
+}
+
+/// This blanket impl allows any [`Tagged`] type to function as a [`Choice`]
+/// with a single alternative.
+impl<'a, T> Choice<'a> for T
+where
+ T: Decode<'a> + FixedTag,
+{
+ fn can_decode(tag: Tag) -> bool {
+ T::TAG == tag
+ }
+}
diff --git a/vendor/der/src/asn1/context_specific.rs b/vendor/der/src/asn1/context_specific.rs
new file mode 100644
index 000000000..311b5fe74
--- /dev/null
+++ b/vendor/der/src/asn1/context_specific.rs
@@ -0,0 +1,354 @@
+//! Context-specific field.
+
+use crate::{
+ asn1::AnyRef, Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error,
+ Header, Length, Reader, Result, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
+};
+use core::cmp::Ordering;
+
+/// Context-specific field which wraps an owned inner value.
+///
+/// This type decodes/encodes a field which is specific to a particular context
+/// and is identified by a [`TagNumber`].
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct ContextSpecific<T> {
+ /// Context-specific tag number sans the leading `0b10000000` class
+ /// identifier bit and `0b100000` constructed flag.
+ pub tag_number: TagNumber,
+
+ /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
+ pub tag_mode: TagMode,
+
+ /// Value of the field.
+ pub value: T,
+}
+
+impl<T> ContextSpecific<T> {
+ /// Attempt to decode an `EXPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
+ /// provided [`TagNumber`].
+ ///
+ /// This method has the following behavior which is designed to simplify
+ /// handling of extension fields, which are denoted in an ASN.1 schema
+ /// using the `...` ellipsis extension marker:
+ ///
+ /// - Skips over [`ContextSpecific`] fields with a tag number lower than
+ /// the current one, consuming and ignoring them.
+ /// - Returns `Ok(None)` if a [`ContextSpecific`] field with a higher tag
+ /// number is encountered. These fields are not consumed in this case,
+ /// allowing a field with a lower tag number to be omitted, then the
+ /// higher numbered field consumed as a follow-up.
+ /// - Returns `Ok(None)` if anything other than a [`ContextSpecific`] field
+ /// is encountered.
+ pub fn decode_explicit<'a, R: Reader<'a>>(
+ reader: &mut R,
+ tag_number: TagNumber,
+ ) -> Result<Option<Self>>
+ where
+ T: Decode<'a>,
+ {
+ Self::decode_with(reader, tag_number, |reader| Self::decode(reader))
+ }
+
+ /// Attempt to decode an `IMPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
+ /// provided [`TagNumber`].
+ ///
+ /// This method otherwise behaves the same as `decode_explicit`,
+ /// but should be used in cases where the particular fields are `IMPLICIT`
+ /// as opposed to `EXPLICIT`.
+ pub fn decode_implicit<'a, R: Reader<'a>>(
+ reader: &mut R,
+ tag_number: TagNumber,
+ ) -> Result<Option<Self>>
+ where
+ T: DecodeValue<'a> + Tagged,
+ {
+ Self::decode_with(reader, tag_number, |reader| {
+ let header = Header::decode(reader)?;
+ let value = T::decode_value(reader, header)?;
+
+ if header.tag.is_constructed() != value.tag().is_constructed() {
+ return Err(header.tag.non_canonical_error());
+ }
+
+ Ok(Self {
+ tag_number,
+ tag_mode: TagMode::Implicit,
+ value,
+ })
+ })
+ }
+
+ /// Attempt to decode a context-specific field with the given
+ /// helper callback.
+ fn decode_with<'a, F, R: Reader<'a>>(
+ reader: &mut R,
+ tag_number: TagNumber,
+ f: F,
+ ) -> Result<Option<Self>>
+ where
+ F: FnOnce(&mut R) -> Result<Self>,
+ {
+ while let Some(octet) = reader.peek_byte() {
+ let tag = Tag::try_from(octet)?;
+
+ if !tag.is_context_specific() || (tag.number() > tag_number) {
+ break;
+ } else if tag.number() == tag_number {
+ return Some(f(reader)).transpose();
+ } else {
+ AnyRef::decode(reader)?;
+ }
+ }
+
+ Ok(None)
+ }
+}
+
+impl<'a, T> Choice<'a> for ContextSpecific<T>
+where
+ T: Decode<'a> + Tagged,
+{
+ fn can_decode(tag: Tag) -> bool {
+ tag.is_context_specific()
+ }
+}
+
+impl<'a, T> Decode<'a> for ContextSpecific<T>
+where
+ T: Decode<'a>,
+{
+ fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
+ let header = Header::decode(reader)?;
+
+ match header.tag {
+ Tag::ContextSpecific {
+ number,
+ constructed: true,
+ } => Ok(Self {
+ tag_number: number,
+ tag_mode: TagMode::default(),
+ value: reader.read_nested(header.length, |reader| T::decode(reader))?,
+ }),
+ tag => Err(tag.unexpected_error(None)),
+ }
+ }
+}
+
+impl<T> EncodeValue for ContextSpecific<T>
+where
+ T: EncodeValue + Tagged,
+{
+ fn value_len(&self) -> Result<Length> {
+ match self.tag_mode {
+ TagMode::Explicit => self.value.encoded_len(),
+ TagMode::Implicit => self.value.value_len(),
+ }
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ match self.tag_mode {
+ TagMode::Explicit => self.value.encode(writer),
+ TagMode::Implicit => self.value.encode_value(writer),
+ }
+ }
+}
+
+impl<T> Tagged for ContextSpecific<T>
+where
+ T: Tagged,
+{
+ fn tag(&self) -> Tag {
+ let constructed = match self.tag_mode {
+ TagMode::Explicit => true,
+ TagMode::Implicit => self.value.tag().is_constructed(),
+ };
+
+ Tag::ContextSpecific {
+ number: self.tag_number,
+ constructed,
+ }
+ }
+}
+
+impl<'a, T> TryFrom<AnyRef<'a>> for ContextSpecific<T>
+where
+ T: Decode<'a>,
+{
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<ContextSpecific<T>> {
+ match any.tag() {
+ Tag::ContextSpecific {
+ number,
+ constructed: true,
+ } => Ok(Self {
+ tag_number: number,
+ tag_mode: TagMode::default(),
+ value: T::from_der(any.value())?,
+ }),
+ tag => Err(tag.unexpected_error(None)),
+ }
+ }
+}
+
+impl<T> ValueOrd for ContextSpecific<T>
+where
+ T: EncodeValue + ValueOrd + Tagged,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ match self.tag_mode {
+ TagMode::Explicit => self.der_cmp(other),
+ TagMode::Implicit => self.value_cmp(other),
+ }
+ }
+}
+
+/// Context-specific field reference.
+///
+/// This type encodes a field which is specific to a particular context
+/// and is identified by a [`TagNumber`].
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct ContextSpecificRef<'a, T> {
+ /// Context-specific tag number sans the leading `0b10000000` class
+ /// identifier bit and `0b100000` constructed flag.
+ pub tag_number: TagNumber,
+
+ /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
+ pub tag_mode: TagMode,
+
+ /// Value of the field.
+ pub value: &'a T,
+}
+
+impl<'a, T> ContextSpecificRef<'a, T> {
+ /// Convert to a [`ContextSpecific`].
+ fn encoder(&self) -> ContextSpecific<EncodeValueRef<'a, T>> {
+ ContextSpecific {
+ tag_number: self.tag_number,
+ tag_mode: self.tag_mode,
+ value: EncodeValueRef(self.value),
+ }
+ }
+}
+
+impl<'a, T> EncodeValue for ContextSpecificRef<'a, T>
+where
+ T: EncodeValue + Tagged,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.encoder().value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.encoder().encode_value(writer)
+ }
+}
+
+impl<'a, T> Tagged for ContextSpecificRef<'a, T>
+where
+ T: Tagged,
+{
+ fn tag(&self) -> Tag {
+ self.encoder().tag()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::ContextSpecific;
+ use crate::{asn1::BitStringRef, Decode, Encode, SliceReader, TagMode, TagNumber};
+ use hex_literal::hex;
+
+ // Public key data from `pkcs8` crate's `ed25519-pkcs8-v2.der`
+ const EXAMPLE_BYTES: &[u8] =
+ &hex!("A123032100A3A7EAE3A8373830BC47E1167BC50E1DB551999651E0E2DC587623438EAC3F31");
+
+ #[test]
+ fn round_trip() {
+ let field = ContextSpecific::<BitStringRef<'_>>::from_der(EXAMPLE_BYTES).unwrap();
+ assert_eq!(field.tag_number.value(), 1);
+ assert_eq!(
+ field.value,
+ BitStringRef::from_bytes(&EXAMPLE_BYTES[5..]).unwrap()
+ );
+
+ let mut buf = [0u8; 128];
+ let encoded = field.encode_to_slice(&mut buf).unwrap();
+ assert_eq!(encoded, EXAMPLE_BYTES);
+ }
+
+ #[test]
+ fn context_specific_with_explicit_field() {
+ let tag_number = TagNumber::new(0);
+
+ // Empty message
+ let mut reader = SliceReader::new(&[]).unwrap();
+ assert_eq!(
+ ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
+ None
+ );
+
+ // Message containing a non-context-specific type
+ let mut reader = SliceReader::new(&hex!("020100")).unwrap();
+ assert_eq!(
+ ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
+ None
+ );
+
+ // Message containing an EXPLICIT context-specific field
+ let mut reader = SliceReader::new(&hex!("A003020100")).unwrap();
+ let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number)
+ .unwrap()
+ .unwrap();
+
+ assert_eq!(field.tag_number, tag_number);
+ assert_eq!(field.tag_mode, TagMode::Explicit);
+ assert_eq!(field.value, 0);
+ }
+
+ #[test]
+ fn context_specific_with_implicit_field() {
+ // From RFC8410 Section 10.3:
+ // <https://datatracker.ietf.org/doc/html/rfc8410#section-10.3>
+ //
+ // 81 33: [1] 00 19 BF 44 09 69 84 CD FE 85 41 BA C1 67 DC 3B
+ // 96 C8 50 86 AA 30 B6 B6 CB 0C 5C 38 AD 70 31 66
+ // E1
+ let context_specific_implicit_bytes =
+ hex!("81210019BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
+
+ let tag_number = TagNumber::new(1);
+
+ let mut reader = SliceReader::new(&context_specific_implicit_bytes).unwrap();
+ let field = ContextSpecific::<BitStringRef<'_>>::decode_implicit(&mut reader, tag_number)
+ .unwrap()
+ .unwrap();
+
+ assert_eq!(field.tag_number, tag_number);
+ assert_eq!(field.tag_mode, TagMode::Implicit);
+ assert_eq!(
+ field.value.as_bytes().unwrap(),
+ &context_specific_implicit_bytes[3..]
+ );
+ }
+
+ #[test]
+ fn context_specific_skipping_unknown_field() {
+ let tag = TagNumber::new(1);
+ let mut reader = SliceReader::new(&hex!("A003020100A103020101")).unwrap();
+ let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag)
+ .unwrap()
+ .unwrap();
+ assert_eq!(field.value, 1);
+ }
+
+ #[test]
+ fn context_specific_returns_none_on_greater_tag_number() {
+ let tag = TagNumber::new(0);
+ let mut reader = SliceReader::new(&hex!("A103020101")).unwrap();
+ assert_eq!(
+ ContextSpecific::<u8>::decode_explicit(&mut reader, tag).unwrap(),
+ None
+ );
+ }
+}
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());
+ }
+}
diff --git a/vendor/der/src/asn1/ia5_string.rs b/vendor/der/src/asn1/ia5_string.rs
new file mode 100644
index 000000000..3971270a8
--- /dev/null
+++ b/vendor/der/src/asn1/ia5_string.rs
@@ -0,0 +1,132 @@
+//! ASN.1 `IA5String` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header,
+ Length, Reader, Result, StrSlice, Tag, Writer,
+};
+use core::{fmt, ops::Deref, str};
+
+/// ASN.1 `IA5String` type.
+///
+/// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e.
+/// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now
+/// technically known as the International Reference Alphabet or IRA as
+/// specified in the ITU-T's T.50 recommendation).
+///
+/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`].
+///
+/// This is a zero-copy reference type which borrows from the input data.
+///
+/// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
+pub struct Ia5StringRef<'a> {
+ /// Inner value
+ inner: StrSlice<'a>,
+}
+
+impl<'a> Ia5StringRef<'a> {
+ /// Create a new `IA5String`.
+ pub fn new<T>(input: &'a T) -> Result<Self>
+ where
+ T: AsRef<[u8]> + ?Sized,
+ {
+ let input = input.as_ref();
+
+ // Validate all characters are within IA5String's allowed set
+ if input.iter().any(|&c| c > 0x7F) {
+ return Err(Self::TAG.value_error());
+ }
+
+ StrSlice::from_bytes(input)
+ .map(|inner| Self { inner })
+ .map_err(|_| Self::TAG.value_error())
+ }
+}
+
+impl<'a> Deref for Ia5StringRef<'a> {
+ type Target = StrSlice<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl AsRef<str> for Ia5StringRef<'_> {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for Ia5StringRef<'_> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for Ia5StringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::new(ByteSlice::decode_value(reader, header)?.as_slice())
+ }
+}
+
+impl EncodeValue for Ia5StringRef<'_> {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.inner.encode_value(writer)
+ }
+}
+
+impl<'a> FixedTag for Ia5StringRef<'a> {
+ const TAG: Tag = Tag::Ia5String;
+}
+
+impl OrdIsValueOrd for Ia5StringRef<'_> {}
+
+impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> {
+ fn from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for Ia5StringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<Ia5StringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> {
+ fn from(printable_string: Ia5StringRef<'a>) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::Ia5String, printable_string.inner.into())
+ }
+}
+
+impl<'a> fmt::Display for Ia5StringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl<'a> fmt::Debug for Ia5StringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Ia5String({:?})", self.as_str())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Ia5StringRef;
+ use crate::Decode;
+ use hex_literal::hex;
+
+ #[test]
+ fn parse_bytes() {
+ let example_bytes = hex!("16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d");
+ let printable_string = Ia5StringRef::from_der(&example_bytes).unwrap();
+ assert_eq!(printable_string.as_str(), "test1@rsa.com");
+ }
+}
diff --git a/vendor/der/src/asn1/integer.rs b/vendor/der/src/asn1/integer.rs
new file mode 100644
index 000000000..20e2f018f
--- /dev/null
+++ b/vendor/der/src/asn1/integer.rs
@@ -0,0 +1,276 @@
+//! ASN.1 `INTEGER` support.
+
+pub(super) mod bigint;
+pub(super) mod int;
+pub(super) mod uint;
+
+use crate::{
+ asn1::AnyRef, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader,
+ Result, SliceWriter, Tag, ValueOrd, Writer,
+};
+use core::{cmp::Ordering, mem};
+
+macro_rules! impl_int_encoding {
+ ($($int:ty => $uint:ty),+) => {
+ $(
+ impl<'a> DecodeValue<'a> for $int {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let bytes = ByteSlice::decode_value(reader, header)?.as_slice();
+
+ let result = if is_highest_bit_set(bytes) {
+ <$uint>::from_be_bytes(int::decode_to_array(bytes)?) as $int
+ } else {
+ Self::from_be_bytes(uint::decode_to_array(bytes)?)
+ };
+
+ // Ensure we compute the same encoded length as the original any value
+ if header.length != result.value_len()? {
+ return Err(Self::TAG.non_canonical_error());
+ }
+
+ Ok(result)
+ }
+ }
+
+ impl EncodeValue for $int {
+ fn value_len(&self) -> Result<Length> {
+ if *self < 0 {
+ int::encoded_len(&(*self as $uint).to_be_bytes())
+ } else {
+ uint::encoded_len(&self.to_be_bytes())
+ }
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ if *self < 0 {
+ int::encode_bytes(writer, &(*self as $uint).to_be_bytes())
+ } else {
+ uint::encode_bytes(writer, &self.to_be_bytes())
+ }
+ }
+ }
+
+ impl FixedTag for $int {
+ const TAG: Tag = Tag::Integer;
+ }
+
+ impl ValueOrd for $int {
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ value_cmp(*self, *other)
+ }
+ }
+
+ impl TryFrom<AnyRef<'_>> for $int {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'_>) -> Result<Self> {
+ any.decode_into()
+ }
+ }
+ )+
+ };
+}
+
+macro_rules! impl_uint_encoding {
+ ($($uint:ty),+) => {
+ $(
+ impl<'a> DecodeValue<'a> for $uint {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let bytes = ByteSlice::decode_value(reader, header)?.as_slice();
+ let result = Self::from_be_bytes(uint::decode_to_array(bytes)?);
+
+ // Ensure we compute the same encoded length as the original any value
+ if header.length != result.value_len()? {
+ return Err(Self::TAG.non_canonical_error());
+ }
+
+ Ok(result)
+ }
+ }
+
+ impl EncodeValue for $uint {
+ fn value_len(&self) -> Result<Length> {
+ uint::encoded_len(&self.to_be_bytes())
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ uint::encode_bytes(writer, &self.to_be_bytes())
+ }
+ }
+
+ impl FixedTag for $uint {
+ const TAG: Tag = Tag::Integer;
+ }
+
+ impl ValueOrd for $uint {
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ value_cmp(*self, *other)
+ }
+ }
+
+ impl TryFrom<AnyRef<'_>> for $uint {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'_>) -> Result<Self> {
+ any.decode_into()
+ }
+ }
+ )+
+ };
+}
+
+impl_int_encoding!(i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128);
+impl_uint_encoding!(u8, u16, u32, u64, u128);
+
+/// Is the highest bit of the first byte in the slice 1? (if present)
+#[inline]
+fn is_highest_bit_set(bytes: &[u8]) -> bool {
+ bytes
+ .get(0)
+ .map(|byte| byte & 0b10000000 != 0)
+ .unwrap_or(false)
+}
+
+/// Compare two integer values
+fn value_cmp<T>(a: T, b: T) -> Result<Ordering>
+where
+ T: Copy + EncodeValue + Sized,
+{
+ const MAX_INT_SIZE: usize = 16;
+ debug_assert!(mem::size_of::<T>() <= MAX_INT_SIZE);
+
+ let mut buf1 = [0u8; MAX_INT_SIZE];
+ let mut encoder1 = SliceWriter::new(&mut buf1);
+ a.encode_value(&mut encoder1)?;
+
+ let mut buf2 = [0u8; MAX_INT_SIZE];
+ let mut encoder2 = SliceWriter::new(&mut buf2);
+ b.encode_value(&mut encoder2)?;
+
+ Ok(encoder1.finish()?.cmp(encoder2.finish()?))
+}
+
+#[cfg(test)]
+pub(crate) mod tests {
+ use crate::{Decode, Encode};
+
+ // Vectors from Section 5.7 of:
+ // https://luca.ntop.org/Teaching/Appunti/asn1.html
+ pub(crate) const I0_BYTES: &[u8] = &[0x02, 0x01, 0x00];
+ pub(crate) const I127_BYTES: &[u8] = &[0x02, 0x01, 0x7F];
+ pub(crate) const I128_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0x80];
+ pub(crate) const I256_BYTES: &[u8] = &[0x02, 0x02, 0x01, 0x00];
+ pub(crate) const INEG128_BYTES: &[u8] = &[0x02, 0x01, 0x80];
+ pub(crate) const INEG129_BYTES: &[u8] = &[0x02, 0x02, 0xFF, 0x7F];
+
+ // Additional vectors
+ pub(crate) const I255_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0xFF];
+ pub(crate) const I32767_BYTES: &[u8] = &[0x02, 0x02, 0x7F, 0xFF];
+ pub(crate) const I65535_BYTES: &[u8] = &[0x02, 0x03, 0x00, 0xFF, 0xFF];
+ pub(crate) const INEG32768_BYTES: &[u8] = &[0x02, 0x02, 0x80, 0x00];
+
+ #[test]
+ fn decode_i8() {
+ assert_eq!(0, i8::from_der(I0_BYTES).unwrap());
+ assert_eq!(127, i8::from_der(I127_BYTES).unwrap());
+ assert_eq!(-128, i8::from_der(INEG128_BYTES).unwrap());
+ }
+
+ #[test]
+ fn decode_i16() {
+ assert_eq!(0, i16::from_der(I0_BYTES).unwrap());
+ assert_eq!(127, i16::from_der(I127_BYTES).unwrap());
+ assert_eq!(128, i16::from_der(I128_BYTES).unwrap());
+ assert_eq!(255, i16::from_der(I255_BYTES).unwrap());
+ assert_eq!(256, i16::from_der(I256_BYTES).unwrap());
+ assert_eq!(32767, i16::from_der(I32767_BYTES).unwrap());
+ assert_eq!(-128, i16::from_der(INEG128_BYTES).unwrap());
+ assert_eq!(-129, i16::from_der(INEG129_BYTES).unwrap());
+ assert_eq!(-32768, i16::from_der(INEG32768_BYTES).unwrap());
+ }
+
+ #[test]
+ fn decode_u8() {
+ assert_eq!(0, u8::from_der(I0_BYTES).unwrap());
+ assert_eq!(127, u8::from_der(I127_BYTES).unwrap());
+ assert_eq!(255, u8::from_der(I255_BYTES).unwrap());
+ }
+
+ #[test]
+ fn decode_u16() {
+ assert_eq!(0, u16::from_der(I0_BYTES).unwrap());
+ assert_eq!(127, u16::from_der(I127_BYTES).unwrap());
+ assert_eq!(255, u16::from_der(I255_BYTES).unwrap());
+ assert_eq!(256, u16::from_der(I256_BYTES).unwrap());
+ assert_eq!(32767, u16::from_der(I32767_BYTES).unwrap());
+ assert_eq!(65535, u16::from_der(I65535_BYTES).unwrap());
+ }
+
+ #[test]
+ fn encode_i8() {
+ let mut buffer = [0u8; 3];
+
+ assert_eq!(I0_BYTES, 0i8.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I127_BYTES, 127i8.encode_to_slice(&mut buffer).unwrap());
+
+ assert_eq!(
+ INEG128_BYTES,
+ (-128i8).encode_to_slice(&mut buffer).unwrap()
+ );
+ }
+
+ #[test]
+ fn encode_i16() {
+ let mut buffer = [0u8; 4];
+ assert_eq!(I0_BYTES, 0i16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I127_BYTES, 127i16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I128_BYTES, 128i16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I255_BYTES, 255i16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I256_BYTES, 256i16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I32767_BYTES, 32767i16.encode_to_slice(&mut buffer).unwrap());
+
+ assert_eq!(
+ INEG128_BYTES,
+ (-128i16).encode_to_slice(&mut buffer).unwrap()
+ );
+
+ assert_eq!(
+ INEG129_BYTES,
+ (-129i16).encode_to_slice(&mut buffer).unwrap()
+ );
+
+ assert_eq!(
+ INEG32768_BYTES,
+ (-32768i16).encode_to_slice(&mut buffer).unwrap()
+ );
+ }
+
+ #[test]
+ fn encode_u8() {
+ let mut buffer = [0u8; 4];
+ assert_eq!(I0_BYTES, 0u8.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I127_BYTES, 127u8.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I255_BYTES, 255u8.encode_to_slice(&mut buffer).unwrap());
+ }
+
+ #[test]
+ fn encode_u16() {
+ let mut buffer = [0u8; 5];
+ assert_eq!(I0_BYTES, 0u16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I127_BYTES, 127u16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I128_BYTES, 128u16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I255_BYTES, 255u16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I256_BYTES, 256u16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I32767_BYTES, 32767u16.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(I65535_BYTES, 65535u16.encode_to_slice(&mut buffer).unwrap());
+ }
+
+ /// Integers must be encoded with a minimum number of octets
+ #[test]
+ fn reject_non_canonical() {
+ assert!(i8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ assert!(i16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ assert!(u8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ assert!(u16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
+ }
+}
diff --git a/vendor/der/src/asn1/integer/bigint.rs b/vendor/der/src/asn1/integer/bigint.rs
new file mode 100644
index 000000000..f896406a6
--- /dev/null
+++ b/vendor/der/src/asn1/integer/bigint.rs
@@ -0,0 +1,152 @@
+//! "Big" ASN.1 `INTEGER` types.
+
+use super::uint;
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind,
+ FixedTag, Header, Length, Reader, Result, Tag, Writer,
+};
+
+/// "Big" unsigned ASN.1 `INTEGER` type.
+///
+/// Provides direct access to the underlying big endian bytes which comprise an
+/// unsigned integer value.
+///
+/// Intended for use cases like very large integers that are used in
+/// cryptographic applications (e.g. keys, signatures).
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct UIntRef<'a> {
+ /// Inner value
+ inner: ByteSlice<'a>,
+}
+
+impl<'a> UIntRef<'a> {
+ /// Create a new [`UIntRef`] from a byte slice.
+ pub fn new(bytes: &'a [u8]) -> Result<Self> {
+ let inner = ByteSlice::new(uint::strip_leading_zeroes(bytes))
+ .map_err(|_| ErrorKind::Length { tag: Self::TAG })?;
+
+ Ok(Self { inner })
+ }
+
+ /// Borrow the inner byte slice which contains the least significant bytes
+ /// of a big endian integer value with all leading zeros stripped.
+ pub fn as_bytes(&self) -> &'a [u8] {
+ self.inner.as_slice()
+ }
+
+ /// Get the length of this [`UIntRef`] in bytes.
+ pub fn len(&self) -> Length {
+ self.inner.len()
+ }
+
+ /// Is the inner byte slice empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+}
+
+impl<'a> DecodeValue<'a> for UIntRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let bytes = ByteSlice::decode_value(reader, header)?.as_slice();
+ let result = Self::new(uint::decode_to_slice(bytes)?)?;
+
+ // Ensure we compute the same encoded length as the original any value.
+ if result.value_len()? != header.length {
+ return Err(Self::TAG.non_canonical_error());
+ }
+
+ Ok(result)
+ }
+}
+
+impl<'a> EncodeValue for UIntRef<'a> {
+ fn value_len(&self) -> Result<Length> {
+ uint::encoded_len(self.inner.as_slice())
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ // Add leading `0x00` byte if required
+ if self.value_len()? > self.len() {
+ writer.write_byte(0)?;
+ }
+
+ writer.write(self.as_bytes())
+ }
+}
+
+impl<'a> From<&UIntRef<'a>> for UIntRef<'a> {
+ fn from(value: &UIntRef<'a>) -> UIntRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for UIntRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<UIntRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> FixedTag for UIntRef<'a> {
+ const TAG: Tag = Tag::Integer;
+}
+
+impl<'a> OrdIsValueOrd for UIntRef<'a> {}
+
+#[cfg(test)]
+mod tests {
+ use super::UIntRef;
+ use crate::{
+ asn1::{integer::tests::*, AnyRef},
+ Decode, Encode, ErrorKind, SliceWriter, Tag,
+ };
+
+ #[test]
+ fn decode_uint_bytes() {
+ assert_eq!(&[0], UIntRef::from_der(I0_BYTES).unwrap().as_bytes());
+ assert_eq!(&[127], UIntRef::from_der(I127_BYTES).unwrap().as_bytes());
+ assert_eq!(&[128], UIntRef::from_der(I128_BYTES).unwrap().as_bytes());
+ assert_eq!(&[255], UIntRef::from_der(I255_BYTES).unwrap().as_bytes());
+
+ assert_eq!(
+ &[0x01, 0x00],
+ UIntRef::from_der(I256_BYTES).unwrap().as_bytes()
+ );
+
+ assert_eq!(
+ &[0x7F, 0xFF],
+ UIntRef::from_der(I32767_BYTES).unwrap().as_bytes()
+ );
+ }
+
+ #[test]
+ fn encode_uint_bytes() {
+ for &example in &[
+ I0_BYTES,
+ I127_BYTES,
+ I128_BYTES,
+ I255_BYTES,
+ I256_BYTES,
+ I32767_BYTES,
+ ] {
+ let uint = UIntRef::from_der(example).unwrap();
+
+ let mut buf = [0u8; 128];
+ let mut encoder = SliceWriter::new(&mut buf);
+ uint.encode(&mut encoder).unwrap();
+
+ let result = encoder.finish().unwrap();
+ assert_eq!(example, result);
+ }
+ }
+
+ #[test]
+ fn reject_oversize_without_extra_zero() {
+ let err = UIntRef::try_from(AnyRef::new(Tag::Integer, &[0x81]).unwrap())
+ .err()
+ .unwrap();
+
+ assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer });
+ }
+}
diff --git a/vendor/der/src/asn1/integer/int.rs b/vendor/der/src/asn1/integer/int.rs
new file mode 100644
index 000000000..a9fe43890
--- /dev/null
+++ b/vendor/der/src/asn1/integer/int.rs
@@ -0,0 +1,55 @@
+//! Support for encoding negative integers
+
+use super::is_highest_bit_set;
+use crate::{ErrorKind, Length, Result, Writer};
+
+/// Decode an unsigned integer of the specified size.
+///
+/// Returns a byte array of the requested size containing a big endian integer.
+pub(super) fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> {
+ match N.checked_sub(bytes.len()) {
+ Some(offset) => {
+ let mut output = [0xFFu8; N];
+ output[offset..].copy_from_slice(bytes);
+ Ok(output)
+ }
+ None => {
+ let expected_len = Length::try_from(N)?;
+ let actual_len = Length::try_from(bytes.len())?;
+
+ Err(ErrorKind::Incomplete {
+ expected_len,
+ actual_len,
+ }
+ .into())
+ }
+ }
+}
+
+/// Encode the given big endian bytes representing an integer as ASN.1 DER.
+pub(super) fn encode_bytes<W>(writer: &mut W, bytes: &[u8]) -> Result<()>
+where
+ W: Writer + ?Sized,
+{
+ writer.write(strip_leading_ones(bytes))
+}
+
+/// Get the encoded length for the given unsigned integer serialized as bytes.
+#[inline]
+pub(super) fn encoded_len(bytes: &[u8]) -> Result<Length> {
+ Length::try_from(strip_leading_ones(bytes).len())
+}
+
+/// Strip the leading all-ones bytes from the given byte slice.
+fn strip_leading_ones(mut bytes: &[u8]) -> &[u8] {
+ while let Some((byte, rest)) = bytes.split_first() {
+ if *byte == 0xFF && is_highest_bit_set(rest) {
+ bytes = rest;
+ continue;
+ }
+
+ break;
+ }
+
+ bytes
+}
diff --git a/vendor/der/src/asn1/integer/uint.rs b/vendor/der/src/asn1/integer/uint.rs
new file mode 100644
index 000000000..e45a72f2a
--- /dev/null
+++ b/vendor/der/src/asn1/integer/uint.rs
@@ -0,0 +1,116 @@
+//! Unsigned integer decoders/encoders.
+
+use crate::{Length, Result, Tag, Writer};
+
+/// Decode an unsigned integer into a big endian byte slice with all leading
+/// zeroes removed.
+///
+/// Returns a byte array of the requested size containing a big endian integer.
+pub(crate) fn decode_to_slice(bytes: &[u8]) -> Result<&[u8]> {
+ // The `INTEGER` type always encodes a signed value, so for unsigned
+ // values the leading `0x00` byte may need to be removed.
+ //
+ // We also disallow a leading byte which would overflow a signed ASN.1
+ // integer (since we're decoding an unsigned integer).
+ // We expect all such cases to have a leading `0x00` byte.
+ match bytes {
+ [] => Err(Tag::Integer.non_canonical_error()),
+ [0] => Ok(bytes),
+ [0, byte, ..] if *byte < 0x80 => Err(Tag::Integer.non_canonical_error()),
+ [0, rest @ ..] => Ok(rest),
+ [byte, ..] if *byte >= 0x80 => Err(Tag::Integer.value_error()),
+ _ => Ok(bytes),
+ }
+}
+
+/// Decode an unsigned integer into a byte array of the requested size
+/// containing a big endian integer.
+pub(super) fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> {
+ let input = decode_to_slice(bytes)?;
+
+ // Compute number of leading zeroes to add
+ let num_zeroes = N
+ .checked_sub(input.len())
+ .ok_or_else(|| Tag::Integer.length_error())?;
+
+ // Copy input into `N`-sized output buffer with leading zeroes
+ let mut output = [0u8; N];
+ output[num_zeroes..].copy_from_slice(input);
+ Ok(output)
+}
+
+/// Encode the given big endian bytes representing an integer as ASN.1 DER.
+pub(crate) fn encode_bytes<W>(encoder: &mut W, bytes: &[u8]) -> Result<()>
+where
+ W: Writer + ?Sized,
+{
+ let bytes = strip_leading_zeroes(bytes);
+
+ if needs_leading_zero(bytes) {
+ encoder.write_byte(0)?;
+ }
+
+ encoder.write(bytes)
+}
+
+/// Get the encoded length for the given unsigned integer serialized as bytes.
+#[inline]
+pub(crate) fn encoded_len(bytes: &[u8]) -> Result<Length> {
+ let bytes = strip_leading_zeroes(bytes);
+ Length::try_from(bytes.len())? + u8::from(needs_leading_zero(bytes))
+}
+
+/// Strip the leading zeroes from the given byte slice
+pub(crate) fn strip_leading_zeroes(mut bytes: &[u8]) -> &[u8] {
+ while let Some((byte, rest)) = bytes.split_first() {
+ if *byte == 0 && !rest.is_empty() {
+ bytes = rest;
+ } else {
+ break;
+ }
+ }
+
+ bytes
+}
+
+/// Does the given integer need a leading zero?
+fn needs_leading_zero(bytes: &[u8]) -> bool {
+ matches!(bytes.get(0), Some(byte) if *byte >= 0x80)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::decode_to_array;
+ use crate::{ErrorKind, Tag};
+
+ #[test]
+ fn decode_to_array_no_leading_zero() {
+ let arr = decode_to_array::<4>(&[1, 2]).unwrap();
+ assert_eq!(arr, [0, 0, 1, 2]);
+ }
+
+ #[test]
+ fn decode_to_array_leading_zero() {
+ let arr = decode_to_array::<4>(&[0x00, 0xFF, 0xFE]).unwrap();
+ assert_eq!(arr, [0x00, 0x00, 0xFF, 0xFE]);
+ }
+
+ #[test]
+ fn decode_to_array_extra_zero() {
+ let err = decode_to_array::<4>(&[0, 1, 2]).err().unwrap();
+ assert_eq!(err.kind(), ErrorKind::Noncanonical { tag: Tag::Integer });
+ }
+
+ #[test]
+ fn decode_to_array_missing_zero() {
+ // We're decoding an unsigned integer, but this value would be signed
+ let err = decode_to_array::<4>(&[0xFF, 0xFE]).err().unwrap();
+ assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer });
+ }
+
+ #[test]
+ fn decode_to_array_oversized_input() {
+ let err = decode_to_array::<1>(&[1, 2, 3]).err().unwrap();
+ assert_eq!(err.kind(), ErrorKind::Length { tag: Tag::Integer });
+ }
+}
diff --git a/vendor/der/src/asn1/null.rs b/vendor/der/src/asn1/null.rs
new file mode 100644
index 000000000..e87729f18
--- /dev/null
+++ b/vendor/der/src/asn1/null.rs
@@ -0,0 +1,108 @@
+//! ASN.1 `NULL` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind,
+ FixedTag, Header, Length, Reader, Result, Tag, Writer,
+};
+
+/// ASN.1 `NULL` type.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct Null;
+
+impl<'a> DecodeValue<'a> for Null {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ if header.length.is_zero() {
+ Ok(Null)
+ } else {
+ Err(reader.error(ErrorKind::Length { tag: Self::TAG }))
+ }
+ }
+}
+
+impl EncodeValue for Null {
+ fn value_len(&self) -> Result<Length> {
+ Ok(Length::ZERO)
+ }
+
+ fn encode_value(&self, _writer: &mut dyn Writer) -> Result<()> {
+ Ok(())
+ }
+}
+
+impl FixedTag for Null {
+ const TAG: Tag = Tag::Null;
+}
+
+impl OrdIsValueOrd for Null {}
+
+impl<'a> From<Null> for AnyRef<'a> {
+ fn from(_: Null) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::Null, ByteSlice::default())
+ }
+}
+
+impl TryFrom<AnyRef<'_>> for Null {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'_>) -> Result<Null> {
+ any.decode_into()
+ }
+}
+
+impl TryFrom<AnyRef<'_>> for () {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'_>) -> Result<()> {
+ Null::try_from(any).map(|_| ())
+ }
+}
+
+impl<'a> From<()> for AnyRef<'a> {
+ fn from(_: ()) -> AnyRef<'a> {
+ Null.into()
+ }
+}
+
+impl<'a> DecodeValue<'a> for () {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Null::decode_value(reader, header)?;
+ Ok(())
+ }
+}
+
+impl EncodeValue for () {
+ fn value_len(&self) -> Result<Length> {
+ Ok(Length::ZERO)
+ }
+
+ fn encode_value(&self, _writer: &mut dyn Writer) -> Result<()> {
+ Ok(())
+ }
+}
+
+impl FixedTag for () {
+ const TAG: Tag = Tag::Null;
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Null;
+ use crate::{Decode, Encode};
+
+ #[test]
+ fn decode() {
+ Null::from_der(&[0x05, 0x00]).unwrap();
+ }
+
+ #[test]
+ fn encode() {
+ let mut buffer = [0u8; 2];
+ assert_eq!(&[0x05, 0x00], Null.encode_to_slice(&mut buffer).unwrap());
+ assert_eq!(&[0x05, 0x00], ().encode_to_slice(&mut buffer).unwrap());
+ }
+
+ #[test]
+ fn reject_non_canonical() {
+ assert!(Null::from_der(&[0x05, 0x81, 0x00]).is_err());
+ }
+}
diff --git a/vendor/der/src/asn1/octet_string.rs b/vendor/der/src/asn1/octet_string.rs
new file mode 100644
index 000000000..6f5b91a3d
--- /dev/null
+++ b/vendor/der/src/asn1/octet_string.rs
@@ -0,0 +1,182 @@
+//! ASN.1 `OCTET STRING` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, ErrorKind,
+ FixedTag, Header, Length, Reader, Result, Tag, Writer,
+};
+
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// ASN.1 `OCTET STRING` type: borrowed form.
+///
+/// Octet strings represent contiguous sequences of octets, a.k.a. bytes.
+///
+/// This is a zero-copy reference type which borrows from the input data.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct OctetStringRef<'a> {
+ /// Inner value
+ inner: ByteSlice<'a>,
+}
+
+impl<'a> OctetStringRef<'a> {
+ /// Create a new ASN.1 `OCTET STRING` from a byte slice.
+ pub fn new(slice: &'a [u8]) -> Result<Self> {
+ ByteSlice::new(slice)
+ .map(|inner| Self { inner })
+ .map_err(|_| ErrorKind::Length { tag: Self::TAG }.into())
+ }
+
+ /// Borrow the inner byte slice.
+ pub fn as_bytes(&self) -> &'a [u8] {
+ self.inner.as_slice()
+ }
+
+ /// Get the length of the inner byte slice.
+ pub fn len(&self) -> Length {
+ self.inner.len()
+ }
+
+ /// Is the inner byte slice empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+}
+
+impl AsRef<[u8]> for OctetStringRef<'_> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for OctetStringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let inner = ByteSlice::decode_value(reader, header)?;
+ Ok(Self { inner })
+ }
+}
+
+impl EncodeValue for OctetStringRef<'_> {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.inner.encode_value(writer)
+ }
+}
+
+impl FixedTag for OctetStringRef<'_> {
+ const TAG: Tag = Tag::OctetString;
+}
+
+impl OrdIsValueOrd for OctetStringRef<'_> {}
+
+impl<'a> From<&OctetStringRef<'a>> for OctetStringRef<'a> {
+ fn from(value: &OctetStringRef<'a>) -> OctetStringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for OctetStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<OctetStringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> From<OctetStringRef<'a>> for AnyRef<'a> {
+ fn from(octet_string: OctetStringRef<'a>) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::OctetString, octet_string.inner)
+ }
+}
+
+impl<'a> From<OctetStringRef<'a>> for &'a [u8] {
+ fn from(octet_string: OctetStringRef<'a>) -> &'a [u8] {
+ octet_string.as_bytes()
+ }
+}
+
+/// ASN.1 `OCTET STRING` type: owned form..
+///
+/// Octet strings represent contiguous sequences of octets, a.k.a. bytes.
+///
+/// This type provides the same functionality as [`OctetStringRef`] but owns
+/// the backing data.
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct OctetString {
+ /// Bitstring represented as a slice of bytes.
+ inner: Vec<u8>,
+}
+
+#[cfg(feature = "alloc")]
+impl OctetString {
+ /// Create a new ASN.1 `OCTET STRING`.
+ pub fn new(bytes: impl Into<Vec<u8>>) -> Result<Self> {
+ let inner = bytes.into();
+
+ // Ensure the bytes parse successfully as an `OctetStringRef`
+ OctetStringRef::new(&inner)?;
+
+ Ok(Self { inner })
+ }
+
+ /// Borrow the inner byte slice.
+ pub fn as_bytes(&self) -> &[u8] {
+ self.inner.as_slice()
+ }
+
+ /// Get the length of the inner byte slice.
+ pub fn len(&self) -> Length {
+ self.value_len().expect("invalid OCTET STRING length")
+ }
+
+ /// Is the inner byte slice empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl AsRef<[u8]> for OctetString {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> DecodeValue<'a> for OctetString {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::new(reader.read_vec(header.length)?)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl EncodeValue for OctetString {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.len().try_into()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write(&self.inner)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl FixedTag for OctetString {
+ const TAG: Tag = Tag::OctetString;
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> From<&'a OctetString> for OctetStringRef<'a> {
+ fn from(octet_string: &'a OctetString) -> OctetStringRef<'a> {
+ // Ensured to parse successfully in constructor
+ OctetStringRef::new(&octet_string.inner).expect("invalid OCTET STRING")
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl OrdIsValueOrd for OctetString {}
diff --git a/vendor/der/src/asn1/oid.rs b/vendor/der/src/asn1/oid.rs
new file mode 100644
index 000000000..8b287183d
--- /dev/null
+++ b/vendor/der/src/asn1/oid.rs
@@ -0,0 +1,90 @@
+//! ASN.1 `OBJECT IDENTIFIER`
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, DecodeValue, EncodeValue, Error, FixedTag, Header, Length,
+ Reader, Result, Tag, Tagged, Writer,
+};
+use const_oid::ObjectIdentifier;
+
+impl<'a> DecodeValue<'a> for ObjectIdentifier {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let mut buf = [0u8; ObjectIdentifier::MAX_SIZE];
+ let slice = buf
+ .get_mut(..header.length.try_into()?)
+ .ok_or_else(|| Self::TAG.length_error())?;
+
+ let actual_len = reader.read_into(slice)?.len();
+ debug_assert_eq!(actual_len, header.length.try_into()?);
+ Ok(Self::from_bytes(slice)?)
+ }
+}
+
+impl EncodeValue for ObjectIdentifier {
+ fn value_len(&self) -> Result<Length> {
+ Length::try_from(self.as_bytes().len())
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ writer.write(self.as_bytes())
+ }
+}
+
+impl FixedTag for ObjectIdentifier {
+ const TAG: Tag = Tag::ObjectIdentifier;
+}
+
+impl OrdIsValueOrd for ObjectIdentifier {}
+
+impl<'a> From<&'a ObjectIdentifier> for AnyRef<'a> {
+ fn from(oid: &'a ObjectIdentifier) -> AnyRef<'a> {
+ // Note: ensuring an infallible conversion is possible relies on the
+ // invariant that `const_oid::MAX_LEN <= Length::max()`.
+ //
+ // The `length()` test below ensures this is the case.
+ let value = oid
+ .as_bytes()
+ .try_into()
+ .expect("OID length invariant violated");
+
+ AnyRef::from_tag_and_value(Tag::ObjectIdentifier, value)
+ }
+}
+
+impl TryFrom<AnyRef<'_>> for ObjectIdentifier {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'_>) -> Result<ObjectIdentifier> {
+ any.tag().assert_eq(Tag::ObjectIdentifier)?;
+ Ok(ObjectIdentifier::from_bytes(any.value())?)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::ObjectIdentifier;
+ use crate::{Decode, Encode, Length};
+
+ const EXAMPLE_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549");
+ const EXAMPLE_OID_BYTES: &[u8; 8] = &[0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d];
+
+ #[test]
+ fn decode() {
+ let oid = ObjectIdentifier::from_der(EXAMPLE_OID_BYTES).unwrap();
+ assert_eq!(EXAMPLE_OID, oid);
+ }
+
+ #[test]
+ fn encode() {
+ let mut buffer = [0u8; 8];
+ assert_eq!(
+ EXAMPLE_OID_BYTES,
+ EXAMPLE_OID.encode_to_slice(&mut buffer).unwrap()
+ );
+ }
+
+ #[test]
+ fn length() {
+ // Ensure an infallible `From` conversion to `Any` will never panic
+ assert!(ObjectIdentifier::MAX_SIZE <= Length::MAX.try_into().unwrap());
+ }
+}
diff --git a/vendor/der/src/asn1/optional.rs b/vendor/der/src/asn1/optional.rs
new file mode 100644
index 000000000..a9b923ccc
--- /dev/null
+++ b/vendor/der/src/asn1/optional.rs
@@ -0,0 +1,66 @@
+//! ASN.1 `OPTIONAL` as mapped to Rust's `Option` type
+
+use crate::{Choice, Decode, DerOrd, Encode, Length, Reader, Result, Tag, Writer};
+use core::cmp::Ordering;
+
+impl<'a, T> Decode<'a> for Option<T>
+where
+ T: Choice<'a>, // NOTE: all `Decode + Tagged` types receive a blanket `Choice` impl
+{
+ fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Option<T>> {
+ if let Some(byte) = reader.peek_byte() {
+ if T::can_decode(Tag::try_from(byte)?) {
+ return T::decode(reader).map(Some);
+ }
+ }
+
+ Ok(None)
+ }
+}
+
+impl<T> DerOrd for Option<T>
+where
+ T: DerOrd,
+{
+ fn der_cmp(&self, other: &Self) -> Result<Ordering> {
+ match self {
+ Some(a) => match other {
+ Some(b) => a.der_cmp(b),
+ None => Ok(Ordering::Greater),
+ },
+ None => Ok(Ordering::Less),
+ }
+ }
+}
+
+impl<T> Encode for Option<T>
+where
+ T: Encode,
+{
+ fn encoded_len(&self) -> Result<Length> {
+ (&self).encoded_len()
+ }
+
+ fn encode(&self, writer: &mut dyn Writer) -> Result<()> {
+ (&self).encode(writer)
+ }
+}
+
+impl<T> Encode for &Option<T>
+where
+ T: Encode,
+{
+ fn encoded_len(&self) -> Result<Length> {
+ match self {
+ Some(encodable) => encodable.encoded_len(),
+ None => Ok(0u8.into()),
+ }
+ }
+
+ fn encode(&self, encoder: &mut dyn Writer) -> Result<()> {
+ match self {
+ Some(encodable) => encodable.encode(encoder),
+ None => Ok(()),
+ }
+ }
+}
diff --git a/vendor/der/src/asn1/printable_string.rs b/vendor/der/src/asn1/printable_string.rs
new file mode 100644
index 000000000..d48f90f09
--- /dev/null
+++ b/vendor/der/src/asn1/printable_string.rs
@@ -0,0 +1,168 @@
+//! ASN.1 `PrintableString` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header,
+ Length, Reader, Result, StrSlice, Tag, Writer,
+};
+use core::{fmt, ops::Deref, str};
+
+/// ASN.1 `PrintableString` type.
+///
+/// Supports a subset the ASCII character set (described below).
+///
+/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
+/// For the full ASCII character set, use
+/// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
+///
+/// This is a zero-copy reference type which borrows from the input data.
+///
+/// # Supported characters
+///
+/// The following ASCII characters/ranges are supported:
+///
+/// - `A..Z`
+/// - `a..z`
+/// - `0..9`
+/// - "` `" (i.e. space)
+/// - `\`
+/// - `(`
+/// - `)`
+/// - `+`
+/// - `,`
+/// - `-`
+/// - `.`
+/// - `/`
+/// - `:`
+/// - `=`
+/// - `?`
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
+pub struct PrintableStringRef<'a> {
+ /// Inner value
+ inner: StrSlice<'a>,
+}
+
+impl<'a> PrintableStringRef<'a> {
+ /// Create a new ASN.1 `PrintableString`.
+ pub fn new<T>(input: &'a T) -> Result<Self>
+ where
+ T: AsRef<[u8]> + ?Sized,
+ {
+ let input = input.as_ref();
+
+ // Validate all characters are within PrintableString's allowed set
+ for &c in input.iter() {
+ match c {
+ b'A'..=b'Z'
+ | b'a'..=b'z'
+ | b'0'..=b'9'
+ | b' '
+ | b'\''
+ | b'('
+ | b')'
+ | b'+'
+ | b','
+ | b'-'
+ | b'.'
+ | b'/'
+ | b':'
+ | b'='
+ | b'?' => (),
+ _ => return Err(Self::TAG.value_error()),
+ }
+ }
+
+ StrSlice::from_bytes(input)
+ .map(|inner| Self { inner })
+ .map_err(|_| Self::TAG.value_error())
+ }
+}
+
+impl<'a> Deref for PrintableStringRef<'a> {
+ type Target = StrSlice<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl AsRef<str> for PrintableStringRef<'_> {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for PrintableStringRef<'_> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for PrintableStringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::new(ByteSlice::decode_value(reader, header)?.as_slice())
+ }
+}
+
+impl<'a> EncodeValue for PrintableStringRef<'a> {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.inner.encode_value(writer)
+ }
+}
+
+impl FixedTag for PrintableStringRef<'_> {
+ const TAG: Tag = Tag::PrintableString;
+}
+
+impl OrdIsValueOrd for PrintableStringRef<'_> {}
+
+impl<'a> From<&PrintableStringRef<'a>> for PrintableStringRef<'a> {
+ fn from(value: &PrintableStringRef<'a>) -> PrintableStringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for PrintableStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<PrintableStringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> From<PrintableStringRef<'a>> for AnyRef<'a> {
+ fn from(printable_string: PrintableStringRef<'a>) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::PrintableString, printable_string.inner.into())
+ }
+}
+
+impl<'a> fmt::Display for PrintableStringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl<'a> fmt::Debug for PrintableStringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "PrintableString({:?})", self.as_str())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::PrintableStringRef;
+ use crate::Decode;
+
+ #[test]
+ fn parse_bytes() {
+ let example_bytes = &[
+ 0x13, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
+ ];
+
+ let printable_string = PrintableStringRef::from_der(example_bytes).unwrap();
+ assert_eq!(printable_string.as_str(), "Test User 1");
+ }
+}
diff --git a/vendor/der/src/asn1/real.rs b/vendor/der/src/asn1/real.rs
new file mode 100644
index 000000000..f872d2d0b
--- /dev/null
+++ b/vendor/der/src/asn1/real.rs
@@ -0,0 +1,993 @@
+//! ASN.1 `REAL` support.
+
+// TODO(tarcieri): checked arithmetic
+#![allow(
+ clippy::cast_lossless,
+ clippy::cast_sign_loss,
+ clippy::integer_arithmetic
+)]
+
+use crate::{
+ str_slice::StrSlice, ByteSlice, DecodeValue, EncodeValue, FixedTag, Header, Length, Reader,
+ Result, Tag, Writer,
+};
+
+use super::integer::uint::strip_leading_zeroes;
+
+#[cfg_attr(docsrs, doc(cfg(feature = "real")))]
+impl<'a> DecodeValue<'a> for f64 {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let bytes = ByteSlice::decode_value(reader, header)?.as_slice();
+
+ if header.length == Length::ZERO {
+ Ok(0.0)
+ } else if is_nth_bit_one::<7>(bytes) {
+ // Binary encoding from section 8.5.7 applies
+ let sign: u64 = if is_nth_bit_one::<6>(bytes) { 1 } else { 0 };
+
+ // Section 8.5.7.2: Check the base -- the DER specs say that only base 2 should be supported in DER
+ let base = mnth_bits_to_u8::<5, 4>(bytes);
+
+ if base != 0 {
+ // Real related error: base is not DER compliant (base encoded in enum)
+ return Err(Tag::Real.value_error());
+ }
+
+ // Section 8.5.7.3
+ let scaling_factor = mnth_bits_to_u8::<3, 2>(bytes);
+
+ // Section 8.5.7.4
+ let mantissa_start;
+ let exponent = match mnth_bits_to_u8::<1, 0>(bytes) {
+ 0 => {
+ mantissa_start = 2;
+ let ebytes = (i16::from_be_bytes([0x0, bytes[1]])).to_be_bytes();
+ u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ebytes[0], ebytes[1]])
+ }
+ 1 => {
+ mantissa_start = 3;
+ let ebytes = (i16::from_be_bytes([bytes[1], bytes[2]])).to_be_bytes();
+ u64::from_be_bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ebytes[0], ebytes[1]])
+ }
+ _ => {
+ // Real related error: encoded exponent cannot be represented on an IEEE-754 double
+ return Err(Tag::Real.value_error());
+ }
+ };
+ // Section 8.5.7.5: Read the remaining bytes for the mantissa
+ let mut n_bytes = [0x0; 8];
+ for (pos, byte) in bytes[mantissa_start..].iter().rev().enumerate() {
+ n_bytes[7 - pos] = *byte;
+ }
+ let n = u64::from_be_bytes(n_bytes);
+ // Multiply byt 2^F corresponds to just a left shift
+ let mantissa = n << scaling_factor;
+ // Create the f64
+ Ok(encode_f64(sign, exponent, mantissa))
+ } else if is_nth_bit_one::<6>(bytes) {
+ // This either a special value, or it's the value minus zero is encoded, section 8.5.9 applies
+ match mnth_bits_to_u8::<1, 0>(bytes) {
+ 0 => Ok(f64::INFINITY),
+ 1 => Ok(f64::NEG_INFINITY),
+ 2 => Ok(f64::NAN),
+ 3 => Ok(-0.0_f64),
+ _ => Err(Tag::Real.value_error()),
+ }
+ } else {
+ let astr = StrSlice::from_bytes(&bytes[1..])?;
+ match astr.inner.parse::<f64>() {
+ Ok(val) => Ok(val),
+ // Real related error: encoding not supported or malformed
+ Err(_) => Err(Tag::Real.value_error()),
+ }
+ }
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "real")))]
+impl EncodeValue for f64 {
+ fn value_len(&self) -> Result<Length> {
+ if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE {
+ // Zero: positive yet smaller than the minimum positive number
+ Ok(Length::ZERO)
+ } else if self.is_nan()
+ || self.is_infinite()
+ || (self.is_sign_negative() && -self < f64::MIN_POSITIVE)
+ {
+ // NaN, infinite (positive or negative), or negative zero (negative but its negative is less than the min positive number)
+ Ok(Length::ONE)
+ } else {
+ // The length is that of the first octets plus those needed for the exponent plus those needed for the mantissa
+ let (_sign, exponent, mantissa) = decode_f64(*self);
+
+ let exponent_len = if exponent == 0 {
+ // Section 8.5.7.4: there must be at least one octet for exponent encoding
+ // But, if the exponent is zero, it'll be skipped, so we make sure force it to 1
+ Length::ONE
+ } else {
+ let ebytes = exponent.to_be_bytes();
+ Length::try_from(strip_leading_zeroes(&ebytes).len())?
+ };
+
+ let mantissa_len = if mantissa == 0 {
+ Length::ONE
+ } else {
+ let mbytes = mantissa.to_be_bytes();
+ Length::try_from(strip_leading_zeroes(&mbytes).len())?
+ };
+
+ exponent_len + mantissa_len + Length::ONE
+ }
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ // Check if special value
+ // Encode zero first, if it's zero
+ // Special value from section 8.5.9 if non zero
+ if self.is_nan()
+ || self.is_infinite()
+ || (self.is_sign_negative() && -self < f64::MIN_POSITIVE)
+ || (self.is_sign_positive() && (*self) < f64::MIN_POSITIVE)
+ {
+ if self.is_sign_positive() && (*self) < f64::MIN_POSITIVE {
+ // Zero
+ return Ok(());
+ } else if self.is_nan() {
+ // Not a number
+ writer.write_byte(0b0100_0010)?;
+ } else if self.is_infinite() {
+ if self.is_sign_negative() {
+ // Negative infinity
+ writer.write_byte(0b0100_0001)?;
+ } else {
+ // Plus infinity
+ writer.write_byte(0b0100_0000)?;
+ }
+ } else {
+ // Minus zero
+ writer.write_byte(0b0100_0011)?;
+ }
+ } else {
+ // Always use binary encoding, set bit 8 to 1
+ let mut first_byte = 0b1000_0000;
+
+ if self.is_sign_negative() {
+ // Section 8.5.7.1: set bit 7 to 1 if negative
+ first_byte |= 0b0100_0000;
+ }
+
+ // Bits 6 and 5 are set to 0 to specify that binary encoding is used
+ //
+ // NOTE: the scaling factor is only used to align the implicit point of the mantissa.
+ // This is unnecessary in DER because the base is 2, and therefore necessarily aligned.
+ // Therefore, we do not modify the mantissa in anyway after this function call, which
+ // already adds the implicit one of the IEEE 754 representation.
+ let (_sign, exponent, mantissa) = decode_f64(*self);
+
+ // Encode the exponent as two's complement on 16 bits and remove the bias
+ let exponent_bytes = exponent.to_be_bytes();
+ let ebytes = strip_leading_zeroes(&exponent_bytes);
+
+ match ebytes.len() {
+ 0 | 1 => {}
+ 2 => first_byte |= 0b0000_0001,
+ 3 => first_byte |= 0b0000_0010,
+ _ => {
+ // TODO: support multi octet exponent encoding?
+ return Err(Tag::Real.value_error());
+ }
+ }
+
+ writer.write_byte(first_byte)?;
+
+ // Encode both bytes or just the last one, handled by encode_bytes directly
+ // Rust already encodes the data as two's complement, so no further processing is needed
+ writer.write(ebytes)?;
+
+ // Now, encode the mantissa as unsigned binary number
+ let mantissa_bytes = mantissa.to_be_bytes();
+ let mbytes = strip_leading_zeroes(&mantissa_bytes);
+ writer.write(mbytes)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "real")))]
+impl FixedTag for f64 {
+ const TAG: Tag = Tag::Real;
+}
+
+/// Is the N-th bit 1 in the first octet?
+/// NOTE: this function is zero indexed
+pub(crate) fn is_nth_bit_one<const N: usize>(bytes: &[u8]) -> bool {
+ if N < 8 {
+ bytes
+ .get(0)
+ .map(|byte| byte & (1 << N) != 0)
+ .unwrap_or(false)
+ } else {
+ false
+ }
+}
+
+/// Convert bits M, N into a u8, in the first octet only
+pub(crate) fn mnth_bits_to_u8<const M: usize, const N: usize>(bytes: &[u8]) -> u8 {
+ let bit_m = is_nth_bit_one::<M>(bytes);
+ let bit_n = is_nth_bit_one::<N>(bytes);
+ (bit_m as u8) << 1 | bit_n as u8
+}
+
+/// Decode an f64 as its sign, exponent, and mantissa in u64 and in that order, using bit shifts and masks.
+/// Note: this function **removes** the 1023 bias from the exponent and adds the implicit 1
+#[allow(clippy::cast_possible_truncation)]
+pub(crate) fn decode_f64(f: f64) -> (u64, u64, u64) {
+ let bits = f.to_bits();
+ let sign = bits >> 63;
+ let exponent = bits >> 52 & 0x7ff;
+ let exponent_bytes_no_bias = (exponent as i16 - 1023).to_be_bytes();
+ let exponent_no_bias = u64::from_be_bytes([
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ exponent_bytes_no_bias[0],
+ exponent_bytes_no_bias[1],
+ ]);
+ let mantissa = bits & 0xfffffffffffff;
+ (sign, exponent_no_bias, mantissa + 1)
+}
+
+/// Encode an f64 from its sign, exponent (**without** the 1023 bias), and (mantissa - 1) using bit shifts as received by ASN1
+pub(crate) fn encode_f64(sign: u64, exponent: u64, mantissa: u64) -> f64 {
+ // Add the bias to the exponent
+ let exponent_with_bias =
+ (i16::from_be_bytes([exponent.to_be_bytes()[6], exponent.to_be_bytes()[7]]) + 1023) as u64;
+ let bits = sign << 63 | exponent_with_bias << 52 | (mantissa - 1);
+ f64::from_bits(bits)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{Decode, Encode};
+
+ #[test]
+ fn decode_subnormal() {
+ assert!(f64::from_der(&[0x09, 0x01, 0b0100_0010]).unwrap().is_nan());
+ let plus_infty = f64::from_der(&[0x09, 0x01, 0b0100_0000]).unwrap();
+ assert!(plus_infty.is_infinite() && plus_infty.is_sign_positive());
+ let neg_infty = f64::from_der(&[0x09, 0x01, 0b0100_0001]).unwrap();
+ assert!(neg_infty.is_infinite() && neg_infty.is_sign_negative());
+ let neg_zero = f64::from_der(&[0x09, 0x01, 0b0100_0011]).unwrap();
+ assert!(neg_zero.is_sign_negative() && neg_zero.abs() < f64::EPSILON);
+ }
+
+ #[test]
+ fn encode_subnormal() {
+ // All subnormal fit in three bytes
+ let mut buffer = [0u8; 3];
+ assert_eq!(
+ &[0x09, 0x01, 0b0100_0010],
+ f64::NAN.encode_to_slice(&mut buffer).unwrap()
+ );
+ assert_eq!(
+ &[0x09, 0x01, 0b0100_0000],
+ f64::INFINITY.encode_to_slice(&mut buffer).unwrap()
+ );
+ assert_eq!(
+ &[0x09, 0x01, 0b0100_0001],
+ f64::NEG_INFINITY.encode_to_slice(&mut buffer).unwrap()
+ );
+ assert_eq!(
+ &[0x09, 0x01, 0b0100_0011],
+ (-0.0_f64).encode_to_slice(&mut buffer).unwrap()
+ );
+ }
+
+ #[test]
+ fn encdec_normal() {
+ // The comments correspond to the decoded value from the ASN.1 playground when the bytes are inputed.
+ {
+ // rec1value R ::= 0
+ let val = 0.0;
+ let expected = &[0x09, 0x0];
+ let mut buffer = [0u8; 2];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa 1, base 2, exponent 0 }
+ let val = 1.0;
+ let expected = &[0x09, 0x03, 0x80, 0x00, 0x01];
+ let mut buffer = [0u8; 5];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa -1, base 2, exponent 0 }
+ let val = -1.0;
+ let expected = &[0x09, 0x03, 0xc0, 0x00, 0x01];
+ let mut buffer = [0u8; 5];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa -1, base 2, exponent 1 }
+ let val = -1.0000000000000002;
+ let expected = &[0x09, 0x03, 0xc0, 0x00, 0x02];
+ let mut buffer = [0u8; 5];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa 1, base 2, exponent -1022 }
+ // NOTE: f64::MIN_EXP == -1021 so the exponent decoded by ASN.1 is what we expect
+ let val = f64::MIN_POSITIVE;
+ let expected = &[0x09, 0x04, 0x81, 0xfc, 0x02, 0x01];
+ let mut buffer = [0u8; 7];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec4value R ::= { mantissa 1, base 2, exponent 3 }
+ let val = 1.0000000000000016;
+ let expected = &[0x09, 0x03, 0x80, 0x00, 0x08];
+ let mut buffer = [0u8; 5];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec5value R ::= { mantissa 4222124650659841, base 2, exponent 4 }
+ let val = 31.0;
+ let expected = &[
+ 0x9, 0x9, 0x80, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ ];
+ let mut buffer = [0u8; 11];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+ }
+
+ #[test]
+ fn encdec_irrationals() {
+ {
+ let val = core::f64::consts::PI;
+ let expected = &[
+ 0x09, 0x09, 0x80, 0x01, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x19,
+ ];
+ let mut buffer = [0u8; 11];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = core::f64::consts::E;
+ let expected = &[
+ 0x09, 0x09, 0x80, 0x01, 0x05, 0xbf, 0x0a, 0x8b, 0x14, 0x57, 0x6a,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+ {
+ let val = core::f64::consts::LN_2;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xff, 0x6, 0x2e, 0x42, 0xfe, 0xfa, 0x39, 0xf0,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+ }
+
+ #[test]
+ fn encdec_reasonable_f64() {
+ // Tests the encoding and decoding of reals with some arbitrary numbers
+ {
+ // rec1value R ::= { mantissa 2414341043715239, base 2, exponent 21 }
+ let val = 3221417.1584163485;
+ let expected = &[
+ 0x9, 0x9, 0x80, 0x15, 0x8, 0x93, 0xd4, 0x94, 0x46, 0xfc, 0xa7,
+ ];
+ let mut buffer = [0u8; 11];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa 2671155248072715, base 2, exponent 23 }
+ let val = 13364022.365665454;
+ let expected = &[
+ 0x09, 0x09, 0x80, 0x17, 0x09, 0x7d, 0x66, 0xcb, 0xb3, 0x88, 0x0b,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa -4386812962460287, base 2, exponent 14 }
+ let val = -32343.132588105735;
+ let expected = &[
+ 0x09, 0x09, 0xc0, 0x0e, 0x0f, 0x95, 0xc8, 0x7c, 0x52, 0xd2, 0x7f,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = -27084.866751869475;
+ let expected = &[
+ 0x09, 0x09, 0xc0, 0x0e, 0x0a, 0x73, 0x37, 0x78, 0xdc, 0xd5, 0x4a,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ // rec1value R ::= { mantissa -4372913134428149, base 2, exponent 7 }
+ let val = -252.28566647111404;
+ let expected = &[
+ 0x09, 0x09, 0xc0, 0x07, 0x0f, 0x89, 0x24, 0x2e, 0x02, 0xdf, 0xf5,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = -14.399709612928548;
+ let expected = &[
+ 0x09, 0x09, 0xc0, 0x03, 0x0c, 0xcc, 0xa6, 0xbd, 0x06, 0xd9, 0x92,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = -0.08340570261832964;
+ let expected = &[
+ 0x09, 0x0a, 0xc1, 0xff, 0xfc, 0x05, 0x5a, 0x13, 0x7d, 0x0b, 0xae, 0x3d,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.00536851453803701;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xf8, 0x05, 0xfd, 0x4b, 0xa5, 0xe7, 0x4c, 0x93,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.00045183525648866433;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xf4, 0x0d, 0x9c, 0x89, 0xa6, 0x59, 0x33, 0x39,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.000033869092002682955;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xf1, 0x01, 0xc1, 0xd5, 0x23, 0xd5, 0x54, 0x7c,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.0000011770891033600088;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xec, 0x03, 0xbf, 0x8f, 0x27, 0xf4, 0x62, 0x56,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.00000005549514041997082;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xe7, 0x0d, 0xcb, 0x31, 0xab, 0x6e, 0xb8, 0xd7,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.0000000012707044685547803;
+ let expected = &[
+ 0x09, 0x0a, 0x81, 0xff, 0xe2, 0x05, 0xd4, 0x9e, 0x0a, 0xf2, 0xff, 0x1f,
+ ];
+ let mut buffer = [0u8; 12];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+
+ {
+ let val = 0.00000000002969611878378562;
+ let expected = &[
+ 0x09, 0x09, 0x81, 0xff, 0xdd, 0x53, 0x5b, 0x6f, 0x97, 0xee, 0xb6,
+ ];
+ let mut buffer = [0u8; 11];
+ let encoded = val.encode_to_slice(&mut buffer).unwrap();
+ assert_eq!(
+ expected, encoded,
+ "invalid encoding of {}:\ngot {:x?}\nwant: {:x?}",
+ val, encoded, expected
+ );
+ let decoded = f64::from_der(encoded).unwrap();
+ assert!(
+ (decoded - val).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ val,
+ decoded
+ );
+ }
+ }
+
+ #[test]
+ fn reject_non_canonical() {
+ assert!(f64::from_der(&[0x09, 0x81, 0x00]).is_err());
+ }
+
+ #[test]
+ fn encdec_f64() {
+ use super::{decode_f64, encode_f64};
+ // Test that the extraction and recreation works
+ for val in [
+ 1.0,
+ 0.1,
+ -0.1,
+ -1.0,
+ 0.0,
+ f64::MIN_POSITIVE,
+ f64::MAX,
+ f64::MIN,
+ 3.1415,
+ 951.2357864,
+ -3.1415,
+ -951.2357864,
+ ] {
+ let (s, e, m) = decode_f64(val);
+ let val2 = encode_f64(s, e, m);
+ assert!(
+ (val - val2).abs() < f64::EPSILON,
+ "fail - want {}\tgot {}",
+ val,
+ val2
+ );
+ }
+ }
+
+ #[test]
+ fn validation_cases() {
+ // Caveat: these test cases are validated on the ASN.1 playground: https://asn1.io/asn1playground/ .
+ // The test case consists in inputing the bytes in the "decode" field and checking that the decoded
+ // value corresponds to the one encoded here.
+ // This tool encodes _all_ values that are non-zero in the ISO 6093 NR3 representation.
+ // This does not seem to perfectly adhere to the ITU specifications, Special Cases section.
+ // The implementation of this crate correctly supports decoding such values. It will, however,
+ // systematically encode REALs in their base 2 form, with a scaling factor where needed to
+ // ensure that the mantissa is either odd or zero (as per section 11.3.1).
+
+ // Positive trivial numbers
+ {
+ let expect = 10.0;
+ let testcase = &[0x09, 0x05, 0x03, 0x31, 0x2E, 0x45, 0x31];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = 100.0;
+ let testcase = &[0x09, 0x05, 0x03, 0x31, 0x2E, 0x45, 0x32];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = 101.0;
+ let testcase = &[0x09, 0x08, 0x03, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = 101.0;
+ let testcase = &[0x09, 0x08, 0x03, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = 0.0;
+ let testcase = &[0x09, 0x00];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = 951.2357864;
+ let testcase = &[
+ 0x09, 0x0F, 0x03, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34, 0x2E,
+ 0x45, 0x2D, 0x37,
+ ];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ // Negative trivial numbers
+ {
+ let expect = -10.0;
+ let testcase = &[0x09, 0x06, 0x03, 0x2D, 0x31, 0x2E, 0x45, 0x31];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = -100.0;
+ let testcase = &[0x09, 0x06, 0x03, 0x2D, 0x31, 0x2E, 0x45, 0x32];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = -101.0;
+ let testcase = &[
+ 0x09, 0x09, 0x03, 0x2D, 0x31, 0x30, 0x31, 0x2E, 0x45, 0x2B, 0x30,
+ ];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = -0.5;
+ let testcase = &[0x09, 0x07, 0x03, 0x2D, 0x35, 0x2E, 0x45, 0x2D, 0x31];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ let expect = -0.0;
+ let testcase = &[0x09, 0x03, 0x01, 0x2D, 0x30];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ {
+ // Test NR3 decoding
+ let expect = -951.2357864;
+ let testcase = &[
+ 0x09, 0x10, 0x03, 0x2D, 0x39, 0x35, 0x31, 0x32, 0x33, 0x35, 0x37, 0x38, 0x36, 0x34,
+ 0x2E, 0x45, 0x2D, 0x37,
+ ];
+ let decoded = f64::from_der(testcase).unwrap();
+ assert!(
+ (decoded - expect).abs() < f64::EPSILON,
+ "wanted: {}\tgot: {}",
+ expect,
+ decoded
+ );
+ }
+ }
+}
diff --git a/vendor/der/src/asn1/sequence.rs b/vendor/der/src/asn1/sequence.rs
new file mode 100644
index 000000000..d2f6bc5d1
--- /dev/null
+++ b/vendor/der/src/asn1/sequence.rs
@@ -0,0 +1,84 @@
+//! The [`Sequence`] trait simplifies writing decoders/encoders which map ASN.1
+//! `SEQUENCE`s to Rust structs.
+
+use crate::{
+ ByteSlice, Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Result,
+ Tag, Writer,
+};
+
+/// ASN.1 `SEQUENCE` trait.
+///
+/// Types which impl this trait receive blanket impls for the [`Decode`],
+/// [`Encode`], and [`FixedTag`] traits.
+pub trait Sequence<'a>: Decode<'a> {
+ /// Call the provided function with a slice of [`Encode`] trait objects
+ /// representing the fields of this `SEQUENCE`.
+ ///
+ /// This method uses a callback because structs with fields which aren't
+ /// directly [`Encode`] may need to construct temporary values from
+ /// their fields prior to encoding.
+ fn fields<F, T>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&[&dyn Encode]) -> Result<T>;
+}
+
+impl<'a, M> EncodeValue for M
+where
+ M: Sequence<'a>,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.fields(|fields| {
+ fields
+ .iter()
+ .try_fold(Length::ZERO, |acc, field| acc + field.encoded_len()?)
+ })
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.fields(|fields| {
+ for &field in fields {
+ field.encode(writer)?;
+ }
+
+ Ok(())
+ })
+ }
+}
+
+impl<'a, M> FixedTag for M
+where
+ M: Sequence<'a>,
+{
+ const TAG: Tag = Tag::Sequence;
+}
+
+/// The [`SequenceRef`] type provides raw access to the octets which comprise a
+/// DER-encoded `SEQUENCE`.
+///
+/// This is a zero-copy reference type which borrows from the input data.
+pub struct SequenceRef<'a> {
+ /// Body of the `SEQUENCE`.
+ body: ByteSlice<'a>,
+}
+
+impl<'a> DecodeValue<'a> for SequenceRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Ok(Self {
+ body: ByteSlice::decode_value(reader, header)?,
+ })
+ }
+}
+
+impl EncodeValue for SequenceRef<'_> {
+ fn value_len(&self) -> Result<Length> {
+ Ok(self.body.len())
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.body.encode_value(writer)
+ }
+}
+
+impl<'a> FixedTag for SequenceRef<'a> {
+ const TAG: Tag = Tag::Sequence;
+}
diff --git a/vendor/der/src/asn1/sequence_of.rs b/vendor/der/src/asn1/sequence_of.rs
new file mode 100644
index 000000000..6334d7197
--- /dev/null
+++ b/vendor/der/src/asn1/sequence_of.rs
@@ -0,0 +1,234 @@
+//! ASN.1 `SEQUENCE OF` support.
+
+use crate::{
+ arrayvec, ord::iter_cmp, ArrayVec, Decode, DecodeValue, DerOrd, Encode, EncodeValue, FixedTag,
+ Header, Length, Reader, Result, Tag, ValueOrd, Writer,
+};
+use core::cmp::Ordering;
+
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// ASN.1 `SEQUENCE OF` backed by an array.
+///
+/// This type implements an append-only `SEQUENCE OF` type which is stack-based
+/// and does not depend on `alloc` support.
+// TODO(tarcieri): use `ArrayVec` when/if it's merged into `core`
+// See: https://github.com/rust-lang/rfcs/pull/2990
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SequenceOf<T, const N: usize> {
+ inner: ArrayVec<T, N>,
+}
+
+impl<T, const N: usize> SequenceOf<T, N> {
+ /// Create a new [`SequenceOf`].
+ pub fn new() -> Self {
+ Self {
+ inner: ArrayVec::new(),
+ }
+ }
+
+ /// Add an element to this [`SequenceOf`].
+ pub fn add(&mut self, element: T) -> Result<()> {
+ self.inner.add(element)
+ }
+
+ /// Get an element of this [`SequenceOf`].
+ pub fn get(&self, index: usize) -> Option<&T> {
+ self.inner.get(index)
+ }
+
+ /// Iterate over the elements in this [`SequenceOf`].
+ pub fn iter(&self) -> SequenceOfIter<'_, T> {
+ SequenceOfIter {
+ inner: self.inner.iter(),
+ }
+ }
+
+ /// Is this [`SequenceOf`] empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ /// Number of elements in this [`SequenceOf`].
+ pub fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+
+impl<T, const N: usize> Default for SequenceOf<T, N> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'a, T, const N: usize> DecodeValue<'a> for SequenceOf<T, N>
+where
+ T: Decode<'a>,
+{
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ reader.read_nested(header.length, |reader| {
+ let mut sequence_of = Self::new();
+
+ while !reader.is_finished() {
+ sequence_of.add(T::decode(reader)?)?;
+ }
+
+ Ok(sequence_of)
+ })
+ }
+}
+
+impl<T, const N: usize> EncodeValue for SequenceOf<T, N>
+where
+ T: Encode,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.iter()
+ .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?)
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ for elem in self.iter() {
+ elem.encode(writer)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<T, const N: usize> FixedTag for SequenceOf<T, N> {
+ const TAG: Tag = Tag::Sequence;
+}
+
+impl<T, const N: usize> ValueOrd for SequenceOf<T, N>
+where
+ T: DerOrd,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ iter_cmp(self.iter(), other.iter())
+ }
+}
+
+/// Iterator over the elements of an [`SequenceOf`].
+#[derive(Clone, Debug)]
+pub struct SequenceOfIter<'a, T> {
+ /// Inner iterator.
+ inner: arrayvec::Iter<'a, T>,
+}
+
+impl<'a, T> Iterator for SequenceOfIter<'a, T> {
+ type Item = &'a T;
+
+ fn next(&mut self) -> Option<&'a T> {
+ self.inner.next()
+ }
+}
+
+impl<'a, T> ExactSizeIterator for SequenceOfIter<'a, T> {}
+
+impl<'a, T, const N: usize> DecodeValue<'a> for [T; N]
+where
+ T: Decode<'a>,
+{
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ let sequence_of = SequenceOf::<T, N>::decode_value(reader, header)?;
+
+ // TODO(tarcieri): use `[T; N]::try_map` instead of `expect` when stable
+ if sequence_of.inner.len() == N {
+ Ok(sequence_of
+ .inner
+ .into_array()
+ .map(|elem| elem.expect("arrayvec length mismatch")))
+ } else {
+ Err(Self::TAG.length_error())
+ }
+ }
+}
+
+impl<T, const N: usize> EncodeValue for [T; N]
+where
+ T: Encode,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.iter()
+ .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?)
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ for elem in self {
+ elem.encode(writer)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<T, const N: usize> FixedTag for [T; N] {
+ const TAG: Tag = Tag::Sequence;
+}
+
+impl<T, const N: usize> ValueOrd for [T; N]
+where
+ T: DerOrd,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ iter_cmp(self.iter(), other.iter())
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<'a, T> DecodeValue<'a> for Vec<T>
+where
+ T: Decode<'a>,
+{
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ reader.read_nested(header.length, |reader| {
+ let mut sequence_of = Self::new();
+
+ while !reader.is_finished() {
+ sequence_of.push(T::decode(reader)?);
+ }
+
+ Ok(sequence_of)
+ })
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> EncodeValue for Vec<T>
+where
+ T: Encode,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.iter()
+ .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?)
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ for elem in self {
+ elem.encode(writer)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> FixedTag for Vec<T> {
+ const TAG: Tag = Tag::Sequence;
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> ValueOrd for Vec<T>
+where
+ T: DerOrd,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ iter_cmp(self.iter(), other.iter())
+ }
+}
diff --git a/vendor/der/src/asn1/set_of.rs b/vendor/der/src/asn1/set_of.rs
new file mode 100644
index 000000000..b8c4b0da4
--- /dev/null
+++ b/vendor/der/src/asn1/set_of.rs
@@ -0,0 +1,451 @@
+//! ASN.1 `SET OF` support.
+//!
+//! # Ordering Notes
+//!
+//! Some DER serializer implementations fail to properly sort elements of a
+//! `SET OF`. This is technically non-canonical, but occurs frequently
+//! enough that most DER decoders tolerate it. Unfortunately because
+//! of that, we must also follow suit.
+//!
+//! However, all types in this module sort elements of a set at decode-time,
+//! ensuring they'll be in the proper order if reserialized.
+
+use crate::{
+ arrayvec, ord::iter_cmp, ArrayVec, Decode, DecodeValue, DerOrd, Encode, EncodeValue, Error,
+ ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd, Writer,
+};
+use core::cmp::Ordering;
+
+#[cfg(feature = "alloc")]
+use {alloc::vec::Vec, core::slice};
+
+/// ASN.1 `SET OF` backed by an array.
+///
+/// This type implements an append-only `SET OF` type which is stack-based
+/// and does not depend on `alloc` support.
+// TODO(tarcieri): use `ArrayVec` when/if it's merged into `core`
+// See: https://github.com/rust-lang/rfcs/pull/2990
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct SetOf<T, const N: usize>
+where
+ T: DerOrd,
+{
+ inner: ArrayVec<T, N>,
+}
+
+impl<T, const N: usize> SetOf<T, N>
+where
+ T: DerOrd,
+{
+ /// Create a new [`SetOf`].
+ pub fn new() -> Self {
+ Self {
+ inner: ArrayVec::default(),
+ }
+ }
+
+ /// Add an element to this [`SetOf`].
+ ///
+ /// Items MUST be added in lexicographical order according to the
+ /// [`DerOrd`] impl on `T`.
+ pub fn add(&mut self, new_elem: T) -> Result<()> {
+ // Ensure set elements are lexicographically ordered
+ if let Some(last_elem) = self.inner.last() {
+ if new_elem.der_cmp(last_elem)? != Ordering::Greater {
+ return Err(ErrorKind::SetOrdering.into());
+ }
+ }
+
+ self.inner.add(new_elem)
+ }
+
+ /// Get the nth element from this [`SetOf`].
+ pub fn get(&self, index: usize) -> Option<&T> {
+ self.inner.get(index)
+ }
+
+ /// Iterate over the elements of this [`SetOf`].
+ pub fn iter(&self) -> SetOfIter<'_, T> {
+ SetOfIter {
+ inner: self.inner.iter(),
+ }
+ }
+
+ /// Is this [`SetOf`] empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ /// Number of elements in this [`SetOf`].
+ pub fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+
+impl<T, const N: usize> Default for SetOf<T, N>
+where
+ T: DerOrd,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'a, T, const N: usize> DecodeValue<'a> for SetOf<T, N>
+where
+ T: Decode<'a> + DerOrd,
+{
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ reader.read_nested(header.length, |reader| {
+ let mut result = Self::new();
+
+ while !reader.is_finished() {
+ result.inner.add(T::decode(reader)?)?;
+ }
+
+ der_sort(result.inner.as_mut())?;
+ validate(result.inner.as_ref())?;
+ Ok(result)
+ })
+ }
+}
+
+impl<'a, T, const N: usize> EncodeValue for SetOf<T, N>
+where
+ T: 'a + Decode<'a> + Encode + DerOrd,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.iter()
+ .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?)
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ for elem in self.iter() {
+ elem.encode(writer)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<'a, T, const N: usize> FixedTag for SetOf<T, N>
+where
+ T: Decode<'a> + DerOrd,
+{
+ const TAG: Tag = Tag::Set;
+}
+
+impl<T, const N: usize> TryFrom<[T; N]> for SetOf<T, N>
+where
+ T: DerOrd,
+{
+ type Error = Error;
+
+ fn try_from(mut arr: [T; N]) -> Result<SetOf<T, N>> {
+ der_sort(&mut arr)?;
+
+ let mut result = SetOf::new();
+
+ for elem in arr {
+ result.add(elem)?;
+ }
+
+ Ok(result)
+ }
+}
+
+impl<T, const N: usize> ValueOrd for SetOf<T, N>
+where
+ T: DerOrd,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ iter_cmp(self.iter(), other.iter())
+ }
+}
+
+/// Iterator over the elements of an [`SetOf`].
+#[derive(Clone, Debug)]
+pub struct SetOfIter<'a, T> {
+ /// Inner iterator.
+ inner: arrayvec::Iter<'a, T>,
+}
+
+impl<'a, T> Iterator for SetOfIter<'a, T> {
+ type Item = &'a T;
+
+ fn next(&mut self) -> Option<&'a T> {
+ self.inner.next()
+ }
+}
+
+impl<'a, T> ExactSizeIterator for SetOfIter<'a, T> {}
+
+/// ASN.1 `SET OF` backed by a [`Vec`].
+///
+/// This type implements an append-only `SET OF` type which is heap-backed
+/// and depends on `alloc` support.
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
+pub struct SetOfVec<T>
+where
+ T: DerOrd,
+{
+ inner: Vec<T>,
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T: DerOrd> Default for SetOfVec<T> {
+ fn default() -> Self {
+ Self {
+ inner: Default::default(),
+ }
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> SetOfVec<T>
+where
+ T: DerOrd,
+{
+ /// Create a new [`SetOfVec`].
+ pub fn new() -> Self {
+ Self {
+ inner: Vec::default(),
+ }
+ }
+
+ /// Add an element to this [`SetOfVec`].
+ ///
+ /// Items MUST be added in lexicographical order according to the
+ /// [`DerOrd`] impl on `T`.
+ pub fn add(&mut self, new_elem: T) -> Result<()> {
+ // Ensure set elements are lexicographically ordered
+ if let Some(last_elem) = self.inner.last() {
+ if new_elem.der_cmp(last_elem)? != Ordering::Greater {
+ return Err(ErrorKind::SetOrdering.into());
+ }
+ }
+
+ self.inner.push(new_elem);
+ Ok(())
+ }
+
+ /// Borrow the elements of this [`SetOfVec`] as a slice.
+ pub fn as_slice(&self) -> &[T] {
+ self.inner.as_slice()
+ }
+
+ /// Get the nth element from this [`SetOfVec`].
+ pub fn get(&self, index: usize) -> Option<&T> {
+ self.inner.get(index)
+ }
+
+ /// Convert this [`SetOfVec`] into the inner [`Vec`].
+ pub fn into_vec(self) -> Vec<T> {
+ self.inner
+ }
+
+ /// Iterate over the elements of this [`SetOfVec`].
+ pub fn iter(&self) -> slice::Iter<'_, T> {
+ self.inner.iter()
+ }
+
+ /// Is this [`SetOfVec`] empty?
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ /// Number of elements in this [`SetOfVec`].
+ pub fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> AsRef<[T]> for SetOfVec<T>
+where
+ T: DerOrd,
+{
+ fn as_ref(&self) -> &[T] {
+ self.as_slice()
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<'a, T> DecodeValue<'a> for SetOfVec<T>
+where
+ T: Decode<'a> + DerOrd,
+{
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ reader.read_nested(header.length, |reader| {
+ let mut inner = Vec::new();
+
+ while !reader.is_finished() {
+ inner.push(T::decode(reader)?);
+ }
+
+ der_sort(inner.as_mut())?;
+ validate(inner.as_ref())?;
+ Ok(Self { inner })
+ })
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<'a, T> EncodeValue for SetOfVec<T>
+where
+ T: 'a + Decode<'a> + Encode + DerOrd,
+{
+ fn value_len(&self) -> Result<Length> {
+ self.iter()
+ .fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?)
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ for elem in self.iter() {
+ elem.encode(writer)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> FixedTag for SetOfVec<T>
+where
+ T: DerOrd,
+{
+ const TAG: Tag = Tag::Set;
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> From<SetOfVec<T>> for Vec<T>
+where
+ T: DerOrd,
+{
+ fn from(set: SetOfVec<T>) -> Vec<T> {
+ set.into_vec()
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> TryFrom<Vec<T>> for SetOfVec<T>
+where
+ T: DerOrd,
+{
+ type Error = Error;
+
+ fn try_from(mut vec: Vec<T>) -> Result<SetOfVec<T>> {
+ // TODO(tarcieri): use `[T]::sort_by` here?
+ der_sort(vec.as_mut_slice())?;
+ Ok(SetOfVec { inner: vec })
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T, const N: usize> TryFrom<[T; N]> for SetOfVec<T>
+where
+ T: DerOrd,
+{
+ type Error = Error;
+
+ fn try_from(arr: [T; N]) -> Result<SetOfVec<T>> {
+ Vec::from(arr).try_into()
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<T> ValueOrd for SetOfVec<T>
+where
+ T: DerOrd,
+{
+ fn value_cmp(&self, other: &Self) -> Result<Ordering> {
+ iter_cmp(self.iter(), other.iter())
+ }
+}
+
+/// Sort a mut slice according to its [`DerOrd`], returning any errors which
+/// might occur during the comparison.
+///
+/// The algorithm is insertion sort, which should perform well when the input
+/// is mostly sorted to begin with.
+///
+/// This function is used rather than Rust's built-in `[T]::sort_by` in order
+/// to support heapless `no_std` targets as well as to enable bubbling up
+/// sorting errors.
+#[allow(clippy::integer_arithmetic)]
+fn der_sort<T: DerOrd>(slice: &mut [T]) -> Result<()> {
+ for i in 0..slice.len() {
+ let mut j = i;
+
+ while j > 0 && slice[j - 1].der_cmp(&slice[j])? == Ordering::Greater {
+ slice.swap(j - 1, j);
+ j -= 1;
+ }
+ }
+
+ Ok(())
+}
+
+/// Validate the elements of a `SET OF`, ensuring that they are all in order
+/// and that there are no duplicates.
+fn validate<T: DerOrd>(slice: &[T]) -> Result<()> {
+ if let Some(len) = slice.len().checked_sub(1) {
+ for i in 0..len {
+ let j = i.checked_add(1).ok_or(ErrorKind::Overflow)?;
+
+ match slice.get(i..=j) {
+ Some([a, b]) => {
+ if a.der_cmp(b)? != Ordering::Less {
+ return Err(ErrorKind::SetOrdering.into());
+ }
+ }
+ _ => return Err(Tag::Set.value_error()),
+ }
+ }
+ }
+
+ Ok(())
+}
+
+#[cfg(all(test, feature = "alloc"))]
+mod tests {
+ use super::{SetOf, SetOfVec};
+ use alloc::vec::Vec;
+
+ #[test]
+ fn setof_tryfrom_array() {
+ let arr = [3u16, 2, 1, 65535, 0];
+ let set = SetOf::try_from(arr).unwrap();
+ assert_eq!(
+ set.iter().cloned().collect::<Vec<u16>>(),
+ &[0, 1, 2, 3, 65535]
+ );
+ }
+
+ #[test]
+ fn setofvec_tryfrom_array() {
+ let arr = [3u16, 2, 1, 65535, 0];
+ let set = SetOfVec::try_from(arr).unwrap();
+ assert_eq!(set.as_ref(), &[0, 1, 2, 3, 65535]);
+ }
+
+ #[cfg(feature = "alloc")]
+ #[test]
+ fn setofvec_tryfrom_vec() {
+ let vec = vec![3u16, 2, 1, 65535, 0];
+ let set = SetOfVec::try_from(vec).unwrap();
+ assert_eq!(set.as_ref(), &[0, 1, 2, 3, 65535]);
+ }
+}
diff --git a/vendor/der/src/asn1/teletex_string.rs b/vendor/der/src/asn1/teletex_string.rs
new file mode 100644
index 000000000..7d6621d2d
--- /dev/null
+++ b/vendor/der/src/asn1/teletex_string.rs
@@ -0,0 +1,144 @@
+//! ASN.1 `TeletexString` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header,
+ Length, Reader, Result, StrSlice, Tag, Writer,
+};
+use core::{fmt, ops::Deref, str};
+
+/// ASN.1 `TeletexString` type.
+///
+/// Supports a subset the ASCII character set (described below).
+///
+/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
+/// For the full ASCII character set, use
+/// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
+///
+/// This is a zero-copy reference type which borrows from the input data.
+///
+/// # Supported characters
+///
+/// The standard defines a complex character set allowed in this type. However, quoting the ASN.1
+/// mailing list, "a sizable volume of software in the world treats TeletexString (T61String) as a
+/// simple 8-bit string with mostly Windows Latin 1 (superset of iso-8859-1) encoding".
+///
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
+pub struct TeletexStringRef<'a> {
+ /// Inner value
+ inner: StrSlice<'a>,
+}
+
+impl<'a> TeletexStringRef<'a> {
+ /// Create a new ASN.1 `TeletexString`.
+ pub fn new<T>(input: &'a T) -> Result<Self>
+ where
+ T: AsRef<[u8]> + ?Sized,
+ {
+ let input = input.as_ref();
+
+ // FIXME: support higher part of the charset
+ if input.iter().any(|&c| c > 0x7F) {
+ return Err(Self::TAG.value_error());
+ }
+
+ StrSlice::from_bytes(input)
+ .map(|inner| Self { inner })
+ .map_err(|_| Self::TAG.value_error())
+ }
+}
+
+impl<'a> Deref for TeletexStringRef<'a> {
+ type Target = StrSlice<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl AsRef<str> for TeletexStringRef<'_> {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for TeletexStringRef<'_> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for TeletexStringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::new(ByteSlice::decode_value(reader, header)?.as_slice())
+ }
+}
+
+impl<'a> EncodeValue for TeletexStringRef<'a> {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.inner.encode_value(writer)
+ }
+}
+
+impl FixedTag for TeletexStringRef<'_> {
+ const TAG: Tag = Tag::TeletexString;
+}
+
+impl OrdIsValueOrd for TeletexStringRef<'_> {}
+
+impl<'a> From<&TeletexStringRef<'a>> for TeletexStringRef<'a> {
+ fn from(value: &TeletexStringRef<'a>) -> TeletexStringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for TeletexStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<TeletexStringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> From<TeletexStringRef<'a>> for AnyRef<'a> {
+ fn from(teletex_string: TeletexStringRef<'a>) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::TeletexString, teletex_string.inner.into())
+ }
+}
+
+impl<'a> fmt::Display for TeletexStringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl<'a> fmt::Debug for TeletexStringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "TeletexString({:?})", self.as_str())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::TeletexStringRef;
+ use crate::Decode;
+ use crate::SliceWriter;
+
+ #[test]
+ fn parse_bytes() {
+ let example_bytes = &[
+ 0x14, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
+ ];
+
+ let teletex_string = TeletexStringRef::from_der(example_bytes).unwrap();
+ assert_eq!(teletex_string.as_str(), "Test User 1");
+ let mut out = [0_u8; 30];
+ let mut writer = SliceWriter::new(&mut out);
+ writer.encode(&teletex_string).unwrap();
+ let encoded = writer.finish().unwrap();
+ assert_eq!(encoded, example_bytes);
+ }
+}
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());
+ }
+}
diff --git a/vendor/der/src/asn1/utf8_string.rs b/vendor/der/src/asn1/utf8_string.rs
new file mode 100644
index 000000000..1a0641172
--- /dev/null
+++ b/vendor/der/src/asn1/utf8_string.rs
@@ -0,0 +1,209 @@
+//! ASN.1 `UTF8String` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header,
+ Length, Reader, Result, StrSlice, Tag, Writer,
+};
+use core::{fmt, ops::Deref, str};
+
+#[cfg(feature = "alloc")]
+use alloc::{borrow::ToOwned, string::String};
+
+/// ASN.1 `UTF8String` type.
+///
+/// Supports the full UTF-8 encoding.
+///
+/// Note that the [`Decode`][`crate::Decode`] and [`Encode`][`crate::Encode`]
+/// traits are impl'd for Rust's [`str`][`prim@str`] primitive, which
+/// decodes/encodes as a [`Utf8StringRef`].
+///
+/// You are free to use [`str`][`prim@str`] instead of this type, however it's
+/// still provided for explicitness in cases where it might be ambiguous with
+/// other ASN.1 string encodings such as
+/// [`PrintableStringRef`][`crate::asn1::PrintableStringRef`].
+///
+/// This is a zero-copy reference type which borrows from the input data.
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
+pub struct Utf8StringRef<'a> {
+ /// Inner value
+ inner: StrSlice<'a>,
+}
+
+impl<'a> Utf8StringRef<'a> {
+ /// Create a new ASN.1 `UTF8String`.
+ pub fn new<T>(input: &'a T) -> Result<Self>
+ where
+ T: AsRef<[u8]> + ?Sized,
+ {
+ StrSlice::from_bytes(input.as_ref()).map(|inner| Self { inner })
+ }
+}
+
+impl<'a> Deref for Utf8StringRef<'a> {
+ type Target = StrSlice<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl AsRef<str> for Utf8StringRef<'_> {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for Utf8StringRef<'_> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for Utf8StringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::new(ByteSlice::decode_value(reader, header)?.as_slice())
+ }
+}
+
+impl EncodeValue for Utf8StringRef<'_> {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.inner.encode_value(writer)
+ }
+}
+
+impl FixedTag for Utf8StringRef<'_> {
+ const TAG: Tag = Tag::Utf8String;
+}
+
+impl OrdIsValueOrd for Utf8StringRef<'_> {}
+
+impl<'a> From<&Utf8StringRef<'a>> for Utf8StringRef<'a> {
+ fn from(value: &Utf8StringRef<'a>) -> Utf8StringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for Utf8StringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<Utf8StringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> From<Utf8StringRef<'a>> for AnyRef<'a> {
+ fn from(printable_string: Utf8StringRef<'a>) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::Utf8String, printable_string.inner.into())
+ }
+}
+
+impl<'a> fmt::Display for Utf8StringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl<'a> fmt::Debug for Utf8StringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Utf8String({:?})", self.as_str())
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for &'a str {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<&'a str> {
+ Utf8StringRef::try_from(any).map(|s| s.as_str())
+ }
+}
+
+impl EncodeValue for str {
+ fn value_len(&self) -> Result<Length> {
+ Utf8StringRef::new(self)?.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ Utf8StringRef::new(self)?.encode_value(writer)
+ }
+}
+
+impl FixedTag for str {
+ const TAG: Tag = Tag::Utf8String;
+}
+
+impl OrdIsValueOrd for str {}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<'a> From<Utf8StringRef<'a>> for String {
+ fn from(s: Utf8StringRef<'a>) -> String {
+ s.as_str().to_owned()
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<'a> TryFrom<AnyRef<'a>> for String {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<String> {
+ Utf8StringRef::try_from(any).map(|s| s.as_str().to_owned())
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl<'a> DecodeValue<'a> for String {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Ok(String::from_utf8(reader.read_vec(header.length)?)?)
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl EncodeValue for String {
+ fn value_len(&self) -> Result<Length> {
+ Utf8StringRef::new(self)?.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ Utf8StringRef::new(self)?.encode_value(writer)
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl FixedTag for String {
+ const TAG: Tag = Tag::Utf8String;
+}
+
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+impl OrdIsValueOrd for String {}
+
+#[cfg(test)]
+mod tests {
+ use super::Utf8StringRef;
+ use crate::Decode;
+
+ #[test]
+ fn parse_ascii_bytes() {
+ let example_bytes = &[
+ 0x0c, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
+ ];
+
+ let utf8_string = Utf8StringRef::from_der(example_bytes).unwrap();
+ assert_eq!(utf8_string.as_str(), "Test User 1");
+ }
+
+ #[test]
+ fn parse_utf8_bytes() {
+ let example_bytes = &[0x0c, 0x06, 0x48, 0x65, 0x6c, 0x6c, 0xc3, 0xb3];
+ let utf8_string = Utf8StringRef::from_der(example_bytes).unwrap();
+ assert_eq!(utf8_string.as_str(), "Helló");
+ }
+}
diff --git a/vendor/der/src/asn1/videotex_string.rs b/vendor/der/src/asn1/videotex_string.rs
new file mode 100644
index 000000000..b758a22e6
--- /dev/null
+++ b/vendor/der/src/asn1/videotex_string.rs
@@ -0,0 +1,143 @@
+//! ASN.1 `VideotexString` support.
+
+use crate::{
+ asn1::AnyRef, ord::OrdIsValueOrd, ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header,
+ Length, Reader, Result, StrSlice, Tag, Writer,
+};
+use core::{fmt, ops::Deref, str};
+
+/// ASN.1 `VideotexString` type.
+///
+/// Supports a subset the ASCII character set (described below).
+///
+/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
+/// For the full ASCII character set, use
+/// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
+///
+/// This is a zero-copy reference type which borrows from the input data.
+///
+/// # Supported characters
+///
+/// For the practical purposes VideotexString is treated as IA5string, disallowing non-ASCII chars.
+///
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
+pub struct VideotexStringRef<'a> {
+ /// Inner value
+ inner: StrSlice<'a>,
+}
+
+impl<'a> VideotexStringRef<'a> {
+ /// Create a new ASN.1 `VideotexString`.
+ pub fn new<T>(input: &'a T) -> Result<Self>
+ where
+ T: AsRef<[u8]> + ?Sized,
+ {
+ let input = input.as_ref();
+
+ // Validate all characters are within VideotexString's allowed set
+ // FIXME: treat as if it were IA5String
+ if input.iter().any(|&c| c > 0x7F) {
+ return Err(Self::TAG.value_error());
+ }
+
+ StrSlice::from_bytes(input)
+ .map(|inner| Self { inner })
+ .map_err(|_| Self::TAG.value_error())
+ }
+}
+
+impl<'a> Deref for VideotexStringRef<'a> {
+ type Target = StrSlice<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl AsRef<str> for VideotexStringRef<'_> {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for VideotexStringRef<'_> {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for VideotexStringRef<'a> {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::new(ByteSlice::decode_value(reader, header)?.as_slice())
+ }
+}
+
+impl<'a> EncodeValue for VideotexStringRef<'a> {
+ fn value_len(&self) -> Result<Length> {
+ self.inner.value_len()
+ }
+
+ fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
+ self.inner.encode_value(writer)
+ }
+}
+
+impl FixedTag for VideotexStringRef<'_> {
+ const TAG: Tag = Tag::VideotexString;
+}
+
+impl OrdIsValueOrd for VideotexStringRef<'_> {}
+
+impl<'a> From<&VideotexStringRef<'a>> for VideotexStringRef<'a> {
+ fn from(value: &VideotexStringRef<'a>) -> VideotexStringRef<'a> {
+ *value
+ }
+}
+
+impl<'a> TryFrom<AnyRef<'a>> for VideotexStringRef<'a> {
+ type Error = Error;
+
+ fn try_from(any: AnyRef<'a>) -> Result<VideotexStringRef<'a>> {
+ any.decode_into()
+ }
+}
+
+impl<'a> From<VideotexStringRef<'a>> for AnyRef<'a> {
+ fn from(printable_string: VideotexStringRef<'a>) -> AnyRef<'a> {
+ AnyRef::from_tag_and_value(Tag::VideotexString, printable_string.inner.into())
+ }
+}
+
+impl<'a> From<VideotexStringRef<'a>> for &'a [u8] {
+ fn from(printable_string: VideotexStringRef<'a>) -> &'a [u8] {
+ printable_string.as_bytes()
+ }
+}
+
+impl<'a> fmt::Display for VideotexStringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+impl<'a> fmt::Debug for VideotexStringRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "VideotexString({:?})", self.as_str())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::VideotexStringRef;
+ use crate::Decode;
+
+ #[test]
+ fn parse_bytes() {
+ let example_bytes = &[
+ 0x15, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
+ ];
+
+ let printable_string = VideotexStringRef::from_der(example_bytes).unwrap();
+ assert_eq!(printable_string.as_str(), "Test User 1");
+ }
+}