blob: 81ccef81abef5eec0ac80cefbb1f70b0c7c69dde [file] [log] [blame]
use crate::ber::*;
use crate::error::*;
use crate::oid::*;
use nom::bytes::streaming::take;
use nom::combinator::{complete, map, verify};
use nom::multi::{many0, many_till};
use nom::number::streaming::be_u8;
use nom::{Err, Needed, Offset};
use rusticata_macros::{combinator::parse_hex_to_u64, 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;
/// Skip object content, and return true if object was End-Of-Content
pub(crate) fn ber_skip_object_content<'a>(
i: &'a [u8],
hdr: &BerObjectHeader,
max_depth: usize,
) -> BerResult<'a, bool> {
if max_depth == 0 {
return Err(Err::Error(BerError::BerMaxDepth));
}
match hdr.len {
BerSize::Definite(l) => {
if l == 0 && hdr.tag == BerTag::EndOfContent {
return Ok((i, true));
}
let (i, _) = take(l)(i)?;
Ok((i, false))
}
BerSize::Indefinite => {
if hdr.is_primitive() {
return Err(Err::Error(BerError::ConstructExpected));
}
// read objects until EndOfContent (00 00)
// this is recursive
let mut i = i;
loop {
let (i2, header2) = ber_read_element_header(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;
}
}
}
}
/// Read object raw content (bytes)
pub(crate) fn ber_get_object_content<'a>(
i: &'a [u8],
hdr: &BerObjectHeader,
max_depth: usize,
) -> BerResult<'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.len == BerSize::Indefinite {
let len = content.len();
assert!(len >= 2);
Ok((i, &content[..len - 2]))
} else {
Ok((i, content))
}
}
/// Try to parse input bytes as u64
#[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)
}
/// Try to parse an input bit string as u64.
///
/// Note: this is for the primitive BER/DER encoding only, the
/// constructed BER encoding for BIT STRING does not seem to be
/// supported at all by the library currently.
#[inline]
pub(crate) fn bitstring_to_u64(
padding_bits: usize,
data: &BitStringObject,
) -> Result<u64, BerError> {
let raw_bytes = data.data;
let bit_size = (raw_bytes.len() * 8)
.checked_sub(padding_bits)
.ok_or(BerError::InvalidLength)?;
if bit_size > 64 {
return Err(BerError::IntegerTooLarge);
}
let padding_bits = padding_bits % 8;
let num_bytes = if bit_size % 8 > 0 {
(bit_size / 8) + 1
} else {
bit_size / 8
};
let mut resulting_integer: u64 = 0;
for &c in &raw_bytes[..num_bytes] {
resulting_integer <<= 8;
resulting_integer |= c as u64;
}
Ok(resulting_integer >> padding_bits)
}
pub(crate) fn parse_identifier(i: &[u8]) -> BerResult<(u8, u8, u32, &[u8])> {
if i.is_empty() {
Err(Err::Incomplete(Needed::new(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 {
// Make sure we don't read past the end of our data.
custom_check!(i, tag_byte_count >= i.len(), BerError::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, 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)))
}
}
/// Return the MSB and the rest of the first byte, or an error
pub(crate) fn parse_ber_length_byte(i: &[u8]) -> BerResult<(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)))
}
}
/// Read an object header
///
/// ### Example
///
/// ```
/// # use der_parser::ber::{ber_read_element_header, BerClass, BerSize, BerTag};
/// #
/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (i, hdr) = ber_read_element_header(bytes).expect("could not read header");
///
/// assert_eq!(hdr.class, BerClass::Universal);
/// assert_eq!(hdr.tag, BerTag::Integer);
/// assert_eq!(hdr.len, BerSize::Definite(3));
/// ```
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!(), // Cannot fail, we have read exactly 2 bits
};
let (i2, len) = parse_ber_length_byte(i1)?;
let (i3, len) = match (len.0, len.1) {
(0, l1) => {
// Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4)
(i2, BerSize::Definite(usize::from(l1)))
}
(_, 0) => {
// Indefinite form: MSB is 1, the rest is 0 (8.1.3.6)
// If encoding is primitive, definite form shall be used (8.1.3.2)
if el.1 == 0 {
return Err(Err::Error(BerError::ConstructExpected));
}
(i2, BerSize::Indefinite)
}
(_, l1) => {
// if len is 0xff -> error (8.1.3.5)
if l1 == 0b0111_1111 {
return Err(::nom::Err::Error(BerError::InvalidTag));
}
let (i3, llen) = take(l1)(i2)?;
match bytes_to_u64(llen) {
Ok(l) => {
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))
}
#[allow(clippy::unnecessary_wraps)]
#[inline]
fn ber_read_content_eoc(i: &[u8]) -> BerResult<BerObjectContent> {
Ok((i, BerObjectContent::EndOfContent))
}
#[inline]
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]
fn ber_read_content_integer(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::Integer)(i)
}
#[inline]
fn ber_read_content_bitstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
custom_check!(i, len == 0, BerError::InvalidLength)?;
let (i, ignored_bits) = be_u8(i)?;
let (i, data) = take(len - 1)(i)?;
Ok((
i,
BerObjectContent::BitString(ignored_bits, BitStringObject { data }),
))
}
#[inline]
fn ber_read_content_octetstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::OctetString)(i)
}
#[allow(clippy::unnecessary_wraps)]
#[inline]
fn ber_read_content_null(i: &[u8]) -> BerResult<BerObjectContent> {
Ok((i, BerObjectContent::Null))
}
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]
fn ber_read_content_enum(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
let (rem, num) = parse_hex_to_u64(i, len).map_err(|_| BerError::BerValueError)?;
Ok((rem, BerObjectContent::Enum(num)))
}
fn ber_read_content_utf8string(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
let (i, bytes) = take(len)(i)?;
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::UTF8String(s))?;
Ok((i, s))
}
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))
}
fn ber_read_content_sequence(
i: &[u8],
len: BerSize,
max_depth: usize,
) -> BerResult<BerObjectContent> {
custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
match len {
BerSize::Definite(len) => {
let (i, data) = take(len)(i)?;
let (_, l) = many0(complete(r_parse_ber(max_depth - 1)))(data)?;
// trailing bytes are ignored
Ok((i, BerObjectContent::Sequence(l)))
}
BerSize::Indefinite => {
// indefinite form
// read until end-of-content
let (rem, (l, _)) = many_till(r_parse_ber(max_depth - 1), parse_ber_endofcontent)(i)?;
Ok((rem, BerObjectContent::Sequence(l)))
}
}
}
fn ber_read_content_set(i: &[u8], len: BerSize, max_depth: usize) -> BerResult<BerObjectContent> {
custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
match len {
BerSize::Definite(len) => {
let (i, data) = take(len)(i)?;
let (_, l) = many0(complete(r_parse_ber(max_depth - 1)))(data)?;
// trailing bytes are ignored
Ok((i, BerObjectContent::Set(l)))
}
BerSize::Indefinite => {
// indefinite form
// read until end-of-content
let (rem, (l, _)) = many_till(r_parse_ber(max_depth - 1), parse_ber_endofcontent)(i)?;
Ok((rem, BerObjectContent::Set(l)))
}
}
}
fn ber_read_content_numericstring<'a>(i: &'a [u8], len: usize) -> BerResult<BerObjectContent<'a>> {
// Argument must be a reference, because of the .iter().all(F) call below
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_numeric(b: &u8) -> bool {
matches!(*b, b'0'..=b'9' | b' ')
}
let (i, bytes) = take(len)(i)?;
if !bytes.iter().all(is_numeric) {
return Err(Err::Error(BerError::StringInvalidCharset));
}
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::NumericString(s))?;
Ok((i, s))
}
fn ber_read_content_visiblestring<'a>(i: &'a [u8], len: usize) -> BerResult<BerObjectContent<'a>> {
// Argument must be a reference, because of the .iter().all(F) call below
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
let (i, bytes) = take(len)(i)?;
if !bytes.iter().all(is_visible) {
return Err(Err::Error(BerError::StringInvalidCharset));
}
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::VisibleString(s))?;
Ok((i, s))
}
fn ber_read_content_printablestring<'a>(
i: &'a [u8],
len: usize,
) -> BerResult<BerObjectContent<'a>> {
// Argument must be a reference, because of the .iter().all(F) call below
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_printable(b: &u8) -> bool {
matches!(*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'?')
}
let (i, bytes) = take(len)(i)?;
if !bytes.iter().all(is_printable) {
return Err(Err::Error(BerError::StringInvalidCharset));
}
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::PrintableString(s))?;
Ok((i, s))
}
#[inline]
fn ber_read_content_t61string(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::T61String)(i)
}
#[inline]
fn ber_read_content_videotexstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::VideotexString)(i)
}
fn ber_read_content_ia5string<'a>(i: &'a [u8], len: usize) -> BerResult<BerObjectContent<'a>> {
let (i, bytes) = take(len)(i)?;
if !bytes.iter().all(u8::is_ascii) {
return Err(Err::Error(BerError::StringInvalidCharset));
}
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::IA5String(s))?;
Ok((i, s))
}
fn ber_read_content_utctime<'a>(i: &'a [u8], len: usize) -> BerResult<BerObjectContent<'a>> {
// Argument must be a reference, because of the .iter().all(F) call below
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
let (i, bytes) = take(len)(i)?;
if !bytes.iter().all(is_visible) {
return Err(Err::Error(BerError::StringInvalidCharset));
}
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::UTCTime(s))?;
Ok((i, s))
}
fn ber_read_content_generalizedtime<'a>(
i: &'a [u8],
len: usize,
) -> BerResult<BerObjectContent<'a>> {
// Argument must be a reference, because of the .iter().all(F) call below
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
let (i, bytes) = take(len)(i)?;
if !bytes.iter().all(is_visible) {
return Err(Err::Error(BerError::StringInvalidCharset));
}
let s = core::str::from_utf8(bytes)
.map_err(|_| Err::Error(BerError::StringInvalidCharset))
.map(|s| BerObjectContent::GeneralizedTime(s))?;
Ok((i, s))
}
#[inline]
fn ber_read_content_objectdescriptor(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::ObjectDescriptor)(i)
}
#[inline]
fn ber_read_content_graphicstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::GraphicString)(i)
}
#[inline]
fn ber_read_content_generalstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::GeneralString)(i)
}
#[inline]
fn ber_read_content_bmpstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::BmpString)(i)
}
#[inline]
fn ber_read_content_universalstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::UniversalString)(i)
}
/// Parse the next bytes as the *content* of a BER object.
///
/// Content type is *not* checked to match tag, caller is responsible of providing the correct tag
///
/// This function is mostly used when parsing implicit tagged objects, when reading primitive
/// types.
///
/// `max_depth` is the maximum allowed recursion for objects.
///
/// ### Example
///
/// ```
/// # use der_parser::ber::{ber_read_element_content_as, ber_read_element_header, BerTag};
/// #
/// # let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (i, hdr) = ber_read_element_header(bytes).expect("could not read header");
/// let (_, content) = ber_read_element_content_as(
/// i, hdr.tag, hdr.len, hdr.is_constructed(), 5
/// ).expect("parsing failed");
/// #
/// # assert_eq!(hdr.tag, BerTag::Integer);
/// # assert_eq!(content.as_u32(), Ok(0x10001));
/// ```
pub fn ber_read_element_content_as(
i: &[u8],
tag: BerTag,
len: BerSize,
constructed: bool,
max_depth: usize,
) -> BerResult<BerObjectContent> {
if let BerSize::Definite(l) = len {
custom_check!(i, l > MAX_OBJECT_SIZE, BerError::InvalidLength)?;
if i.len() < l {
return Err(Err::Incomplete(Needed::new(l)));
}
}
match tag {
// 0x00 end-of-content
BerTag::EndOfContent => {
custom_check!(i, len != BerSize::Definite(0), BerError::InvalidLength)?;
ber_read_content_eoc(i)
}
// 0x01 bool
BerTag::Boolean => {
let len = len.primitive()?;
custom_check!(i, len != 1, BerError::InvalidLength)?;
ber_read_content_bool(i)
}
// 0x02
BerTag::Integer => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
let len = len.primitive()?;
ber_read_content_integer(i, len)
}
// 0x03: bitstring
BerTag::BitString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.6.3)
let len = len.primitive()?;
ber_read_content_bitstring(i, len)
}
// 0x04: octetstring
BerTag::OctetString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.7.1)
let len = len.primitive()?;
ber_read_content_octetstring(i, len)
}
// 0x05: null
BerTag::Null => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
let len = len.primitive()?;
custom_check!(i, len != 0, BerError::InvalidLength)?;
ber_read_content_null(i)
}
// 0x06: object identifier
BerTag::Oid => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?; // forbidden in 8.19.1
let len = len.primitive()?;
ber_read_content_oid(i, len)
}
// 0x07: object descriptor - Alias for GraphicString with a different
// implicit tag, see below
BerTag::ObjDescriptor => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_objectdescriptor(i, len)
}
// 0x0a: enumerated
BerTag::Enumerated => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?; // forbidden in 8.4
let len = len.primitive()?;
ber_read_content_enum(i, len)
}
// 0x0c: UTF8String - Unicode encoded with the UTF-8 charset (ISO/IEC
// 10646-1, Annex D)
BerTag::Utf8String => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_utf8string(i, len)
}
// 0x0d: relative object identified
BerTag::RelativeOid => {
custom_check!(i, constructed, BerError::ConstructUnexpected)?;
let len = len.primitive()?;
ber_read_content_relativeoid(i, len)
}
// 0x10: sequence
BerTag::Sequence => {
custom_check!(i, !constructed, BerError::ConstructExpected)?;
ber_read_content_sequence(i, len, max_depth)
}
// 0x11: set
BerTag::Set => {
custom_check!(i, !constructed, BerError::ConstructExpected)?;
ber_read_content_set(i, len, max_depth)
}
// 0x12: numericstring - ASCII string with digits an spaces only
BerTag::NumericString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_numericstring(i, len)
}
// 0x13: printablestring - ASCII string with certain printable
// characters only (specified in Table 10 of X.680)
BerTag::PrintableString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_printablestring(i, len)
}
// 0x14: t61string - ISO 2022 string with a Teletex (T.61) charset,
// ASCII is possible but only when explicit escaped, as by default
// the G0 character range (0x20-0x7f) will match the graphic character
// set. https://en.wikipedia.org/wiki/ITU_T.61
BerTag::T61String => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_t61string(i, len)
}
// 0x15: videotexstring - ISO 2022 string with a Videotex (T.100/T.101)
// charset, excluding ASCII. https://en.wikipedia.org/wiki/Videotex_character_set
BerTag::VideotexString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_videotexstring(i, len)
}
// 0x16: ia5string - ASCII string
BerTag::Ia5String => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_ia5string(i, len)
}
// 0x17: utctime - Alias for a VisibleString with a different implicit
// tag, see below
BerTag::UtcTime => {
let len = len.primitive()?;
ber_read_content_utctime(i, len)
}
// 0x18: generalizedtime - Alias for a VisibleString with a different
// implicit tag, see below
BerTag::GeneralizedTime => {
let len = len.primitive()?;
ber_read_content_generalizedtime(i, len)
}
// 0x19: graphicstring - Generic ISO 2022 container with explicit
// escape sequences, without control characters
BerTag::GraphicString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_graphicstring(i, len)
}
// 0x1a: visiblestring - ASCII string with no control characters except
// SPACE
BerTag::VisibleString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_visiblestring(i, len)
}
// 0x1b: generalstring - Generic ISO 2022 container with explicit
// escape sequences
BerTag::GeneralString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_generalstring(i, len)
}
// 0x1e: bmpstring - Unicode encoded with the UCS-2 big-endian charset
// (ISO/IEC 10646-1, section 13.1), restricted to the BMP (Basic
// Multilingual Plane) except certain control cahracters
BerTag::BmpString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_bmpstring(i, len)
}
// 0x1c: universalstring - Unicode encoded with the UCS-4 big-endian
// charset (ISO/IEC 10646-1, section 13.2)
BerTag::UniversalString => {
custom_check!(i, constructed, BerError::Unsupported)?; // XXX valid in BER (8.21)
let len = len.primitive()?;
ber_read_content_universalstring(i, len)
}
// all unknown values
_ => Err(Err::Error(BerError::UnknownTag)),
}
}
/// Parse the next bytes as the content of a BER object (combinator, header reference)
///
/// Content type is *not* checked to match tag, caller is responsible of providing the correct tag
///
/// Caller is also responsible to check if parsing function consumed the expected number of
/// bytes (`header.len`).
///
/// The arguments of the parse function are: `(input, ber_object_header, max_recursion)`.
///
/// This function differs from [`parse_ber_content2`](fn.parse_ber_content2.html) because it passes
/// the BER object header by reference (required for ex. by `parse_ber_implicit`).
///
/// Example: manually parsing header and content
///
/// ```
/// # use der_parser::ber::*;
/// #
/// # let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (i, header) = ber_read_element_header(bytes).expect("parsing failed");
/// let (rem, content) = parse_ber_content(header.tag)(i, &header, MAX_RECURSION)
/// .expect("parsing failed");
/// #
/// # assert_eq!(header.tag, BerTag::Integer);
/// ```
pub fn parse_ber_content<'a>(
tag: BerTag,
) -> impl Fn(&'a [u8], &'_ BerObjectHeader, usize) -> BerResult<'a, BerObjectContent<'a>> {
move |i: &[u8], hdr: &BerObjectHeader, max_recursion: usize| {
ber_read_element_content_as(i, tag, hdr.len, hdr.is_constructed(), max_recursion)
}
}
/// Parse the next bytes as the content of a BER object (combinator, owned header)
///
/// Content type is *not* checked to match tag, caller is responsible of providing the correct tag
///
/// Caller is also responsible to check if parsing function consumed the expected number of
/// bytes (`header.len`).
///
/// The arguments of the parse function are: `(input, ber_object_header, max_recursion)`.
///
/// This function differs from [`parse_ber_content`](fn.parse_ber_content.html) because it passes
/// an owned BER object header (required for ex. by `parse_ber_tagged_implicit_g`).
///
/// Example: manually parsing header and content
///
/// ```
/// # use der_parser::ber::*;
/// #
/// # let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (i, header) = ber_read_element_header(bytes).expect("parsing failed");
/// let (rem, content) = parse_ber_content(header.tag)(i, &header, MAX_RECURSION)
/// .expect("parsing failed");
/// #
/// # assert_eq!(header.tag, BerTag::Integer);
/// ```
pub fn parse_ber_content2<'a>(
tag: BerTag,
) -> impl Fn(&'a [u8], BerObjectHeader<'a>, usize) -> BerResult<'a, BerObjectContent<'a>> {
move |i: &[u8], hdr: BerObjectHeader, max_recursion: usize| {
ber_read_element_content_as(i, tag, hdr.len, hdr.is_constructed(), max_recursion)
}
}
/// Parse a BER object, expecting a value with specified tag
///
/// The object is parsed recursively, with a maximum depth of `MAX_RECURSION`.
///
/// ### Example
///
/// ```
/// use der_parser::ber::BerTag;
/// use der_parser::ber::parse_ber_with_tag;
///
/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (_, obj) = parse_ber_with_tag(bytes, BerTag::Integer).expect("parsing failed");
///
/// assert_eq!(obj.header.tag, BerTag::Integer);
/// ```
pub fn parse_ber_with_tag<Tag: Into<BerTag>>(i: &[u8], tag: Tag) -> BerResult {
let tag = tag.into();
let (i, hdr) = ber_read_element_header(i)?;
if hdr.tag != tag {
return Err(nom::Err::Error(BerError::InvalidTag));
}
let (i, content) =
ber_read_element_content_as(i, hdr.tag, hdr.len, hdr.is_constructed(), MAX_RECURSION)?;
Ok((i, BerObject::from_header_and_content(hdr, content)))
}
/// Read end of content marker
#[inline]
pub fn parse_ber_endofcontent(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::EndOfContent)
}
/// Read a boolean value
///
/// The encoding of a boolean value shall be primitive. The contents octets shall consist of a
/// single octet.
///
/// If the boolean value is FALSE, the octet shall be zero.
/// If the boolean value is TRUE, the octet shall be one byte, and have all bits set to one (0xff).
#[inline]
pub fn parse_ber_bool(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Boolean)
}
/// Read an integer value
///
/// The encoding of a boolean value shall be primitive. The contents octets shall consist of one or
/// more octets.
///
/// To access the content, use the [`as_u64`](struct.BerObject.html#method.as_u64),
/// [`as_u32`](struct.BerObject.html#method.as_u32),
/// [`as_biguint`](struct.BerObject.html#method.as_biguint) or
/// [`as_bigint`](struct.BerObject.html#method.as_bigint) methods.
/// Remember that a BER integer has unlimited size, so these methods return `Result` or `Option`
/// objects.
///
/// # Examples
///
/// ```rust
/// # extern crate nom;
/// # use der_parser::ber::parse_ber_integer;
/// # use der_parser::ber::{BerObject,BerObjectContent};
/// let empty = &b""[..];
/// let bytes = [0x02, 0x03, 0x01, 0x00, 0x01];
/// let expected = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01"));
/// assert_eq!(
/// parse_ber_integer(&bytes),
/// Ok((empty, expected))
/// );
/// ```
#[inline]
pub fn parse_ber_integer(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Integer)
}
/// Read an bitstring value
#[inline]
pub fn parse_ber_bitstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::BitString)
}
/// Read an octetstring value
#[inline]
pub fn parse_ber_octetstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::OctetString)
}
/// Read a null value
#[inline]
pub fn parse_ber_null(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Null)
}
/// Read an object identifier value
#[inline]
pub fn parse_ber_oid(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Oid)
}
/// Read an enumerated value
#[inline]
pub fn parse_ber_enum(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Enumerated)
}
/// Read a UTF-8 string value. The encoding is checked.
#[inline]
pub fn parse_ber_utf8string(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Utf8String)
}
/// Read a relative object identifier value
#[inline]
pub fn parse_ber_relative_oid(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::RelativeOid)
}
/// Parse a sequence of BER elements
///
/// Read a sequence of BER objects, without any constraint on the types.
/// Sequence is parsed recursively, so if structured elements are found, they are parsed using the
/// same function.
///
/// To read a specific sequence of objects (giving the expected types), use the
/// [`parse_ber_sequence_defined`](macro.parse_ber_sequence_defined.html) macro.
#[inline]
pub fn parse_ber_sequence(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Sequence)
}
/// Parse a set of BER elements
///
/// Read a set of BER objects, without any constraint on the types.
/// Set is parsed recursively, so if structured elements are found, they are parsed using the
/// same function.
///
/// To read a specific set of objects (giving the expected types), use the
/// [`parse_ber_set_defined`](macro.parse_ber_set_defined.html) macro.
#[inline]
pub fn parse_ber_set(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Set)
}
/// Read a numeric string value. The content is verified to
/// contain only digits and spaces.
#[inline]
pub fn parse_ber_numericstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::NumericString)
}
/// Read a visible string value. The content is verified to
/// contain only the allowed characters.
#[inline]
pub fn parse_ber_visiblestring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::VisibleString)
}
/// Read a printable string value. The content is verified to
/// contain only the allowed characters.
#[inline]
pub fn parse_ber_printablestring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::PrintableString)
}
/// Read a T61 string value
#[inline]
pub fn parse_ber_t61string(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::T61String)
}
/// Read a Videotex string value
#[inline]
pub fn parse_ber_videotexstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::VideotexString)
}
/// Read an IA5 string value. The content is verified to be ASCII.
#[inline]
pub fn parse_ber_ia5string(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::Ia5String)
}
/// Read an UTC time value
#[inline]
pub fn parse_ber_utctime(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::UtcTime)
}
/// Read a Generalized time value
#[inline]
pub fn parse_ber_generalizedtime(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::GeneralizedTime)
}
/// Read an ObjectDescriptor value
#[inline]
pub fn parse_ber_objectdescriptor(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::ObjDescriptor)
}
/// Read a GraphicString value
#[inline]
pub fn parse_ber_graphicstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::GraphicString)
}
/// Read a GeneralString value
#[inline]
pub fn parse_ber_generalstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::GeneralString)
}
/// Read a BmpString value
#[inline]
pub fn parse_ber_bmpstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::BmpString)
}
/// Read a UniversalString value
#[inline]
pub fn parse_ber_universalstring(i: &[u8]) -> BerResult {
parse_ber_with_tag(i, BerTag::UniversalString)
}
/// Parse an optional tagged object, applying function to get content
///
/// This function returns a `BerObject`, trying to read content as generic BER objects.
/// If parsing failed, return an optional object containing `None`.
///
/// To support other return or error types, use
/// [parse_ber_tagged_explicit_g](fn.parse_ber_tagged_explicit_g.html)
///
/// This function will never fail: if parsing content failed, the BER value `Optional(None)` is
/// returned.
pub fn parse_ber_explicit_optional<F>(i: &[u8], tag: BerTag, f: F) -> BerResult
where
F: Fn(&[u8]) -> BerResult,
{
parse_ber_optional(parse_ber_tagged_explicit_g(tag, |content, hdr| {
let (rem, obj) = f(content)?;
let content = BerObjectContent::Tagged(hdr.class, hdr.tag, Box::new(obj));
let tagged = BerObject::from_header_and_content(hdr, content);
Ok((rem, tagged))
}))(i)
}
/// Parse an implicit tagged object, applying function to read content
///
/// Note: unlike explicit tagged functions, the callback must be a *content* parsing function,
/// often based on the [`parse_ber_content`](fn.parse_ber_content.html) combinator.
///
/// The built object will use the original header (and tag), so the content may not match the tag
/// value.
///
/// For a combinator version, see [parse_ber_tagged_implicit](fn.parse_ber_tagged_implicit.html).
///
/// For a generic version (different output and error types), see
/// [parse_ber_tagged_implicit_g](fn.parse_ber_tagged_implicit_g.html).
///
/// # Examples
///
/// The following parses `[3] IMPLICIT INTEGER` into a `BerObject`:
///
/// ```rust
/// # use der_parser::ber::*;
/// # use der_parser::error::BerResult;
/// #
/// fn parse_int_implicit(i:&[u8]) -> BerResult<BerObject> {
/// parse_ber_implicit(
/// i,
/// 3,
/// parse_ber_content(BerTag::Integer),
/// )
/// }
///
/// # let bytes = &[0x83, 0x03, 0x01, 0x00, 0x01];
/// let res = parse_int_implicit(bytes);
/// # match res {
/// # Ok((rem, content)) => {
/// # assert!(rem.is_empty());
/// # assert_eq!(content.as_u32(), Ok(0x10001));
/// # },
/// # _ => assert!(false)
/// # }
/// ```
#[inline]
pub fn parse_ber_implicit<'a, Tag, F>(i: &'a [u8], tag: Tag, f: F) -> BerResult<'a>
where
F: Fn(&'a [u8], &'_ BerObjectHeader, usize) -> BerResult<'a, BerObjectContent<'a>>,
Tag: Into<BerTag>,
{
parse_ber_tagged_implicit(tag, f)(i)
}
/// Combinator for building optional BER values
///
/// To read optional BER values, it is to use the nom `opt()` combinator. However, this results in
/// a `Option<BerObject>` and prevents using some functions from this crate (the generic functions
/// can still be used).
///
/// This combinator is used when parsing BER values, while keeping `BerObject` output only.
///
/// This function will never fail: if parsing content failed, the BER value `Optional(None)` is
/// returned.
///
/// ### Example
///
/// ```
/// # use der_parser::ber::*;
/// #
/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let mut parser = parse_ber_optional(parse_ber_integer);
/// let (_, obj) = parser(bytes).expect("parsing failed");
///
/// assert_eq!(obj.header.tag, BerTag::Integer);
/// assert!(obj.as_optional().is_ok());
/// ```
pub fn parse_ber_optional<'a, F>(mut f: F) -> impl FnMut(&'a [u8]) -> BerResult<'a>
where
F: FnMut(&'a [u8]) -> BerResult<'a>,
{
move |i: &[u8]| {
let res = f(i);
match res {
Ok((rem, inner)) => {
let opt = BerObject::from_header_and_content(
inner.header.clone(),
BerObjectContent::Optional(Some(Box::new(inner))),
);
Ok((rem, opt))
}
Err(_) => Ok((i, BerObject::from_obj(BerObjectContent::Optional(None)))),
}
}
}
/// Parse BER object and try to decode it as a 32-bits signed integer
///
/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
/// integer type.
#[inline]
pub fn parse_ber_i32(i: &[u8]) -> BerResult<i32> {
let (rem, ber) = parse_ber_integer(i)?;
let int = ber.as_i32().map_err(nom::Err::Error)?;
Ok((rem, int))
}
/// Parse BER object and try to decode it as a 64-bits signed integer
///
/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
/// integer type.
#[inline]
pub fn parse_ber_i64(i: &[u8]) -> BerResult<i64> {
let (rem, ber) = parse_ber_integer(i)?;
let int = ber.as_i64().map_err(nom::Err::Error)?;
Ok((rem, int))
}
/// Parse BER object and try to decode it as a 32-bits unsigned integer
///
/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
/// integer type.
#[inline]
pub fn parse_ber_u32(i: &[u8]) -> BerResult<u32> {
let (rem, ber) = parse_ber_integer(i)?;
let int = ber.as_u32().map_err(nom::Err::Error)?;
Ok((rem, int))
}
/// Parse BER object and try to decode it as a 64-bits unsigned integer
///
/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
/// integer type.
#[inline]
pub fn parse_ber_u64(i: &[u8]) -> BerResult<u64> {
let (rem, ber) = parse_ber_integer(i)?;
let int = ber.as_u64().map_err(nom::Err::Error)?;
Ok((rem, int))
}
/// Parse BER object and get content as slice
#[inline]
pub fn parse_ber_slice<Tag: Into<BerTag>>(i: &[u8], tag: Tag) -> BerResult<&[u8]> {
let tag = tag.into();
parse_ber_container(move |content, hdr| {
if hdr.tag != tag {
return Err(Err::Error(BerError::InvalidTag));
}
Ok((&b""[..], content))
})(i)
}
/// Helper combinator, to create a parser with a maximum parsing depth
#[inline]
pub(crate) fn r_parse_ber(max_depth: usize) -> impl Fn(&[u8]) -> BerResult {
move |i: &[u8]| parse_ber_recursive(i, max_depth)
}
/// Parse BER object recursively, specifying the maximum recursion depth
///
/// Return a tuple containing the remaining (unparsed) bytes and the BER Object, or an error.
///
/// ### Example
///
/// ```
/// use der_parser::ber::{parse_ber_recursive, BerTag};
///
/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (_, obj) = parse_ber_recursive(bytes, 1).expect("parsing failed");
///
/// assert_eq!(obj.header.tag, BerTag::Integer);
/// ```
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)?;
if let BerSize::Definite(l) = hdr.len {
custom_check!(i, l > MAX_OBJECT_SIZE, BerError::InvalidLength)?;
}
match hdr.class {
BerClass::Universal => (),
BerClass::Private => {
let (rem, content) = ber_get_object_content(rem, &hdr, max_depth)?;
let content = BerObjectContent::Private(hdr.clone(), content);
let obj = BerObject::from_header_and_content(hdr, content);
return Ok((rem, obj));
}
_ => {
let (rem, content) = ber_get_object_content(rem, &hdr, max_depth)?;
let content = BerObjectContent::Unknown(hdr.class, hdr.tag, content);
let obj = BerObject::from_header_and_content(hdr, content);
return Ok((rem, obj));
}
}
match ber_read_element_content_as(rem, hdr.tag, hdr.len, hdr.is_constructed(), max_depth) {
Ok((rem, content)) => Ok((rem, BerObject::from_header_and_content(hdr, content))),
Err(Err::Error(BerError::UnknownTag)) => {
let (rem, content) = ber_get_object_content(rem, &hdr, max_depth)?;
let content = BerObjectContent::Unknown(hdr.class, hdr.tag, content);
let obj = BerObject::from_header_and_content(hdr, content);
Ok((rem, obj))
}
Err(e) => Err(e),
}
}
/// Parse BER object recursively
///
/// Return a tuple containing the remaining (unparsed) bytes and the BER Object, or an error.
///
/// *Note*: this is the same as calling `parse_ber_recursive` with `MAX_RECURSION`.
///
/// ### Example
///
/// ```
/// use der_parser::ber::{parse_ber, BerTag};
///
/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
/// let (_, obj) = parse_ber(bytes).expect("parsing failed");
///
/// assert_eq!(obj.header.tag, BerTag::Integer);
/// ```
#[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 text_visiblestring() {
assert_eq!(
ber_read_content_visiblestring(b"AZaz]09 '()+,-./:=?", 19),
Ok((
[].as_ref(),
BerObjectContent::VisibleString("AZaz]09 '()+,-./:=?")
)),
);
assert_eq!(
ber_read_content_visiblestring(b"", 0),
Ok(([].as_ref(), BerObjectContent::VisibleString(""))),
);
assert!(ber_read_content_visiblestring(b"\n", 1).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"]\n", 2).is_err());
}
#[test]
fn test_ia5string() {
assert_eq!(
ber_read_content_ia5string(b"AZaz\n09 '()+,-./:=?[]{}\0\n", 25),
Ok((
[].as_ref(),
BerObjectContent::IA5String("AZaz\n09 '()+,-./:=?[]{}\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());
}
#[test]
fn test_bitstring_to_u64() {
// ignored bits modulo 8 to 0
let data = &hex_literal::hex!("0d 71 82");
let r = bitstring_to_u64(8, &BitStringObject { data });
assert_eq!(r, Ok(0x0d71));
// input too large to fit a 64-bits integer
let data = &hex_literal::hex!("0d 71 82 0e 73 72 76 6e 67 6e 62 6c 6e 2d 65 78 30 31");
let r = bitstring_to_u64(0, &BitStringObject { data });
assert!(r.is_err());
// test large number but with many ignored bits
let data = &hex_literal::hex!("0d 71 82 0e 73 72 76 6e 67 6e 62 6c 6e 2d 65 78 30 31");
let r = bitstring_to_u64(130, &BitStringObject { data });
// 2 = 130 % 8
assert_eq!(r, Ok(0x0d71 >> 2));
}