GithubHelp home page GithubHelp logo

librasn / rasn Goto Github PK

View Code? Open in Web Editor NEW
175.0 7.0 42.0 1.08 MB

A Safe #[no_std] ASN.1 Codec Framework

License: Other

Rust 99.82% Shell 0.18%
asn1 ber cer der parser rust codec encoding-rules encoding-formats asn

rasn's Introduction

Rasn

crates.io Help Wanted Lines Of Code Documentation

Welcome to rasn (pronounced "raisin"), a safe #[no_std] ASN.1 codec framework. That enables you to safely create, share, and handle ASN.1 data types from and to different encoding rules. If you are unfamiliar with ASN.1 and encoding formats like BER/DER, I would recommend reading "A Warm Welcome to ASN.1 and DER" by Let's Encrypt as a quick introduction before continuing. In short it is an "Interface Description Language" (and data model) with a set of encoding formats (called rules) for that model. It was originally designed in the late 1980s and is used throughout the industry especially in telecommunications and cryptography.

Features

Abstract Codec Data Model

There are quite a few existing ASN.1 related Rust crates already, however they are currently specific to a single format or even a single standard, this makes it hard to share and re-use standards that are specified in ASN.1. Now with rasn's abstract model you can build and share ASN.1 data types as crates that work with any encoder or decoder regardless of the underlying encoding rules, whether it's BER, CER, DER, or your own custom encoding.

#[no_std] Support

Rasn is entirely #[no_std], so you can share the same ASN.1 implementation on any Rust target platform that can support alloc.

Rich Data Types

Rasn currently has support for nearly all of ASN.1's data types. rasn uses popular community libraries such as bitvec, bytes, and chrono for some of its data types as well as providing a couple of its own. Check out the types module for what's currently available.

Safe Codecs

The encoder and decoder have been written in 100% safe Rust and fuzzed with American Fuzzy Lop Plus Plus to ensure that the decoder correctly handles random input, and if valid that the encoder can correctly re-encode that value.

Supported Codecs

  • 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)

RFC implementations

Rasn also provides implementations for a number of IETF RFCs using the rasn framework for use out of the box. These crates provide strongly typed definitions for the necessary data types. Like rasn they are #[no_std], as well as being transport layer and encoding rule agnostic.

Powerful Derive Macros

Easily model your structs and enums with derive equivalents of all of the traits. These macros provide a automatic implementation that ensures your model is a valid ASN.1 type at compile-time. To explain that though, first we have to explain…

How It Works

The codec API has been designed for ease of use, safety, and being hard to misuse. The most common mistakes are around handling the length and ensuring it's correctly encoded and decoded. In rasn this is completely abstracted away letting you focus on the abstract model. Let's look at what decoding a simple custom SEQUENCE type looks like.

Person ::= SEQUENCE {
  age INTEGER,
  name UTF8String
}

Which we want to map to the following equivalent Rust code.

struct Person {
    age: rasn::types::Integer,
    name: String, // or rasn::types::Utf8String
}

Implementing The Traits

When modelling an ASN.1 data type, there are three traits we'll need to implement. Decode and Encode for converting to and from encoding rules, and the shared AsnType trait; which defines some associated data needed to be given to the encoder and decoder. Currently the only thing we have define is the tag to use to identify our type.

# struct Person;
use rasn::{AsnType, Tag};

impl AsnType for Person {
    // Default tag for sequences.
    const TAG: Tag = Tag::SEQUENCE;
}

Next is the Decode and Encode traits. These are mirrors of each other and both have one provided method (decode/encode) and one required method (decode_with_tag/encode_with_tag). Since in ASN.1 nearly every type can be implicitly tagged allowing anyone to override the tag associated with the type, having *_with_tag as a required method requires the implementer to correctly handle this case, and the provided methods simply calls *_with_tag with the type's associated AsnType::TAG. Let's look at what the codec implementation of Person looks like.

# use rasn::{AsnType, types::{Constructed, fields::{Field, Fields}}};
# struct Person { name: Utf8String, age: Integer }
# impl AsnType for Person { const TAG: Tag = Tag::SEQUENCE; }
# impl Constructed for Person {
#     const FIELDS: Fields = Fields::from_static(&[
#          Field::new_required(Utf8String::TAG, Utf8String::TAG_TREE, "age"),
#          Field::new_required(Integer::TAG, Integer::TAG_TREE, "name"),
#     ]);
# }
use rasn::{prelude::*, types::{Integer, Utf8String}};

impl Decode for Person {
    fn decode_with_tag_and_constraints<D: Decoder>(decoder: &mut D, tag: Tag, constraints: Constraints) -> Result<Self, D::Error> {
        // Accepts a closure that decodes the contents of the sequence.
        decoder.decode_sequence(tag, None::<fn () -> Self>, |decoder| {
            let age = Integer::decode(decoder)?;
            let name = Utf8String::decode(decoder)?;
            Ok(Self { age, name })
        })
    }
}

impl Encode for Person {
    fn encode_with_tag_and_constraints<E: Encoder>(&self, encoder: &mut E, tag: Tag, constraints: Constraints) -> Result<(), E::Error> {
        // Accepts a closure that encodes the contents of the sequence.
        encoder.encode_sequence::<Self, _>(tag, |encoder| {
            self.age.encode(encoder)?;
            self.name.encode(encoder)?;
            Ok(())
        })?;

        Ok(())
    }
}

That's it! We've just created a new ASN.1 that be encoded and decoded to BER, CER, and DER; and nowhere did we have to check the tag, the length, or whether the string was primitive or constructed encoded. All those nasty encoding rules details are completely abstracted away so your type only has handle how to map to and from ASN.1's data model.

With all the actual conversion code isolated to the codec implementations you can know that your model is always safe to use. The API has also been designed to prevent you from making common logic errors that can lead to invalid encoding. For example; if we look back our Encode implementation, and what if we forgot to use the encoder we were given in encode_sequence and tired to use the parent instead?

error[E0501]: cannot borrow `*encoder` as mutable because previous closure requires unique access
   --> tests/derive.rs:122:9
    |
122 |           encoder.encode_sequence(tag, |sequence| {
    |           ^       ---------------      ---------- closure construction occurs here
    |           |       |
    |  _________|       first borrow later used by call
    | |
123 | |             self.age.encode(encoder)?;
    | |                             ------- first borrow occurs due to use of `encoder` in closure
124 | |             self.name.encode(sequence)?;
125 | |             Ok(())
126 | |         })?;
    | |__________^ second borrow occurs here

error[E0500]: closure requires unique access to `encoder` but it is already borrowed
   --> tests/derive.rs:122:38
    |
