rasn 0.29.0

A safe no_std ASN.1 codec framework.
Documentation
#[test]
fn test_ber_encoding_of_tagged_type_round_trip() {
    //  -- BER encoding example from ITU-T X.690 clause 8.14:
    //
    //  With ASN.1 type definitions (in an explicit tagging environment) of:
    //  TestModule DEFINITIONS EXPLICIT TAGS::= BEGIN
    //      Type1 ::= VisibleString
    //      Type2 ::= [APPLICATION 3] IMPLICIT Type1
    //      Type3 ::= [2] Type2
    //      Type4 ::= [APPLICATION 7] IMPLICIT Type3
    //      Type5 ::= [2] IMPLICIT Type2
    //  END
    //  a value of "Jones" is encoded as follows ...

    use rasn::prelude::*;

    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate)]
    pub struct Type1(pub VisibleString);
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(application, 3))]
    pub struct Type2(pub Type1);
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(explicit(context, 2)))]
    pub struct Type3(pub Type2);
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(application, 7))]
    pub struct Type4(pub Type3);
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(context, 2))]
    pub struct Type5(pub Type2);

    let type1 = Type1(VisibleString::from_iso646_bytes(b"Jones").unwrap());
    let type2 = Type2(type1.clone());
    let type3 = Type3(type2.clone());
    let type4 = Type4(type3.clone());
    let type5 = Type5(type2.clone());

    let type1_ber = rasn::ber::encode(&type1).unwrap();
    let type2_ber = rasn::ber::encode(&type2).unwrap();
    let type3_ber = rasn::ber::encode(&type3).unwrap();
    let type4_ber = rasn::ber::encode(&type4).unwrap();
    let type5_ber = rasn::ber::encode(&type5).unwrap();

    assert_eq!(type1_ber, vec![0x1A, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73_u8]);
    assert_eq!(type2_ber, vec![0x43, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73_u8]);
    assert_eq!(
        type3_ber,
        vec![0xa2, 0x07, 0x43, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73_u8]
    );
    assert_eq!(
        type4_ber,
        vec![0x67, 0x07, 0x43, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73_u8]
    );
    assert_eq!(type5_ber, vec![0x82, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73_u8]);

    assert_eq!(type1, rasn::ber::decode::<Type1>(&type1_ber).unwrap());
    assert_eq!(type2, rasn::ber::decode::<Type2>(&type2_ber).unwrap());
    assert_eq!(type3, rasn::ber::decode::<Type3>(&type3_ber).unwrap());
    assert_eq!(type4, rasn::ber::decode::<Type4>(&type4_ber).unwrap());
    assert_eq!(type5, rasn::ber::decode::<Type5>(&type5_ber).unwrap());
}

#[test]
fn test_issue_519_automatic_tagging_for_tagged_choice_round_trip() {
    // https://github.com/librasn/rasn/issues/519

    /*
        With following ASN.1 definitions, BER encoding of TC1 and TC2 should be identical,
        and alternatives in a choice (`a` and `b`) are automatically tagged.

        TestModuleA DEFINITIONS AUTOMATIC TAGS::= BEGIN
            --untagged choice
            C1 ::= CHOICE { a INTEGER, b BOOLEAN }

            --tagged choice. This is explicit tagging. (see ITU-T X.680 section 31.2.7 clause c)
            TC1  ::= [4] C1
            --another form of tagged choice
            TC2  ::= [4] CHOICE { a INTEGER, b BOOLEAN }
        END
    */

    use rasn::prelude::*;

    #[doc = "untagged choice"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(choice, automatic_tags)]
    pub enum C1 {
        A(Integer),
        B(bool),
    }
    #[doc = "tagged choice. This is explicit tagging. (see ITU-T X.680 section 31.2.7 clause c)"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(explicit(context, 4)))]
    pub struct TC1(pub C1);
    #[doc = "another form of tagged choice"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(choice, tag(explicit(context, 4)), automatic_tags)]
    pub enum TC2 {
        A(Integer),
        B(bool),
    }

    let choice = C1::A(0x55.into());
    let tc1 = TC1(choice.clone());
    let tc2 = TC2::A(0x55.into());

    let choice_enc = rasn::ber::encode(&choice).unwrap();
    let tc1_enc = rasn::ber::encode(&tc1).unwrap();
    let tc2_enc = rasn::ber::encode(&tc2).unwrap();

    assert_eq!(choice_enc, vec![0x80, 0x01, 0x55]);
    assert_eq!(tc1_enc, vec![0xa4, 0x03, 0x80, 0x01, 0x55]);
    assert_eq!(tc2_enc, tc1_enc); // should be identical to tc1_enc

    let tc1_de = rasn::ber::decode::<TC1>(&tc1_enc).unwrap();
    let tc2_de = rasn::ber::decode::<TC2>(&tc2_enc).unwrap();
    assert_eq!(tc1, tc1_de);
    assert_eq!(tc2, tc2_de);
}

