use crate::ber::*;
use crate::der::*;
use crate::der_constraint_fail_if;
use crate::error::*;
use alloc::borrow::ToOwned;
use asn1_rs::{Any, FromDer};
use nom::bytes::streaming::take;
use nom::number::streaming::be_u8;
use nom::{Err, Needed};
use rusticata_macros::custom_check;
#[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 Length::Definite(l) = hdr.length() {
custom_check!(i, l > MAX_OBJECT_SIZE, BerError::InvalidLength)?;
}
der_read_element_content_recursive(i, hdr, max_depth)
}
pub fn parse_der_with_tag<T: Into<Tag>>(i: &[u8], tag: T) -> DerResult {
let tag = tag.into();
let (i, hdr) = der_read_element_header(i)?;
hdr.assert_tag(tag)?;
let (i, content) = der_read_element_content_as(
i,
hdr.tag(),
hdr.length(),
hdr.is_constructed(),
MAX_RECURSION,
)?;
Ok((i, DerObject::from_header_and_content(hdr, content)))
}
#[inline]
pub fn parse_der_endofcontent(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::EndOfContent)
}
#[inline]
pub fn parse_der_bool(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Boolean)
}
#[inline]
pub fn parse_der_integer(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Integer)
}
#[inline]
pub fn parse_der_bitstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::BitString)
}
#[inline]
pub fn parse_der_octetstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::OctetString)
}
#[inline]
pub fn parse_der_null(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Null)
}
#[inline]
pub fn parse_der_oid(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Oid)
}
#[inline]
pub fn parse_der_enum(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Enumerated)
}
#[inline]
pub fn parse_der_utf8string(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Utf8String)
}
#[inline]
pub fn parse_der_relative_oid(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::RelativeOid)
}
#[inline]
pub fn parse_der_sequence(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Sequence)
}
#[inline]
pub fn parse_der_set(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Set)
}
#[inline]
pub fn parse_der_numericstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::NumericString)
}
#[inline]
pub fn parse_der_visiblestring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::VisibleString)
}
#[deprecated(since = "8.2.0", note = "Use `parse_der_visiblestring` instead")]
#[inline]
pub fn visiblestring(i: &[u8]) -> DerResult {
parse_der_visiblestring(i)
}
#[inline]
pub fn parse_der_printablestring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::PrintableString)
}
#[inline]
pub fn parse_der_t61string(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::T61String)
}
#[inline]
pub fn parse_der_videotexstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::VideotexString)
}
#[inline]
pub fn parse_der_ia5string(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::Ia5String)
}
#[inline]
pub fn parse_der_utctime(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::UtcTime)
}
#[inline]
pub fn parse_der_generalizedtime(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::GeneralizedTime)
}
#[inline]
pub fn parse_der_objectdescriptor(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::ObjectDescriptor)
}
#[inline]
pub fn parse_der_graphicstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::GraphicString)
}
#[inline]
pub fn parse_der_generalstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::GeneralString)
}
#[inline]
pub fn parse_der_bmpstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::BmpString)
}
#[inline]
pub fn parse_der_universalstring(i: &[u8]) -> DerResult {
parse_der_with_tag(i, Tag::UniversalString)
}
#[inline]
pub fn parse_der_explicit_optional<F>(i: &[u8], tag: Tag, f: F) -> DerResult
where
F: Fn(&[u8]) -> DerResult,
{
parse_ber_explicit_optional(i, tag, f)
}
#[inline]
pub fn parse_der_implicit<'a, T, F>(i: &'a [u8], tag: T, f: F) -> DerResult<'a>
where
F: Fn(&'a [u8], &'_ Header, usize) -> BerResult<'a, DerObjectContent<'a>>,
T: Into<Tag>,
{
parse_ber_implicit(i, tag, f)
}
#[inline]
pub fn parse_der_i32(i: &[u8]) -> BerResult<i32> {
<i32>::from_der(i)
}
#[inline]
pub fn parse_der_i64(i: &[u8]) -> BerResult<i64> {
<i64>::from_der(i)
}
pub fn parse_der_u32(i: &[u8]) -> BerResult<u32> {
<u32>::from_der(i)
}
pub fn parse_der_u64(i: &[u8]) -> BerResult<u64> {
<u64>::from_der(i)
}
#[inline]
pub fn parse_der_slice<T: Into<Tag>>(i: &[u8], tag: T) -> BerResult<&[u8]> {
let tag = tag.into();
parse_der_container(move |content, hdr| {
hdr.assert_tag(tag)?;
Ok((&b""[..], content))
})(i)
}
pub fn parse_der_content<'a>(
tag: Tag,
) -> impl Fn(&'a [u8], &'_ Header, usize) -> BerResult<'a, DerObjectContent<'a>> {
move |i: &[u8], hdr: &Header, max_recursion: usize| {
der_read_element_content_as(i, tag, hdr.length(), hdr.is_constructed(), max_recursion)
}
}
pub fn parse_der_content2<'a>(
tag: Tag,
) -> impl Fn(&'a [u8], Header<'a>, usize) -> BerResult<'a, DerObjectContent<'a>> {
move |i: &[u8], hdr: Header, max_recursion: usize| {
der_read_element_content_as(i, tag, hdr.length(), hdr.is_constructed(), max_recursion)
}
}
pub fn der_read_element_content_as(
i: &[u8],
tag: Tag,
length: Length,
constructed: bool,
max_depth: usize,
) -> BerResult<DerObjectContent> {
let l = length.definite()?;
if i.len() < l {
return Err(Err::Incomplete(Needed::new(l)));
}
match tag {
Tag::Boolean => {
custom_check!(i, l != 1, BerError::InvalidLength)?;
der_constraint_fail_if!(i, i[0] != 0 && i[0] != 0xff, DerConstraint::InvalidBoolean);
}
Tag::BitString => {
der_constraint_fail_if!(i, constructed, DerConstraint::Constructed);
return der_read_content_bitstring(i, l);
}
Tag::Integer => {
match i[..l] {
[] => {
return Err(Err::Error(BerError::DerConstraintFailed(
DerConstraint::IntegerEmpty,
)))
}
[0, 0, ..] => {
return Err(Err::Error(BerError::DerConstraintFailed(
DerConstraint::IntegerLeadingZeroes,
)))
}
[0, byte, ..] if byte < 0x80 => {
return Err(Err::Error(BerError::DerConstraintFailed(
DerConstraint::IntegerLeadingZeroes,
)));
}
_ => (),
}
}
Tag::NumericString
| Tag::VisibleString
| Tag::PrintableString
| Tag::Ia5String
| Tag::Utf8String
| Tag::T61String
| Tag::VideotexString
| Tag::BmpString
| Tag::UniversalString
| Tag::ObjectDescriptor
| Tag::GraphicString
| Tag::GeneralString => {
der_constraint_fail_if!(i, constructed, DerConstraint::Constructed);
}
Tag::UtcTime | Tag::GeneralizedTime => {
if l == 0 || i.get(l - 1).cloned() != Some(b'Z') {
return Err(Err::Error(BerError::DerConstraintFailed(
DerConstraint::MissingTimeZone,
)));
}
}
_ => (),
}
ber_read_element_content_as(i, tag, length, constructed, max_depth)
}
pub fn der_read_element_content<'a>(i: &'a [u8], hdr: Header<'a>) -> DerResult<'a> {
der_read_element_content_recursive(i, hdr, MAX_RECURSION)
}
fn der_read_element_content_recursive<'a>(
i: &'a [u8],
hdr: Header<'a>,
max_depth: usize,
) -> DerResult<'a> {
match hdr.class() {
Class::Universal => (),
_ => {
let (i, data) = ber_get_object_content(i, &hdr, max_depth)?;
let any = Any::new(hdr.clone(), data);
let content = DerObjectContent::Unknown(any);
let obj = DerObject::from_header_and_content(hdr, content);
return Ok((i, obj));
}
}
match der_read_element_content_as(i, hdr.tag(), hdr.length(), hdr.is_constructed(), max_depth) {
Ok((rem, content)) => Ok((rem, DerObject::from_header_and_content(hdr, content))),
Err(Err::Error(BerError::UnknownTag(_))) => {
let (rem, data) = ber_get_object_content(i, &hdr, max_depth)?;
let any = Any::new(hdr.clone(), data);
let content = DerObjectContent::Unknown(any);
let obj = DerObject::from_header_and_content(hdr, content);
Ok((rem, obj))
}
Err(e) => Err(e),
}
}
fn der_read_content_bitstring(i: &[u8], len: usize) -> BerResult<DerObjectContent> {
let (i, ignored_bits) = be_u8(i)?;
if ignored_bits > 7 {
return Err(Err::Error(BerError::invalid_value(
Tag::BitString,
"More than 7 unused bits".to_owned(),
)));
}
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, DerConstraint::UnusedBitsNotZero);
last_byte >>= 1;
}
}
Ok((
i,
DerObjectContent::BitString(ignored_bits, BitStringObject { data }),
))
}
#[inline]
pub fn der_read_element_header(i: &[u8]) -> BerResult<Header> {
Header::from_der(i)
}