122 |         encoder.encode_sequence(tag, |sequence| {
    |         ------- ---------------      ^^^^^^^^^^ closure construction occurs here
    |         |       |
    |         |       first borrow later used by call
    |         borrow occurs here
123 |             self.age.encode(encoder)?;
    |                             ------- second borrow occurs due to use of `encoder` in closure

Our code fails to compile! Which, in this case is great, there's no chance that our contents will accidentally be encoded in the wrong sequence because we forgot to change the name of a variable. These ownership semantics also mean that an Encoder can't accidentally encode the contents of a sequence multiple times in their implementation. Let's see how we can try to take this even further.

Compile-Safe ASN.1 With Macros

So far we've shown how rasn's API takes steps to be safe and protect from accidentally creating an invalid model. However, it's often hard to cover everything in an imperative API. Something that is important to understand about ASN.1 that isn't obvious in the above examples is that; in ASN.1, all types can be identified by a tag (essentially two numbers e.g. INTEGER's tag is 0, 2). Field and variant names are not transmitted in most encoding rules, so this tag is also used to identify fields or variants in a SEQUENCE or CHOICE. This means that every that in a ASN.1 struct or enum every field and variant must have a distinct tag for the whole type to be considered valid. For example ; If we changed age in Person to be a String like below it would be invalid ASN.1 even though it compiles and runs correctly, we have to either use a different type or override age's tag to be distinct from name's. When implementing the AsnType trait yourself this requirement must checked by manually, however as we'll see you generally won't need to do that.

Included with rasn is a set of derive macros that enable you to have your ASN.1 model implementation implemented declaratively. The Encode and Decode macros will essentially auto-generate the implementations we showed earlier, but the real magic is the AsnType derive macro. Thanks to the static-assertations crate and recent developments in const fn; the AsnType derive will not only generate your AsnType implementation, it will also generate a check that asserts that every field or variant has a distinct tag at compile-time. This means now if for some reason we made a change to one of the types in person, we don't have re-check that our model is still valid, the compiler takes care of that for us.

// Invalid
#[derive(rasn::AsnType)]
struct Person {
    age: Option<String>,
    name: Option<String>,
}

We'll now get the following error trying to compile the above definition.

error[E0080]: evaluation of constant value failed
   --> tests/derive.rs:146:10
    |
146 | #[derive(rasn::AsnType)]
    |          ^^^^^^^^^^^^^ the evaluated program panicked at 'Person's fields is not a valid order of ASN.1 tags, ensure that your field's tags and OPTIONAL
s are correct.', tests/derive.rs:146:10
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)

Validating your model at compile-time enables you to work on ASN.1 code without fear that you're unintentionally changing something in the background. I bet you're wondering now though, how we are supposed to have a struct with two strings for fields? The answer is thankfully pretty simple, you just add #[rasn(tag)] attribute to override the tags of one or more of the types. However we can actually go further, because in ASN.1 there's the concept of having AUTOMATIC TAGS which essentially tells your ASN.1 compiler to automatically generate distinct tags for your ASN.1 definition. Now with rasn you can do that in Rust! Applying #[rasn(automatic_tags)] to the container automatically generate tags will apply the same automatic tagging transformation you'd expect from an ASN.1 compiler.

use rasn::AsnType;

// Valid
#[derive(AsnType)]
struct Person {
    #[rasn(tag(context, 0))] // or just #[rasn(tag(0))]
    age: Option<String>,
    name: Option<String>,
}

// Also valid
#[derive(AsnType)]
#[rasn(automatic_tags)]
struct Person2 {
    age: Option<String>,
    name: Option<String>,
}

Reference

The following table provides a range of examples showing how to declare data types with rasn.

ASN1 rasn
Type alias
Test-type-b ::= BOOLEAN
Test-type-a ::= Test-type-b
// either
use rasn::prelude::*;

type TestTypeB = bool;

type TestTypeA = TestTypeB;
// or 
use rasn::prelude::*;

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeB(pub bool);

/// or
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub TestTypeB);
BOOLEAN type
Test-type-a ::= BOOLEAN
// either
use rasn::prelude::*;

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub bool);
// or
use rasn::prelude::*;

type TestTypeA = bool;
NULL type
Test-type-a ::= NULL
// either
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
struct TestTypeA;
// or
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(());
// or
use rasn::prelude::*;

type TestTypeA = ();
INTEGER type
Test-type-a ::= INTEGER
use rasn::prelude::*;
// either
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub u8 /* or any other rust integer type */);
// or
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub Integer);
// or
use rasn::prelude::*;
type TestTypeA = Integer;
// or
use rasn::prelude::*;
type TestTypeA = u8; // or any other rust integer type
Single-value contraint
Test-type-a ::= INTEGER (8)
// either
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("8"))]
struct TestTypeA(pub u8);
// or
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("8"))]
struct TestTypeA(pub Integer);
Value range constraint
Test-type-a ::= INTEGER (-8..360)
Test-type-b ::= INTEGER (MIN..360)
Test-type-c ::= INTEGER (42..MAX)
use rasn::prelude::*;
/// of course a primitive rust integer would still work in these examples
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("-8..=360"))]
struct TestTypeA(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("..=360"))]
struct TestTypeB(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("42..="))]
struct TestTypeC(pub Integer);
Extensible value constraint
Test-type-a ::= INTEGER (42,...)
Test-type-b ::= INTEGER (1..360,...)
use rasn::prelude::*;
/// of course a primitive rust integer would still work in these examples
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("42", extensible))]
struct TestTypeA(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("1..=360", extensible))]
struct TestTypeB(pub Integer);
ENUMERATED type
Test-type-a ::= ENUMERATED { seed, grape, raisin }
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)] /// See below
enum TestTypeA {
    Seed,
    Grape,
    Raisin
}
Extensible ENUMERATED type
Test-type-a ::= ENUMERATED { seed, grape, ..., raisin }
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)] /// See below
#[non_exhaustive]
enum TestTypeA {
    Seed,
    Grape,
    #[rasn(extension_addition)]
    Raisin
}
AUTOMATIC TAGS environment
TestModule DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
Test-type-a ::= ENUMERATED { seed, grape, raisin }
Test-type-b ::= ENUMERATED { juice, wine, grappa }
END
use rasn::prelude::*;
/// The tagging encironment has to be declared for every rasn-annotated struct or enum
/// There is no implicit extensibility
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)]
enum TestTypeB {
    Juice,
    Wine,
    Grappa
}

#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)]
enum TestTypeA {
    Seed,
    Grape,
    Raisin
}
EXPLICIT TAGS environment
TestModule DEFINITIONS EXPLICIT TAGS ::=
BEGIN
Test-type-a ::= [APPLICATION 1] ENUMERATED { seed, grape, raisin }
Test-type-b ::= [APPLICATION 2] ENUMERATED { juice, wine, grappa }
END
use rasn::prelude::*;
/// The tagging encironment has to be declared for every rasn-annotated struct or enum
/// There is no implicit extensibility
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(explicit(application, 1)))]
enum TestTypeB {
    #[rasn(tag(explicit(0)))]
    Juice,
    #[rasn(tag(explicit(1)))]
    Wine,
    #[rasn(tag(explicit(2)))]
    Grappa
}

#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(explicit(application, 2)))]
enum TestTypeA {
    #[rasn(tag(explicit(0)))]
    Seed,
    #[rasn(tag(explicit(1)))]
    Grape,
    #[rasn(tag(explicit(2)))]
    Raisin
}
IMPLICIT TAGS environment
TestModule DEFINITIONS IMPLICIT TAGS ::=
BEGIN
Test-type-a ::= [APPLICATION 1] ENUMERATED { seed, grape, raisin }
Test-type-b ::= [APPLICATION 2] ENUMERATED { juice, wine, grappa }
END
use rasn::prelude::*;
/// The tagging encironment has to be declared for every rasn-annotated struct or enum
/// There is no implicit extensibility
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(application, 1))]
enum TestTypeB {
    Juice = 0,
    Wine = 1,
    Grappa = 2
}

#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(application, 2))]
enum TestTypeA {
    Seed = 0,
    Grape = 1,
    Raisin = 2
}
CHOICE type
Test-type-a ::= CHOICE {
    seed BOOLEAN,
    grape BIT STRING SIZE(1,...),
    raisin OCTET STRING
}

Test-type-b ::= CHOICE { 
    juice INTEGER (0..3,...), 
    wine OCTET STRING,
    ...,
    grappa INTEGER 
}
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(choice, automatic_tags)]
enum TestTypeA {
    Seed(bool),
    #[rasn(size("1", extensible))]
    Grape(BitString),
    Raisin(OctetString)
}

#[derive(AsnType, Decode, Encode)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
enum TestTypeB {
    #[rasn(value("0..3", extensible))]
    Juice(Integer),
    Wine(OctetString),
    #[rasn(extension_addition)]
    Grappa(Integer)
}
SEQUENCE type
Test-type-a ::= SEQUENCE { 
    juice INTEGER (0..3,...), 
    wine OCTET STRING,
    ...,
    grappa INTEGER OPTIONAL,
    water BIT STRING (SIZE(1)) OPTIONAL
}
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(automatic_tags)]
#[non_exhaustive]
struct TestTypeA {
    #[rasn(value("0..3", extensible))]
    juice: Integer,
    wine: OctetString,
    #[rasn(extension_addition)]
    grappa: Option<Integer>,
    #[rasn(extension_addition, size("1"))]
    water: Option<BitString>
}
SET type
Test-type-a ::= SET { 
    seed NULL,
    grape BOOLEAN,
    raisin INTEGER
}
use rasn::prelude::*;
/// the SET declaration is basically identical to a SEQUENCE declaration, 
/// except for the `set` annotation
#[derive(AsnType, Decode, Encode)]
#[rasn(set, automatic_tags)]
struct TestTypeA {
    seed: (),
    grape: bool,
    raisin: Integer
}
Renaming fields
Test-type-a ::= SEQUENCE { 
    notQuiteRustCase INTEGER
}
use rasn::prelude::*;

