use std::fmt; fn assert_roundtrips< 'a, T: asn1::Asn1Readable<'a> + asn1::Asn1Writable + PartialEq + fmt::Debug, >( data: &[(asn1::ParseResult, &'a [u8])], ) { for (value, der_bytes) in data { let parsed = asn1::parse_single::(der_bytes); assert_eq!(value, &parsed); if let Ok(v) = value { let result = asn1::write_single(v).unwrap(); assert_eq!(&result, der_bytes); } } } #[test] fn test_struct_no_fields() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct NoFields; assert_roundtrips(&[ (Ok(NoFields), b"\x30\x00"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::ExtraData)), b"\x30\x01\x00", ), ]); } #[test] fn test_struct_simple_fields() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct SimpleFields { a: u64, b: u64, } assert_roundtrips(&[( Ok(SimpleFields { a: 2, b: 3 }), b"\x30\x06\x02\x01\x02\x02\x01\x03", )]); } #[test] fn test_tuple_struct_simple_fields() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct SimpleFields(u8, u8); assert_roundtrips(&[(Ok(SimpleFields(2, 3)), b"\x30\x06\x02\x01\x02\x02\x01\x03")]); } #[test] fn test_struct_lifetime() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct Lifetimes<'a> { a: &'a [u8], } assert_roundtrips(&[(Ok(Lifetimes { a: b"abc" }), b"\x30\x05\x04\x03abc")]); } #[test] fn test_optional() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct OptionalFields { zzz: Option, } assert_roundtrips(&[ (Ok(OptionalFields { zzz: None }), b"\x30\x00"), (Ok(OptionalFields { zzz: Some(8) }), b"\x30\x03\x02\x01\x08"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::ExtraData)), b"\x30\x03\x04\x00\x00", ), ]); } #[test] fn test_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct EmptySequence; #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct ExplicitFields { #[explicit(5)] a: Option, #[explicit(7)] b: Option, } assert_roundtrips(&[ ( Ok(ExplicitFields { a: Some(3), b: Some(EmptySequence), }), b"\x30\x09\xa5\x03\x02\x01\x03\xa7\x02\x30\x00", ), ( Ok(ExplicitFields { a: None, b: Some(EmptySequence), }), b"\x30\x04\xa7\x02\x30\x00", ), ( Ok(ExplicitFields { a: Some(3), b: None, }), b"\x30\x05\xa5\x03\x02\x01\x03", ), (Ok(ExplicitFields { a: None, b: None }), b"\x30\x00"), ]); } #[test] fn test_explicit_tlv() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct ExplicitTlv<'a> { #[explicit(5)] a: Option>, } assert_roundtrips(&[ (Ok(ExplicitTlv { a: None }), b"\x30\x00"), ( Ok(ExplicitTlv { a: asn1::parse_single(b"\x05\x00").unwrap(), }), b"\x30\x04\xa5\x02\x05\x00", ), ]); } #[test] fn test_implicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct EmptySequence; #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct ImplicitFields { #[implicit(5)] a: Option, #[implicit(7)] b: Option, } assert_roundtrips(&[ ( Ok(ImplicitFields { a: Some(3), b: Some(EmptySequence), }), b"\x30\x05\x85\x01\x03\xa7\x00", ), ( Ok(ImplicitFields { a: None, b: Some(EmptySequence), }), b"\x30\x02\xa7\x00", ), ( Ok(ImplicitFields { a: Some(3), b: None, }), b"\x30\x03\x85\x01\x03", ), (Ok(ImplicitFields { a: None, b: None }), b"\x30\x00"), ]); } #[test] fn test_default() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct DefaultFields { #[default(13)] a: u8, #[default(15)] #[explicit(1)] b: u8, #[default(17)] #[implicit(5)] c: u8, } assert_roundtrips(&[ ( Ok(DefaultFields { a: 13, b: 15, c: 17, }), b"\x30\x00", ), ( Ok(DefaultFields { a: 3, b: 15, c: 17 }), b"\x30\x03\x02\x01\x03", ), ( Ok(DefaultFields { a: 13, b: 5, c: 17 }), b"\x30\x05\xa1\x03\x02\x01\x05", ), ( Ok(DefaultFields { a: 13, b: 15, c: 7 }), b"\x30\x03\x85\x01\x07", ), ( Ok(DefaultFields { a: 3, b: 5, c: 7 }), b"\x30\x0b\x02\x01\x03\xa1\x03\x02\x01\x05\x85\x01\x07", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::a"))), b"\x30\x03\x02\x01\x0d", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::b"))), b"\x30\x05\xa1\x03\x02\x01\x0f", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::c"))), b"\x30\x03\x85\x01\x11", ), ]); } #[test] fn test_default_not_literal() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3, 4); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct DefaultFields { #[default(OID1)] a: asn1::ObjectIdentifier, } assert_roundtrips(&[ (Ok(DefaultFields { a: OID1 }), b"\x30\x00"), ( Ok(DefaultFields { a: OID2 }), b"\x30\x05\x06\x03\x2a\x03\x04", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::a"))), b"\x30\x04\x06\x02\x2a\x03", ), ]); } #[test] fn test_default_const_generics() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug)] struct DefaultFields { #[default(15)] a: asn1::Explicit, #[default(17)] b: asn1::Implicit, } assert_roundtrips(&[ ( Ok(DefaultFields { a: asn1::Explicit::new(15), b: asn1::Implicit::new(17), }), b"\x30\x00", ), ( Ok(DefaultFields { a: asn1::Explicit::new(5), b: asn1::Implicit::new(17), }), b"\x30\x05\xa1\x03\x02\x01\x05", ), ( Ok(DefaultFields { a: asn1::Explicit::new(15), b: asn1::Implicit::new(7), }), b"\x30\x03\x85\x01\x07", ), ( Ok(DefaultFields { a: asn1::Explicit::new(5), b: asn1::Implicit::new(7), }), b"\x30\x08\xa1\x03\x02\x01\x05\x85\x01\x07", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::a"))), b"\x30\x05\xa1\x03\x02\x01\x0f", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::b"))), b"\x30\x03\x85\x01\x11", ), ]); } #[test] fn test_default_bool() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct DefaultField { #[default(false)] a: bool, } assert_roundtrips(&[ (Ok(DefaultField { a: true }), b"\x30\x03\x01\x01\xff"), (Ok(DefaultField { a: false }), b"\x30\x00"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultField::a"))), b"\x30\x03\x01\x01\x00", ), ]); } #[test] fn test_enum() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum BasicChoice { A(u64), B(()), } assert_roundtrips(&[ (Ok(BasicChoice::A(17)), b"\x02\x01\x11"), (Ok(BasicChoice::B(())), b"\x05\x00"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(4), })), b"\x04\x00", ), ]); assert_roundtrips(&[ (Ok(Some(BasicChoice::A(17))), b"\x02\x01\x11"), (Ok(Some(BasicChoice::B(()))), b"\x05\x00"), (Ok(None), b""), ]); } #[test] fn test_enum_lifetimes() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum LifetimesChoice<'a> { A(u64), B(&'a [u8]), } assert_roundtrips(&[ (Ok(LifetimesChoice::A(17)), b"\x02\x01\x11"), (Ok(LifetimesChoice::B(b"lol")), b"\x04\x03lol"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(5), })), b"\x05\x00", ), ]); assert_roundtrips(&[ (Ok(Some(LifetimesChoice::A(17))), b"\x02\x01\x11"), (Ok(Some(LifetimesChoice::B(b"lol"))), b"\x04\x03lol"), (Ok(None), b""), ]); } #[test] fn test_enum_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum ExplicitChoice<'a> { #[explicit(5)] A(u64), B(&'a [u8]), } assert_roundtrips(&[ (Ok(ExplicitChoice::A(17)), b"\xa5\x03\x02\x01\x11"), (Ok(ExplicitChoice::B(b"lol")), b"\x04\x03lol"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(5), })), b"\x05\x00", ), ]); assert_roundtrips(&[ (Ok(Some(ExplicitChoice::A(17))), b"\xa5\x03\x02\x01\x11"), (Ok(Some(ExplicitChoice::B(b"lol"))), b"\x04\x03lol"), (Ok(None), b""), ]); } #[test] fn test_enum_implicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct EmptySequence; #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum ImplicitChoice<'a> { #[implicit(5)] A(u64), #[implicit(7)] B(EmptySequence), C(&'a [u8]), } assert_roundtrips(&[ (Ok(ImplicitChoice::A(17)), b"\x85\x01\x11"), (Ok(ImplicitChoice::B(EmptySequence)), b"\xa7\x00"), (Ok(ImplicitChoice::C(b"lol")), b"\x04\x03lol"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(5), })), b"\x05\x00", ), ]); assert_roundtrips(&[ (Ok(Some(ImplicitChoice::A(17))), b"\x85\x01\x11"), (Ok(Some(ImplicitChoice::B(EmptySequence))), b"\xa7\x00"), (Ok(Some(ImplicitChoice::C(b"lol"))), b"\x04\x03lol"), (Ok(None), b""), ]); } #[test] fn test_enum_in_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum BasicChoice { A(u64), } #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct StructWithExplicitChoice { #[explicit(0)] c: Option, } assert_roundtrips(&[ (Ok(StructWithExplicitChoice { c: None }), b"\x30\x00"), ( Ok(StructWithExplicitChoice { c: Some(BasicChoice::A(3)), }), b"\x30\x05\xa0\x03\x02\x01\x03", ), ]); } #[test] fn test_error_parse_location() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct InnerSeq(u64); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum InnerEnum { Int(u64), } #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct OuterSeq { inner: InnerSeq, inner_enum: Option, } assert_roundtrips::(&[ ( Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue) .add_location(asn1::ParseLocation::Field("InnerSeq::0")) .add_location(asn1::ParseLocation::Field("OuterSeq::inner"))), b"\x30\x04\x30\x02\x02\x00", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue) .add_location(asn1::ParseLocation::Field("InnerEnum::Int")) .add_location(asn1::ParseLocation::Field("OuterSeq::inner_enum"))), b"\x30\x07\x30\x03\x02\x01\x01\x02\x00", ), ]); } #[test] fn test_required_implicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct RequiredImplicit { #[implicit(0, required)] value: u8, } assert_roundtrips::(&[ (Ok(RequiredImplicit { value: 8 }), b"\x30\x03\x80\x01\x08"), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::ShortData { needed: 1 }) .add_location(asn1::ParseLocation::Field("RequiredImplicit::value")), ), b"\x30\x00", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(11), }) .add_location(asn1::ParseLocation::Field("RequiredImplicit::value"))), b"\x30\x03\x0b\x01\x00", ), ]); } #[test] fn test_required_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct RequiredExplicit { #[explicit(0, required)] value: u8, } assert_roundtrips::(&[ ( Ok(RequiredExplicit { value: 8 }), b"\x30\x05\xa0\x03\x02\x01\x08", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::ShortData { needed: 1 }) .add_location(asn1::ParseLocation::Field("RequiredExplicit::value")), ), b"\x30\x00", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(11), }) .add_location(asn1::ParseLocation::Field("RequiredExplicit::value"))), b"\x30\x03\x0b\x01\x00", ), ]); } #[test] fn test_defined_by() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 5); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] OctetString(&'a [u8]), #[defined_by(OID2)] Integer(u32), } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::OctetString(b"abc"), }), b"\x30\x09\x06\x02\x2a\x03\x04\x03abc", ), ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Integer(17), }), b"\x30\x07\x06\x02\x2a\x05\x02\x01\x11", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::UnknownDefinedBy) .add_location(asn1::ParseLocation::Field("S::value")), ), b"\x30\x04\x06\x02\x2a\x07", ), ]); } #[test] fn test_defined_by_default() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 5); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] Integer(u32), #[default] Other(asn1::ObjectIdentifier, asn1::Tlv<'a>), } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Integer(7), }), b"\x30\x07\x06\x02\x2a\x03\x02\x01\x07", ), ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Other(OID2, asn1::parse_single(b"\x05\x00").unwrap()), }), b"\x30\x06\x06\x02\x2a\x05\x05\x00", ), ]) } #[test] fn test_defined_by_optional() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 5); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] OctetString(&'a [u8]), #[defined_by(OID2)] Other, } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::OctetString(b"abc"), }), b"\x30\x09\x06\x02\x2a\x03\x04\x03abc", ), ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Other, }), b"\x30\x04\x06\x02\x2a\x05", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::UnknownDefinedBy) .add_location(asn1::ParseLocation::Field("S::value")), ), b"\x30\x04\x06\x02\x2a\x07", ), ]); } #[test] fn test_defined_by_mod() { mod oids { pub const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); } #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(oids::OID1)] OctetString(&'a [u8]), } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::OctetString(b"abc"), }), b"\x30\x09\x06\x02\x2a\x03\x04\x03abc", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::UnknownDefinedBy) .add_location(asn1::ParseLocation::Field("S::value")), ), b"\x30\x04\x06\x02\x2a\x07", ), ]); } #[test] fn test_defined_by_explicit() { pub const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: asn1::Explicit, 1>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] OctetString(&'a [u8]), } assert_roundtrips::(&[( Ok(S { oid: asn1::DefinedByMarker::marker(), value: asn1::Explicit::new(Value::OctetString(b"abc")), }), b"\x30\x0b\x06\x02\x2a\x03\xa1\x05\x04\x03abc", )]); } #[test] fn test_generics() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S { value: T, } assert_roundtrips::>(&[(Ok(S { value: 12 }), b"\x30\x03\x02\x01\x0c")]); assert_roundtrips::>(&[(Ok(S { value: true }), b"\x30\x03\x01\x01\xff")]); assert_eq!( asn1::write_single(&S { value: asn1::SequenceOfWriter::new([true, true]), }) .unwrap(), b"\x30\x08\x30\x06\x01\x01\xff\x01\x01\xff" ) } #[test] fn test_perfect_derive() { trait X { type Type: PartialEq + std::fmt::Debug; } #[derive(PartialEq, Debug)] struct Op; impl X for Op { type Type = u64; } #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S { value: T::Type, } assert_roundtrips::>(&[(Ok(S { value: 12 }), b"\x30\x03\x02\x01\x0c")]); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct TaggedRequiredFields { #[implicit(1, required)] a: T::Type, #[explicit(2, required)] b: T::Type, } assert_roundtrips::>(&[( Ok(TaggedRequiredFields { a: 1, b: 3 }), b"\x30\x08\x81\x01\x01\xa2\x03\x02\x01\x03", )]); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct TaggedOptionalFields { #[implicit(1)] a: Option, #[explicit(2)] b: Option, } assert_roundtrips::>(&[ ( Ok(TaggedOptionalFields { a: Some(1), b: Some(3), }), b"\x30\x08\x81\x01\x01\xa2\x03\x02\x01\x03", ), (Ok(TaggedOptionalFields { a: None, b: None }), b"\x30\x00"), ]); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum TaggedEnum { #[implicit(0)] Implicit(T::Type), #[explicit(1)] Explicit(T::Type), } assert_roundtrips::>(&[ (Ok(TaggedEnum::Implicit(1)), b"\x80\x01\x01"), (Ok(TaggedEnum::Explicit(1)), b"\xa1\x03\x02\x01\x01"), ]); }