use crate::ber::*;
use crate::error::*;
use crate::oid::*;
use nom::{be_u8, Context, Err, ErrorKind, IResult, Needed};
pub const MAX_RECURSION: usize = 50;
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 = u << 8;
u |= c as u64;
}
Ok(u)
}
pub(crate) fn parse_identifier(i: &[u8]) -> IResult<&[u8], (u8, u8, 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 c = i[0] & 0b0001_1111;
Ok((&i[1..], (a, b, c)))
}
}
pub(crate) fn parse_ber_length_byte(i: &[u8]) -> IResult<&[u8], (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)))
}
}
fn ber_read_relative_oid(i: &[u8]) -> Result<Vec<u64>, u64> {
let mut oid = Vec::new();
let mut acc: u64;
if i.is_empty() {
return Ok(oid);
};
acc = 0;
for &c in &i[0..] {
acc = (acc << 7) | (c & 0b0111_1111) as u64;
if (c & (1 << 7)) == 0 {
oid.push(acc);
acc = 0;
}
}
match acc {
0 => Ok(oid),
_ => Err(acc),
}
}
fn ber_read_oid(i: &[u8]) -> Result<Vec<u64>, u64> {
let mut oid = Vec::new();
let mut index = 0;
if i.is_empty() {
return Err(0);
};
let acc = i[0] as u64;
if acc < 128 {
oid.push(acc / 40);
oid.push(acc % 40);
index = 1;
}
let rel_oid = ber_read_relative_oid(&i[index..])?;
oid.extend(&rel_oid);
Ok(oid)
}
pub fn ber_read_element_header(i: &[u8]) -> IResult<&[u8], BerObjectHeader> {
do_parse! {
i,
el: parse_identifier >>
len: parse_ber_length_byte >>
llen: cond!(len.0 == 1, take!(len.1)) >>
( {
let len : u64 = match len.0 {
0 => len.1 as u64,
_ => {
if len.1 == 0b0111_1111 {
return Err(::nom::Err::Error(error_position!(&i[1..], ErrorKind::Custom(BER_INVALID_LENGTH))));
}
match bytes_to_u64(llen.unwrap()) {
Ok(l) => l,
Err(_) => { return Err(::nom::Err::Error(error_position!(llen.unwrap(), ErrorKind::Custom(BER_TAG_ERROR)))); },
}
},
};
BerObjectHeader {
class: el.0,
structured: el.1,
tag: BerTag(el.2),
len,
}
} )
}
}
#[inline]
pub(crate) fn ber_read_content_eoc(i: &[u8]) -> IResult<&[u8], BerObjectContent> {
Ok((i, BerObjectContent::EndOfContent))
}
#[inline]
pub(crate) fn ber_read_content_bool(i: &[u8]) -> IResult<&[u8], 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) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |i| { BerObjectContent::Integer(i) })
}
#[inline]
pub(crate) fn ber_read_content_bitstring(i: &[u8], len: usize) -> IResult<&[u8], BerObjectContent> {
do_parse! {
i,
ignored_bits: be_u8 >>
error_if!(len == 0, ErrorKind::Custom(BER_INVALID_LENGTH)) >>
s: take!(len - 1) >>
( BerObjectContent::BitString(ignored_bits,BitStringObject{ data:s }) )
}
}
#[inline]
pub(crate) fn ber_read_content_octetstring(
i: &[u8],
len: usize,
) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::OctetString(s))
}
#[inline]
pub(crate) fn ber_read_content_null(i: &[u8]) -> IResult<&[u8], BerObjectContent> {
Ok((i, BerObjectContent::Null))
}
#[inline]
pub(crate) fn ber_read_content_oid(i: &[u8], len: usize) -> IResult<&[u8], BerObjectContent> {
do_parse! {
i,
error_if!(len == 0, ErrorKind::LengthValue) >>
oid: map_res!(take!(len),ber_read_oid) >>
( BerObjectContent::OID(Oid::from(&oid)) )
}
}
#[inline]
pub(crate) fn ber_read_content_enum(i: &[u8], len: usize) -> IResult<&[u8], 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,
) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::UTF8String(s))
}
#[inline]
pub(crate) fn ber_read_content_relativeoid(
i: &[u8],
len: usize,
) -> IResult<&[u8], BerObjectContent> {
do_parse! {
i,
error_if!(len == 0, ErrorKind::LengthValue) >>
oid: map_res!(take!(len), ber_read_relative_oid) >>
( BerObjectContent::RelativeOID(Oid::from(&oid)) )
}
}
#[inline]
pub(crate) fn ber_read_content_sequence(
i: &[u8],
len: usize,
depth: usize,
) -> IResult<&[u8], BerObjectContent> {
if len == 0 {
map!(
i,
many_till!(
apply!(parse_ber_recursive, depth + 1),
parse_ber_endofcontent
),
|(l, _)| { BerObjectContent::Sequence(l) }
)
} else {
map!(
i,
flat_take!(
len,
many0!(complete!(apply!(parse_ber_recursive, depth + 1)))
),
|l| { BerObjectContent::Sequence(l) }
)
}
}
#[inline]
pub(crate) fn ber_read_content_set(
i: &[u8],
len: usize,
depth: usize,
) -> IResult<&[u8], BerObjectContent> {
if len == 0 {
map!(
i,
many_till!(
apply!(parse_ber_recursive, depth + 1),
parse_ber_endofcontent
),
|(l, _)| { BerObjectContent::Set(l) }
)
} else {
map!(
i,
flat_take!(
len,
many0!(complete!(apply!(parse_ber_recursive, depth + 1)))
),
|l| { BerObjectContent::Set(l) }
)
}
}
#[inline]
pub(crate) fn ber_read_content_numericstring(
i: &[u8],
len: usize,
) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::NumericString(s))
}
#[inline]
pub(crate) fn ber_read_content_printablestring(
i: &[u8],
len: usize,
) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::PrintableString(s))
}
#[inline]
pub(crate) fn ber_read_content_t61string(i: &[u8], len: usize) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::T61String(s))
}
#[inline]
pub(crate) fn ber_read_content_ia5string(i: &[u8], len: usize) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::IA5String(s))
}
#[inline]
pub(crate) fn ber_read_content_utctime(i: &[u8], len: usize) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::UTCTime(s))
}
#[inline]
pub(crate) fn ber_read_content_generalizedtime(
i: &[u8],
len: usize,
) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::GeneralizedTime(s))
}
#[inline]
pub(crate) fn ber_read_content_generalstring(
i: &[u8],
len: usize,
) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::GeneralString(s))
}
#[inline]
pub(crate) fn ber_read_content_bmpstring(i: &[u8], len: usize) -> IResult<&[u8], BerObjectContent> {
map!(i, take!(len), |s| BerObjectContent::BmpString(s))
}
pub fn ber_read_element_content_as(
i: &[u8],
tag: BerTag,
len: usize,
constructed: bool,
depth: usize,
) -> IResult<&[u8], BerObjectContent> {
if i.len() < len {
return Err(Err::Incomplete(Needed::Size(len)));
}
match tag {
BerTag::EndOfContent => {
error_if!(i, len != 0, ErrorKind::Custom(BER_INVALID_LENGTH))?;
ber_read_content_eoc(i)
}
BerTag::Boolean => {
error_if!(i, len != 1, ErrorKind::Custom(BER_INVALID_LENGTH))?;
ber_read_content_bool(i)
}
BerTag::Integer => {
error_if!(i, constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
ber_read_content_integer(i, len)
}
BerTag::BitString => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_bitstring(i, len)
}
BerTag::OctetString => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_octetstring(i, len)
}
BerTag::Null => {
error_if!(i, constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
error_if!(i, len != 0, ErrorKind::Custom(BER_INVALID_LENGTH))?;
ber_read_content_null(i)
}
BerTag::Oid => {
error_if!(i, constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
ber_read_content_oid(i, len)
}
BerTag::Enumerated => {
error_if!(i, constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
ber_read_content_enum(i, len)
}
BerTag::Utf8String => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_utf8string(i, len)
}
BerTag::RelativeOid => {
error_if!(i, constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
ber_read_content_relativeoid(i, len)
}
BerTag::Sequence => {
error_if!(i, !constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
ber_read_content_sequence(i, len, depth)
}
BerTag::Set => {
error_if!(i, !constructed, ErrorKind::Custom(BER_STRUCT_ERROR))?;
ber_read_content_set(i, len, depth)
}
BerTag::NumericString => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_numericstring(i, len)
}
BerTag::PrintableString => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_printablestring(i, len)
}
BerTag::T61String => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_t61string(i, len)
}
BerTag::Ia5String => {
error_if!(i, constructed, ErrorKind::Custom(BER_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 => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_generalstring(i, len)
}
BerTag::BmpString => {
error_if!(i, constructed, ErrorKind::Custom(BER_UNSUPPORTED))?;
ber_read_content_bmpstring(i, len)
}
_ => Err(Err::Error(error_position!(
i,
ErrorKind::Custom(BER_TAG_UNKNOWN)
))),
}
}
pub fn parse_ber_with_tag(i: &[u8], tag: BerTag) -> IResult<&[u8], BerObject> {
do_parse! {
i,
hdr: ber_read_element_header >>
error_if!(hdr.tag != tag, ErrorKind::Custom(BER_TAG_ERROR)) >>
o: apply!(ber_read_element_content_as, hdr.tag, hdr.len as usize, hdr.is_constructed(), 0) >>
( BerObject::from_header_and_content(hdr, o) )
}
}
#[inline]
pub fn parse_ber_endofcontent(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::EndOfContent)
}
#[inline]
pub fn parse_ber_bool(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Boolean)
}
#[inline]
pub fn parse_ber_integer(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Integer)
}
#[inline]
pub fn parse_ber_bitstring(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::BitString)
}
#[inline]
pub fn parse_ber_octetstring(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::OctetString)
}
#[inline]
pub fn parse_ber_null(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Null)
}
#[inline]
pub fn parse_ber_oid(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Oid)
}
#[inline]
pub fn parse_ber_enum(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Enumerated)
}
#[inline]
pub fn parse_ber_utf8string(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Utf8String)
}
#[inline]
pub fn parse_ber_relative_oid(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::RelativeOid)
}
#[inline]
pub fn parse_ber_sequence(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Sequence)
}
#[inline]
pub fn parse_ber_set(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Set)
}
#[inline]
pub fn parse_ber_numericstring(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::NumericString)
}
#[inline]
pub fn parse_ber_printablestring(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::PrintableString)
}
#[inline]
pub fn parse_ber_t61string(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::T61String)
}
#[inline]
pub fn parse_ber_ia5string(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::Ia5String)
}
#[inline]
pub fn parse_ber_utctime(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::UtcTime)
}
#[inline]
pub fn parse_ber_generalizedtime(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::GeneralizedTime)
}
#[inline]
pub fn parse_ber_generalstring(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::GeneralString)
}
#[inline]
pub fn parse_ber_bmpstring(i: &[u8]) -> IResult<&[u8], BerObject> {
parse_ber_with_tag(i, BerTag::BmpString)
}
pub fn parse_ber_explicit_failed(i: &[u8], tag: BerTag) -> IResult<&[u8], BerObject, u32> {
value!(
i,
BerObject::from_obj(BerObjectContent::ContextSpecific(tag, None))
)
}
pub fn parse_ber_explicit<F>(i: &[u8], tag: BerTag, f: F) -> IResult<&[u8], BerObject, u32>
where
F: Fn(&[u8]) -> IResult<&[u8], BerObject, u32>,
{
alt_complete! {
i,
do_parse!(
hdr: ber_read_element_header >>
error_if!(hdr.tag != tag, ErrorKind::Custom(BER_TAG_ERROR)) >>
content: f >>
(
BerObject::from_header_and_content(
hdr,
BerObjectContent::ContextSpecific(tag,Some(Box::new(content)))
)
)
) |
apply!(parse_ber_explicit_failed, tag)
}
}
pub fn parse_ber_implicit<F>(i: &[u8], tag: BerTag, f: F) -> IResult<&[u8], BerObject, u32>
where
F: Fn(&[u8], BerTag, usize) -> IResult<&[u8], BerObjectContent, u32>,
{
alt_complete! {
i,
do_parse!(
hdr: ber_read_element_header >>
error_if!(hdr.tag != tag, ErrorKind::Custom(BER_TAG_ERROR)) >>
content: map!(
apply!(f, tag, hdr.len as usize),
|b| { BerObject::from_obj(b) }
) >>
(
BerObject::from_header_and_content(
hdr,
BerObjectContent::ContextSpecific(tag,Some(Box::new(content)))
)
)
) |
apply!(parse_ber_explicit_failed, tag)
}
}
fn parse_ber_recursive(i: &[u8], depth: usize) -> IResult<&[u8], BerObject, u32> {
error_if!(i, depth > MAX_RECURSION, ErrorKind::Custom(BER_MAX_DEPTH))?;
let (rem, hdr) = ber_read_element_header(i)?;
error_if!(
i,
hdr.len as usize > i.len() || hdr.len > ::std::u32::MAX as u64,
ErrorKind::Custom(BER_INVALID_LENGTH)
)?;
match hdr.class {
0b00 |
0b11 => (),
0b01 |
0b10 => return map!(
rem,
take!(hdr.len),
|b| { BerObject::from_header_and_content(hdr,BerObjectContent::Unknown(hdr.tag, b)) }
),
_ => { return Err(Err::Error(error_position!(i, ErrorKind::Custom(BER_CLASS_ERROR)))); },
}
match ber_read_element_content_as(rem, hdr.tag, hdr.len as usize, hdr.is_constructed(), depth) {
Ok((rem, content)) => Ok((rem, BerObject::from_header_and_content(hdr, content))),
Err(Err::Error(Context::Code(_, ErrorKind::Custom(BER_TAG_UNKNOWN)))) => {
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]) -> IResult<&[u8], BerObject, u32> {
parse_ber_recursive(i, 0)
}