#[derive(AsnType, Decode, Encode)]
#[rasn(automatic_tags, identifier = "Test-type-a")]
struct TestTypeA {
    #[rasn(identifier = "notQuiteRustCase")]
    rust_case_indeed: Integer
}
OPTIONAL and DEFAULT fields
Test-type-a ::= SEQUENCE { 
    seed BOOLEAN DEFAULT TRUE,
    grape INTEGER OPTIONAL,
    raisin INTEGER DEFAULT 1
}
use rasn::prelude::*;
/// DEFAULTs are provided via linked helper functions
#[derive(AsnType, Decode, Encode)]
#[rasn(automatic_tags)]
struct TestTypeA {
    #[rasn(default = "default_seed")] 
    seed: bool,
    grape: Option<Integer>,
    #[rasn(default = "default_raisin")]
    raisin: Integer
}

fn default_seed() -> bool {
    true
}

fn default_raisin() -> Integer {
    1.into()
}
SEQUENCE OF type
Test-type-a ::= SEQUENCE OF BOOLEAN
Test-type-b ::= SEQUENCE OF INTEGER(1,...)
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub SequenceOf<bool>);

/// Constrained inner primitive types need to be wrapped in a helper newtype
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("1", extensible))]
struct InnerTestTypeB(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeB(pub SequenceOf<InnerTestTypeB>);
Character string types
Test-type-a ::= UTF8String
use rasn::prelude::*;
/// the other charater types supported by rasn behave exactly the same:
/// NumericString, VisibleString, Ia5String, TeletexString, GeneralString, BmpString, PrintableString
/// (and also for BIT STRING and OCTET STRING)
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub Utf8String);
BIT STRING type
Test-type-a ::= BIT STRING
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub BitString);
OCTET STRING type
Test-type-a ::= OCTET STRING
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub OctetString);
Size contraint
Test-type-a ::= UTF8String (SIZE (42,...))
Test-type-b ::= SEQUENCE (SIZE (1..8)) OF BOOLEAN
use rasn::prelude::*;
/// The size constraint definition behaves similar to the value definition (see above)
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, size("42", extensible))]
struct TestTypeA(pub Utf8String);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, size("1..=8"))]
struct TestTypeB(pub SequenceOf<bool>);
Permitted alphabet contraint
Test-type-a ::= UTF8String (FROM ("A".."Z"))
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, from("\u{0041}..\u{005A}"))]
struct TestTypeA(pub Utf8String);

Sponsorship

This project was funded through the NGI Assure Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 957073.

Disclaimer

The software is provided "as is" and the authors disclaim all warranties with regard to this software including all implied warranties of merchant-ability and fitness. In no event shall the authors be liable for any special, direct, indirect, or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortuous action, arising out of or in connection with the use or performance of this software.

rasn's People

Contributors

6d7a avatar ancwrd1 avatar anthok avatar benmaddison avatar chrisgreenaway avatar chrismoos avatar curtisleefulton avatar dvc94ch avatar edarc avatar frankhorv avatar github-actions[bot] avatar hcldan avatar jsatchell avatar makavity avatar masihyeganeh avatar mds5000 avatar memoryruins avatar messense avatar nicceboy avatar orthecreedence avatar repnop avatar rodrimati1992 avatar shortbus-rider avatar trolldemorted avatar wiktor-k avatar x-delfino avatar xampprocky avatar yerke avatar zaczkows avatar zkonge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

rasn's Issues

Can't parse ContentInfo.

I have some data which is supposed to be a ContentInfo (received from AWS KMS), but the following code fails:

let data: Vec<u8> = vec![ 48, 128, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 3, 160, 128, 48, 128, 2, 1, 2, 49, 130, 1, 107, 48, 130, 1, 103, 2, 1, 2, 128, 32, 212, 17, 115, 195, 21, 153, 39, 196, 140, 239, 85, 90, 110, 51, 220, 56, 29, 10, 60, 34, 101, 42, 71, 97, 142, 255, 112, 248, 99, 5, 201, 148, 48, 60, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 7, 48, 47, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 4, 130, 1, 0, 17, 238, 11, 93, 44, 255, 28, 111, 215, 103, 19, 197, 237, 193, 145, 116, 70, 103, 208, 137, 8, 71, 74, 138, 141, 169, 169, 238, 40, 84, 122, 21, 216, 49, 63, 90, 105, 66, 69, 178, 78, 28, 66, 236, 23, 81, 214, 78, 0, 210, 182, 109, 105, 36, 16, 214, 250, 246, 112, 244, 33, 164, 75, 118, 120, 18, 29, 71, 174, 23, 38, 95, 37, 244, 10, 75, 229, 177, 41, 169, 74, 68, 183, 204, 187, 113, 244, 225, 65, 222, 187, 86, 224, 102, 147, 85, 248, 213, 228, 192, 93, 127, 5, 184, 123, 196, 165, 110, 16, 248, 25, 242, 194, 38, 156, 219, 100, 43, 167, 72, 184, 66, 34, 225, 225, 216, 104, 156, 247, 26, 150, 12, 151, 202, 76, 173, 179, 197, 118, 217, 109, 245, 52, 171, 99, 177, 28, 138, 137, 206, 246, 175, 166, 254, 119, 86, 66, 141, 227, 120, 66, 76, 120, 93, 186, 204, 38, 91, 48, 34, 107, 23, 191, 92, 251, 246, 248, 112, 227, 238, 122, 165, 22, 18, 94, 232, 208, 209, 129, 1, 6, 28, 235, 238, 198, 204, 221, 234, 17, 111, 190, 163, 148, 215, 140, 71, 132, 48, 37, 169, 178, 25, 196, 20, 9, 240, 39, 137, 22, 40, 71, 155, 200, 59, 244, 156, 220, 212, 154, 94, 211, 132, 198, 195, 245, 49, 89, 152, 238, 178, 200, 226, 193, 145, 28, 175, 31, 179, 137, 30, 168, 96, 210, 208, 54, 181, 48, 128, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 1, 48, 29, 6, 9, 96, 134, 72, 1, 101, 3, 4, 1, 42, 4, 16, 171, 84, 103, 126, 243, 106, 225, 202, 106, 15, 104, 146, 100, 201, 219, 97, 160, 128, 4, 48, 75, 109, 64, 64, 47, 137, 225, 83, 92, 88, 253, 78, 185, 232, 53, 253, 41, 237, 32, 149, 221, 227, 20, 25, 156, 80, 137, 130, 53, 140, 158, 244, 110, 86, 121, 214, 90, 195, 21, 63, 0, 86, 150, 198, 189, 2, 120, 239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
let _content_info: rasn_cms::ContentInfo = rasn::ber::decode(&*data).unwrap();

with the error:

panicked at 'called `Result::unwrap()` on an `Err` value: FieldError { name: "ContentInfo.content", error: "Error in Parser: Parsing Failure: Parsing Error: Error { input: [2, 1, 2, 49, 130, 1, 107, ... , 0, 0], code: Tag }" }'

However, if I define a new struct:

#[derive(rasn::AsnType, Clone, Debug, rasn::Decode, rasn::Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MyContentInfo {
    pub content_type: rasn_cms::ContentType,
    #[rasn(tag(explicit(0)))]
    pub content: rasn_cms::EnvelopedData,
}

then I can decode to that fine. The only change is changing the type of content from Any to EnvelopedData.

microsoft seems to use a different EncapsulatedContentInfo struct

microsoft msix and appx packages contain a CodeIntegrity.cat and AppxSignature.p7x file, which while undocumented seems to be cms encoded SignedData. however it seems like it's not quite spec compliant and maybe has an extra field or something.

thread 'test_msix_p7x' panicked at 'called `Result::unwrap()` on an `Err` value: FieldError { name: "SignedData.encap_content_info", error: "Field `EncapsulatedContentInfo.content`: Unexpected extra data found: length `264` bytes" }', standards/cms/tests/test_cms.rs:44:62
thread 'test_msix_cat' panicked at 'called `Result::unwrap()` on an `Err` value: FieldError { name: "SignedData.encap_content_info", error: "Field `EncapsulatedContentInfo.content`: Unexpected extra data found: length `1303` bytes" }', standards/cms/tests/test_cms.rs:58:62

