use crate::ber::*;
use crate::error::*;
use crate::oid::*;
use nom::bytes::streaming::take;
use nom::combinator::{map, map_res, verify};
use nom::number::streaming::be_u8;
use nom::*;
use rusticata_macros::{custom_check, flat_take, parse_hex_to_u64};
use std::borrow::Cow;
use std::convert::TryFrom;
pub const MAX_RECURSION: usize = 50;
#[inline]
pub(crate) fn bytes_to_u64(s: &[u8]) -> Result<u64, BerError> {
let mut u: u64 = 0;
for &c in s {
if u & 0xff00_0000_0000_0000 != 0 {
return Err(BerError::IntegerTooLarge);
}
u <<= 8;
u |= u64::from(c);
}
Ok(u)
}
pub(crate) fn parse_identifier(i: &[u8]) -> BerResult<(u8, u8, u32, &[u8])> {
if i.is_empty() {
Err(Err::Incomplete(Needed::Size(1)))
} else {
let a = i[0] >> 6;
let b = if i[0] & 0b0010_0000 != 0 { 1 } else { 0 };
let mut c = u32::from(i[0] & 0b0001_1111);
let mut tag_byte_count = 1;
if c == 0x1f {
c = 0;
loop {
custom_check!(i, tag_byte_count >= i.len(), BerError::InvalidTag)?;
custom_check!(i, tag_byte_count > 5, BerError::InvalidTag)?;
c = (c << 7) | (u32::from(i[tag_byte_count]) & 0x7f);
let done = i[tag_byte_count] & 0x80 == 0;
tag_byte_count += 1;
if done {
break;
}
}
}
let (raw_tag, rem) = i.split_at(tag_byte_count);
Ok((rem, (a, b, c, raw_tag)))
}
}
pub(crate) fn parse_ber_length_byte(i: &[u8]) -> BerResult<(u8, u8)> {
if i.is_empty() {
Err(Err::Incomplete(Needed::Size(1)))
} else {
let a = i[0] >> 7;
let b = i[0] & 0b0111_1111;
Ok((&i[1..], (a, b)))
}
}
pub fn ber_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 {
0 => (i2, u64::from(len.1)),
_ => {
if len.1 == 0b0111_1111 {
return Err(::nom::Err::Error(BerError::InvalidTag));
}
let (i3, llen) = take(len.1)(i2)?;
match bytes_to_u64(llen) {
Ok(l) => (i3, l),
Err(_) => {
return Err(::nom::Err::Error(BerError::InvalidTag));
}
}
}
};
Ok((
i3,
BerObjectHeader {
class,
structured: el.1,
tag: BerTag(el.2),
len,
raw_tag: Some(el.3),
},
))
}
#[inline]
pub(crate) fn ber_read_content_eoc(i: &[u8]) -> BerResult<BerObjectContent> {
Ok((i, BerObjectContent::EndOfContent))
}
#[inline]
pub(crate) fn ber_read_content_bool(i: &[u8]) -> BerResult<BerObjectContent> {
match be_u8(i) {
Ok((rem, 0)) => Ok((rem, BerObjectContent::Boolean(false))),
Ok((rem, _)) => Ok((rem, BerObjectContent::Boolean(true))),
Err(e) => Err(e),
}
}
#[inline]
pub(crate) fn ber_read_content_integer(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::Integer)(i)
}
#[inline]
pub(crate) fn ber_read_content_bitstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
do_parse! {
i,
ignored_bits: be_u8 >>
custom_check!(len == 0, BerError::InvalidLength) >>
s: take!(len - 1) >>
( BerObjectContent::BitString(ignored_bits,BitStringObject{ data:s }) )
}
}
#[inline]
pub(crate) fn ber_read_content_octetstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::OctetString)(i)
}
#[inline]
pub(crate) fn ber_read_content_null(i: &[u8]) -> BerResult<BerObjectContent> {
Ok((i, BerObjectContent::Null))
}
#[inline]
pub(crate) fn ber_read_content_oid(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
custom_check!(i, len == 0, BerError::InvalidLength)?;
let (i1, oid) = verify(take(len), |os: &[u8]| os.last().unwrap() >> 7 == 0u8)(i)?;
let obj = BerObjectContent::OID(Oid::new(Cow::Borrowed(oid)));
Ok((i1, obj))
}
#[inline]
pub(crate) fn ber_read_content_enum(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
parse_hex_to_u64!(i, len).map(|(rem, i)| (rem, BerObjectContent::Enum(i)))
}
#[inline]
pub(crate) fn ber_read_content_utf8string(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map_res(take(len), |bytes| {
std::str::from_utf8(bytes)
.map(|s| BerObjectContent::UTF8String(s))
.map_err(|_| BerError::BerValueError)
})(i)
}
#[inline]
pub(crate) fn ber_read_content_relativeoid(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
custom_check!(i, len == 0, BerError::InvalidLength)?;
let (i1, oid) = verify(take(len), |os: &[u8]| os.last().unwrap() >> 7 == 0u8)(i)?;
let obj = BerObjectContent::RelativeOID(Oid::new_relative(Cow::Borrowed(oid)));
Ok((i1, obj))
}
#[inline]
pub(crate) fn ber_read_content_sequence(
i: &[u8],
len: usize,
max_depth: usize,
) -> BerResult<BerObjectContent> {
custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
if len == 0 {
map!(
i,
many_till!(
call!(parse_ber_recursive, max_depth - 1),
parse_ber_endofcontent
),
|(l, _)| { BerObjectContent::Sequence(l) }
)
} else {
map!(
i,
flat_take!(
len,
many0!(complete!(call!(parse_ber_recursive, max_depth - 1)))
),
|l| { BerObjectContent::Sequence(l) }
)
}
}
#[inline]
pub(crate) fn ber_read_content_set(
i: &[u8],
len: usize,
max_depth: usize,
) -> BerResult<BerObjectContent> {
custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
if len == 0 {
map!(
i,
many_till!(
call!(parse_ber_recursive, max_depth - 1),
parse_ber_endofcontent
),
|(l, _)| { BerObjectContent::Set(l) }
)
} else {
map!(
i,
flat_take!(
len,
many0!(complete!(call!(parse_ber_recursive, max_depth - 1)))
),
|l| { BerObjectContent::Set(l) }
)
}
}
#[inline]
pub(crate) fn ber_read_content_numericstring<'a>(
i: &'a [u8],
len: usize,
) -> BerResult<BerObjectContent<'a>> {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_numeric(b: &u8) -> bool {
match *b {
b'0'..=b'9' | b' ' => true,
_ => false,
}
}
map_res!(i, take!(len), |bytes: &'a [u8]| {
if !bytes.iter().all(is_numeric) {
return Err(BerError::BerValueError);
}
std::str::from_utf8(bytes)
.map_err(|_| BerError::BerValueError)
.map(|s| BerObjectContent::NumericString(s))
})
}
#[inline]
pub(crate) fn ber_read_content_printablestring<'a>(
i: &'a [u8],
len: usize,
) -> BerResult<BerObjectContent<'a>> {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_printable(b: &u8) -> bool {
match *b {
b'a'..=b'z'
| b'A'..=b'Z'
| b'0'..=b'9'
| b' '
| b'\''
| b'('
| b')'
| b'+'
| b','
| b'-'
| b'.'
| b'/'
| b':'
| b'='
| b'?' => true,
_ => false,
}
}
map_res!(i, take!(len), |bytes: &'a [u8]| {
if !bytes.iter().all(is_printable) {
return Err(BerError::BerValueError);
}
std::str::from_utf8(bytes)
.map(|s| BerObjectContent::PrintableString(s))
.map_err(|_| BerError::BerValueError)
})
}
#[inline]
pub(crate) fn ber_read_content_t61string(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::T61String)(i)
}
#[inline]
pub(crate) fn ber_read_content_ia5string<'a>(
i: &'a [u8],
len: usize,
) -> BerResult<BerObjectContent<'a>> {
map_res(take(len), |bytes: &'a [u8]| {
if !bytes.iter().all(u8::is_ascii) {
return Err(BerError::BerValueError);
}
std::str::from_utf8(bytes)
.map(BerObjectContent::IA5String)
.map_err(|_| BerError::BerValueError)
})(i)
}
#[inline]
pub(crate) fn ber_read_content_utctime(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::UTCTime)(i)
}
#[inline]
pub(crate) fn ber_read_content_generalizedtime(
i: &[u8],
len: usize,
) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::GeneralizedTime)(i)
}
#[inline]
pub(crate) fn ber_read_content_generalstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::GeneralString)(i)
}
#[inline]
pub(crate) fn ber_read_content_bmpstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::BmpString)(i)
}
pub fn ber_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::EndOfContent => {
custom_check!(i, len != 0, BerError::InvalidLength)?;
ber_read_content_eoc(i)
}
BerTag::Boolean => {
custom_check!(i, len != 1, BerError::InvalidLength)?;
ber_read_content_bool(i)
}
BerTag::Integer => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
ber_read_content_integer(i, len)
}
BerTag::BitString => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_bitstring(i, len)
}
BerTag::OctetString => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_octetstring(i, len)
}
BerTag::Null => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
custom_check!(i, len != 0, BerError::InvalidLength)?;
ber_read_content_null(i)
}
BerTag::Oid => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
ber_read_content_oid(i, len)
}
BerTag::Enumerated => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
ber_read_content_enum(i, len)
}
BerTag::Utf8String => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_utf8string(i, len)
}
BerTag::RelativeOid => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
ber_read_content_relativeoid(i, len)
}
BerTag::Sequence => {
custom_check!(i, !constructed, BerError::ConstructExpected)?;
ber_read_content_sequence(i, len, max_depth)
}
BerTag::Set => {
custom_check!(i, !constructed, BerError::ConstructExpected)?;
ber_read_content_set(i, len, max_depth)
}
BerTag::NumericString => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_numericstring(i, len)
}
BerTag::PrintableString => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_printablestring(i, len)
}
BerTag::T61String => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_t61string(i, len)
}
BerTag::Ia5String => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_ia5string(i, len)
}
BerTag::UtcTime => ber_read_content_utctime(i, len),
BerTag::GeneralizedTime => ber_read_content_generalizedtime(i, len),
BerTag::GeneralString => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_generalstring(i, len)
}
BerTag::BmpString => {
custom_check!(i, constructed, BerError::Unsupported)?;
ber_read_content_bmpstring(i, len)
}
_ => Err(Err::Error(BerError::UnknownTag)),
}
}
pub fn parse_ber_with_tag(i: &[u8], tag: BerTag) -> BerResult {
do_parse! {
i,
hdr: ber_read_element_header >>
custom_check!(hdr.tag != tag, BerError::InvalidTag) >>
o: call!(ber_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_ber_endofcontent(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::EndOfContent)
}
#[inline]
pub fn parse_ber_bool(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Boolean)
}
#[inline]
pub fn parse_ber_integer(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Integer)
}
#[inline]
pub fn parse_ber_bitstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::BitString)
}
#[inline]
pub fn parse_ber_octetstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::OctetString)
}
#[inline]
pub fn parse_ber_null(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Null)
}
#[inline]
pub fn parse_ber_oid(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Oid)
}
#[inline]
pub fn parse_ber_enum(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Enumerated)
}
#[inline]
pub fn parse_ber_utf8string(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Utf8String)
}
#[inline]
pub fn parse_ber_relative_oid(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::RelativeOid)
}
#[inline]
pub fn parse_ber_sequence(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Sequence)
}
#[inline]
pub fn parse_ber_set(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Set)
}
#[inline]
pub fn parse_ber_numericstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::NumericString)
}
#[inline]
pub fn parse_ber_printablestring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::PrintableString)
}
#[inline]
pub fn parse_ber_t61string(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::T61String)
}
#[inline]
pub fn parse_ber_ia5string(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Ia5String)
}
#[inline]
pub fn parse_ber_utctime(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::UtcTime)
}
#[inline]
pub fn parse_ber_generalizedtime(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::GeneralizedTime)
}
#[inline]
pub fn parse_ber_generalstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::GeneralString)
}
#[inline]
pub fn parse_ber_bmpstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::BmpString)
}
pub fn parse_ber_explicit_failed(i: &[u8], tag: BerTag) -> BerResult {
Ok((
i,
BerObject::from_obj(BerObjectContent::ContextSpecific(tag, None)),
))
}
pub fn parse_ber_explicit<F>(i: &[u8], tag: BerTag, f: F) -> BerResult
where
F: Fn(&[u8]) -> BerResult,
{
alt! {
i,
complete!(do_parse!(
hdr: ber_read_element_header >>
custom_check!(hdr.tag != tag, BerError::InvalidTag) >>
content: f >>
(
BerObject::from_header_and_content(
hdr,
BerObjectContent::ContextSpecific(tag,Some(Box::new(content)))
)
)
)) |
complete!(call!(parse_ber_explicit_failed, tag))
}
}
pub fn parse_ber_implicit<F>(i: &[u8], tag: BerTag, f: F) -> BerResult
where
F: Fn(&[u8], BerTag, usize) -> BerResult<BerObjectContent>,
{
alt! {
i,
complete!(do_parse!(
hdr: ber_read_element_header >>
custom_check!(hdr.tag != tag, BerError::InvalidTag) >>
content: call!(f, tag, hdr.len as usize) >>
(
BerObject::from_header_and_content(
hdr,
BerObjectContent::ContextSpecific(tag,Some(Box::new(BerObject::from_obj(content))))
)
)
)) |
complete!(call!(parse_ber_explicit_failed, tag))
}
}
#[inline]
pub fn parse_ber_u32(i: &[u8]) -> BerResult<u32> {
map_res(parse_ber_integer, |o| o.as_u32())(i)
}
#[inline]
pub fn parse_ber_u64(i: &[u8]) -> BerResult<u64> {
map_res(parse_ber_integer, |o| o.as_u64())(i)
}
pub fn parse_ber_recursive(i: &[u8], max_depth: usize) -> BerResult {
custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
let (rem, hdr) = ber_read_element_header(i)?;
custom_check!(
i,
hdr.len as usize > i.len() || hdr.len > u64::from(::std::u32::MAX),
BerError::InvalidLength
)?;
match hdr.class {
BerClass::Universal | BerClass::Private => (),
_ => {
return map!(rem, take!(hdr.len), |b| {
BerObject::from_header_and_content(hdr, BerObjectContent::Unknown(hdr.tag, b))
})
}
}
match ber_read_element_content_as(
rem,
hdr.tag,
hdr.len as usize,
hdr.is_constructed(),
max_depth,
) {
Ok((rem, content)) => Ok((rem, BerObject::from_header_and_content(hdr, content))),
Err(Err::Error(BerError::UnknownTag)) => map!(rem, take!(hdr.len), |b| {
BerObject::from_header_and_content(hdr, BerObjectContent::Unknown(hdr.tag, b))
}),
Err(e) => Err(e),
}
}
#[inline]
pub fn parse_ber(i: &[u8]) -> BerResult {
parse_ber_recursive(i, MAX_RECURSION)
}
#[test]
fn test_numericstring() {
assert_eq!(
ber_read_content_numericstring(b" 0123 4495768 ", 15),
Ok((
[].as_ref(),
BerObjectContent::NumericString(" 0123 4495768 ")
)),
);
assert_eq!(
ber_read_content_numericstring(b"", 0),
Ok(([].as_ref(), BerObjectContent::NumericString(""))),
);
assert!(ber_read_content_numericstring(b"123a", 4).is_err());
}
#[test]
fn test_printablestring() {
assert_eq!(
ber_read_content_printablestring(b"AZaz09 '()+,-./:=?", 18),
Ok((
[].as_ref(),
BerObjectContent::PrintableString("AZaz09 '()+,-./:=?")
)),
);
assert_eq!(
ber_read_content_printablestring(b"", 0),
Ok(([].as_ref(), BerObjectContent::PrintableString(""))),
);
assert!(ber_read_content_printablestring(b"]", 1).is_err());
}
#[test]
fn test_ia5string() {
assert_eq!(
ber_read_content_ia5string(b"AZaz09 '()+,-./:=?[]{}\0\n", 24),
Ok((
[].as_ref(),
BerObjectContent::IA5String("AZaz09 '()+,-./:=?[]{}\0\n")
)),
);
assert_eq!(
ber_read_content_ia5string(b"", 0),
Ok(([].as_ref(), BerObjectContent::IA5String(""))),
);
assert!(ber_read_content_ia5string(b"\xFF", 1).is_err());
}
#[test]
fn test_utf8string() {
assert_eq!(
ber_read_content_utf8string("AZaz09 '()+,-./:=?[]{}\0\nüÜ".as_ref(), 28),
Ok((
[].as_ref(),
BerObjectContent::UTF8String("AZaz09 '()+,-./:=?[]{}\0\nüÜ")
)),
);
assert_eq!(
ber_read_content_utf8string(b"", 0),
Ok(([].as_ref(), BerObjectContent::UTF8String(""))),
);
assert!(ber_read_content_utf8string(b"\xe2\x28\xa1", 3).is_err());
}