A parser for Basic Encoding Rules (BER [X.690]) and Distinguished Encoding Rules(DER [X.690]), implemented with the nom parser combinator framework.
It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken to ensure security and safety of this crate, including design (recursion limit, defensive programming), tests, and fuzzing. It also aims to be panic-free.
Historically, this parser was intended for DER only, and BER support was added later. This may
still reflect on some naming schemes, but has no other consequence: the BerObject
and
DerObject
used in this crate are type aliases, so all functions are compatible.
DER parsing functions have additional constraints verification, however.
Serialization has also been added (see Serialization )
The code is available on Github and is part of the Rusticata project.
BER stands for Basic Encoding Rules, and is defined in X.690. It defines a set of rules to encode and decode ASN.1 objects in binary.
X.690 also defines Distinguished Encoding Rules (DER), which is BER with added rules to ensure canonical and unequivocal binary representation of objects.
The choice of which one to use is usually guided by the speficication of the data format based on BER or DER: for example, X.509 uses DER as encoding representation.
See the related modules for object definitions, functions, and example:
Parse two BER integers (see BER/DER Integers):
use der_parser::ber::parse_ber_integer;
let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01,
0x02, 0x03, 0x01, 0x00, 0x00,
];
let (rem, obj1) = parse_ber_integer(&bytes).expect("parsing failed");
let (rem, obj2) = parse_ber_integer(&rem).expect("parsing failed");
Parse a DER sequence of integers:
use der_parser::der::{parse_der_integer, parse_der_sequence_of};
let bytes = [ 0x30, 0x0a,
0x02, 0x03, 0x01, 0x00, 0x01,
0x02, 0x03, 0x01, 0x00, 0x00,
];
let (rem, seq) = parse_der_sequence_of(parse_der_integer)(&bytes)
.expect("parsing failed");
Note: all parsing functions return the remaining (unparsed) bytes and the parsed object, or an error.
Parsing functions are inspired from nom
, and follow the same interface. The most common
return type is BerResult
, that stores the remaining bytes and
parsed BerObject
, or an error. Reading the nom documentation may
help understanding how to write parsers and use the output.
There are two different approaches for parsing DER objects: reading the objects recursively as long as the tags are known, or specifying a description of the expected objects (generally from the ASN.1 description).
The first parsing method can be done using the parse_ber
and
parse_der
methods.
It is useful when decoding an arbitrary DER object.
However, it cannot fully parse all objects, especially those containing IMPLICIT, OPTIONAL, or
DEFINED BY items.
use der_parser::parse_der;
let bytes = [ 0x30, 0x0a,
0x02, 0x03, 0x01, 0x00, 0x01,
0x02, 0x03, 0x01, 0x00, 0x00,
];
let parsed = parse_der(&bytes);
The second (and preferred) parsing method is to specify the expected objects recursively. The following functions can be used:
parse_ber_sequence_defined
and similar functions
for sequences and sets variantsparse_ber_tagged_explicit
for tagged explicitparse_ber_tagged_implicit
for tagged implicitparse_ber_container
for generic parsing, etc._der_
variantsFor example, to read a BER sequence containing two integers:
use der_parser::ber::*;
use der_parser::error::BerResult;
fn localparse_seq(i:&[u8]) -> BerResult {
parse_ber_sequence_defined(|data| {
let (rem, a) = parse_ber_integer(data)?;
let (rem, b) = parse_ber_integer(rem)?;
Ok((rem, vec![a, b]))
})(i)
}
let bytes = [ 0x30, 0x0a,
0x02, 0x03, 0x01, 0x00, 0x01,
0x02, 0x03, 0x01, 0x00, 0x00,
];
let (_, parsed) = localparse_seq(&bytes).expect("parsing failed");
assert_eq!(parsed[0].as_u64(), Ok(65537));
assert_eq!(parsed[1].as_u64(), Ok(65536));
All functions return a BerResult
object: the parsed
BerObject
, an Incomplete
value, or an error.
Note that this type is also a Result
, so usual functions (map
, unwrap
etc.) are available.
DER integers can be of any size, so it is not possible to store them as simple integers (they are stored as raw bytes).
Note that, by default, BER/DER integers are signed. Functions are provided to request reading unsigned values, but they will fail if the integer value is negative.
To get the integer value for all possible integer sign and size, use
BerObject::as_bigint
) (requires the bigint
feature).
To get a simple value expected to be in a known range, use methods like
BerObject::as_i32
) and
BerObject::as_i64
(or the unsigned versions
BerObject::as_u32
and
BerObject::as_u64
),
which will return the value, or an error if the integer is too large (or is negative).
use der_parser::ber::*;
let data = &[0x02, 0x03, 0x01, 0x00, 0x01];
let (_, object) = parse_ber_integer(data).expect("parsing failed");
assert_eq!(object.as_u64(), Ok(65537));
#[cfg(feature = "bigint")]
assert_eq!(object.as_bigint(), Ok(65537.into()))
Access to the raw value is possible using the as_slice
method.
Some parsing tools (for ex for tagged objects) are available in different forms:
parse_der
.BerObject
and DerObject
are the same objects (type alias). The only difference is the
verification of constraints during parsing.The 7.0 series of der-parser
requires Rustc version 1.53 or greater, based on asn1-rs
and nom
7 dependencies.
Support for encoding BER/DER objects is currently being tested and can be used by activating the serialize
feature.
Note that current status is experimental.
See the ber_encode_*
functions in the ber
module, and
BerObject::to_vec
"1.2.840.113549.1.1.5"
.