replacing content with Any works.

Recent commit causing choice errors

The commit 3d94b96 is causing some issues with choices, on that commit most of my unit tests are failing with Invalid `CHOICE` discriminant." error. Does that commit require some changes to the way choices are used?

BACnet encoding

BACnet uses ASN.1 to encode it's datastructures. It also defines an encoding that is similar but not the same as the standard BER encoding.

The following are the main difference I identified between the BACnet Encoding and BER:

  • While BACnet also uses a TLV encoding simular to BER, it uses a different header:
|------|------|------|--------|------|------|------|------|
|   0  |   1  |  2   |   3    |   4  |  5   |  6   |    7 |
|------|------|------|--------|------|------|------|------|
|  Tag Number                 |Class |Length/Value/Type   |
|------|------|------|--------|------|------|------|------|
  • Because of the above header, the class attribute can is only one bit. This means the standard only supports context and application tagging.
  • A list off application specific datatypes is defined in the standard:
0 = Null
1 = Boolean
2 = Unsigned Integer
3 = Signed Integer (2's complement notation)
4 = Real (ANSI/IEEE-754 floating point)
5 = Double (ANSI/IEEE-754 double precision floating point)
6 = Octet String
7 = Character String
8 = Bit String
9 = Enumerated
10 = Date
11 = Time
12 = BACnetObjectIdentifier

These are used instead of the universal ones.

I see the following possible ways of implemementing this:

  1. Add the encoding as an option to the current BER encoding, similart to CER and DER.
  2. Add it as a completely new encoding alongside the current BER encoding.
  3. Implement it in a spearate crate.

Currently I'm playing around with the code to see if option 1 or 2 is a better fit.

@XAMPPRocky I would like to get your opinion which option you think is preferable? Specifically I would like to know if you see the BACnet encoding as part of this repo, or if I should go with a separate one.

rasn-pkix release for pr #84?

Any chance we can get a release for rasn-pkix that includes pr #84? Latest for crates.io still has 0.5.2, which doesn't include #84 .

My devs will thank you :)

best,

Curtis

Add Python Bindings

I'd like to be able to provide easy integration between rasn and python code, so I'd like to add a feature to the macros to have them also generate PyO3 bindings, and provide these bindings for the crates in the repository. My hope is that all that should be required is adding #[pyclass], and adding a decode and encode #[pymethod] functions that accept a enum (enum Codec { Ber, Der, Cer }, I know python doesn't have enums like rust but you know what I mean) and call the appropriate encode/decode Rust functions depending on the Codec.

I don't have time to work on this in the near future, but I would be able to help and review a PR to add it, so if anyone is interested in adding this themselves feel free to post in the issue.

Missing `SetOf` implementation

Hello,

I'm trying to implement very ASN.1 schema from RFC7030:

 CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID

   AttrOrOID ::= CHOICE (oid OBJECT IDENTIFIER, attribute Attribute }

   Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
        type   ATTRIBUTE.&id({IOSet}),
        values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type}) }

I created rust code which tries to encode/decode it:

#[derive(rasn::AsnType, rasn::Decode, rasn::Encode, Debug, PartialEq, Clone)]
struct CsrAttrs {
    attr_or_oid: Vec<AttrOrOid>,
}

#[derive(rasn::AsnType, rasn::Decode, rasn::Encode, Debug, PartialEq, Clone)]
#[rasn(choice)]
enum AttrOrOid {
    OID(rasn::types::ObjectIdentifier),
    ATTRIBUTE(Attribute),
}

#[derive(rasn::AsnType, rasn::Decode, rasn::Encode, Debug, PartialEq, Clone)]
struct Attribute {
    r#type: rasn::types::ObjectIdentifier,
    values: rasn::types::SetOf<rasn::types::Open>,
}

#[derive(rasn::AsnType, rasn::Decode, rasn::Encode, Debug, PartialEq, Clone)]
struct AttributeValue(Vec<u8>);

fn main() {}

However, I got compilation error, as it looks like the SetOf is not fully implemented:

error[E0277]: the trait bound `BTreeSet<Open>: Decode` is not satisfied
  --> src/main.rs:23:25
   |
23 | #[derive(rasn::AsnType, rasn::Decode, rasn::Encode, Debug, PartialEq, Clone)]
   |                         ^^^^^^^^^^^^ the trait `Decode` is not implemented for `BTreeSet<Open>`
   |
   = note: required by `decode_with_tag`
   = note: this error originates in the derive macro `rasn::Decode` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: no method named `encode` found for struct `BTreeSet` in the current scope
  --> src/main.rs:23:39
   |
23 | #[derive(rasn::AsnType, rasn::Decode, rasn::Encode, Debug, PartialEq, Clone)]
   |                                       ^^^^^^^^^^^^ method not found in `BTreeSet<Open>`
   |
   = note: this error originates in the derive macro `rasn::Encode` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

rasn-kerberos: Optional but not Empty

In the Kerberos Spec (RFC 4120) - there are a few instances of OPTIONAL fields that cannot be empty, eg. padata:

KDC-REQ         ::= SEQUENCE {
        -- NOTE: first tag is [1], not [0]
        pvno            [1] INTEGER (5) ,
        msg-type        [2] INTEGER (10 -- AS -- | 12 -- TGS --),
        padata          [3] SEQUENCE OF PA-DATA OPTIONAL
                            -- NOTE: not empty --,
        req-body        [4] KDC-REQ-BODY
}

In the rasn implementation, it looks like OPTIONAL fields with a None value are added with zero-length instead of being omitted.

Eg, if we look at the encoding of:

KDC-REQ         ::= SEQUENCE {
        pvno            [1] INTEGER = 5,
        msg-type        [2] INTEGER = 10,
        padata          [3] SEQUENCE OF PA-DATA = None,
        req-body        [4] KDC-REQ-BODY = ...
}

We get 30, 81, DF, A1, 03, 02, 01, 05, A2, 03, 02, 01, 0A, A3, 00, A4 ... (bear in mind that I've changed the context-specific tags in rasn-kerberos to explicit as per conversation in #69). Breaking that down for clarity:

30, 81, DF = KDC-REQ tag & length,
A1, 03, 02, 01, 05 = pvno,
A2, 03, 02, 01, 0A = msg-type,
A3, 00 = padata (SHOULD BE OMITTED),
A4 ... = req-body

Additionally, it seems like the Windows kerberos implementation doesn't like empty OPTIONAL fields even if they're not marked as such in the RFC eg. some of the time fields.

        from                    [4] KerberosTime OPTIONAL,
        till                    [5] KerberosTime,
        rtime                   [6] KerberosTime OPTIONAL,

Is there functionality within rasn to support this scenario? Let me know if there are any other details that would be helpful here.

Non-exhaustive LDAP structs

Most of the top-level rasn-ldap structs are declared with non-exhaustive attribute which prevents them from being constructed outside of the rasn-ldap crate.
How is the LdapMessage supposed to be constructed for encoding?

new release

it's been a while and there are some juicy bugfixes. would be nice to get a new release

Map Parameters to Generics

Currently the compiler does not support parameterisation which prevents it from being able to support many modern ASN.1 modules. These should be translated into type or const generics in Rust.

Compiler Codegen API

Similar to #101 ideally we’d have a public codegen API that allows the compiler to generate languages other than Rust, and we’d have an API for generating ASN.1 notation back from Rust code so that compiler can be integrated with the derive macros to enable generating ASN.1 modules from derived code.

Use `const-oid` crate?

Hi, a friend recently made me aware of the const-oid crate which implements const fn new on the type to allow compile-time parsing and construction of the OID from the textual representation, which I think would be quite useful to have re-exported & the Decode and Encode traits implemented for. As far as I can see, the only limitation is that it has a fixed-size internal representation which limits the lengths of the OID values (in bytes), but I'm not sure how much of an issue this would be in practice (it seems most, if not all, of the OIDs that I work with at my job are well below the current limit of 23 bytes for the OID, at least). I would imagine this would likely replace the current ConstOid type in rasn, or at least be available as an opt-in feature. Thoughts?

Implicitly tagged sequences should be constructed

Terrific library, and I'm trying to use it to implement a rust SNMP library. However, I've run into an issue that I believe isn't handled correctly. I'm no expert with BER encodings, so may be mistaken.

Per X.690 (2008):

8.14.4 If implicit tagging was used in the definition of the type, then:
a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise;
and
b) the contents octets shall be the same as the contents octets of the base encoding.

