Git Product home page Git Product logo

compiler's Introduction

Rasn compiler

crates.io Help Wanted Documentation

Try compiling some ASN.1 online.

The rasn-compiler library is a parser combinator that parses ASN.1 specifications and outputs bindings for ASN.1 data elements using pluggable backends. Currently, the compiler can output:

  • rust bindings to be used with the rasn crate
  • typescript type definitions for JER-encoded ASN.1 data elements

The compiler heavily relies on the great library nom for its basic parsers. The parser has been designed to generate bindings for ASN.1 and it should not be used as a validating tool for ASN.1 modules.

Example

In order to compile ASN.1 in your build process, invoke the rasn-compiler in your build.rs build script.

// build.rs build script
use std::path::PathBuf;
use rasn_compiler::prelude::*;

fn main() {
    // Initialize the compiler with the rust/rasn backend.
    // To use the typescript backend, initialize the compiler using
    // `Compiler::<TypescriptBackend, _>::new()`
    match Compiler::<RasnBackend, _>::new()
        // add a single ASN1 source file
        .add_asn_by_path(PathBuf::from("spec_1.asn"))
        // add several ASN1 source files
        .add_asn_sources_by_path(vec![
            PathBuf::from("spec_2.asn"),
            PathBuf::from("spec_3.asn"),
        ].iter())
        // set an output path for the generated rust code
        .set_output_path(PathBuf::from("./asn/generated.rs"))
        // you may also compile literal ASN1 snippets
        .add_asn_literal(format!(
            "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END",
            "My-test-integer ::= INTEGER (1..128)"
        ))
        .compile() {
        Ok(warnings /* Vec<Box<dyn Error>> */) => { /* handle compilation warnings */ }
        Err(error /* Box<dyn Error> */) => { /* handle unrecoverable compilation error */ }
    }
}

Configuring the Backend

The compiler backends can be configured by instantiating the compiler using the Compiler::new_with_config constructor.

rasn Backend Configuration

The RasnBackend configuration supports the following parameters:

  • opaque_open_types: bool: [Default: false] ASN.1 Open Types are represented as the rasn::types::Any type, which holds a binary content. If opaque_open_types is false, the compiler will generate additional de-/encode methods for all rust types that hold an open type. For example, bindings for a SEQUENCE with a field of Open Type value will include a method for explicitly decoding the Open Type field. Non-opaque open types are still experimental. If you have trouble generating correct bindings, switch back to opaque open types.
  • default_wildcard_imports: bool: [Default: false] The compiler will try to match module import dependencies of the ASN.1 module as close as possible, importing only those types from other modules that are imported in the ASN.1 module. If the default_wildcard_imports is set to true , the compiler will instead always import the entire module using the wildcard * for each module that the input ASN.1 module imports from.

Creating a Custom Backend

The compiler's backend can be replaced with a custom backend to generate bindings for a different language or framework. Backends must implement the Backend trait that rasn-compiler exports.

// build.rs build script
use std::path::PathBuf;
use rasn_compiler::prelude::*;

// The `Backend` trait requires the implementor to implement `Default`
#[derive(Default)]
struct CustomBackend;

impl Backend for CustomBackend {
    type Config = ();

    const FILE_EXTENSION: &'static str = ".ext";

    fn generate_module(
         &self,
         top_level_declarations: Vec<ToplevelDefinition>,
    ) -> Result<GeneratedModule, GeneratorError> {
        Ok(GeneratedModule::empty())
    }

    fn generate(
        &self,
        tld: ToplevelDefinition
    ) -> Result<String, GeneratorError> {
        Ok(String::new())
    }

    fn config(&self) -> &Self::Config {
        &()
    }

    fn from_config(config: Self::Config) -> Self {
        CustomBackend
    }
}

fn main() {
    // Initialize the compiler
    match Compiler::<CustomBackend, _>::new()
        // add a single ASN1 source file
        .add_asn_by_path(PathBuf::from("spec_1.asn"))
        // add several ASN1 source files
        .add_asn_sources_by_path(vec![
            PathBuf::from("spec_2.asn"),
            PathBuf::from("spec_3.asn"),
        ].iter())
        // set an output path for the generated rust code
        .set_output_path(PathBuf::from("./asn/generated.rs"))
        // you may also compile literal ASN1 snippets
        .add_asn_literal(format!(
            "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {} END",
            "My-test-integer ::= INTEGER (1..128)"
        ))
        .compile() {
        Ok(warnings /* Vec<Box<dyn Error>> */) => { /* handle compilation warnings */ }
        Err(error /* Box<dyn Error> */) => { /* handle unrecoverable compilation error */ }
    }
}

