Giter Site home page Giter Site logo

rasn's People

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  avatar  avatar

rasn's Issues

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.

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

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.

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.

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

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!

`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

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.

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

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

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.

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

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]

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());
}

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.

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?

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.

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.

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.

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.

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.

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.

[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.

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?

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.

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.

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!

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.

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!

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?

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.

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?

new release

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

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]

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

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

`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

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.

`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.

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

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.

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.

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.

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

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.