diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /rust/vendor/der-parser/src/ber/ber.rs | |
parent | Initial commit. (diff) | |
download | suricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip |
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'rust/vendor/der-parser/src/ber/ber.rs')
-rw-r--r-- | rust/vendor/der-parser/src/ber/ber.rs | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/rust/vendor/der-parser/src/ber/ber.rs b/rust/vendor/der-parser/src/ber/ber.rs new file mode 100644 index 0000000..3063ff2 --- /dev/null +++ b/rust/vendor/der-parser/src/ber/ber.rs @@ -0,0 +1,950 @@ +use super::{Class, Header, Length, Tag}; +use crate::ber::bitstring_to_u64; +use crate::ber::integer::*; +use crate::error::BerError; +use crate::oid::Oid; +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::vec::Vec; +use asn1_rs::ASN1DateTime; +use asn1_rs::Any; +#[cfg(feature = "bitvec")] +use bitvec::{order::Msb0, slice::BitSlice}; +use core::convert::AsRef; +use core::convert::From; +use core::ops::Index; + +/// Representation of a BER-encoded (X.690) object +/// +/// A BER object is composed of a header describing the object class, type and length, +/// and the content. +/// +/// Note that the content may sometimes not match the header tag (for ex when parsing IMPLICIT +/// tagged values). +#[derive(Debug, Clone, PartialEq)] +pub struct BerObject<'a> { + pub header: Header<'a>, + pub content: BerObjectContent<'a>, +} + +/// BER object content +#[derive(Debug, Clone, PartialEq)] +#[allow(clippy::upper_case_acronyms)] +pub enum BerObjectContent<'a> { + /// EOC (no content) + EndOfContent, + /// BOOLEAN: decoded value + Boolean(bool), + /// INTEGER: raw bytes + /// + /// Note: the reason to store the raw bytes is that integers have non-finite length in the + /// spec, and also that the raw encoding is also important for some applications. + /// + /// To extract the number, see the `as_u64`, `as_u32`, `as_bigint` and `as_biguint` methods. + Integer(&'a [u8]), + /// BIT STRING: number of unused bits, and object + BitString(u8, BitStringObject<'a>), + /// OCTET STRING: slice + OctetString(&'a [u8]), + /// NULL (no content) + Null, + /// ENUMERATED: decoded enum number + Enum(u64), + /// OID + OID(Oid<'a>), + /// RELATIVE OID + RelativeOID(Oid<'a>), + /// NumericString: decoded string + NumericString(&'a str), + /// VisibleString: decoded string + VisibleString(&'a str), + /// PrintableString: decoded string + PrintableString(&'a str), + /// IA5String: decoded string + IA5String(&'a str), + /// UTF8String: decoded string + UTF8String(&'a str), + /// T61String: decoded string + T61String(&'a str), + /// VideotexString: decoded string + VideotexString(&'a str), + + /// BmpString: decoded string + BmpString(&'a str), + /// UniversalString: raw object bytes + UniversalString(&'a [u8]), + + /// SEQUENCE: list of objects + Sequence(Vec<BerObject<'a>>), + /// SET: list of objects + Set(Vec<BerObject<'a>>), + + /// UTCTime: decoded string + UTCTime(ASN1DateTime), + /// GeneralizedTime: decoded string + GeneralizedTime(ASN1DateTime), + + /// Object descriptor: decoded string + ObjectDescriptor(&'a str), + /// GraphicString: decoded string + GraphicString(&'a str), + /// GeneralString: decoded string + GeneralString(&'a str), + + /// Optional object + Optional(Option<Box<BerObject<'a>>>), + /// Tagged object (EXPLICIT): class, tag and content of inner object + Tagged(Class, Tag, Box<BerObject<'a>>), + + /// Private or Unknown (for ex. unknown tag) object + Unknown(Any<'a>), +} + +impl<'a> BerObject<'a> { + /// Build a BerObject from a header and content. + /// + /// Note: values are not checked, so the tag can be different from the real content, or flags + /// can be invalid. + #[inline] + pub const fn from_header_and_content<'o>( + header: Header<'o>, + content: BerObjectContent<'o>, + ) -> BerObject<'o> { + BerObject { header, content } + } + + /// Build a BerObject from its content, using default flags (no class, correct tag, + /// and constructed flag set only for Set and Sequence) + pub const fn from_obj(c: BerObjectContent) -> BerObject { + let class = Class::Universal; + let tag = c.tag(); + let constructed = matches!(tag, Tag::Sequence | Tag::Set); + let header = Header::new(class, constructed, tag, Length::Definite(0)); + BerObject { header, content: c } + } + + /// Build a DER integer object from a slice containing an encoded integer + pub const fn from_int_slice(i: &'a [u8]) -> BerObject<'a> { + let header = Header::new(Class::Universal, false, Tag::Integer, Length::Definite(0)); + BerObject { + header, + content: BerObjectContent::Integer(i), + } + } + + /// Set a tag for the BER object + pub fn set_raw_tag(self, raw_tag: Option<&'a [u8]>) -> BerObject { + let header = self.header.with_raw_tag(raw_tag.map(|x| x.into())); + BerObject { header, ..self } + } + + /// Build a DER sequence object from a vector of DER objects + pub const fn from_seq(l: Vec<BerObject>) -> BerObject { + BerObject::from_obj(BerObjectContent::Sequence(l)) + } + + /// Build a DER set object from a vector of DER objects + pub const fn from_set(l: Vec<BerObject>) -> BerObject { + BerObject::from_obj(BerObjectContent::Set(l)) + } + + /// Attempt to read a signed integer value from DER object. + /// + /// This can fail if the object is not an integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # use der_parser::ber::BerObject; + /// let der_int = BerObject::from_int_slice(b"\x80"); + /// assert_eq!( + /// der_int.as_i64(), + /// Ok(-128) + /// ); + /// ``` + pub fn as_i64(&self) -> Result<i64, BerError> { + self.content.as_i64() + } + + /// Attempt to read a signed integer value from DER object. + /// + /// This can fail if the object is not an integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # use der_parser::ber::BerObject; + /// let der_int = BerObject::from_int_slice(b"\x80"); + /// assert_eq!( + /// der_int.as_i32(), + /// Ok(-128) + /// ); + /// ``` + pub fn as_i32(&self) -> Result<i32, BerError> { + self.content.as_i32() + } + + /// Attempt to read integer value from DER object. + /// + /// This can fail if the object is not an unsigned integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # use der_parser::ber::BerObject; + /// let der_int = BerObject::from_int_slice(b"\x01\x00\x01"); + /// assert_eq!( + /// der_int.as_u64(), + /// Ok(0x10001) + /// ); + /// ``` + pub fn as_u64(&self) -> Result<u64, BerError> { + self.content.as_u64() + } + + /// Attempt to read integer value from DER object. + /// + /// This can fail if the object is not an unsigned integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # extern crate der_parser; + /// # use der_parser::ber::{BerObject,BerObjectContent}; + /// let der_int = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01")); + /// assert_eq!( + /// der_int.as_u32(), + /// Ok(0x10001) + /// ); + /// ``` + pub fn as_u32(&self) -> Result<u32, BerError> { + self.content.as_u32() + } + + /// Attempt to read integer value from DER object. + /// This can fail if the object is not a boolean. + pub fn as_bool(&self) -> Result<bool, BerError> { + self.content.as_bool() + } + + /// Attempt to read an OID value from DER object. + /// This can fail if the object is not an OID. + pub fn as_oid(&self) -> Result<&Oid<'a>, BerError> { + self.content.as_oid() + } + + /// Attempt to read an OID value from DER object. + /// This can fail if the object is not an OID. + pub fn as_oid_val(&self) -> Result<Oid<'a>, BerError> { + self.content.as_oid_val() + } + + /// Attempt to get a reference on the content from an optional object. + /// This can fail if the object is not optional. + pub fn as_optional(&self) -> Result<Option<&BerObject<'a>>, BerError> { + self.content.as_optional() + } + + /// Attempt to get a reference on the content from a tagged object. + /// This can fail if the object is not tagged. + pub fn as_tagged(&self) -> Result<(Class, Tag, &BerObject<'a>), BerError> { + self.content.as_tagged() + } + + /// Attempt to read a reference to a BitString value from DER object. + /// This can fail if the object is not an BitString. + /// + /// Note that this function returns a reference to the BitString. To get an owned value, + /// use [`as_bitstring`](struct.BerObject.html#method.as_bitstring) + pub fn as_bitstring_ref(&self) -> Result<&BitStringObject, BerError> { + self.content.as_bitstring_ref() + } + + /// Attempt to read a BitString value from DER object. + /// This can fail if the object is not an BitString. + pub fn as_bitstring(&self) -> Result<BitStringObject<'a>, BerError> { + self.content.as_bitstring() + } + + /// Constructs a shared `&BitSlice` reference over the object data, if available as slice. + #[cfg(feature = "bitvec")] + pub fn as_bitslice(&self) -> Result<&BitSlice<Msb0, u8>, BerError> { + self.content.as_bitslice() + } + + /// Attempt to extract the list of objects from a DER sequence. + /// This can fail if the object is not a sequence. + pub fn as_sequence(&self) -> Result<&Vec<BerObject<'a>>, BerError> { + self.content.as_sequence() + } + + /// Attempt to extract the list of objects from a DER set. + /// This can fail if the object is not a set. + pub fn as_set(&self) -> Result<&Vec<BerObject<'a>>, BerError> { + self.content.as_set() + } + + /// Attempt to get the content from a DER object, as a slice. + /// This can fail if the object does not contain a type directly equivalent to a slice (e.g a + /// sequence). + /// This function mostly concerns string types, integers, or unknown DER objects. + pub fn as_slice(&self) -> Result<&'a [u8], BerError> { + self.content.as_slice() + } + + /// Attempt to get the content from a DER object, as a str. + /// This can fail if the object does not contain a string type. + /// + /// Only some string types are considered here. Other + /// string types can be read using `as_slice`. + pub fn as_str(&self) -> Result<&'a str, BerError> { + self.content.as_str() + } + + /// Get the BER object header's class. + #[inline] + pub const fn class(&self) -> Class { + self.header.class() + } + + /// Get the BER object header's tag. + #[inline] + pub const fn tag(&self) -> Tag { + self.header.tag() + } + + /// Get the BER object header's length. + #[inline] + pub const fn length(&self) -> Length { + self.header.length() + } + + /// Test if object class is Universal + #[inline] + pub const fn is_universal(&self) -> bool { + self.header.is_universal() + } + /// Test if object class is Application + #[inline] + pub const fn is_application(&self) -> bool { + self.header.is_application() + } + /// Test if object class is Context-specific + #[inline] + pub const fn is_contextspecific(&self) -> bool { + self.header.is_contextspecific() + } + /// Test if object class is Private + #[inline] + pub fn is_private(&self) -> bool { + self.header.is_private() + } + + /// Test if object is primitive + #[inline] + pub const fn is_primitive(&self) -> bool { + self.header.is_primitive() + } + /// Test if object is constructed + #[inline] + pub const fn is_constructed(&self) -> bool { + self.header.is_constructed() + } + + /// Return error if `class` is not the expected class + #[inline] + pub const fn assert_class(&self, class: Class) -> Result<(), BerError> { + self.header.assert_class(class) + } + + /// Return error if `tag` is not the expected tag + #[inline] + pub const fn assert_tag(&self, tag: Tag) -> Result<(), BerError> { + self.header.assert_tag(tag) + } + + /// Return error if object is not constructed + #[inline] + pub const fn assert_constructed(&self) -> Result<(), BerError> { + self.header.assert_constructed() + } + + /// Return error if object is not primitive + #[inline] + pub const fn assert_primitive(&self) -> Result<(), BerError> { + self.header.assert_primitive() + } +} + +/// Build a DER object from an OID. +impl<'a> From<Oid<'a>> for BerObject<'a> { + fn from(oid: Oid<'a>) -> BerObject<'a> { + BerObject::from_obj(BerObjectContent::OID(oid)) + } +} + +/// Build a DER object from a BerObjectContent. +impl<'a> From<BerObjectContent<'a>> for BerObject<'a> { + fn from(obj: BerObjectContent<'a>) -> BerObject<'a> { + BerObject::from_obj(obj) + } +} + +impl<'a> BerObjectContent<'a> { + /// Attempt to read a signed integer value from this object. + /// + /// This can fail if the object is not an integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # use der_parser::ber::BerObject; + /// let der_int = BerObject::from_int_slice(b"\x80"); + /// assert_eq!( + /// der_int.as_i64(), + /// Ok(-128) + /// ); + /// ``` + pub fn as_i64(&self) -> Result<i64, BerError> { + if let BerObjectContent::Integer(bytes) = self { + let result = if is_highest_bit_set(bytes) { + <i64>::from_be_bytes(decode_array_int8(bytes)?) + } else { + <u64>::from_be_bytes(decode_array_uint8(bytes)?) as i64 + }; + Ok(result) + } else { + Err(BerError::BerValueError) + } + } + + /// Attempt to read a signed integer value from this object. + /// + /// This can fail if the object is not an integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # use der_parser::ber::BerObject; + /// let der_int = BerObject::from_int_slice(b"\x80"); + /// assert_eq!( + /// der_int.as_i32(), + /// Ok(-128) + /// ); + /// ``` + pub fn as_i32(&self) -> Result<i32, BerError> { + if let BerObjectContent::Integer(bytes) = self { + let result = if is_highest_bit_set(bytes) { + <i32>::from_be_bytes(decode_array_int4(bytes)?) + } else { + <u32>::from_be_bytes(decode_array_uint4(bytes)?) as i32 + }; + Ok(result) + } else { + Err(BerError::BerValueError) + } + } + + /// Attempt to read integer value from this object. + /// + /// This can fail if the object is not an unsigned integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # use der_parser::ber::BerObject; + /// let der_int = BerObject::from_int_slice(b"\x01\x00\x01"); + /// assert_eq!( + /// der_int.as_u64(), + /// Ok(0x10001) + /// ); + /// ``` + pub fn as_u64(&self) -> Result<u64, BerError> { + match self { + BerObjectContent::Integer(i) => { + let result = <u64>::from_be_bytes(decode_array_uint8(i)?); + Ok(result) + } + BerObjectContent::BitString(ignored_bits, data) => { + bitstring_to_u64(*ignored_bits as usize, data) + } + BerObjectContent::Enum(i) => Ok(*i), + _ => Err(BerError::BerTypeError), + } + } + + /// Attempt to read integer value from this object. + /// + /// This can fail if the object is not an unsigned integer, or if it is too large. + /// + /// # Examples + /// + /// ```rust + /// # extern crate der_parser; + /// # use der_parser::ber::{BerObject,BerObjectContent}; + /// let der_int = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01")); + /// assert_eq!( + /// der_int.as_u32(), + /// Ok(0x10001) + /// ); + /// ``` + pub fn as_u32(&self) -> Result<u32, BerError> { + match self { + BerObjectContent::Integer(i) => { + let result = <u32>::from_be_bytes(decode_array_uint4(i)?); + Ok(result) + } + BerObjectContent::BitString(ignored_bits, data) => { + bitstring_to_u64(*ignored_bits as usize, data).and_then(|x| { + if x > u64::from(core::u32::MAX) { + Err(BerError::IntegerTooLarge) + } else { + Ok(x as u32) + } + }) + } + BerObjectContent::Enum(i) => { + if *i > u64::from(core::u32::MAX) { + Err(BerError::IntegerTooLarge) + } else { + Ok(*i as u32) + } + } + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_bool(&self) -> Result<bool, BerError> { + match *self { + BerObjectContent::Boolean(b) => Ok(b), + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_oid(&self) -> Result<&Oid<'a>, BerError> { + match *self { + BerObjectContent::OID(ref o) => Ok(o), + BerObjectContent::RelativeOID(ref o) => Ok(o), + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_oid_val(&self) -> Result<Oid<'a>, BerError> { + self.as_oid().map(|o| o.clone()) + } + + pub fn as_optional(&self) -> Result<Option<&BerObject<'a>>, BerError> { + match *self { + BerObjectContent::Optional(Some(ref o)) => Ok(Some(o)), + BerObjectContent::Optional(None) => Ok(None), + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_tagged(&self) -> Result<(Class, Tag, &BerObject<'a>), BerError> { + match *self { + BerObjectContent::Tagged(class, tag, ref o) => Ok((class, tag, o.as_ref())), + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_bitstring_ref(&self) -> Result<&BitStringObject, BerError> { + match *self { + BerObjectContent::BitString(_, ref b) => Ok(b), + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_bitstring(&self) -> Result<BitStringObject<'a>, BerError> { + match *self { + BerObjectContent::BitString(_, ref b) => Ok(b.to_owned()), + _ => Err(BerError::BerTypeError), + } + } + + /// Constructs a shared `&BitSlice` reference over the object data, if available as slice. + #[cfg(feature = "bitvec")] + pub fn as_bitslice(&self) -> Result<&BitSlice<Msb0, u8>, BerError> { + self.as_slice() + .and_then(|s| BitSlice::<Msb0, _>::from_slice(s).map_err(|_| BerError::BerValueError)) + } + + pub fn as_sequence(&self) -> Result<&Vec<BerObject<'a>>, BerError> { + match *self { + BerObjectContent::Sequence(ref s) => Ok(s), + _ => Err(BerError::BerTypeError), + } + } + + pub fn as_set(&self) -> Result<&Vec<BerObject<'a>>, BerError> { + match *self { + BerObjectContent::Set(ref s) => Ok(s), + _ => Err(BerError::BerTypeError), + } + } + + #[rustfmt::skip] + pub fn as_slice(&self) -> Result<&'a [u8],BerError> { + match *self { + BerObjectContent::NumericString(s) | + BerObjectContent::BmpString(s) | + BerObjectContent::VisibleString(s) | + BerObjectContent::PrintableString(s) | + BerObjectContent::GeneralString(s) | + BerObjectContent::ObjectDescriptor(s) | + BerObjectContent::GraphicString(s) | + BerObjectContent::T61String(s) | + BerObjectContent::VideotexString(s) | + BerObjectContent::UTF8String(s) | + BerObjectContent::IA5String(s) => Ok(s.as_ref()), + BerObjectContent::Integer(s) | + BerObjectContent::BitString(_,BitStringObject{data:s}) | + BerObjectContent::OctetString(s) | + BerObjectContent::UniversalString(s) => Ok(s), + BerObjectContent::Unknown(ref any) => Ok(any.data), + _ => Err(BerError::BerTypeError), + } + } + + #[rustfmt::skip] + pub fn as_str(&self) -> Result<&'a str,BerError> { + match *self { + BerObjectContent::NumericString(s) | + BerObjectContent::BmpString(s) | + BerObjectContent::VisibleString(s) | + BerObjectContent::PrintableString(s) | + BerObjectContent::GeneralString(s) | + BerObjectContent::ObjectDescriptor(s) | + BerObjectContent::GraphicString(s) | + BerObjectContent::T61String(s) | + BerObjectContent::VideotexString(s) | + BerObjectContent::UTF8String(s) | + BerObjectContent::IA5String(s) => Ok(s), + _ => Err(BerError::BerTypeError), + } + } + + #[rustfmt::skip] + const fn tag(&self) -> Tag { + match self { + BerObjectContent::EndOfContent => Tag::EndOfContent, + BerObjectContent::Boolean(_) => Tag::Boolean, + BerObjectContent::Integer(_) => Tag::Integer, + BerObjectContent::BitString(_,_) => Tag::BitString, + BerObjectContent::OctetString(_) => Tag::OctetString, + BerObjectContent::Null => Tag::Null, + BerObjectContent::Enum(_) => Tag::Enumerated, + BerObjectContent::OID(_) => Tag::Oid, + BerObjectContent::NumericString(_) => Tag::NumericString, + BerObjectContent::VisibleString(_) => Tag::VisibleString, + BerObjectContent::PrintableString(_) => Tag::PrintableString, + BerObjectContent::IA5String(_) => Tag::Ia5String, + BerObjectContent::UTF8String(_) => Tag::Utf8String, + BerObjectContent::RelativeOID(_) => Tag::RelativeOid, + BerObjectContent::T61String(_) => Tag::T61String, + BerObjectContent::VideotexString(_) => Tag::VideotexString, + BerObjectContent::BmpString(_) => Tag::BmpString, + BerObjectContent::UniversalString(_) => Tag::UniversalString, + BerObjectContent::Sequence(_) => Tag::Sequence, + BerObjectContent::Set(_) => Tag::Set, + BerObjectContent::UTCTime(_) => Tag::UtcTime, + BerObjectContent::GeneralizedTime(_) => Tag::GeneralizedTime, + BerObjectContent::ObjectDescriptor(_) => Tag::ObjectDescriptor, + BerObjectContent::GraphicString(_) => Tag::GraphicString, + BerObjectContent::GeneralString(_) => Tag::GeneralString, + BerObjectContent::Tagged(_,x,_) => *x, + BerObjectContent::Unknown(any) => any.tag(), + BerObjectContent::Optional(Some(obj)) => obj.content.tag(), + BerObjectContent::Optional(None) => Tag(0x00), // XXX invalid ! + } + } +} + +#[cfg(feature = "bigint")] +#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] +use num_bigint::{BigInt, BigUint}; + +#[cfg(feature = "bigint")] +#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] +impl<'a> BerObject<'a> { + /// Attempt to read an integer value from this object. + /// + /// This can fail if the object is not an integer. + /// + /// # Examples + /// + /// ```rust + /// use der_parser::ber::*; + /// + /// let data = &[0x02, 0x03, 0x01, 0x00, 0x01]; + /// + /// let (_, object) = parse_ber_integer(data).expect("parsing failed"); + /// # #[cfg(feature = "bigint")] + /// assert_eq!(object.as_bigint(), Ok(65537.into())) + /// ``` + pub fn as_bigint(&self) -> Result<BigInt, BerError> { + match self.content { + BerObjectContent::Integer(s) => Ok(BigInt::from_signed_bytes_be(s)), + _ => Err(BerError::BerValueError), + } + } + + /// Attempt to read a positive integer value from this object. + /// + /// This can fail if the object is not an integer, or is negative. + /// + /// # Examples + /// + /// ```rust + /// use der_parser::ber::*; + /// + /// let data = &[0x02, 0x03, 0x01, 0x00, 0x01]; + /// + /// let (_, object) = parse_ber_integer(data).expect("parsing failed"); + /// # #[cfg(feature = "bigint")] + /// assert_eq!(object.as_biguint(), Ok(65537_u32.into())) + /// ``` + pub fn as_biguint(&self) -> Result<BigUint, BerError> { + match self.content { + BerObjectContent::Integer(s) => { + if is_highest_bit_set(s) { + return Err(BerError::IntegerNegative); + } + Ok(BigUint::from_bytes_be(s)) + } + _ => Err(BerError::BerValueError), + } + } +} + +// This is a consuming iterator +impl<'a> IntoIterator for BerObject<'a> { + type Item = BerObject<'a>; + type IntoIter = BerObjectIntoIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + // match self { + // BerObjectContent::Sequence(ref v) => (), + // _ => (), + // }; + BerObjectIntoIterator { val: self, idx: 0 } + } +} + +#[derive(Debug)] +pub struct BerObjectIntoIterator<'a> { + val: BerObject<'a>, + idx: usize, +} + +impl<'a> Iterator for BerObjectIntoIterator<'a> { + type Item = BerObject<'a>; + fn next(&mut self) -> Option<BerObject<'a>> { + // let result = if self.idx < self.vec.len() { + // Some(self.vec[self.idx].clone()) + // } else { + // None + // }; + let res = match self.val.content { + BerObjectContent::Sequence(ref v) if self.idx < v.len() => Some(v[self.idx].clone()), + BerObjectContent::Set(ref v) if self.idx < v.len() => Some(v[self.idx].clone()), + _ => { + if self.idx == 0 { + Some(self.val.clone()) + } else { + None + } + } + }; + self.idx += 1; + res + } +} + +// impl<'a> Iterator for BerObjectContent<'a> { +// type Item = BerObjectContent<'a>; +// +// fn next(&mut self) -> Option<BerObjectContent<'a>> { +// None +// } +// } + +#[derive(Debug)] +pub struct BerObjectRefIterator<'a> { + obj: &'a BerObject<'a>, + idx: usize, +} + +impl<'a> Iterator for BerObjectRefIterator<'a> { + type Item = &'a BerObject<'a>; + fn next(&mut self) -> Option<&'a BerObject<'a>> { + let res = match (self.obj).content { + BerObjectContent::Sequence(ref v) if self.idx < v.len() => Some(&v[self.idx]), + BerObjectContent::Set(ref v) if self.idx < v.len() => Some(&v[self.idx]), + _ => None, + }; + self.idx += 1; + res + } +} + +impl<'a> BerObject<'a> { + pub fn ref_iter(&'a self) -> BerObjectRefIterator<'a> { + BerObjectRefIterator { obj: self, idx: 0 } + } +} + +impl<'a> Index<usize> for BerObject<'a> { + type Output = BerObject<'a>; + + fn index(&self, idx: usize) -> &BerObject<'a> { + match (self).content { + BerObjectContent::Sequence(ref v) if idx < v.len() => &v[idx], + BerObjectContent::Set(ref v) if idx < v.len() => &v[idx], + _ => panic!("Try to index BerObjectContent which is not constructed"), + } + // XXX the following + // self.ref_iter().nth(idx).unwrap() + // fails with: + // error: cannot infer an appropriate lifetime for autoref due to conflicting requirements [E0495] + // self.ref_iter().nth(idx).unwrap() + } +} + +/// BitString wrapper +#[derive(Clone, Debug, PartialEq)] +pub struct BitStringObject<'a> { + pub data: &'a [u8], +} + +impl<'a> BitStringObject<'a> { + /// Test if bit `bitnum` is set + pub fn is_set(&self, bitnum: usize) -> bool { + let byte_pos = bitnum / 8; + if byte_pos >= self.data.len() { + return false; + } + let b = 7 - (bitnum % 8); + (self.data[byte_pos] & (1 << b)) != 0 + } + + /// Constructs a shared `&BitSlice` reference over the object data. + #[cfg(feature = "bitvec")] + pub fn as_bitslice(&self) -> Option<&BitSlice<Msb0, u8>> { + BitSlice::<Msb0, _>::from_slice(self.data).ok() + } +} + +impl<'a> AsRef<[u8]> for BitStringObject<'a> { + fn as_ref(&self) -> &[u8] { + self.data + } +} + +#[cfg(test)] +mod tests { + use crate::ber::*; + use crate::oid::*; + + #[test] + fn test_der_as_u64() { + let der_obj = BerObject::from_int_slice(b"\x01\x00\x02"); + assert_eq!(der_obj.as_u64(), Ok(0x10002)); + } + + #[test] + fn test_ber_as_u64_bitstring() { + let (_, ber_obj) = parse_ber_bitstring(b"\x03\x04\x06\x6e\x5d\xc0").unwrap(); + assert_eq!(ber_obj.as_u64(), Ok(0b011011100101110111)); + + let (_, ber_obj_with_nonzero_padding) = + parse_ber_bitstring(b"\x03\x04\x06\x6e\x5d\xe0").unwrap(); + assert_eq!( + ber_obj_with_nonzero_padding.as_u64(), + Ok(0b011011100101110111) + ); + } + + #[test] + fn test_der_seq_iter() { + let der_obj = BerObject::from_obj(BerObjectContent::Sequence(vec![ + BerObject::from_int_slice(b"\x01\x00\x01"), + BerObject::from_int_slice(b"\x01\x00\x00"), + ])); + let expected_values = vec![ + BerObject::from_int_slice(b"\x01\x00\x01"), + BerObject::from_int_slice(b"\x01\x00\x00"), + ]; + + for (idx, v) in der_obj.ref_iter().enumerate() { + // println!("v: {:?}", v); + assert_eq!((*v), expected_values[idx]); + } + } + + #[test] + fn test_der_from_oid() { + let obj: BerObject = Oid::from(&[1, 2]).unwrap().into(); + let expected = BerObject::from_obj(BerObjectContent::OID(Oid::from(&[1, 2]).unwrap())); + + assert_eq!(obj, expected); + } + + #[test] + fn test_der_bitstringobject() { + let obj = BitStringObject { + data: &[0x0f, 0x00, 0x40], + }; + assert!(!obj.is_set(0)); + assert!(obj.is_set(7)); + assert!(!obj.is_set(9)); + assert!(obj.is_set(17)); + } + + #[cfg(feature = "bitvec")] + #[test] + fn test_der_bitslice() { + use std::string::String; + let obj = BitStringObject { + data: &[0x0f, 0x00, 0x40], + }; + let slice = obj.as_bitslice().expect("as_bitslice"); + assert_eq!(slice.get(0).as_deref(), Some(&false)); + assert_eq!(slice.get(7).as_deref(), Some(&true)); + assert_eq!(slice.get(9).as_deref(), Some(&false)); + assert_eq!(slice.get(17).as_deref(), Some(&true)); + let s = slice.iter().fold(String::with_capacity(24), |mut acc, b| { + acc += if *b { "1" } else { "0" }; + acc + }); + assert_eq!(&s, "000011110000000001000000"); + } + + #[test] + fn test_der_bistringobject_asref() { + fn assert_equal<T: AsRef<[u8]>>(s: T, b: &[u8]) { + assert_eq!(s.as_ref(), b); + } + let b: &[u8] = &[0x0f, 0x00, 0x40]; + let obj = BitStringObject { data: b }; + assert_equal(obj, b); + } + + #[cfg(feature = "bigint")] + #[test] + fn test_der_to_bigint() { + let obj = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01")); + let expected = ::num_bigint::BigInt::from(0x10001); + + assert_eq!(obj.as_bigint(), Ok(expected)); + } + + #[cfg(feature = "bigint")] + #[test] + fn test_der_to_biguint() { + let obj = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01")); + let expected = ::num_bigint::BigUint::from(0x10001_u32); + + assert_eq!(obj.as_biguint(), Ok(expected)); + } +} |