To me this would imply that if I implicitly tagged a sequence, the 'constructed' bit of the tag should be set (carried over from the base encoding). And indeed if I try this in a test case I get the following:

#[test]
    fn implicit_tagged_constructed() {
        use crate::{tag::Class, types::Implicit, AsnType, Tag};
        struct C0;
        impl AsnType for C0 {
            const TAG: Tag = Tag::new(Class::Context, 0);
        }

        let value = <Implicit<C0, _>>::new(vec![1, 2]);
        let data = &[0xA0, 6, 2, 1, 1, 2, 1, 2][..];

        assert_eq!(data, &*crate::ber::encode(&value).unwrap());
        // thread 'ber::tests::implicit_tagged_constructed' panicked at 'assertion failed: `(left == right)`
        //   left: `[160, 6, 2, 1, 1, 2, 1, 2]`,
        //. right: `[128, 6, 2, 1, 1, 2, 1, 2]`'
}

Happy to submit a PR if you agree this is non-conforming to the spec.

Treat newtypes as passthrough containers when encoding/decoding

Hello! I'm just starting to use this project and it's everything I want in a DER encoder/decoder (especially the derive macros, which are incredibly helpful). One thing I'm wondering if is there's a built-in way to have something like this:

#[derive(Debug, AsnType, Encode, Decode)]
struct SignatureWrapper(Bytes);

#[derive(Debug, AsnType, Encode, Decode)]
#[rasn(choice)]
enum Signature {
    #[rasn(tag(0))]
    Ed25519(SignatureWrapper),
}

And encode it as if SignatureWrapper was just a Bytes. Right now, it seems to wrap it. Is there a #[rasn(...)] attribute that can do this, or would this be something I'd need to open a PR for? Thank you!

asn message with choice and tag is not encoded correctly

hello, here is my asn file:

World-Schema DEFINITIONS AUTOMATIC TAGS ::= 
BEGIN
Request ::= SEQUENCE {
  num INTEGER
}
Response ::= SEQUENCE {
  ret INTEGER
}     
BODY ::= CHOICE { 
  request [3000] Request,
  response [3001] Response
}                 
Message ::= SEQUENCE {
  id   INTEGER, 
  body BODY
}                              
END

and message with value

value Message ::= {
  id 1,
  body request : {
    num 1
  }
}

I have tested this on https://asn1.io/asn1playground/Default.aspx and the message is encoded as:
0101BF9738 0101

here is my rust code using rasn

use rasn::AsnType;
use rasn::Decode;
use rasn::Encode;
use std::error::Error;

#[derive(Debug, AsnType, Decode, Encode)]
struct Message {
    id: i32,
    #[rasn(choice)]
    body: BODY,
}

#[derive(Debug, AsnType, Decode, Encode)]
#[rasn(choice)]
enum BODY {
    #[rasn(tag(context, 3000))]
    Request(Request),

    #[rasn(tag(context, 3001))]
    Response(Response),
}

#[derive(Debug, AsnType, Decode, Encode)]
struct Request {
    num: i32,
}

#[derive(Debug, AsnType, Decode, Encode)]
struct Response {
    ret: i32,
}

fn main() -> Result<(), Box<dyn Error>> {
    let p = Message {
        id: 1,
        body: BODY::Request(Request { num: 1 }),
    };
    let e = rasn::der::encode(&p).unwrap();
    println!("e: {:#04x?}", e);
    Ok(())
}

and it outputs
0x30,0x0a,0x02,0x01,0x01,0xbf,0xb8,0x17,0x03,0x02,0x01,0x01,

the big difference is the body choice on https://asn1.io/asn1playground/Default.aspx is decoded as BF9738 but using rasn decoded as BFB817.

Error with .map_err(error::map_nom_err)?; at several places

E.g. at /src/ber/de.rs:60:70 I get the error:

    --> /home/sivizius/.cargo/registry/src/github.com-1ecc6299db9ec823/rasn-0.2.0/src/ber/de.rs:60:70
   |
60 |             self::parser::parse_identifier_octet(self.input).map_err(error::map_nom_err)?;
   |                                                                      ^^^^^^^^^^^^^^^^^^ expected signature of `fn(nom::Err<nom::error::Error<&[u8]>>) -> _`
   | 
  ::: /home/sivizius/.cargo/registry/src/github.com-1ecc6299db9ec823/rasn-0.2.0/src/ber/de/error.rs:20:1
   |
