1 stable release

new 1.0.0 May 25, 2025

#433 in Encoding

MIT license

310KB
6K SLoC

Abstract Syntax Notation 1 (ASN.1) Library

This crate contains types and functions for using ASN.1 values. This library has little to do with encoding or decoding such values according to the Basic, Canonical, Distinguished, Packed, Octet, XML, JSON, General String, or BACNet Encoding Rules. This library is purposely abstracted from any particular encoding so that ASN.1 values can be translated between different encodings. Other crates will use this library as a dependency to implement encoding and decoding for the types defined in this crate.

This crate is intended to be high-performance and opines toward more code in exchange for better performance, notably using a lot of SIMD and other optimizations. This is not a lightweight crate; it is intended to be a fast, feature-complete, meticulous, and well-tested ASN.1 crate.

Feature Flags

  • atoi_simd - This will make integer parsing slightly faster, but only if you enable SIMD features at compile-time. See this crate's documentation.
  • faster-hex - Faster hex compilation. Not tested to see if it's really faster.
  • itoa - Faster integer printing, only in a few select cases like OIDs and DURATION values
  • likely_stable - Helps branch prediction, thereby making the code generally a little faster
  • smallvec - Use SmallVec<[u8; 16]> to store OBJECT IDENTIFIER, RELATIVE-OID and BIT STRING. Experimentation has shown this to be slower for some strange reason, so it should probably stay turned off.

Capabilities

All universal types are supported, including the newer TIME subtypes: DATE, DATE-TIME, TIME-OF-DAY, and DURATION.

SEQUENCE or SET parsing features

This is not a complete solution, but this library provides TagSelector for defining sequences of rules for matching components in a SEQUENCE or SET data types, and ComponentSpec for more data, such as the component's name and whether it is OPTIONAL.

Context-Switching Types

This library defines EXTERNAL, EMBEDDED_PDV, and CHARACTER_STRING. All of them can be printed.

Display

All types defined in this crate implement Display, and when printed, they are printed according to their ASN.1 abstract syntax (which may differ from how they are encoded).

Detailed Errors

ASN1Errors are very detailed, printable, and you should be able to figure out whatever your problem is based on the output:

pub struct ASN1Error {
    pub error_code: ASN1ErrorCode,
    pub component_name: Option<String>,
    pub tag: Option<Tag>,
    pub length: Option<usize>,
    pub constructed: Option<bool>,
    pub value_preview: Option<String>,
    pub bytes_read: Option<usize>, // The number of bytes into the IO read stream where this error appeared.
    pub values_read: Option<usize>, // The number of ASN.1 values into the IO read stream where this error appeared.
    pub err_source: Option<Box<dyn std::error::Error + 'static>>,
}

Of course, they implement std::error::Error.

ASN.1 Types and Values

ASN.1 abstract values can be represented via ASN1Value. This is a pretty large and complex enum. You probably should not use it unless you are parsing ASN.1 abstract values. When decoding presentation syntax (such as BER or DER), you probably should represent presentation values using the decoding libraries concept of a "node" or "value," whatever that might be.

pub enum ASN1Value {
    BitStringValue(BIT_STRING),
    BooleanValue(BOOLEAN),
    ChoiceValue(Box<ASN1Value>),
    EmbeddedPDVValue(EMBEDDED_PDV),
    EnumeratedValue(ENUMERATED),
    ExternalValue(EXTERNAL),
    InstanceOfValue(INSTANCE_OF),
    IntegerValue(INTEGER),
    IRIValue(OID_IRI),
    NullValue,
    /// ...
}

Embedded and no-std Use

This library is not ideal for embedded use cases. It is intended to be complete and compliant, not lightweight. Every valid ASN.1 requirement and capability is expected to be supported correctly, and if it is not, please report it as a bug.

This library is not no-std and probably never will be, because so many things in this library rely on std, and so few things outside of that are useful.

In embedded environments, it might be better for you to use encoding-specific, simpler libraries that do the bare minimum. This might be a suitable library for you in this case. Also consider that, since this library is licensed under an FLOSS license, you could just copy whatever code you need into your embedded project.

serde Integration

I don't think this crate will support integration with serde, unless this is requested. Its intended design is for dependent crates to implement most of the encoding-specific logic.

Fuzz Testing

In the root of this monorepo, run:

cargo fuzz run bitstring
cargo fuzz run date -- -max_len=10
cargo fuzz run datetime -- -max_len=20
cargo fuzz run time_of_day -- -max_len=8
cargo fuzz run asn1utils -- -max_len=8
cargo fuzz run duration -- -max_len=32
cargo fuzz run gentime -- -max_len=35
cargo fuzz run utctime -- -max_len=20
cargo fuzz run oid -- -max_len=24

These will run forever, so you will want to kill them by pressing Ctrl+C.

Versioning

This is currently in beta. Be prepared for potentially large breaking changes until 1.0.0 is released, after which I will adhere to semantic versioning.

However, I have successfully built applications using this library, so it is somewhat battle-tested.

Dependencies

~0–295KB