#asn1

asn1

ASN.1 (DER) parser and writer for Rust

24 releases (6 breaking)

new 0.8.5 Oct 26, 2021
0.7.0 Oct 10, 2021
0.6.0 Jul 17, 2021
0.1.0 Jul 21, 2015

#6 in #asn1

Download history 949/week @ 2021-07-06 1176/week @ 2021-07-13 1113/week @ 2021-07-20 1622/week @ 2021-07-27 1398/week @ 2021-08-03 1433/week @ 2021-08-10 1370/week @ 2021-08-17 2291/week @ 2021-08-24 1719/week @ 2021-08-31 535/week @ 2021-09-07 879/week @ 2021-09-14 2387/week @ 2021-09-21 20898/week @ 2021-09-28 32598/week @ 2021-10-05 27177/week @ 2021-10-12 30676/week @ 2021-10-19

41,522 downloads per month

BSD-3-Clause

120KB
3K SLoC

rust-asn1

Dependency Status Documentation

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.6"

Builds on Rust 1.41.0 and newer, but versions older than 1.51.0 require disabling the const-generics feature, which allows using the Implicit and Explicit types.

rust-asn1 is compatible with #![no_std] environments:

asn1 = { version = "0.6", 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 data = b"";
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 r = 0u64;
# let s = 0u64;
let result = asn1::write(|w| {
    w.write_element(&asn1::SequenceWriter::new(&|w| {
        w.write_element(&r);
        w.write_element(&s);
    }));
});

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 data = b"";
# let r = 0u64;
# let s = 0u64;
let sig = asn1::parse_single::<Signature>(data);
let result = asn1::write_single(&Signature{r, s});

On Rust >= 1.51.0, [Explicit] and [Implicit] tagging may be specified with struct members of those types. However on Rust < 1.51.0, this is not possible, since they require const generics. Instead, the #[implicit] and #[explicit] attributes may be used:

#[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 enums 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.

Dependencies

~0.6–1MB
~15K SLoC