20 | pub(crate) fn map_nom_err(error: nom::Err<(&[u8], nom::error::ErrorKind)>) -> Error {
   | ----------------------------------------------------------------------------------- found signature of `for<'r> fn(nom::Err<(&'r [u8], nom::error::ErrorKind)>) -> _`

similar errors at other places, where .map_err(error::map_nom_err)? is used.

Optional Choice causing errors

Unit test that shows error

Error : Custom { msg: "Encoding field of type `OptionalChoice`: CHOICE-style enums do not allow implicit tagging. If using the derive macro on a sequence with a choice field ensure it is marked with `#[rasn(choice)]`."

use rasn::{types::*, Decode, Encode};

#[derive(AsnType, Decode, Encode, Debug, PartialEq, Clone)]
pub struct OptionTest {
    #[rasn(tag(0))]
    pub regular: rasn::types::Integer,

    #[rasn(tag(1))]
    pub optional: Option<OptionalChoice>,
}

#[derive(AsnType, Decode, Encode, Debug, PartialEq, Clone)]
#[rasn(choice)]
pub enum OptionalChoice {
    #[rasn(tag(0))]
    Test(rasn::types::Integer),
}

#[test]
fn it_works() {
    let data = OptionTest {
        regular: 0.into(),
        optional: Some(OptionalChoice::Test(0.into())),
    };
    let bin = rasn::ber::encode(&data).unwrap();
    assert_eq!(data, rasn::ber::decode(&bin).unwrap());
}

Implicit string value is private

When you have a Implicit string like below, it's not possible to access the value by itself because value is private.
Implicit { _tag: PhantomData, value: "TestString" }

error[E0616]: field `value` of struct `Implicit` is private
    |             println!("{:?}", obj.value);
    |                                  ^^^^^ private field

`rasn_snmp::v2::IpAddress` allows for !=4 byte IPs whereas the RFC does not

Section 3 of the Version 2 of the Protocol Operations for the Simple Network Management Protocol (SNMP) RFC specifies IpAddress as the following:
IpAddress ::= [APPLICATION 0] IMPLICIT OCTET STRING (SIZE (4)) which only allows 4-byte IPv4 addresses, whereas rasn_snmp::v2::IpAddress allows for arbitrarily sized, malformed IPs:

#[test]
fn bad_ip_address() {
    let ip = IpAddress {
        0: OctetString::from_static(&[255, 255, 255]),
    };
    assert_eq!(
        rasn::ber::decode::<IpAddress>(&rasn::ber::encode(&ip).unwrap()).unwrap(),
        ip
    );
}

Also unrelated, but I think the type alias for rasn_smi::v2::Integer32 is incorrect as well, its currently:

pub type Integer32 = u32;

but I think it should be

pub type Integer32 = i32;

since the unsigned 32-bit integer type is already covered by Gauge and by type alias Unsigned32

SetOf without sorting

Hello!
In my project, I faced with some suspicious behaviour SetOf decoding.

I have correct cms structure, and I'm trying to get SignerInfo.signed_attrs as bytes.
CMS Structure decoded correctly

My code for decode:

#[derive(AsnType, Decode, Encode)]
#[derive(Debug)]
struct StbStruct {
    oid: ObjectIdentifier,
    #[rasn(tag(0))]
    data: SequenceOf<SignedData>,
}
fn verify_cms(structure: &SignedData) -> bool {
    let data = structure.encap_content_info.content.as_ref().to_vec();

    let certset = structure.certificates.as_ref().unwrap();
    let mut key: Vec<u8> = vec![];
    for cert in certset {
        if let CertificateChoices::Certificate(c) = cert {
            key = c.tbs_certificate.subject_public_key_info.subject_public_key.clone().into_vec();
        }
    }
    let signersset = &structure.signer_infos;
    for signer in signersset {
        let mut signed_attrs = signer.signed_attrs.clone().unwrap();


        let mut signed_attrs_der = rasn::der::encode(&signed_attrs).unwrap();
        println!("{:02X?}", signed_attrs_der);
    }
    // correct setof from lapoit
    let mut signed_attrs_der = vec![
        0x31, 0x69, 0x30, 0x18, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x03, 0x31, 0x0B, 0x06, 0x09, 0x2A,     0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
        0x30, 0x1C, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x05, 0x31, 0x0F, 0x17, 0x0D, 0x32, 0x32, 0x30,     0x33, 0x31, 0x31, 0x31, 0x31, 0x32, 0x38, 0x30, 0x34, 0x5A,
        0x30, 0x2F, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04, 0x31, 0x22, 0x04, 0x20, 0x8D, 0xA7, 0x88,     0xFB, 0xD0, 0xC5, 0x7D, 0x46, 0x9B, 0x39, 0x20, 0x7B, 0x68, 0x95, 0xF0, 0x05, 0x77, 0xCB, 0x78, 0x77, 0xA1, 0x43, 0x72, 0xC2, 0x8C, 0xDF, 0x6D, 0x9B, 0x9C, 0x49, 0xE1, 0x25
    ];
    println!("{:02X?}", signed_attrs_der);

    true
}
fn main() {
    let cms_bytes = base64::decode("MIIFlQYJK...").unwrap();
    let d: StbStruct = rasn::der::decode(&cms_bytes).unwrap();
    let signeddata = d.data.first().unwrap();
    println!("{:?}", verify_cms(&signeddata));
}

And from my decode i am getting sorted set:

[31, 69, 30, 18, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 03, 31, 0B, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 07, 01, 30, 2F, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 04, 31, 22, 04, 20, 8D, A7, 88, FB, D0, C5, 7D, 46, 9B, 39, 20, 7B, 68, 95, F0, 05, 77, CB, 78, 77, A1, 43, 72, C2, 8C, DF, 6D, 9B, 9C, 49, E1, 25, 30, 1C, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 05, 31, 0F, 17, 0D, 32, 32, 30, 33, 31, 31, 31, 31, 32, 38, 30, 34, 5A]

So, difference is:

My:
[31, 69, 30, 18, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 03, 31, 0B, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 07, 01, 30, 2F, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 04, 31, 22, 04, 20, 8D, A7, 88, FB, D0, C5, 7D, 46, 9B, 39, 20, 7B, 68, 95, F0, 05, 77, CB, 78, 77, A1, 43, 72, C2, 8C, DF, 6D, 9B, 9C, 49, E1, 25, 30, 1C, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 05, 31, 0F, 17, 0D, 32, 32, 30, 33, 31, 31, 31, 31, 32, 38, 30, 34, 5A]
"MWkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAvBgkqhkiG9w0BCQQxIgQgjaeI+9DFfUabOSB7aJXwBXfLeHehQ3LCjN9tm5xJ4SUwHAYJKoZIhvcNAQkFMQ8XDTIyMDMxMTExMjgwNFo="

Correct:
[31, 69, 30, 18, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 03, 31, 0B, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 07, 01, 30, 1C, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 05, 31, 0F, 17, 0D, 32, 32, 30, 33, 31, 31, 31, 31, 32, 38, 30, 34, 5A, 30, 2F, 06, 09, 2A, 86, 48, 86, F7, 0D, 01, 09, 04, 31, 22, 04, 20, 8D, A7, 88, FB, D0, C5, 7D, 46, 9B, 39, 20, 7B, 68, 95, F0, 05, 77, CB, 78, 77, A1, 43, 72, C2, 8C, DF, 6D, 9B, 9C, 49, E1, 25]
"MWkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwMzExMTEyODA0WjAvBgkqhkiG9w0BCQQxIgQgjaeI+9DFfUabOSB7aJXwBXfLeHehQ3LCjN9tm5xJ4SU="

So there is a question, is there any way to get der structure without sorted values?
Correct der:
image

My sorted der:
image

Move parser to chumsky/ariadne

Eventually I'd like to move the compiler to using chumsky/ariadne crates for parsing, in order to provide better error messages when parsing fails.

Alternatives

  • nom-supreme, nom based, so we'd use the same dependency in the codec as the parser.

`GeneralizedTime` encodes unaccepted value.

My fuzzer for this was

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    if let Ok(value) = rasn::der::decode::<rasn::types::Open>(data) {
        assert_eq!(value, rasn::der::decode(&rasn::der::encode(&value).unwrap()).unwrap());
    }
});

Not sure if this a valid round fuzz test, feel free to close as invalid if not.

Reproducer is

fn main() {
    let data = [
        24, 19, 43, 53, 49, 54, 49, 53, 32, 32, 48, 53, 50, 52, 48, 57, 52, 48, 50, 48, 90,
    ];

    let value = rasn::der::decode::<rasn::types::Open>(&data).expect("passes");

    rasn::der::decode::<rasn::types::Open>(&rasn::der::encode(&value).unwrap()).expect("fails");
}

Fails with thread 'main' panicked at 'fails: NoValidChoice { name: "Open" }', src/main.rs:7:81

Problem parsing and encoding Option for CHOICE

Hi.

I am trying to follow the RFC5280 section for CRL distribution point certificate extension

GeneralName ::= CHOICE {
     otherName                 [0]  AnotherName,
     rfc822Name                [1]  IA5String,
     dNSName                   [2]  IA5String,
     x400Address               [3]  ORAddress,
     directoryName             [4]  Name,
     ediPartyName              [5]  EDIPartyName,
     uniformResourceIdentifier [6]  IA5String,
     iPAddress                 [7]  OCTET STRING,
     registeredID              [8]  OBJECT IDENTIFIER }
     
  CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint

   DistributionPoint ::= SEQUENCE {
        distributionPoint       [0]     DistributionPointName OPTIONAL,
        reasons                 [1]     ReasonFlags OPTIONAL,
        cRLIssuer               [2]     GeneralNames OPTIONAL }

   DistributionPointName ::= CHOICE {
        fullName                [0]     GeneralNames,
        nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }   

I've only implemented a basic part of it and trying to parse the extension field from a well-known CA certificate

Here's my code: https://gist.github.com/Albibek/bdfaa3464ac7b8f009865b3426164a68
I'm using the main branch of the library since Option does not work in 0.3 at all giving a macro error.

I see two problems:

  • decoding does not work when I specify an Option<...> field, but starts working with a non-optional field
  • encoding of the same data does not give the same result, giving the wrong tags(0x80 instead of 0x0a) and strings lengths (0x74 instead of 0x78 for the very first sequence)

It is still probably me using the library wrong but can also be it's some problems with the library.

Thanks in advance.

X9CM OIDs belong ISO (1) not JOINT_ISO_ITU_T (2)

https://github.com/XAMPPRocky/rasn/blob/8f3a6731283bf4102ad9bd774b97bd69ec241c5e/src/types/oid.rs#L352

Hey, I am trying to implement ITU-T X.227 (ISO Association Control Service Element). Part of it requires me to write down the following OID:
joint-iso-itu-t(2) association-control(2) as-id(3) acse-ase(1) version(1)

