use crate::ber::*;
use crate::der::DerObject;
use crate::error::*;
use nom::number::streaming::be_u8;
use nom::*;
use rusticata_macros::custom_check;
use std::convert::TryFrom;
pub use crate::ber::MAX_RECURSION;
#[inline]
pub fn parse_der(i: &[u8]) -> DerResult {
parse_der_recursive(i, MAX_RECURSION)
}
pub fn parse_der_recursive(i: &[u8], max_depth: usize) -> DerResult {
do_parse! {
i,
hdr: der_read_element_header >>
custom_check!(hdr.len > u64::from(::std::u32::MAX), BerError::InvalidLength) >>
content: call!(der_read_element_content_recursive, hdr, max_depth) >>
( content )
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! der_constraint_fail_if(
($slice:expr, $cond:expr) => (
{
if $cond {
return Err(::nom::Err::Error(BerError::DerConstraintFailed));
}
}
);
);
pub fn parse_der_with_tag(i: &[u8], tag: BerTag) -> DerResult {
do_parse! {
i,
hdr: der_read_element_header >>
custom_check!(hdr.tag != tag, BerError::InvalidTag) >>
o: call!(der_read_element_content_as, hdr.tag, hdr.len as usize, hdr.is_constructed(), MAX_RECURSION) >>
( BerObject::from_header_and_content(hdr, o) )
}
}
#[inline]
pub fn parse_der_endofcontent(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::EndOfContent)
}
#[inline]
pub fn parse_der_bool(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Boolean)
}
#[inline]
pub fn parse_der_integer(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Integer)
}
pub fn parse_der_bitstring(i: &[u8]) -> DerResult {
do_parse! {
i,
hdr: der_read_element_header >>
custom_check!(hdr.tag != BerTag::BitString, BerError::InvalidTag) >>
custom_check!(hdr.is_constructed(), BerError::DerConstraintFailed) >>
b: call!(der_read_content_bitstring, hdr.len as usize) >>
( DerObject::from_header_and_content(hdr, b) )
}
}
#[inline]
pub fn parse_der_octetstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::OctetString)
}
#[inline]
pub fn parse_der_null(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Null)
}
#[inline]
pub fn parse_der_oid(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Oid)
}
#[inline]
pub fn parse_der_enum(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Enumerated)
}
#[inline]
pub fn parse_der_utf8string(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Utf8String)
}
#[inline]
pub fn parse_der_relative_oid(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::RelativeOid)
}
#[inline]
pub fn parse_der_sequence(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Sequence)
}
#[inline]
pub fn parse_der_set(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Set)
}
#[inline]
pub fn parse_der_numericstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::NumericString)
}
#[inline]
pub fn parse_der_printablestring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::PrintableString)
}
#[inline]
pub fn parse_der_t61string(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::T61String)
}
#[inline]
pub fn parse_der_ia5string(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::Ia5String)
}
#[inline]
pub fn parse_der_utctime(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::UtcTime)
}
#[inline]
pub fn parse_der_generalizedtime(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::GeneralizedTime)
}
#[inline]
pub fn parse_der_generalstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::GeneralString)
}
#[inline]
pub fn parse_der_bmpstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::BmpString)
}
#[inline]
pub fn parse_der_explicit<F>(i: &[u8], tag: BerTag, f: F) -> DerResult
where
F: Fn(&[u8]) -> DerResult,
{
parse_ber_explicit(i, tag, f)
}
#[inline]
pub fn parse_der_implicit<F>(i: &[u8], tag: BerTag, f: F) -> DerResult
where
F: Fn(&[u8], BerTag, usize) -> BerResult<BerObjectContent>,
{
parse_ber_implicit(i, tag, f)
}
pub fn parse_der_u32(i: &[u8]) -> BerResult<u32> {
match parse_ber_integer(i) {
Ok((rem, ref obj)) => match obj.content {
BerObjectContent::Integer(i) => match i.len() {
1 => Ok((rem, u32::from(i[0]))),
2 => Ok((rem, u32::from(i[0]) << 8 | u32::from(i[1]))),
3 => Ok((
rem,
u32::from(i[0]) << 16 | u32::from(i[1]) << 8 | u32::from(i[2]),
)),
4 => Ok((
rem,
u32::from(i[0]) << 24
| u32::from(i[1]) << 16
| u32::from(i[2]) << 8
| u32::from(i[3]),
)),
_ => Err(Err::Error(BerError::IntegerTooLarge)),
},
_ => Err(Err::Error(BerError::InvalidTag)),
},
Err(e) => Err(e),
}
}
pub fn parse_der_u64(i: &[u8]) -> BerResult<u64> {
match parse_ber_integer(i) {
Ok((rem, ref obj)) => match obj.content {
BerObjectContent::Integer(i) => match bytes_to_u64(i) {
Ok(l) => Ok((rem, l)),
Err(_) => Err(Err::Error(BerError::IntegerTooLarge)),
},
_ => Err(Err::Error(BerError::InvalidTag)),
},
Err(e) => Err(e),
}
}
pub fn der_read_element_content_as(
i: &[u8],
tag: BerTag,
len: usize,
constructed: bool,
max_depth: usize,
) -> BerResult<BerObjectContent> {
if i.len() < len {
return Err(Err::Incomplete(Needed::Size(len)));
}
match tag {
BerTag::Boolean => {
custom_check!(i, len != 1, BerError::InvalidLength)?;
der_constraint_fail_if!(i, i[0] != 0 && i[0] != 0xff);
}
BerTag::BitString => {
der_constraint_fail_if!(i, constructed);
return der_read_content_bitstring(i, len);
}
BerTag::NumericString
| BerTag::PrintableString
| BerTag::Ia5String
| BerTag::Utf8String
| BerTag::T61String
| BerTag::BmpString
| BerTag::GeneralString => {
der_constraint_fail_if!(i, constructed);
}
BerTag::UtcTime | BerTag::GeneralizedTime => {
if len == 0 || i.get(len - 1).cloned() != Some(b'Z') {
return Err(Err::Error(BerError::DerConstraintFailed));
}
}
_ => (),
}
ber_read_element_content_as(i, tag, len, constructed, max_depth)
}
pub fn der_read_element_content<'a>(i: &'a [u8], hdr: BerObjectHeader<'a>) -> DerResult<'a> {
der_read_element_content_recursive(i, hdr, MAX_RECURSION)
}
fn der_read_element_content_recursive<'a>(
i: &'a [u8],
hdr: BerObjectHeader<'a>,
max_depth: usize,
) -> DerResult<'a> {
match hdr.class {
BerClass::Universal | BerClass::Private => (),
_ => {
return map!(i, take!(hdr.len), |b| {
DerObject::from_header_and_content(hdr, BerObjectContent::Unknown(hdr.tag, b))
})
}
}
match der_read_element_content_as(
i,
hdr.tag,
hdr.len as usize,
hdr.is_constructed(),
max_depth,
) {
Ok((rem, content)) => Ok((rem, DerObject::from_header_and_content(hdr, content))),
Err(Err::Error(BerError::UnknownTag)) => map!(i, take!(hdr.len), |b| {
DerObject::from_header_and_content(hdr, BerObjectContent::Unknown(hdr.tag, b))
}),
Err(e) => Err(e),
}
}
#[inline]
fn der_read_content_bitstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
do_parse! {
i,
ignored_bits: be_u8 >>
custom_check!(ignored_bits > 7, BerError::DerConstraintFailed) >>
custom_check!(len == 0, BerError::InvalidLength) >>
s: take!(len - 1) >>
call!(|input| {
if len > 1 {
let mut last_byte = s[len-2];
for _ in 0..ignored_bits as usize {
der_constraint_fail_if!(i, last_byte & 1 != 0);
last_byte >>= 1;
}
}
Ok((input,()))
}) >>
( BerObjectContent::BitString(ignored_bits,BitStringObject{ data:s }) )
}
}
pub fn der_read_element_header(i: &[u8]) -> BerResult<BerObjectHeader> {
do_parse! {
i,
el: parse_identifier >>
len: parse_ber_length_byte >>
llen: cond!(len.0 == 1, take!(len.1)) >>
( {
let class = match BerClass::try_from(el.0) {
Ok(c) => c,
Err(_) => unreachable!(),
};
let len : u64 = match len.0 {
0 => u64::from(len.1),
_ => {
custom_check!(&i[1..], len.1 == 0b0111_1111, BerError::InvalidLength)?;
der_constraint_fail_if!(&i[1..], len.1 == 0 && el.1 != 1);
let llen = llen.unwrap();
match bytes_to_u64(llen) {
Ok(l) => {
der_constraint_fail_if!(i, l < 127);
l
},
Err(_) => { return Err(::nom::Err::Error(BerError::InvalidTag)); },
}
},
};
BerObjectHeader {
class,
structured: el.1,
tag: BerTag(el.2),
len,
raw_tag: Some(el.3),
}
} )
}
}