47 releases
0.18.0 | Oct 24, 2024 |
---|---|
0.16.2 | Jun 2, 2024 |
0.16.1 | Mar 4, 2024 |
0.15.5 | Aug 9, 2023 |
0.1.0 | Jul 21, 2015 |
#76 in Parser implementations
236,560 downloads per month
Used in 5 crates
185KB
4.5K
SLoC
rust-asn1
This is a Rust library for parsing and generating ASN.1 data (DER only).
Installation
Add asn1
to the [dependencies]
section of your Cargo.toml
:
[dependencies]
asn1 = "0.18"
Builds on Rust 1.59.0 and newer.
rust-asn1
is compatible with #![no_std]
environments:
asn1 = { version = "0.18", default-features = false }
lib.rs
:
This crate provides you with the ability to generate and parse ASN.1 encoded data. More precisely, it provides you with the ability to generate and parse data encoded with ASN.1's DER (Distinguished Encoding Rules) encoding. It does not support BER (Basic Encoding Rules), CER (Canonical Encoding Rules), XER (XML Encoding Rules), CXER (Canonical XML Encoding Rules), or any other alphabet soup encodings -- and it never will.
If you wanted to parse an ASN.1 structure like this:
Signature ::= SEQUENCE {
r INTEGER,
s INTEGER
}
Then you'd write the following code:
let result: asn1::ParseResult<_> = asn1::parse(data, |d| {
return d.read_element::<asn1::Sequence>()?.parse(|d| {
let r = d.read_element::<u64>()?;
let s = d.read_element::<u64>()?;
return Ok((r, s));
})
});
In general everything about parsing is driven by providing different type
parameters to Parser.read_element
. Some types implement the
Asn1Readable
trait directly on a basic type, as seen with u64
or
&[u8]
(OCTET STRING
), while others use wrapper types which simply
provide ASN.1 encoding and decoding for some other type (PrintableString
or UtcTime
). There are also types such as Implicit
and Explicit
for
handling tagged values, Choice1
, Choice2
, and Choice3
available for
choices, and Option<T>
for handling OPTIONAL
values.
To serialize DER for the Sequence
structure, you'd write the following:
let result = asn1::write(|w| {
w.write_element(&asn1::SequenceWriter::new(&|w| {
w.write_element(&r)?;
w.write_element(&s)?;
Ok(())
}))
});
Derive
When built with the derive
feature (enabled by default), these can also
be expressed as Rust structs:
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
struct Signature {
r: u64,
s: u64,
}
let sig = asn1::parse_single::<Signature>(data);
let result = asn1::write_single(&Signature{r, s});
Fields may be marked as EXPLICIT
or IMPLICIT
either by struct members
having the types Explicit
and Implicit
or via the use of
#[explicit]
and #[implicit]
annotations:
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
struct SomeSequence<'a> {
#[implicit(0)]
a: Option<&'a [u8]>,
#[explicit(1)]
b: Option<u64>,
}
Fields can also be annotated with #[default(VALUE)]
to indicate ASN.1
OPTIONAL DEFAULT
values. In this case, the field's type should be T
,
and not Option<T>
.
These derives may also be used with enum
s to generate CHOICE
implementations.
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
enum Time {
UTCTime(asn1::UtcTime),
GeneralizedTime(asn1::GeneralizedTime)
}
All variants must have a single un-named field.
DEFINED BY
rust-asn1 also provides utilities for more easily handling the case of
ANY DEFINED BY
in an ASN.1 structure. For example, given the following
ASN.1;
MySequence ::= SEQUENCE {
contentType OBJECT IDENTIFIER,
content ANY DEFINED BY contentType
}
This can be represented by:
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
struct MySequence {
content_type: asn1::DefinedByMarker<asn1::ObjectIdentifier>,
#[defined_by(content_type)]
content: Content,
}
#[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite)]
enum Content {
#[defined_by(SOME_OID_CONSTANT)]
SomeVariant(i32),
}
Design philosophy
As we have designed the asn1
crate, we value the following things, in
this order:
- Security
- Correctness
- Performance
- Ergonomics
Dependencies
~255–710KB
~17K SLoC