CLI

The rasn-compiler provides a CLI application that can be activated with the cli cargo feature. Run ./rasn_compiler_cli -h for usage info.

ASN1 Support

ASN1 is a complex standard, and not all of its features and encoding rules are supported, yet.

Currently, rasn supports the following encoding rules:

  • Basic Encoding Rules (BER)
  • Canonical Encoding Rules (CER)
  • Distinguished Encoding Rules (DER)
  • Aligned Packed Encoding Rules (APER)
  • Unaligned Packed Encoding Rules (UPER)
  • JSON Encoding Rules (JER)

rasn and the rasn-compiler support the following ASN1 features:

Types

  • NULL type and value
  • BOOLEAN type and value
  • NumericString type and value
  • VisibleString type and value
  • IA5String type and value
  • GeneralString type and value
  • UTF8String type and value
  • BMPString type and value
  • PrintableString type and value
  • BIT STRING type and value (hex- and bitstring declations)
  • OCTET STRING type and value (hex- and bitstring declations)
  • OBJECT IDENTIFIER type and value
  • RELATIVE-OID type and value
  • SEQUENCE type and value
  • SET type and value
  • SEQUENCE OF type and value
  • SET OF type and value
  • ENUMERATED type and value
  • CHOICE type and value
  • UTCTime type and value
  • GeneralizedTime type and value

Constraints

  • Single value constraints
  • Value range constraints
  • Contained subtype constraints
  • Size constraints
  • Permitted alphabet constraints
  • Constraint set operations
  • Table constraints

Misc

  • DEFAULT member values
  • COMPONENTS OF notation
  • Choice selection type notation (e.g. option-1 < Example-choice)
  • extensions and extension groups
  • Parameterization (the rasn-compiler creates rust representations for invocations of the parameterized data elements in the given spec, i.e. it does not preserve the parameterization itself)
  • Information Object Classes (however, they are not represented in the rust bindings)
  • Information Objects
  • Information Object Sets

Troubleshooting

If you have trouble generating correct bindings:

  • try playing around with the compiler configuration that can be passed to the Compiler::new_with_config constructor
  • open an issue here, if possible with a sample of the problematic ASN.1 spec

compiler's People

Contributors

6d7a avatar dmtarua avatar nicceboy avatar shahn avatar v0-e avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

compiler's Issues

Backend trait with TokenStream

PR #17 introduced the generate() method into the Backend trait,

fn generate(&self, tld: ToplevelDefinition) -> Result<TokenStream, GeneratorError>;

However, based on my understanding, TokenStream is specialized for Rust tokens, which contradicts the intention of allowing custom Backends for other language bindings.

Snake case conversion and text-based encodings

Hi @6d7a,
Just want to start by saying a big thank you for the awesome work on the compiler!

On the issue, the compiler is currently replacing uppercase letters of ASN.1 identifiers to an underscore and the respective lowercase. For example the ETSI ITS ItsPduHeader is currently compiled into (using https://librasn.github.io):

#[derive(AsnType, Debug, Clone, Decode, Encode, Default, PartialEq)]
#[rasn(automatic_tags)]
pub struct ItsPduHeader {
    #[rasn(value("0..=255"))]
    pub protocol_version: u8,
    #[rasn(value("0..=255"))]
    pub message_i_d: u8,
    pub station_i_d: StationID,
}
impl ItsPduHeader {
    pub fn new(protocol_version: u8, message_i_d: u8, station_i_d: StationID) -> Self {
        Self {
            protocol_version,
            message_i_d,
            station_i_d,
        }
    }
}

That is, protocolVersion is converted to protocol_version, messageID to message_i_d, and stationID to station_i_d.
In the current JER implementation, rasn uses the struct field names to encode the JSON names of name/value pairs.
Instead of,

{"protocolVersion":1,"messageID":5,"stationID":999}

we have,

{"protocol_version":1,"message_i_d":5,"station_i_d":999}

Which goes a bit against X.697 27.3.2 a) 2021.

Could we add an option, or disable the conversion altogether, to preserve the original ASN.1 identifiers format in the compiler?

Hello! ๐Ÿ‘‹

Hello! ๐Ÿ‘‹

Thank you for wanting to contribute to rasn and its compiler. Currently, the main goal of the compiler development is to achieve full compliance with the specifications that define ASN.1.

