use crate::ber::*;
use crate::der::DerObject;
use crate::error::*;
use nom::bytes::streaming::take;
use nom::number::streaming::be_u8;
use nom::{Err, Needed};
use rusticata_macros::custom_check;
use std::convert::{Into, TryFrom};
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 {
let (i, hdr) = der_read_element_header(i)?;
if let BerSize::Definite(l) = hdr.len {
custom_check!(i, l > MAX_OBJECT_SIZE, BerError::InvalidLength)?;
}
der_read_element_content_recursive(i, hdr, max_depth)
}
#[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<Tag: Into<BerTag>>(i: &[u8], tag: Tag) -> DerResult {
let tag = tag.into();
let (i, hdr) = der_read_element_header(i)?;
if hdr.tag != tag {
return Err(nom::Err::Error(BerError::InvalidTag));
}
let (i, content) =
der_read_element_content_as(i, hdr.tag, hdr.len, hdr.is_constructed(), MAX_RECURSION)?;
Ok((i, BerObject::from_header_and_content(hdr, content)))
}
#[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)
}
#[inline]
pub fn parse_der_bitstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::BitString)
}
#[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 visiblestring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::VisibleString)
}
#[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_videotexstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::VideotexString)
}
#[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_objectdescriptor(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::ObjDescriptor)
}
#[inline]
pub fn parse_der_graphicstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::GraphicString)
}
#[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_universalstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, BerTag::UniversalString)
}
#[inline]
pub fn parse_der_explicit_optional<F>(i: &[u8], tag: BerTag, f: F) -> DerResult
where
F: Fn(&[u8]) -> DerResult,
{
parse_ber_explicit_optional(i, tag, f)
}
#[deprecated(
since = "4.1.0",
note = "Please use `parse_der_explicit_optional` instead"
)]
#[inline]
pub fn parse_der_explicit<F>(i: &[u8], tag: BerTag, f: F) -> BerResult
where
F: Fn(&[u8]) -> BerResult,
{
parse_der_explicit_optional(i, tag, f)
}
#[inline]
pub fn parse_der_implicit<'a, Tag, F>(i: &'a [u8], tag: Tag, f: F) -> DerResult<'a>
where
F: Fn(&'a [u8], &'_ BerObjectHeader, usize) -> BerResult<'a, BerObjectContent<'a>>,
Tag: Into<BerTag>,
{
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 parse_der_content<'a>(
tag: BerTag,
) -> impl Fn(&'a [u8], &'_ BerObjectHeader, usize) -> BerResult<'a, BerObjectContent<'a>> {
move |i: &[u8], hdr: &BerObjectHeader, max_recursion: usize| {
der_read_element_content_as(i, tag, hdr.len, hdr.is_constructed(), max_recursion)
}
}
pub fn der_read_element_content_as(
i: &[u8],
tag: BerTag,
len: BerSize,
constructed: bool,
max_depth: usize,
) -> BerResult<BerObjectContent> {
if let BerSize::Definite(l) = len {
if i.len() < l {
return Err(Err::Incomplete(Needed::new(l)));
}
}
match tag {
BerTag::Boolean => {
let len = len.primitive()?;
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);
let len = len.primitive()?;
return der_read_content_bitstring(i, len);
}
BerTag::NumericString
| BerTag::VisibleString
| BerTag::PrintableString
| BerTag::Ia5String
| BerTag::Utf8String
| BerTag::T61String
| BerTag::VideotexString
| BerTag::BmpString
| BerTag::UniversalString
| BerTag::ObjDescriptor
| BerTag::GraphicString
| BerTag::GeneralString => {
der_constraint_fail_if!(i, constructed);
}
BerTag::UtcTime | BerTag::GeneralizedTime => {
let len = len.primitive()?;
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 => (),
_ => {
let (i, content) = ber_get_object_content(i, &hdr)?;
let content = BerObjectContent::Unknown(hdr.tag, content);
let obj = BerObject::from_header_and_content(hdr, content);
return Ok((i, obj));
}
}
match der_read_element_content_as(i, hdr.tag, hdr.len, hdr.is_constructed(), max_depth) {
Ok((rem, content)) => Ok((rem, DerObject::from_header_and_content(hdr, content))),
Err(Err::Error(BerError::UnknownTag)) => {
let (rem, content) = ber_get_object_content(i, &hdr)?;
let content = BerObjectContent::Unknown(hdr.tag, content);
let obj = BerObject::from_header_and_content(hdr, content);
Ok((rem, obj))
}
Err(e) => Err(e),
}
}
fn der_read_content_bitstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
let (i, ignored_bits) = be_u8(i)?;
if ignored_bits > 7 {
return Err(Err::Error(BerError::DerConstraintFailed));
}
if len == 0 {
return Err(Err::Error(BerError::InvalidLength));
}
let (i, data) = take(len - 1)(i)?;
if len > 1 {
let mut last_byte = data[len - 2];
for _ in 0..ignored_bits as usize {
der_constraint_fail_if!(i, last_byte & 1 != 0);
last_byte >>= 1;
}
}
Ok((
i,
BerObjectContent::BitString(ignored_bits, BitStringObject { data }),
))
}
pub fn der_read_element_header(i: &[u8]) -> BerResult<BerObjectHeader> {
let (i1, el) = parse_identifier(i)?;
let class = match BerClass::try_from(el.0) {
Ok(c) => c,
Err(_) => unreachable!(), };
let (i2, len) = parse_ber_length_byte(i1)?;
let (i3, len) = match (len.0, len.1) {
(0, l1) => {
(i2, BerSize::Definite(usize::from(l1)))
}
(_, 0) => {
(i2, BerSize::Indefinite)
}
(_, l1) => {
if l1 == 0b0111_1111 {
return Err(::nom::Err::Error(BerError::InvalidTag));
}
der_constraint_fail_if!(&i[1..], len.1 == 0 && el.1 != 1);
let (i3, llen) = take(l1)(i2)?;
match bytes_to_u64(llen) {
Ok(l) => {
der_constraint_fail_if!(i, l < 127);
let l =
usize::try_from(l).or(Err(::nom::Err::Error(BerError::InvalidLength)))?;
(i3, BerSize::Definite(l))
}
Err(_) => {
return Err(::nom::Err::Error(BerError::InvalidTag));
}
}
}
};
let hdr = BerObjectHeader::new(class, el.1, BerTag(el.2), len).with_raw_tag(Some(el.3));
Ok((i3, hdr))
}