blob: c2b9aa52c058b334ef270c4320ff4546d8437633 [file] [log] [blame] [edit]
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<Cow<'a, [u8]>>,
}
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<Cow<'a, [u8]>>) -> 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<Tag> 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<Cow<'static, [u8]>> =
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<Self> {
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<Self> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
Ok(0)
}
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// 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<Header<'a>> 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..]);
}
}