If you don't already know how you may want to contribute to the compiler's development, check out the parses_modules test. The test runs the compiler against a collection of public ASN.1 modules and outputs a rudimentary log file (parse_test_results.txt) that is a good place to figure out where some work needs to be done.

A typical error might look like this in parse_text_result.txt:

...
----------------------------------------------------------------------------
"./tests/modules/itu-t_q_q1228_1997_IN-CS2-SCF-SCF-pkgs-contracts-acs.asn1"
----------------------------------------------------------------------------
ParserError {
    details: "Error matching ASN syntax while parsing:scf-scfContract CONTRACT ::= {\n  CONNECTION             scf-scfConnectionPackage...

The header deliminated by hyphens indicates the path to the module that caused the error when the compiler was trying to parse it. Below, the ParserError lets us know that the compiler was having trouble digesting a particular segment of the ASN.1 module: Something around the occurrence of scf-scfContract CONTRACT ::= {...

Any further investigation from this point on can only be of help. Already an issue with a minimal reproduction of the error is a great contribution.

Issue with DEFAULT {} in SEQUENCE OF Type

Following example from standard X.680 document (slightly simplified) emits warning: Unidentified generating bindings for : Unexpectedly encountered unlinked struct-like ASN1 value! and skips generation of PersonelRecord:

ExampleFromX680Spec DEFINITIONS AUTOMATIC TAGS ::=

BEGIN

PersonnelRecord ::=  SET {
        title VisibleString,
        children SEQUENCE OF VisibleString DEFAULT {}
}

END

Issue when trying to compile asn1 files for s1ap protocol

Hello everyone!

I'm having an issue when I'm trying to compile asn1 files of s1ap protocol through the CLI tool. I'm using the files from the Wireshark repository.

The error message that I'm getting is:
Encountered error while parsing MatchingError(Tag) - Error matching ASN syntax while parsing:SONInformationReply-ExtIEs S1AP-PROTOCOL-EXTENSION

Which appears in the S1AP-IEs.asn file.

Another issue that I get:
Encountered error while parsing MatchingError(Tag) - Error matching ASN syntax while parsing:S1AP-ELEMENTARY-PROCEDURES-CLASS-1 S1AP-ELEMENTARY-PROCEDURE

from S1AP-PDU-Descriptions.asn file.

Thanks for anyone who can help!

Performance: Reduce Clones

Since performance has not been a priority in the compiler's development, the compiler currently clones all over the place. Strings that reference another data type from the module that is compiled are particularly prone to being cloned. These could be easily replaced with Cows or similar borrowing types. Similarly, the map of TopLevelDeclarations is cloned in some places in the validator module.

imports part parsing error

Hi,

I am trying the online tool https://librasn.github.io/, but got an error.

It seems that it does not support multiple FROM in IMPORTS like below:

IMPORTS

	Criticality,
	Presence,
	PrivateIE-ID,
	ProtocolExtensionID,
	ProtocolIE-ID
FROM NGAP-CommonDataTypes

	maxPrivateIEs,
	maxProtocolExtensions,
	maxProtocolIEs
FROM NGAP-Constants;

If I remove the first FROM, then there is no error.
Sample ASN file attached.
ngap_container.zip

Compiler config to disable generation of new method

Hello,

In case of complex and big ASN1 definitions (like the ones from 3gpp) generated new() methods are causing clippy warnings:
"this function has too many arguments"
Having an option to choose whether to generate new or not would solve this kind of issue.

Add a fallible new_with_constraints or similar?

Would there be interest in deriving a function which checks the constraints when constructing an AsnType, such as new_with_constraints() -> Result <_, ConstraintError>? That would allow users to ensure they are not operating with invalid values in their application logic before calling encode.

Derive Default

It would be pretty useful to have an option to instruct the compiler to add the Default trait to the generated binding types, to help out in especially large ASN.1 definitions.
This could be something similar to bindgen's derive_default.

For enums we can default to the first enum choice, for constrained values we can default to the first value in the allowed interval, etc.,

I would be happy to help implement this :). However, the generate() functions currently only use the TLD as an argument, so I'm not sure of the best way to implement this. It should be in a way that allows several options, not only this proposed derive default.

add support for asn type DATE

Hello,

is it possible to support the ASN1-type DATE?
I have a sequence like this and can't generate code with the rasn-compiler.

common DEFINITIONS IMPLICIT TAGS ::=
BEGIN
EXPORTS ALL;
     Subject ::= [1] SEQUENCE {
         subjectName [1] UTF8String OPTIONAL,
         -- YYYY-MM-DD
         dateOfBirth [2] DATE OPTIONAL
    }
END

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.