diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
commit | 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch) | |
tree | 033cc839730fda84ff08db877037977be94e5e3a /vendor/der/src/asn1/bit_string.rs | |
parent | Initial commit. (diff) | |
download | cargo-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.rs | 552 |
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() + ) + } +} |