#[test]
fn test_issue_520_implicit_tag_on_tagged_choice_round_trip() {
    // https://github.com/librasn/rasn/issues/520

    /*
    TestModuleA DEFINITIONS AUTOMATIC TAGS::= BEGIN
        --tagged choice
        TC2  ::= [4] CHOICE { a INTEGER, b BOOLEAN }

        --implicit tag on tagged choice TC2
        TTC2 ::= [6] TC2
    END
    */

    use rasn::prelude::*;
    #[doc = "tagged choice"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(choice, tag(explicit(context, 4)), automatic_tags)]
    pub enum TC2 {
        A(Integer),
        B(bool),
    }
    #[doc = "implicit tag on tagged choice TC2"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(context, 6))]
    pub struct TTC2(pub TC2);

    let tc2 = TC2::A(0x55.into());
    let ttc2 = TTC2(tc2.clone());

    let tc2_enc = rasn::ber::encode(&tc2).unwrap();
    let ttc2_enc = rasn::ber::encode(&ttc2).unwrap();

    assert_eq!(tc2_enc, vec![0xa4, 0x03, 0x80, 0x01, 0x55]);
    assert_eq!(ttc2_enc, vec![0xa6, 0x03, 0x80, 0x01, 0x55]);

    let tc2_de = rasn::ber::decode::<TC2>(&tc2_enc).unwrap();
    let ttc2_de = rasn::ber::decode::<TTC2>(&ttc2_enc).unwrap();
    assert_eq!(tc2, tc2_de);
    assert_eq!(ttc2, ttc2_de);
}

#[test]
fn test_tagged_any_round_trip() {
    use rasn::prelude::*;

    /*
    TestModuleA DEFINITIONS ::= BEGIN
        --tagged ANY
        OpaqueInfo ::= [34] ANY

        --actual content of OpaqueInfo
        Info ::= [34] SEQUENCE {
          version [1] OCTET STRING(SIZE(3)), -- implicitly tagged
          data    [2] OCTET STRING OPTIONAL,
        }
    END
    */

    /// make it opaque by replacing `Info` with ANY.
    #[doc = "tagged ANY"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, tag(context, 34))]
    pub struct OpaqueInfo(pub Any);

    /// a SEQUENCE where we don't need to know the details.
    #[doc = "actual content of OpaqueInfo"]
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(tag(context, 34))]
    pub struct Info {
        #[rasn(size("3"), tag(context, 1))]
        pub version: OctetString,
        #[rasn(tag(context, 2))]
        pub data: Option<OctetString>,
    }

    let version = [0x02_u8, 0x03, 0x00];

    // round-trip test. encoding of `Info` and `OpaqueInfo` should be identical.
    let value = Info {
        version: OctetString::from(version),
        data: None,
    };
    let encoded = rasn::der::encode(&value).unwrap();
    assert_eq!(
        [0xbf_u8, 0x22, 0x05, 0x81, 0x03, 0x02, 0x03, 0x00],
        encoded.as_slice()
    );

    let opaque = rasn::der::decode::<OpaqueInfo>(&encoded).unwrap();
    let encoded2 = rasn::der::encode(&opaque).unwrap();

    assert_eq!(encoded, encoded2);
}

#[test]
fn issue_488_tagged_any_round_trip() {
    use rasn::prelude::*;

    // This test is mostly identical to https://github.com/librasn/rasn/issues/488. (round-trip test is added)
    // All fields in actual IncomingRequest are replaced with Option<Any> since user is not interested in them.

    #[derive(Debug, AsnType, Decode, Encode, PartialEq, Eq)]
    struct IncomingRequest {
        #[rasn(tag(Context, 0))]
        handshake: Option<Any>,
        #[rasn(tag(Context, 1))]
        user_data: Option<Any>,
    }

    let incoming = vec![0x30_u8, 0x06, 0xA1, 0x04, 0x01, 0x02, 0x03, 0x04];
    let decoded: IncomingRequest = rasn::Codec::Der.decode_from_binary(&incoming).unwrap();
    let expected = IncomingRequest {
        handshake: None,
        user_data: Some(Any::new(vec![0x01, 0x02, 0x03, 0x04])),
    };
    assert_eq!(decoded, expected);

    // round-trip test.
    let encoded = rasn::der::encode(&decoded).unwrap();
    assert_eq!(incoming, encoded);
    // encoding of an explicitly tagged type shall be constructed (X.690 section 8.14.3)
    assert_eq!(encoded[2] & 0x20, 0x20);
}