summaryrefslogtreecommitdiff
path: root/src/ber/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ber/parser.rs')
-rw-r--r--src/ber/parser.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/ber/parser.rs b/src/ber/parser.rs
new file mode 100644
index 0000000..fdfdb88
--- /dev/null
+++ b/src/ber/parser.rs
@@ -0,0 +1,169 @@
+use crate::error::*;
+use crate::header::*;
+use crate::{BerParser, DerParser, FromBer, Length, Tag};
+use nom::bytes::streaming::take;
+use nom::{Err, Needed, Offset};
+use rusticata_macros::custom_check;
+
+/// Default maximum recursion limit
+pub const MAX_RECURSION: usize = 50;
+
+/// Default maximum object size (2^32)
+// pub const MAX_OBJECT_SIZE: usize = 4_294_967_295;
+
+pub trait GetObjectContent {
+ /// Return the raw content (bytes) of the next ASN.1 encoded object
+ ///
+ /// Note: if using BER and length is indefinite, terminating End-Of-Content is NOT included
+ fn get_object_content<'a>(
+ i: &'a [u8],
+ hdr: &'_ Header,
+ max_depth: usize,
+ ) -> ParseResult<'a, &'a [u8]>;
+}
+
+impl GetObjectContent for BerParser {
+ fn get_object_content<'a>(
+ i: &'a [u8],
+ hdr: &'_ Header,
+ max_depth: usize,
+ ) -> ParseResult<'a, &'a [u8]> {
+ let start_i = i;
+ let (i, _) = ber_skip_object_content(i, hdr, max_depth)?;
+ let len = start_i.offset(i);
+ let (content, i) = start_i.split_at(len);
+ // if len is indefinite, there are 2 extra bytes for EOC
+ if hdr.length == Length::Indefinite {
+ let len = content.len();
+ assert!(len >= 2);
+ Ok((i, &content[..len - 2]))
+ } else {
+ Ok((i, content))
+ }
+ }
+}
+
+impl GetObjectContent for DerParser {
+ /// Skip object content, accepting only DER
+ ///
+ /// This this function is for DER only, it cannot go into recursion (no indefinite length)
+ fn get_object_content<'a>(
+ i: &'a [u8],
+ hdr: &'_ Header,
+ _max_depth: usize,
+ ) -> ParseResult<'a, &'a [u8]> {
+ match hdr.length {
+ Length::Definite(l) => take(l)(i),
+ Length::Indefinite => Err(Err::Error(Error::DerConstraintFailed(
+ DerConstraint::IndefiniteLength,
+ ))),
+ }
+ }
+}
+
+/// Skip object content, and return true if object was End-Of-Content
+fn ber_skip_object_content<'a>(
+ i: &'a [u8],
+ hdr: &Header,
+ max_depth: usize,
+) -> ParseResult<'a, bool> {
+ if max_depth == 0 {
+ return Err(Err::Error(Error::BerMaxDepth));
+ }
+ match hdr.length {
+ Length::Definite(l) => {
+ if l == 0 && hdr.tag == Tag::EndOfContent {
+ return Ok((i, true));
+ }
+ let (i, _) = take(l)(i)?;
+ Ok((i, false))
+ }
+ Length::Indefinite => {
+ hdr.assert_constructed()?;
+ // read objects until EndOfContent (00 00)
+ // this is recursive
+ let mut i = i;
+ loop {
+ let (i2, header2) = Header::from_ber(i)?;
+ let (i3, eoc) = ber_skip_object_content(i2, &header2, max_depth - 1)?;
+ if eoc {
+ // return false, since top object was not EndOfContent
+ return Ok((i3, false));
+ }
+ i = i3;
+ }
+ }
+ }
+}
+
+/// Try to parse input bytes as u64
+#[inline]
+pub(crate) fn bytes_to_u64(s: &[u8]) -> core::result::Result<u64, Error> {
+ let mut u: u64 = 0;
+ for &c in s {
+ if u & 0xff00_0000_0000_0000 != 0 {
+ return Err(Error::IntegerTooLarge);
+ }
+ u <<= 8;
+ u |= u64::from(c);
+ }
+ Ok(u)
+}
+
+pub(crate) fn parse_identifier(i: &[u8]) -> ParseResult<(u8, u8, u32, &[u8])> {
+ if i.is_empty() {
+ Err(Err::Incomplete(Needed::new(1)))
+ } else {
+ let a = i[0] >> 6;
+ let b = u8::from(i[0] & 0b0010_0000 != 0);
+ let mut c = u32::from(i[0] & 0b0001_1111);
+
+ let mut tag_byte_count = 1;
+
+ if c == 0x1f {
+ c = 0;
+ loop {
+ // Make sure we don't read past the end of our data.
+ custom_check!(i, tag_byte_count >= i.len(), Error::InvalidTag)?;
+
+ // With tag defined as u32 the most we can fit in is four tag bytes.
+ // (X.690 doesn't actually specify maximum tag width.)
+ custom_check!(i, tag_byte_count > 5, Error::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)))
+ }
+}
+
+/// Return the MSB and the rest of the first byte, or an error
+pub(crate) fn parse_ber_length_byte(i: &[u8]) -> ParseResult<(u8, u8)> {
+ if i.is_empty() {
+ Err(Err::Incomplete(Needed::new(1)))
+ } else {
+ let a = i[0] >> 7;
+ let b = i[0] & 0b0111_1111;
+ Ok((&i[1..], (a, b)))
+ }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! der_constraint_fail_if(
+ ($slice:expr, $cond:expr, $constraint:expr) => (
+ {
+ if $cond {
+ return Err(::nom::Err::Error(Error::DerConstraintFailed($constraint)));
+ }
+ }
+ );
+);