So I decided to add it to your src/types/oid.rs file in then import it into my own crate (that I'm putting in the standards folder for a pull request later). That's when I noticed that a block of OIDs for X9-CM (or X9.57) is incorrectly written under root OID JOINT_ISO_ITU_T(2). I think it should be under ISO(1).

Here is the reference for my belief: Reference record for OID 1.2.840.10040.2.

Let me know if this is correct, I'll be happy to draw up a pull request.

[snmp] All fields are private?

Hi, a friend just linked your 0.3.0 announcement and I'm super excited to see SNMP stuff get a crate! :D However, reading through the docs and then the code, I'm assuming some fields were forgotten to be made pub? e.g. https://github.com/XAMPPRocky/rasn/blob/fb424b886f15d2d3943fffbb72ae0d2fccdf4e7b/standards/snmp/src/v1.rs#L52-L58

There's no way to construct or inspect any Pdu since all fields are private and there's no constructor or accessor methods. Seems like the other structs have similar issues. Haven't looked at the other new crates yet so not sure if its the case there either.

Fix long form tag decoding

Hello,

I believe that long form tag decoding might be currently broken due to endianness being little instead of big (as per https://en.wikipedia.org/wiki/X.690#Long_form).

Here's a possible fix that worked for me:

    Fix long form tag decoding
    
    * change endianness to big,
    * change the test to make the first octet nonzero

diff --git a/src/ber/de/parser.rs b/src/ber/de/parser.rs
index d66da33..16dc8a9 100644
--- a/src/ber/de/parser.rs
+++ b/src/ber/de/parser.rs
@@ -188,14 +188,19 @@ impl Appendable for crate::types::BitString {
 /// Concatenates a series of 7 bit numbers delimited by `1`'s and
 /// ended by a `0` in the 8th bit.
 fn concat_number(body: &[u8], start: u8) -> Integer {
-    let mut number = Integer::from(start);
+    let start = Integer::from(start);
+    if body.is_empty() {
+        return start;
+    }
+
+    let mut number = Integer::from(body[0] & 0x7F);
 
-    for byte in body.iter().rev() {
+    for byte in body[1..].iter() {
         number <<= 7usize;
         number |= Integer::from(byte & 0x7F);
     }
 
-    number
+    (number << 7usize) | start
 }
 
 fn take_contents(input: &[u8], length: u8) -> IResult<&[u8], &[u8]> {
@@ -237,9 +242,9 @@ mod tests {
 
     #[test]
     fn long_tag() {
-        let (_, identifier) = parse_identifier_octet([0xFF, 0x80, 0x7F][..].into()).unwrap();
+        let (_, identifier) = parse_identifier_octet([0xFF, 0x83, 0x7F][..].into()).unwrap();
         assert!(identifier.is_constructed);
-        assert_eq!(Tag::new(Class::Private, 16256), identifier.tag);
+        assert_eq!(Tag::new(Class::Private, 511), identifier.tag);
     }
 
     #[test]

Allow using user-supplied buffer for encoding data?

Hi, I'm working on building a SNMP client and as an optimization to prevent allocating memory on every send/receive, I want to internally store a Vec in my client, however there doesn't appear to be a way to provide the Encoder a &mut Vec<u8> or &mut [u8] to encode into, which would be really useful!

How to deal with CHOICE-style enums do not allow implicit tagging.

use rasn::{types::*, *};

#[test]
fn choice() {
    #[derive(AsnType, Clone, Debug, Encode, Decode, PartialEq)]
    struct ChoiceField {
        choice: VecChoice,
    }

    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
    #[rasn(choice)]
    enum VecChoice {
        #[rasn(tag(1))]
        Bar(Vec<bool>),
        #[rasn(tag(2))]
        Foo(Vec<OctetString>),
    }
    let bar = ChoiceField { choice: VecChoice::Bar(vec![true]) };
    let foo = ChoiceField { choice: VecChoice::Foo(vec![OctetString::from(vec![1, 2, 3, 4, 5])]) };
    assert_eq!(foo, ber::decode(&ber::encode(&foo).unwrap()).unwrap());
    assert_eq!(bar, ber::decode(&ber::encode(&bar).unwrap()).unwrap());
}

errors with thread 'choice' panicked at 'called Result::unwrap() on an Err value: Custom { msg: "CHOICE-style enums do not allow implicit tagging." }', tests/derive.rs:67:52

if impl Decode manually for ChoiceField:

use rasn::{types::*, *};

#[derive(AsnType, Clone, Debug, Encode, PartialEq)]
    struct ChoiceField {
        choice: VecChoice,
    }

    impl Decode for ChoiceField {
        fn decode_with_tag<D: rasn::Decoder>(decoder: &mut D, tag: Tag) -> Result<Self, D::Error> {
            let mut decoder = decoder.decode_sequence(tag)?;
            Ok(Self {
                choice: <_>::decode(&mut decoder)?,
            })
        }
    }

    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
    #[rasn(choice)]
    enum VecChoice {
        #[rasn(tag(1))]
        Bar(Vec<bool>),
        #[rasn(tag(2))]
        Foo(Vec<OctetString>),
    }
    let bar = ChoiceField { choice: VecChoice::Bar(vec![true]) };
    let foo = ChoiceField { choice: VecChoice::Foo(vec![OctetString::from(vec![1, 2, 3, 4, 5])]) };
    assert_eq!(foo, ber::decode(&ber::encode(&foo).unwrap()).unwrap());
    assert_eq!(bar, ber::decode(&ber::encode(&bar).unwrap()).unwrap());

then it errors with thread 'choice' panicked at 'called Result::unwrap() on an Err value: Custom { msg: "CHOICE-style enums do not allow implicit tagging." }', tests/derive.rs:66:52

So what's the proper way to deal with it? Thanks!

support explicitly tagged types encoding

use rasn::{AsnType,Decode,Encode};
use std::error::Error;

#[derive(Debug, AsnType, Decode, Encode)]
struct Message {
    id: i32,
    #[rasn(choice, enumerated)]
    body: BODY,
}

#[derive(Debug, AsnType, Decode, Encode)]
#[rasn(choice)]
enum BODY {
    #[rasn(tag(context, 3000))]
    Request(Request),

    #[rasn(tag(context, 3001))]
    Response(Response),
}

#[derive(Debug, AsnType, Decode, Encode)]
struct Request {
    num: i32,
}

#[derive(Debug, AsnType, Decode, Encode)]
struct Response {
    ret: i32,
}

fn main() -> Result<(), Box<dyn Error>> {
    let p = Message {
        id: 1,
        body: BODY::Request(Request { num: 1 }),
    };
    let e = rasn::der::encode(&p).unwrap();
    println!("e: {:02x?}", e);
    Ok(())
}

the above piece of code prints: 30, 0a, 02, 01, 01, bf, 97, 38, 03, 02, 01, 01
i want it to print 30, 0c, 02, 01, 01, bf, 97, 38, 05, 30, 03, 02, 01, 01 because it need to talk to some program written using c++ and golang. This encoding requires each CHOICE of BODY explicitly tagged. See 5.2 Explicitly tagged types of http://luca.ntop.org/Teaching/Appunti/asn1.html
Is it possible to achieve this?

Representing Objects/Classes

One of the things that needs a good representation the the rasn framework is defining and using Objects/Classes from ASN.1 in rasn.

Compilation error due to docs

error[E0658]: arbitrary expressions in key-value attributes are unstable
#![doc = include_str!("../README.md")]
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Believe this is due to the changes in 4e69c11

Support OCSP in PKIX

Task

Implement RFC 6960 as pub mod ocsp; in rasn-pkix. Currently using the 1998 syntax version is preferred, as the 2008 syntax requires classes from RFC 5912 which isn't implemented currently (see #44).

Support for constraints

Hey, amazing crate!

Any update on when this crate will support constraints?

I'm implementing the IEEE 11073 standard for medical device communication in Rust. It encodes its data types in ASN.1 BER and DER. My implementation targets embedded environments so it would really save me memory and storage if I can constraint data types to u8/16/32 as per the IEEE 11073 recommendations.

ASN.1 Compiler Tracking Issue

This is a non-trivial amount of work and I would not at all mind if it was declared out-of-scope, but it would be nice for rasn to be a full-fledged ASN.1 compiler. Some protocol definitions (cough 3GPP) are huge and writing models by hand would be very error-prone.

Explicit tag on struct (no delegate)

Hey - it looks like the macro explicit tagging on structs is only working correctly for structs with delegate set (as tested here). For other structs, the sequence tag isn't being applied.

I'm specifically trying to use the kerberos library and the ticket struct, defined as:

/// Record that helps a client authenticate to a service.
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rasn(tag(explicit(application, 1)))]
pub struct Ticket {
    /// The version number for the ticket format.
    #[rasn(tag(explicit(0)))]
    pub tkt_vno: Integer,
    /// The realm that issued a ticket.  It also serves to identify the realm
    /// part of the server's principal identifier. Since a Kerberos server can
    /// only issue tickets for servers within its realm, the two will always
    /// be identical.
    #[rasn(tag(explicit(1)))]
    pub realm: Realm,
    /// All components of the name part of the server's identity, including
    /// those parts that identify a specific instance of a service.
    #[rasn(tag(explicit(2)))]
    pub sname: PrincipalName,
    /// The encrypted encoding of [EncTicketPart]. It is encrypted in the key
    /// shared by Kerberos and the end server (the server's secret key), using a
    /// key usage value of `2`.
    #[rasn(tag(explicit(3)))]
    pub enc_part: EncryptedData,
}

Code snippet:

let ticket = Ticket {
    tkt_vno: Integer::parse_bytes(b"5", 10).unwrap(),
    realm: KerberosString::new("COMPANY.INT".to_string()),
    sname: PrincipalName {
        r#type: 2,
        string: vec![
            KerberosString::new(String::from("krbtgt")),
            KerberosString::new(String::from("COMPANY.INT")),
        ],
    },
    enc_part: EncryptedData {
        etype: 28,
        kvno: None,
        cipher: OctetString::from("ab"),
    },
};

let data: &[u8] = &[0x61, 0x43, 0x30, 0x41, 0xA0, 0x03, 0x02, 0x01, 0x05];
let enc = rasn::der::encode(&ticket).unwrap();

// this fails
assert_eq!(data, &enc[..data.len()]);

the encoded ticket is:
61, 41, A0, 03, 02, 01, 05...

it's gone straight from the explicit application tag 61, 41 to the first field's tag A0, 03 ..., however I would expect the sequence tag as per the above assert against data.

Getting more familiar with the code and rust in general so will hopefully be able to make useful contributions soon

DER BitString Padding Incorrect

With the following example struct

#[derive(AsnType, Decode, Encode, Debug, PartialEq, Clone)]
pub struct PaddingError {
    #[rasn(tag(0))]
    pub normal: rasn::types::Integer,

    #[rasn(tag(1))]
    pub bitstring_1: rasn::types::BitString,

    #[rasn(tag(2))]
    pub bitstring_2: rasn::types::BitString,
}

let expected = hex::decode("3016800101810305e100820c03ee0800000400000001ed18").unwrap();
        let actual: Vec<u8> = rasn::der::encode(&PaddingError{
            normal: 1.into(),
            bitstring_1: BitString::from_vec([0xe1, 0x00].to_vec()),
            bitstring_2: BitString::from_vec([0xee,0x08,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0xed,0x18].to_vec()),
        }).unwrap();
        assert_eq!(actual, expected);

We would expect the resulting bytes to look like
[48, 22, 128, 1, 1, 129, 3, 5, 225, 0, 130, 12, 3, 238, 8, 0, 0, 4, 0, 0, 0, 1, 237, 24]

Instead what we get from our encode is
[48, 21, 128, 1, 1, 129, 2, 0, 225, 130, 12, 0, 238, 8, 0, 0, 4, 0, 0, 0, 1, 237, 24]

`Oid` is const constructable since Rust 1.58.0

Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3f86d7648af09a1fb561b0c510fe0c9e

I didn't notice any MSRV policy, but if you're not concerned with supporting older Rust versions, ConstOid can be removed and replaced with const associated functions on Oid now 🎉 Option::unwrap still isn't const however its trivial to create a function to do the same thing as shown in the playground link, so I don't think that's really a blocker since its on the user's end.

OID duplicated - wrong value for SHA1_RSA

OID for SHA1 looks wrong:

ISO_MEMBER_BODY_US_RSADSI_PKCS1_MD5_RSA => 1, 2, 840, 113549, 1, 1, 4;
ISO_MEMBER_BODY_US_RSADSI_PKCS1_SHA1_RSA => 1, 2, 840, 113549, 1, 1, 4;

SHA1 with RSA should be 1.2.840.113549.1.1.5.

Replace type aliases with newtypes

Instead of directly reexporting types such as BigInt as Integer and Bytes as OctetString, I think it makes a lot more sense to newtype these and provide a more stable interface so that the implementations can be changed without affecting the public API. For instance, being able to switch from using num-bigint to ibig for parsing variable-sized integers could provide a significant speed increase, avoiding unnecessary allocations for small integers. However, changing the Integer alias to IBig would be a breaking change. Reexporting types also makes the docs quite a bit more confusing in my opinion, as the documentation for the types ends up using a completely, totally different type name, for example when I go to view the docs for OctetString, I'm met with the following docs:

A cheaply cloneable and sliceable chunk of contiguous memory.

Bytes is an efficient container for storing and operating on contiguous slices of memory. It is intended for use primarily in networking code, but could have applications elsewhere as well.

Bytes values facilitate zero-copy network programming by allowing multiple Bytes objects to point to the same underlying memory.

Which is a bit jarring when the type signature is named something completely different IMO

Replace current standards types with compiler generated implementations.

As a way to know when the compiler is ready for release, we should replace some of the handwritten implementations in this crate with compiler generated implementations. This is also a good way of finding what needs to be done next, as if it can't compile these standards then there's something that needs to be fixed.

Invalid encoding and decoding of OIDs

Hello,
This crate is amazing and speeds up my small ASN.1 encoding project enormously :-)

However, I found a bug when encoding OIDs, having values bigger than 127. E.g. when taking following example ID from MS website encoding using rasn gives back:

  • wrong [2b, 6, 1, 4, 1, b7, 2,15,14] (this actually decodes to 1.3.6.1.4.1.7042.21.20, look here)
  • instead of correct [2b, 6, 1, 4, 1, b7, 2, 15, 14]

Another example is from stackoverflow and 1.2.840.113549.1.1.5 (sha1WithRsaEncryption):

  • current (incorrect) encoding: [2a, c8, 6, 8d, f7, 6, 1, 1, 5] (lapo.it)
  • correct encoding: [2a, 86, 48, 86, f7, d, 1, 1, 5] (lapo.it)

BR, Piotr

rasn-kerberos: explicit tagging

Thanks for the great library.

I’m trying to use the rasn_kerberos structures but am having some issues. I’m by no means an expert here, so may be doing something wrong.

Specifically, if I’m building an AS-REQ - which is defined in RFC 4120 as follows:

AS-REQ          ::= [APPLICATION 10] KDC-REQ
KDC-REQ         ::= SEQUENCE {
        -- NOTE: first tag is [1], not [0]
        pvno            [1] INTEGER (5) ,
        msg-type        [2] INTEGER (10 -- AS -- | 12 -- TGS --),
        padata          [3] SEQUENCE OF PA-DATA OPTIONAL
                            -- NOTE: not empty --,
        req-body        [4] KDC-REQ-BODY
} 

The rasn generated der encoded value starts:
6A, 81, 9D, 81, 01, 05 …

With 6A representing the application tag of 10, and 81, 9D representing the length. However I would be expecting a sequence tag next (30).

Based on this - I believe that implicit tagging is being used here?

As per RFC 4120, explicit tagging should be used:

   The type definitions in this section assume an ASN.1 module
   definition of the following form:

   KerberosV5Spec2 {
           iso(1) identified-organization(3) dod(6) internet(1)
           security(5) kerberosV5(2) modules(4) krb5spec2(2)
   } DEFINITIONS EXPLICIT TAGS ::= BEGIN

   -- rest of definitions here

   END

   This specifies that the tagging context for the module will be
   explicit and non-automatic.

If I compare the rasn generated encoding with a functioning AS-REQ that I’ve captured (bare in mind different size), it includes the tag and length for the sequence (30, 81, DC) as well as for the Integer (02, 01)

6A, 81, DF, 30, 81, DC, A1, 03, 02, 01, 05 …

I note from #36 that explicit tagging is available via the macro now. This seems to have worked fine for the fields, but I can’t figure out how one would set the application tag as explicit.

#[rasn(tag(explicit(application, 11)), delegate)]

Panics with:

the `#[rasn(tag)]` attribute must be a list

I would appreciate your thoughts - apologies if I’m incorrect with my assessment here

Compiler CLI API

The compiler should be able to be used as a standalone executable to compile ASN.1 to a variety of languages (currently just Rust). Ideally we'd have a much more fully featured CLI for generating code. This issue requires some research and finding prior art to see if there's anything rasn should adopt.

Move to RFC 5912 Syntax in PKIX

Currently the syntax in the PKIX crate is based on the 1998 syntax, because that was easier to implement, however it would be preferable if we could move to a similar abstraction of modules as in RFC 5912 while maintaining the same level of flexibility. Ideally more of constraints can also be implemented as part of the refactor.

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.