| 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; |
| #[cfg(feature = "bitvec")] |
| use bitvec::{order::Msb0, slice::BitSlice}; |
| use core::convert::AsRef; |
| use core::convert::From; |
| use core::convert::TryFrom; |
| use core::fmt; |
| use core::ops::Index; |
| use rusticata_macros::newtype_enum; |
| |
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| pub struct BerClassFromIntError(pub(crate) ()); |
| |
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| pub struct BerSizeError(pub(crate) ()); |
| |
| /// BER Object class of tag |
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| #[repr(u8)] |
| pub enum BerClass { |
| Universal = 0b00, |
| Application = 0b01, |
| ContextSpecific = 0b10, |
| Private = 0b11, |
| } |
| |
| /// Ber Object Length |
| #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| pub enum BerSize { |
| /// Definite form (X.690 8.1.3.3) |
| Definite(usize), |
| /// Indefinite form (X.690 8.1.3.6) |
| Indefinite, |
| } |
| |
| /// BER/DER Tag as defined in X.680 section 8.4 |
| /// |
| /// X.690 doesn't specify the maximum tag size so we're assuming that people |
| /// aren't going to need anything more than a u32. |
| #[derive(Clone, Copy, PartialEq, Eq)] |
| pub struct BerTag(pub u32); |
| |
| newtype_enum! { |
| impl debug BerTag { |
| EndOfContent = 0x0, |
| Boolean = 0x1, |
| Integer = 0x2, |
| BitString = 0x3, |
| OctetString = 0x4, |
| Null = 0x05, |
| Oid = 0x06, |
| ObjDescriptor = 0x07, |
| External = 0x08, |
| RealType = 0x09, |
| Enumerated = 0xa, |
| EmbeddedPdv = 0xb, |
| Utf8String = 0xc, |
| RelativeOid = 0xd, |
| |
| Sequence = 0x10, |
| Set = 0x11, |
| NumericString = 0x12, |
| PrintableString = 0x13, |
| T61String = 0x14, |
| VideotexString = 0x15, |
| |
| Ia5String = 0x16, |
| UtcTime = 0x17, |
| GeneralizedTime = 0x18, |
| |
| GraphicString = 25, // 0x19 |
| VisibleString = 26, // 0x1a |
| GeneralString = 27, // 0x1b |
| |
| UniversalString = 0x1c, |
| BmpString = 0x1e, |
| |
| Invalid = 0xff, |
| } |
| } |
| |
| /// 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: BerObjectHeader<'a>, |
| pub content: BerObjectContent<'a>, |
| } |
| |
| /// BER object header (identifier and length) |
| #[derive(Clone, Debug)] |
| pub struct BerObjectHeader<'a> { |
| /// Object class: universal, application, context-specific, or private |
| pub class: BerClass, |
| /// Constructed attribute: 1 if constructed, else 0 |
| pub structured: u8, |
| /// Tag number |
| pub tag: BerTag, |
| /// Object length: definite or indefinite |
| pub len: BerSize, |
| |
| /// Optionally, the raw encoding of the tag |
| /// |
| /// This is useful in some cases, where different representations of the same |
| /// BER tags have different meanings (BER only) |
| pub raw_tag: Option<&'a [u8]>, |
| } |
| |
| /// 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: raw object bytes |
| T61String(&'a [u8]), |
| /// VideotexString: raw object bytes |
| VideotexString(&'a [u8]), |
| |
| /// BmpString: raw object bytes |
| BmpString(&'a [u8]), |
| /// 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(&'a str), |
| /// GeneralizedTime: decoded string |
| GeneralizedTime(&'a str), |
| |
| /// Object descriptor: raw object bytes |
| ObjectDescriptor(&'a [u8]), |
| /// GraphicString: raw object bytes |
| GraphicString(&'a [u8]), |
| /// GeneralString: raw object bytes |
| GeneralString(&'a [u8]), |
| |
| /// Optional object |
| Optional(Option<Box<BerObject<'a>>>), |
| /// Tagged object (EXPLICIT): class, tag and content of inner object |
| Tagged(BerClass, BerTag, Box<BerObject<'a>>), |
| /// Private |
| Private(BerObjectHeader<'a>, &'a [u8]), |
| |
| /// Unknown object: object tag (copied from header), and raw content |
| Unknown(BerClass, BerTag, &'a [u8]), |
| } |
| |
| impl fmt::Display for BerClass { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let s = match self { |
| BerClass::Universal => "UNIVERSAL", |
| BerClass::Application => "APPLICATION", |
| BerClass::ContextSpecific => "CONTEXT-SPECIFIC", |
| BerClass::Private => "PRIVATE", |
| }; |
| write!(f, "{}", s) |
| } |
| } |
| |
| impl From<u32> for BerTag { |
| fn from(v: u32) -> Self { |
| BerTag(v) |
| } |
| } |
| |
| impl BerSize { |
| /// Return true if length is definite and equal to 0 |
| pub fn is_null(&self) -> bool { |
| *self == BerSize::Definite(0) |
| } |
| |
| /// Get length of primitive object |
| #[inline] |
| pub fn primitive(&self) -> Result<usize, BerError> { |
| match self { |
| BerSize::Definite(sz) => Ok(*sz), |
| BerSize::Indefinite => Err(BerError::IndefiniteLengthUnexpected), |
| } |
| } |
| } |
| |
| impl From<usize> for BerSize { |
| fn from(v: usize) -> Self { |
| BerSize::Definite(v) |
| } |
| } |
| |
| impl TryFrom<u64> for BerSize { |
| type Error = BerSizeError; |
| |
| fn try_from(value: u64) -> Result<Self, Self::Error> { |
| let v = usize::try_from(value).or(Err(BerSizeError(())))?; |
| Ok(BerSize::Definite(v)) |
| } |
| } |
| |
| impl TryFrom<BerSize> for usize { |
| type Error = BerSizeError; |
| |
| #[inline] |
| fn try_from(value: BerSize) -> Result<Self, Self::Error> { |
| match value { |
| BerSize::Definite(sz) => Ok(sz), |
| BerSize::Indefinite => Err(BerSizeError(())), |
| } |
| } |
| } |
| |
| impl TryFrom<u8> for BerClass { |
| type Error = BerClassFromIntError; |
| |
| #[inline] |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 0b00 => Ok(BerClass::Universal), |
| 0b01 => Ok(BerClass::Application), |
| 0b10 => Ok(BerClass::ContextSpecific), |
| 0b11 => Ok(BerClass::Private), |
| _ => Err(BerClassFromIntError(())), |
| } |
| } |
| } |
| |
| impl<'a> BerObjectHeader<'a> { |
| /// Build a new BER header |
| pub fn new<Len: Into<BerSize>>(class: BerClass, structured: u8, tag: BerTag, len: Len) -> Self { |
| BerObjectHeader { |
| tag, |
| structured, |
| class, |
| len: len.into(), |
| raw_tag: None, |
| } |
| } |
| |
| /// Update header class |
| #[inline] |
| pub fn with_class(self, class: BerClass) -> Self { |
| BerObjectHeader { class, ..self } |
| } |
| |
| /// Update header tag |
| #[inline] |
| pub fn with_tag(self, tag: BerTag) -> Self { |
| BerObjectHeader { tag, ..self } |
| } |
| |
| /// Update header length |
| #[inline] |
| pub fn with_len(self, len: BerSize) -> Self { |
| BerObjectHeader { len, ..self } |
| } |
| |
| /// Update header to add reference to raw tag |
| #[inline] |
| pub fn with_raw_tag(self, raw_tag: Option<&'a [u8]>) -> Self { |
| BerObjectHeader { raw_tag, ..self } |
| } |
| |
| /// Test if object class is Universal |
| #[inline] |
| pub fn is_universal(&self) -> bool { |
| self.class == BerClass::Universal |
| } |
| /// Test if object class is Application |
| #[inline] |
| pub fn is_application(&self) -> bool { |
| self.class == BerClass::Application |
| } |
| /// Test if object class is Context-specific |
| #[inline] |
| pub fn is_contextspecific(&self) -> bool { |
| self.class == BerClass::ContextSpecific |
| } |
| /// Test if object class is Private |
| #[inline] |
| pub fn is_private(&self) -> bool { |
| self.class == BerClass::Private |
| } |
| |
| /// Test if object is primitive |
| #[inline] |
| pub fn is_primitive(&self) -> bool { |
| self.structured == 0 |
| } |
| /// Test if object is constructed |
| #[inline] |
| pub fn is_constructed(&self) -> bool { |
| self.structured == 1 |
| } |
| } |
| |
| 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. |
| pub fn from_header_and_content<'o>( |
| header: BerObjectHeader<'o>, |
| content: BerObjectContent<'o>, |
| ) -> BerObject<'o> { |
| BerObject { header, content } |
| } |
| |
| /// Build a BerObject from its content, using default flags (no class, correct tag, |
| /// and structured flag set only for Set and Sequence) |
| pub fn from_obj(c: BerObjectContent) -> BerObject { |
| let class = BerClass::Universal; |
| let tag = c.tag(); |
| let structured = match tag { |
| BerTag::Sequence | BerTag::Set => 1, |
| _ => 0, |
| }; |
| let header = BerObjectHeader::new(class, structured, tag, BerSize::Definite(0)); |
| BerObject { header, content: c } |
| } |
| |
| /// Build a DER integer object from a slice containing an encoded integer |
| pub fn from_int_slice(i: &'a [u8]) -> BerObject<'a> { |
| let header = BerObjectHeader::new( |
| BerClass::Universal, |
| 0, |
| BerTag::Integer, |
| BerSize::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 = BerObjectHeader { |
| raw_tag, |
| ..self.header |
| }; |
| BerObject { header, ..self } |
| } |
| |
| /// Build a DER sequence object from a vector of DER objects |
| pub 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 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(&'a 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(&'a self) -> Result<(BerClass, BerTag, &'_ 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(&'a 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 NumericString, VisibleString, UTCTime, GeneralizedTime, |
| /// PrintableString, UTF8String and IA5String 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() |
| } |
| |
| /// Test if object class is Universal |
| pub fn is_universal(&self) -> bool { |
| self.header.class == BerClass::Universal |
| } |
| /// Test if object class is Application |
| pub fn is_application(&self) -> bool { |
| self.header.class == BerClass::Application |
| } |
| /// Test if object class is Context-specific |
| pub fn is_contextspecific(&self) -> bool { |
| self.header.class == BerClass::ContextSpecific |
| } |
| /// Test if object class is Private |
| pub fn is_private(&self) -> bool { |
| self.header.class == BerClass::Private |
| } |
| |
| /// Test if object is primitive |
| pub fn is_primitive(&self) -> bool { |
| self.header.structured == 0 |
| } |
| /// Test if object is constructed |
| pub fn is_constructed(&self) -> bool { |
| self.header.structured == 1 |
| } |
| } |
| |
| /// 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) |
| } |
| } |
| |
| /// Compare two BER headers. `len` fields are compared only if both objects have it set (same for `raw_tag`) |
| impl<'a> PartialEq<BerObjectHeader<'a>> for BerObjectHeader<'a> { |
| fn eq(&self, other: &BerObjectHeader) -> bool { |
| self.class == other.class |
| && self.tag == other.tag |
| && self.structured == other.structured |
| && { |
| if self.len.is_null() && other.len.is_null() { |
| self.len == other.len |
| } else { |
| true |
| } |
| } |
| && { |
| // it tag is present for both, compare it |
| if self.raw_tag.xor(other.raw_tag).is_none() { |
| self.raw_tag == other.raw_tag |
| } else { |
| true |
| } |
| } |
| } |
| } |
| |
| 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::InvalidTag) |
| } |
| } |
| |
| /// 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::InvalidTag) |
| } |
| } |
| |
| /// 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 as u64), |
| _ => 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(&'a 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(&'a self) -> Result<(BerClass, BerTag, &'_ 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(&'a 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::GeneralizedTime(s) | |
| BerObjectContent::UTCTime(s) | |
| BerObjectContent::VisibleString(s) | |
| BerObjectContent::PrintableString(s) | |
| BerObjectContent::UTF8String(s) | |
| BerObjectContent::IA5String(s) => Ok(s.as_ref()), |
| BerObjectContent::Integer(s) | |
| BerObjectContent::BitString(_,BitStringObject{data:s}) | |
| BerObjectContent::OctetString(s) | |
| BerObjectContent::T61String(s) | |
| BerObjectContent::VideotexString(s) | |
| BerObjectContent::BmpString(s) | |
| BerObjectContent::UniversalString(s) | |
| BerObjectContent::ObjectDescriptor(s) | |
| BerObjectContent::GraphicString(s) | |
| BerObjectContent::GeneralString(s) | |
| BerObjectContent::Unknown(_, _,s) | |
| BerObjectContent::Private(_,s) => Ok(s), |
| _ => Err(BerError::BerTypeError), |
| } |
| } |
| |
| #[rustfmt::skip] |
| pub fn as_str(&self) -> Result<&'a str,BerError> { |
| match *self { |
| BerObjectContent::NumericString(s) | |
| BerObjectContent::GeneralizedTime(s) | |
| BerObjectContent::UTCTime(s) | |
| BerObjectContent::VisibleString(s) | |
| BerObjectContent::PrintableString(s) | |
| BerObjectContent::UTF8String(s) | |
| BerObjectContent::IA5String(s) => Ok(s), |
| _ => Err(BerError::BerTypeError), |
| } |
| } |
| |
| #[rustfmt::skip] |
| fn tag(&self) -> BerTag { |
| match self { |
| BerObjectContent::EndOfContent => BerTag::EndOfContent, |
| BerObjectContent::Boolean(_) => BerTag::Boolean, |
| BerObjectContent::Integer(_) => BerTag::Integer, |
| BerObjectContent::BitString(_,_) => BerTag::BitString, |
| BerObjectContent::OctetString(_) => BerTag::OctetString, |
| BerObjectContent::Null => BerTag::Null, |
| BerObjectContent::Enum(_) => BerTag::Enumerated, |
| BerObjectContent::OID(_) => BerTag::Oid, |
| BerObjectContent::NumericString(_) => BerTag::NumericString, |
| BerObjectContent::VisibleString(_) => BerTag::VisibleString, |
| BerObjectContent::PrintableString(_) => BerTag::PrintableString, |
| BerObjectContent::IA5String(_) => BerTag::Ia5String, |
| BerObjectContent::UTF8String(_) => BerTag::Utf8String, |
| BerObjectContent::RelativeOID(_) => BerTag::RelativeOid, |
| BerObjectContent::T61String(_) => BerTag::T61String, |
| BerObjectContent::VideotexString(_) => BerTag::VideotexString, |
| BerObjectContent::BmpString(_) => BerTag::BmpString, |
| BerObjectContent::UniversalString(_) => BerTag::UniversalString, |
| BerObjectContent::Sequence(_) => BerTag::Sequence, |
| BerObjectContent::Set(_) => BerTag::Set, |
| BerObjectContent::UTCTime(_) => BerTag::UtcTime, |
| BerObjectContent::GeneralizedTime(_) => BerTag::GeneralizedTime, |
| BerObjectContent::ObjectDescriptor(_) => BerTag::ObjDescriptor, |
| BerObjectContent::GraphicString(_) => BerTag::GraphicString, |
| BerObjectContent::GeneralString(_) => BerTag::GeneralString, |
| BerObjectContent::Tagged(_,x,_) | |
| BerObjectContent::Unknown(_, x,_) => *x, |
| &BerObjectContent::Private(ref hdr, _) => hdr.tag, |
| BerObjectContent::Optional(Some(obj)) => obj.content.tag(), |
| BerObjectContent::Optional(None) => BerTag(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::InvalidTag), |
| } |
| } |
| |
| /// 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::InvalidTag), |
| } |
| } |
| } |
| |
| // 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 structured"), |
| } |
| // 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)); |
| } |
| } |