44 releases

0.16.1 Mar 4, 2024
0.15.5 Aug 9, 2023
0.15.4 Jun 15, 2023
0.13.0 Nov 27, 2022
0.1.0 Jul 21, 2015

#95 in Parser implementations

Download history 17324/week @ 2023-12-23 23811/week @ 2023-12-30 30391/week @ 2024-01-06 31646/week @ 2024-01-13 35580/week @ 2024-01-20 35808/week @ 2024-01-27 36066/week @ 2024-02-03 38999/week @ 2024-02-10 42833/week @ 2024-02-17 44634/week @ 2024-02-24 41461/week @ 2024-03-02 44005/week @ 2024-03-09 38896/week @ 2024-03-16 37459/week @ 2024-03-23 37029/week @ 2024-03-30 32311/week @ 2024-04-06

152,479 downloads per month
Used in 4 crates

BSD-3-Clause

180KB
4.5K 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.16"

Builds on Rust 1.56.0 and newer.

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

asn1 = { version = "0.16", 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 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.

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

~320–770KB
~18K SLoC