summaryrefslogtreecommitdiffstats
path: root/vendor/der/src/asn1/bit_string.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
commit2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch)
tree033cc839730fda84ff08db877037977be94e5e3a /vendor/der/src/asn1/bit_string.rs
parentInitial commit. (diff)
downloadcargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.tar.xz
cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.zip
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/der/src/asn1/bit_string.rs')
-rw-r--r--vendor/der/src/asn1/bit_string.rs552
1 files changed, 552 insertions, 0 deletions
diff --git a/vendor/der/src/asn1/bit_string.rs b/vendor/der/src/asn1/bit_string.rs
new file mode 100644
index 0000000..bf3371c
--- /dev/null
+++ b/vendor/der/src/asn1/bit_string.rs
@@ -0,0 +1,552 @@
+//! ASN.1 `BIT STRING` support.
+
+use crate::{
+ BytesRef, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader,
+ Result, Tag, ValueOrd, Writer,
+};
+use core::{cmp::Ordering, iter::FusedIterator};
+
+/// 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: BytesRef<'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 = BytesRef::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_any_conversions!(BitStringRef<'a>, 'a);
+
+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 = BytesRef::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 impl 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<&'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;
+}
+
+// Implement by hand because the derive would create invalid values.
+// Use the constructor to create a valid value.
+#[cfg(feature = "arbitrary")]
+impl<'a> arbitrary::Arbitrary<'a> for BitStringRef<'a> {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+ Self::new(
+ u.int_in_range(0..=Self::MAX_UNUSED_BITS)?,
+ BytesRef::arbitrary(u)?.as_slice(),
+ )
+ .map_err(|_| arbitrary::Error::IncorrectFormat)
+ }
+
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ arbitrary::size_hint::and(u8::size_hint(depth), BytesRef::size_hint(depth))
+ }
+}
+
+#[cfg(feature = "alloc")]
+pub use self::allocating::BitString;
+
+#[cfg(feature = "alloc")]
+mod allocating {
+ use super::*;
+ use crate::referenced::*;
+ use alloc::vec::Vec;
+
+ /// Owned form of ASN.1 `BIT STRING` type.
+ ///
+ /// This type provides the same functionality as [`BitStringRef`] but owns the
+ /// backing data.
+ #[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>,
+ }
+
+ 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()
+ }
+ }
+
+ impl_any_conversions!(BitString);
+
+ 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)
+ }
+ }
+
+ impl EncodeValue for BitString {
+ fn value_len(&self) -> Result<Length> {
+ Length::ONE + Length::try_from(self.inner.len())?
+ }
+
+ fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
+ writer.write_byte(self.unused_bits)?;
+ writer.write(&self.inner)
+ }
+ }
+
+ impl FixedTag for BitString {
+ const TAG: Tag = Tag::BitString;
+ }
+
+ 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")
+ }
+ }
+
+ 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),
+ }
+ }
+ }
+
+ // Implement by hand because the derive would create invalid values.
+ // Use the constructor to create a valid value.
+ #[cfg(feature = "arbitrary")]
+ impl<'a> arbitrary::Arbitrary<'a> for BitString {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+ Self::new(
+ u.int_in_range(0..=Self::MAX_UNUSED_BITS)?,
+ BytesRef::arbitrary(u)?.as_slice(),
+ )
+ .map_err(|_| arbitrary::Error::IncorrectFormat)
+ }
+
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ arbitrary::size_hint::and(u8::size_hint(depth), BytesRef::size_hint(depth))
+ }
+ }
+
+ impl<'a> RefToOwned<'a> for BitStringRef<'a> {
+ type Owned = BitString;
+ fn ref_to_owned(&self) -> Self::Owned {
+ BitString {
+ unused_bits: self.unused_bits,
+ bit_length: self.bit_length,
+ inner: Vec::from(self.inner.as_slice()),
+ }
+ }
+ }
+
+ impl OwnedToRef for BitString {
+ type Borrowed<'a> = BitStringRef<'a>;
+ fn owned_to_ref(&self) -> Self::Borrowed<'_> {
+ self.into()
+ }
+ }
+}
+
+/// 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 impl 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!(u8::from(bits.next().unwrap()), 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()
+ )
+ }
+}