SkillAgentSearch skills...

Asn1rs

Generates Rust Code and optionally compatible Protobuf schema files from ASN.1 definitions.

Install / Use

/learn @kellerkindt/Asn1rs
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

asn1rs - ASN.1 Compiler for Rust

This crate generates Rust Code and optionally compatible Protobuf and SQL schema files from ASN.1 definitions. Integration with serde is supported.

The crate can be used as standalone CLI binary or used as library through its API (for example inside your build.rs script).

Build Status License Crates.io Coverage Status Documentation PRs Welcome

Supported Features

| Feature | Parses | UPER | Protobuf | | --------------------|:--------|:-------|:-----------| | ...extensible | ✔️ yes | ✔️ yes | 🆗 ignored | | SEQUENCE OF | ✔️ yes | ✔️ yes | ✔️ yes | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | SET | ✔️ yes | ✔️ yes | ✔️ yes | | ...extensible | ✔️ yes | ✔️ yes | 🆗 ignored | | SET OF | ✔️ yes | ✔️ yes | ✔️ yes | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | ENUMERATED | ✔️ yes | ✔️ yes | ✔️ yes | | ...extensible | ✔️ yes | ✔️ yes | 🆗 ignored | | CHOICE | ✔️ yes | ✔️ yes | ✔️ yes | | ...extensible | ✔️ yes | ✔️ yes | 🆗 ignored | | BIT STRING | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | OCTET STRING | ✔️ yes | ✔️ yes | ✔️ yes | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | UTF8String | ✔️ yes | ✔️ yes | ✔️ yes | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | IA5String | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | NumericString | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | PrintableString | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | VisibleString | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...SIZE(A..B) | ✔️ yes | ✔️ yes | 🆗 ignored | | ...SIZE(A..B,...) | ✔️ yes | ✔️ yes | 🆗 ignored | | INTEGER | ✔️ yes | ✔️ yes | ✔️ yes | | ...A..B | ✔️ yes | ✔️ yes | ✔️ yes² | | ...A..B,... | ✔️ yes | ✔️ yes | ✔️ yes² | | BOOLEAN | ✔️ yes | ✔️ yes | ✔️ yes | | OPTIONAL | ✔️ yes | ✔️ yes | ✔️ yes | | DEFAULT ... | ✔️ yes | | | | ...INTEGER | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...*String | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...BOOLEAN | ✔️ yes | ✔️ yes | ✔️ yes¹ | | ...ENUMERATED | ✔️ yes | ✔️ yes | ✔️ yes¹ | | NULL | ✔️ yes | ✔️ yes | ✔️ yes¹ | | IMPORTS..FROM..; | ✔️ yes | | | | ObjectIdentifiers | ✔️ yes | | | | Value References | ✔️ yes | | | | ... in Range | ✔️ yes | | | | ... in Size | ✔️ yes | | | | ... in Default | ✔️ yes | | | | WITH COMPONENTS | ✔️ yes | | |

  • ✔️ yes: according to specification
  • ✔️ yes¹: different representation
  • ✔️ yes²: as close as possible to the original specification (sometimes yes, sometimes yes¹)
  • 🔶 not serialized: values are not serialized or deserialized in this case, might break compatibility
  • ⚠️ ignored️: constraint is ignored, this most likely breaks compatibility
  • 🆗 ignored: constraint is ignored but it does not break compatibility
  • ❌ ub: undefined behavior - whatever seems reasonable to prevent compiler errors and somehow transmit the value
  • 🟥 error: fails to compile / translate

Supported standards

CLI usage

It is always helpful to check asn1rs --help in advance. The basic usage can be seen blow:

asn1rs -t rust directory/for/rust/files some.asn1 messages.asn1
asn1rs -t proto directory/for/protobuf/files some.asn1 messages.asn1

Example: build.rs

The following example generates Rust and Protobuf files for all .asn1-files in the asn/ directory of a workspace. While the generated Rust code is written to the src/ directory, the Protobuf files are written to proto/. Additionally, in this example each generated Rust-Type also receives Serialize and Deserialize derive directives (#[derive(Serialize, Deserialize)]) for serde integration.

Sample build.rs file:

use asn1rs::converter::Converter;
use asn1rs::gen::rust::RustCodeGenerator;

pub fn main() {
    let mut converter = Converter::default();

    // collecting all relevant .asn1 files
    std::fs::read_dir("../protocol/asn")
        .into_iter()
        .flat_map(|read_dir| {
            read_dir
                .into_iter()
                .flat_map(|dir_entry| dir_entry.into_iter())
                .flat_map(|entry| {
                    entry
                        .path()
                        .as_os_str()
                        .to_os_string()
                        .into_string()
                        .into_iter()
                })
                .filter(|entry| entry.ends_with(".asn1"))
        })
        .for_each(|path| {
            println!("cargo:rerun-if-changed={}", path);
            if let Err(e) = converter.load_file(&path) {
                panic!("Loading of .asn1 file failed {}: {:?}", path, e);
            }
        });

    // writing the .rs files into src with serde_derive support
    // feature flags decide whether additional code for protobuf is generated
    if let Err(e) = converter.to_rust("src/", |generator: &mut RustCodeGenerator| {
        generator.add_global_derive("Serialize"); // Adds serde_derive support: #[derive(Serialize)]
        generator.add_global_derive("Deserialize"); // Adds serde_derive support: #[derive(Deserialize)]
    }) {
        panic!("Conversion to rust failed: {:?}", e);
    }

    // OPTIONAL: writing the .proto representation to ../protocol/proto
    if let Err(e) = converter.to_protobuf("../protocol/proto/") {
        panic!("Conversion to proto failed: {:?}", e);
    }
}

Example: Inlining ASN.1 with procedural macros

Minimal example by inlining the ASN.1 definition. For more examples see tests/.

use asn1rs::prelude::*;

asn_to_rust!(
    r"BasicInteger DEFINITIONS AUTOMATIC TAGS ::=
    BEGIN
    
    RangedMax ::= Integer (0..MAX)
    
    NotRanged ::= Integer
    
    END"
);

#[test]
fn test_write_read() {
    // inner INTEGER identified as u64
    let value = NotRanged(123_u64);

    let mut writer = UperWriter::default();
    writer.write(&value).expect("Failed to serialize");

    let mut reader = writer.into_reader();
    let value2 = reader.read::<NotRanged>().expect("Failed to deserialize");
    
    assert_eq!(value, value2);
}

#[test]
fn test_constraint_eq() {
    // these types should normally not be accessed, but in this exampled they show
    // the way the ASN.1 constraints are encoded with the Rust type system.
    use asn1rs::syn::numbers::Constraint;
    assert_eq!(
        ___asn1rs_RangedMaxField0Constraint::MIN,
        ___asn1rs_NotRangedField0Constraint::MIN,
    );
    assert_eq!(
        ___asn1rs_RangedMaxField0Constraint::MAX,
        ___asn1rs_NotRangedField0Constraint::MAX,
    );
}

Example: ASN.1-Definition converted to Rust and Protobuf

Minimal example showcasing what is being generated from an ASN.1 definition:

MyMessages DEFINITIONS AUTOMATIC TAGS ::=
BEGIN

Header ::= SEQUENCE {
    timestamp    INTEGER (0..1209600000)
}

END

The generated Rust file:

use asn1rs::prelude::*;

#[asn(sequence)]
#[derive(Default, Debug, Clone, PartialEq, Hash)]
pub struct Header {
    #[
View on GitHub
GitHub Stars62
CategoryData
Updated3mo ago
Forks19

Languages

Rust

Security Score

97/100

Audited on Dec 13, 2025

No findings