use crate::ber::*; use crate::der_constraint_fail_if; use crate::error::*; #[cfg(feature = "std")] use crate::ToDer; use crate::{BerParser, Class, DerParser, DynTagged, FromBer, FromDer, Length, Tag, ToStatic}; use alloc::borrow::Cow; use core::convert::TryFrom; use nom::bytes::streaming::take; /// BER/DER object header (identifier and length) #[derive(Clone, Debug)] pub struct Header<'a> { /// Object class: universal, application, context-specific, or private pub(crate) class: Class, /// Constructed attribute: true if constructed, else false pub(crate) constructed: bool, /// Tag number pub(crate) tag: Tag, /// Object length: value if definite, or indefinite pub(crate) length: Length, /// 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(crate) raw_tag: Option>, } impl<'a> Header<'a> { /// Build a new BER/DER header from the provided values pub const fn new(class: Class, constructed: bool, tag: Tag, length: Length) -> Self { Header { tag, constructed, class, length, raw_tag: None, } } /// Build a new BER/DER header from the provided tag, with default values for other fields #[inline] pub const fn new_simple(tag: Tag) -> Self { let constructed = matches!(tag, Tag::Sequence | Tag::Set); Self::new(Class::Universal, constructed, tag, Length::Definite(0)) } /// Set the class of this `Header` #[inline] pub fn with_class(self, class: Class) -> Self { Self { class, ..self } } /// Set the constructed flags of this `Header` #[inline] pub fn with_constructed(self, constructed: bool) -> Self { Self { constructed, ..self } } /// Set the tag of this `Header` #[inline] pub fn with_tag(self, tag: Tag) -> Self { Self { tag, ..self } } /// Set the length of this `Header` #[inline] pub fn with_length(self, length: Length) -> Self { Self { length, ..self } } /// Update header to add reference to raw tag #[inline] pub fn with_raw_tag(self, raw_tag: Option>) -> Self { Header { raw_tag, ..self } } /// Return the class of this header. #[inline] pub const fn class(&self) -> Class { self.class } /// Return true if this header has the 'constructed' flag. #[inline] pub const fn constructed(&self) -> bool { self.constructed } /// Return the tag of this header. #[inline] pub const fn tag(&self) -> Tag { self.tag } /// Return the length of this header. #[inline] pub const fn length(&self) -> Length { self.length } /// Return the raw tag encoding, if it was stored in this object #[inline] pub fn raw_tag(&self) -> Option<&[u8]> { self.raw_tag.as_ref().map(|cow| cow.as_ref()) } /// Test if object is primitive #[inline] pub const fn is_primitive(&self) -> bool { !self.constructed } /// Test if object is constructed #[inline] pub const fn is_constructed(&self) -> bool { self.constructed } /// Return error if class is not the expected class #[inline] pub const fn assert_class(&self, class: Class) -> Result<()> { self.class.assert_eq(class) } /// Return error if tag is not the expected tag #[inline] pub const fn assert_tag(&self, tag: Tag) -> Result<()> { self.tag.assert_eq(tag) } /// Return error if object is not primitive #[inline] pub const fn assert_primitive(&self) -> Result<()> { if self.is_primitive() { Ok(()) } else { Err(Error::ConstructUnexpected) } } /// Return error if object is primitive #[inline] pub const fn assert_constructed(&self) -> Result<()> { if !self.is_primitive() { Ok(()) } else { Err(Error::ConstructExpected) } } /// Test if object class is Universal #[inline] pub const fn is_universal(&self) -> bool { self.class as u8 == Class::Universal as u8 } /// Test if object class is Application #[inline] pub const fn is_application(&self) -> bool { self.class as u8 == Class::Application as u8 } /// Test if object class is Context-specific #[inline] pub const fn is_contextspecific(&self) -> bool { self.class as u8 == Class::ContextSpecific as u8 } /// Test if object class is Private #[inline] pub const fn is_private(&self) -> bool { self.class as u8 == Class::Private as u8 } /// Return error if object length is definite #[inline] pub const fn assert_definite(&self) -> Result<()> { if self.length.is_definite() { Ok(()) } else { Err(Error::DerConstraintFailed(DerConstraint::IndefiniteLength)) } } /// Get the content following a BER header #[inline] pub fn parse_ber_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> { // defaults to maximum depth 8 // depth is used only if BER, and length is indefinite BerParser::get_object_content(i, self, 8) } /// Get the content following a DER header #[inline] pub fn parse_der_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> { self.assert_definite()?; DerParser::get_object_content(i, self, 8) } } impl From for Header<'_> { #[inline] fn from(tag: Tag) -> Self { let constructed = matches!(tag, Tag::Sequence | Tag::Set); Self::new(Class::Universal, constructed, tag, Length::Definite(0)) } } impl<'a> ToStatic for Header<'a> { type Owned = Header<'static>; fn to_static(&self) -> Self::Owned { let raw_tag: Option> = self.raw_tag.as_ref().map(|b| Cow::Owned(b.to_vec())); Header { tag: self.tag, constructed: self.constructed, class: self.class, length: self.length, raw_tag, } } } impl<'a> FromBer<'a> for Header<'a> { fn from_ber(bytes: &'a [u8]) -> ParseResult { let (i1, el) = parse_identifier(bytes)?; let class = match Class::try_from(el.0) { Ok(c) => c, Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits }; let (i2, len) = parse_ber_length_byte(i1)?; let (i3, len) = match (len.0, len.1) { (0, l1) => { // Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4) (i2, Length::Definite(usize::from(l1))) } (_, 0) => { // Indefinite form: MSB is 1, the rest is 0 (8.1.3.6) // If encoding is primitive, definite form shall be used (8.1.3.2) if el.1 == 0 { return Err(nom::Err::Error(Error::ConstructExpected)); } (i2, Length::Indefinite) } (_, l1) => { // if len is 0xff -> error (8.1.3.5) if l1 == 0b0111_1111 { return Err(::nom::Err::Error(Error::InvalidLength)); } let (i3, llen) = take(l1)(i2)?; match bytes_to_u64(llen) { Ok(l) => { let l = usize::try_from(l).or(Err(::nom::Err::Error(Error::InvalidLength)))?; (i3, Length::Definite(l)) } Err(_) => { return Err(::nom::Err::Error(Error::InvalidLength)); } } } }; let constructed = el.1 != 0; let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into())); Ok((i3, hdr)) } } impl<'a> FromDer<'a> for Header<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult { let (i1, el) = parse_identifier(bytes)?; let class = match Class::try_from(el.0) { Ok(c) => c, Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits }; let (i2, len) = parse_ber_length_byte(i1)?; let (i3, len) = match (len.0, len.1) { (0, l1) => { // Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4) (i2, Length::Definite(usize::from(l1))) } (_, 0) => { // Indefinite form is not allowed in DER (10.1) return Err(::nom::Err::Error(Error::DerConstraintFailed( DerConstraint::IndefiniteLength, ))); } (_, l1) => { // if len is 0xff -> error (8.1.3.5) if l1 == 0b0111_1111 { return Err(::nom::Err::Error(Error::InvalidLength)); } // DER(9.1) if len is 0 (indefinite form), obj must be constructed der_constraint_fail_if!( &i[1..], len.1 == 0 && el.1 != 1, DerConstraint::NotConstructed ); let (i3, llen) = take(l1)(i2)?; match bytes_to_u64(llen) { Ok(l) => { // DER: should have been encoded in short form (< 127) // XXX der_constraint_fail_if!(i, l < 127); let l = usize::try_from(l).or(Err(::nom::Err::Error(Error::InvalidLength)))?; (i3, Length::Definite(l)) } Err(_) => { return Err(::nom::Err::Error(Error::InvalidLength)); } } } }; let constructed = el.1 != 0; let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into())); Ok((i3, hdr)) } } impl DynTagged for (Class, bool, Tag) { fn tag(&self) -> Tag { self.2 } } #[cfg(feature = "std")] impl ToDer for (Class, bool, Tag) { fn to_der_len(&self) -> Result { let (_, _, tag) = self; match tag.0 { 0..=30 => Ok(1), t => { let mut sz = 1; let mut val = t; loop { if val <= 127 { return Ok(sz + 1); } else { val >>= 7; sz += 1; } } } } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let (class, constructed, tag) = self; let b0 = (*class as u8) << 6; let b0 = b0 | if *constructed { 0b10_0000 } else { 0 }; if tag.0 > 30 { let b0 = b0 | 0b1_1111; let mut sz = writer.write(&[b0])?; let mut val = tag.0; loop { if val <= 127 { sz += writer.write(&[val as u8])?; return Ok(sz); } else { let b = (val & 0b0111_1111) as u8 | 0b1000_0000; sz += writer.write(&[b])?; val >>= 7; } } } else { let b0 = b0 | (tag.0 as u8); let sz = writer.write(&[b0])?; Ok(sz) } } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } } impl DynTagged for Header<'_> { fn tag(&self) -> Tag { self.tag } } #[cfg(feature = "std")] impl ToDer for Header<'_> { fn to_der_len(&self) -> Result { let tag_len = (self.class, self.constructed, self.tag).to_der_len()?; let len_len = self.length.to_der_len()?; Ok(tag_len + len_len) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let sz = (self.class, self.constructed, self.tag).write_der_header(writer)?; let sz = sz + self.length.write_der_header(writer)?; Ok(sz) } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // use raw_tag if present let sz = match &self.raw_tag { Some(t) => writer.write(t)?, None => (self.class, self.constructed, self.tag).write_der_header(writer)?, }; let sz = sz + self.length.write_der_header(writer)?; Ok(sz) } } /// Compare two BER headers. `len` fields are compared only if both objects have it set (same for `raw_tag`) impl<'a> PartialEq> for Header<'a> { fn eq(&self, other: &Header) -> bool { self.class == other.class && self.tag == other.tag && self.constructed == other.constructed && { if self.length.is_null() && other.length.is_null() { self.length == other.length } else { true } } && { // it tag is present for both, compare it if self.raw_tag.as_ref().xor(other.raw_tag.as_ref()).is_none() { self.raw_tag == other.raw_tag } else { true } } } } impl Eq for Header<'_> {} #[cfg(test)] mod tests { use crate::*; use hex_literal::hex; /// Generic tests on methods, and coverage tests #[test] fn methods_header() { // Getters let input = &hex! {"02 01 00"}; let (rem, header) = Header::from_ber(input).expect("parsing header failed"); assert_eq!(header.class(), Class::Universal); assert_eq!(header.tag(), Tag::Integer); assert!(header.assert_primitive().is_ok()); assert!(header.assert_constructed().is_err()); assert!(header.is_universal()); assert!(!header.is_application()); assert!(!header.is_private()); assert_eq!(rem, &input[2..]); // test PartialEq let hdr2 = Header::new_simple(Tag::Integer); assert_eq!(header, hdr2); // builder methods let hdr3 = hdr2 .with_class(Class::ContextSpecific) .with_constructed(true) .with_length(Length::Definite(1)); assert!(hdr3.constructed()); assert!(hdr3.is_constructed()); assert!(hdr3.assert_constructed().is_ok()); assert!(hdr3.is_contextspecific()); let xx = hdr3.to_der_vec().expect("serialize failed"); assert_eq!(&xx, &[0xa2, 0x01]); // indefinite length let hdr4 = hdr3.with_length(Length::Indefinite); assert!(hdr4.assert_definite().is_err()); let xx = hdr4.to_der_vec().expect("serialize failed"); assert_eq!(&xx, &[0xa2, 0x80]); // parse_*_content let hdr = Header::new_simple(Tag(2)).with_length(Length::Definite(1)); let (_, r) = hdr.parse_ber_content(&input[2..]).unwrap(); assert_eq!(r, &input[2..]); let (_, r) = hdr.parse_der_content(&input[2..]).unwrap(); assert_eq!(r, &input[2..]); } }