pub use super::oer::*;
use crate::error::{DecodeError, EncodeError};
use crate::types::Constraints;
pub fn decode<T: crate::Decode>(input: &[u8]) -> Result<T, DecodeError> {
T::decode(&mut Decoder::<0, 0>::new(input, de::DecoderOptions::coer()))
}
pub fn decode_with_remainder<T: crate::Decode>(input: &[u8]) -> Result<(T, &[u8]), DecodeError> {
let decoder = &mut Decoder::<0, 0>::new(input, de::DecoderOptions::coer());
let decoded_instance = T::decode(decoder)?;
Ok((decoded_instance, decoder.remaining()))
}
pub fn encode<T: crate::Encode>(value: &T) -> Result<alloc::vec::Vec<u8>, EncodeError> {
let mut buffer = alloc::vec::Vec::with_capacity(core::mem::size_of::<T>());
let mut worker = alloc::vec::Vec::new();
let mut enc = Encoder::<0>::from_buffer(enc::EncoderOptions::coer(), &mut buffer, &mut worker);
value.encode(&mut enc)?;
Ok(enc.output())
}
pub fn encode_buf<T: crate::Encode>(
value: &T,
buffer: &mut alloc::vec::Vec<u8>,
) -> Result<(), EncodeError> {
let mut worker = alloc::vec::Vec::new();
let mut enc = Encoder::<0>::from_buffer(enc::EncoderOptions::coer(), buffer, &mut worker);
value.encode(&mut enc)?;
Ok(())
}
pub fn decode_with_constraints<T: crate::Decode>(
constraints: Constraints,
input: &[u8],
) -> Result<T, DecodeError> {
T::decode_with_constraints(
&mut Decoder::<0, 0>::new(input, de::DecoderOptions::coer()),
constraints,
)
}
pub fn encode_with_constraints<T: crate::Encode>(
constraints: Constraints,
value: &T,
) -> Result<alloc::vec::Vec<u8>, EncodeError> {
let mut buffer = alloc::vec::Vec::with_capacity(core::mem::size_of::<T>());
let mut worker = alloc::vec::Vec::new();
let mut enc = Encoder::<0>::from_buffer(enc::EncoderOptions::coer(), &mut buffer, &mut worker);
value.encode_with_constraints(&mut enc, constraints)?;
Ok(enc.output())
}
#[cfg(test)]
#[allow(clippy::items_after_statements)]
mod tests {
use crate::prelude::*;
use crate::{self as rasn};
use bitvec::prelude::*;
#[test]
fn bool() {
round_trip!(coer, bool, true, &[0xff]);
round_trip!(coer, bool, false, &[0]);
}
#[test]
#[allow(clippy::too_many_lines)]
fn integer_no_constraints() {
round_trip!(coer, Integer, 0.into(), &[0x01u8, 0x00]);
round_trip!(coer, Integer, 1.into(), &[0x01u8, 0x01]);
round_trip!(coer, Integer, (-1).into(), &[0x01u8, 0xff]);
round_trip!(coer, Integer, 255.into(), &[0x02u8, 0x00, 0xff]);
round_trip!(coer, Integer, (-255).into(), &[0x02u8, 0xff, 0x01]);
round_trip!(coer, Integer, i16::MAX.into(), &[0x02u8, 0x7f, 0xff]);
round_trip!(coer, Integer, i16::MIN.into(), &[0x02u8, 0x80, 0x00]);
round_trip!(
coer,
Integer,
(i32::from(i16::MAX) + 1).into(),
&[0x03u8, 0x00u8, 0x80, 0x00]
);
round_trip!(
coer,
Integer,
(i32::from(i16::MIN) - 1).into(),
&[0x03u8, 0xff, 0x7f, 0xff]
);
round_trip!(
coer,
Integer,
i32::MAX.into(),
&[0x04u8, 0x7f, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
Integer,
i32::MIN.into(),
&[0x04u8, 0x80, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
Integer,
(i64::from(i32::MAX) + 1).into(),
&[0x05u8, 0x00, 0x80, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
Integer,
(i64::from(i32::MIN) - 1).into(),
&[0x05u8, 0xff, 0x7f, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
Integer,
i64::MAX.into(),
&[0x08u8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
Integer,
i64::MIN.into(),
&[0x08u8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
Integer,
(i128::from(i64::MAX) + 1).into(),
&[0x09u8, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
Integer,
(i128::from(i64::MIN) - 1).into(),
&[0x09u8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
Integer,
i128::MAX.into(),
&[
0x10u8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff
]
);
round_trip!(
coer,
Integer,
i128::MIN.into(),
&[
0x10u8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
]
);
round_trip!(
coer,
Integer,
Integer::from(i128::MAX) + 1,
&[
0x11u8, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
]
);
round_trip!(
coer,
Integer,
Integer::from(i128::MIN) - 1,
&[
0x11u8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff
]
);
}
#[test]
fn test_integer_with_unsigned_constraints() {
type A = ConstrainedInteger<0, { u8::MAX as i128 }>;
type B = ConstrainedInteger<0, { u16::MAX as i128 }>;
type C = ConstrainedInteger<0, { u32::MAX as i128 }>;
type D = ConstrainedInteger<0, { u64::MAX as i128 }>;
type E = ConstrainedInteger<0, { i128::MAX }>;
type F = ConstrainedInteger<2, { u16::MAX as i128 }>;
round_trip!(coer, A, A::new(0), &[0x00]);
round_trip!(coer, A, A::new(5), &[0x05]);
round_trip!(coer, A, A::new(255), &[0xff]);
round_trip!(coer, B, B::new(0), &[0x00, 0x00]);
round_trip!(coer, B, B::new(255), &[0x00, 0xff]);
round_trip!(coer, C, C::new(0), &[0x00, 0x00, 0x00, 0x00]);
round_trip!(coer, C, C::new(u16::MAX), &[0x00, 0x00, 0xff, 0xff]);
round_trip!(
coer,
D,
0.try_into().unwrap(),
&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
D,
i128::from(u32::MAX).try_into().unwrap(),
&[0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
E,
(i128::from(u64::MAX) + 1).try_into().unwrap(),
&[0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
round_trip!(coer, F, F::new(2), &[0x00, 0x02]);
encode_error!(coer, A, A::new(-1));
encode_error!(coer, B, B::new(-1));
encode_error!(coer, C, C::new(-1));
encode_error!(coer, D, D::new(-1));
encode_error!(coer, E, E::new(-1));
encode_error!(coer, F, F::new(1));
encode_error!(coer, A, A::new(u16::from(u8::MAX) + 1));
encode_error!(coer, B, B::new(u32::from(u16::MAX) + 1));
encode_error!(coer, C, C::new(u64::from(u32::MAX) + 1));
encode_error!(coer, D, D::new(u128::from(u64::MAX) + 1));
}
#[test]
fn test_integer_with_signed_constraints() {
type A = ConstrainedInteger<{ i8::MIN as i128 }, { i8::MAX as i128 }>;
type B = ConstrainedInteger<{ i16::MIN as i128 }, { i16::MAX as i128 }>;
type C = ConstrainedInteger<{ i32::MIN as i128 }, { i32::MAX as i128 }>;
type D = ConstrainedInteger<{ i64::MIN as i128 }, { i64::MAX as i128 }>;
type E = ConstrainedInteger<-5, 5>;
round_trip!(coer, A, A::new(0), &[0x00]);
round_trip!(coer, A, A::new(-1), &[0xff]);
round_trip!(coer, A, A::new(i8::MIN), &[0x80]);
round_trip!(coer, A, A::new(i8::MAX), &[0x7f]);
round_trip!(coer, B, B::new(0), &[0x00, 0x00]);
round_trip!(coer, B, B::new(-1), &[0xff, 0xff]);
round_trip!(coer, B, B::new(i8::MIN), &[0xff, 0x80]);
round_trip!(coer, B, B::new(i8::MAX), &[0x00, 0x7f]);
round_trip!(coer, B, B::new(i16::MIN), &[0x80, 0x00]);
round_trip!(coer, B, B::new(i16::MAX), &[0x7f, 0xff]);
round_trip!(coer, C, 0.try_into().unwrap(), &[0x00, 0x00, 0x00, 0x00]);
round_trip!(coer, C, (-1).try_into().unwrap(), &[0xff, 0xff, 0xff, 0xff]);
round_trip!(coer, C, C::new(i16::MIN), &[0xff, 0xff, 0x80, 0x00]);
round_trip!(coer, C, C::new(i16::MAX), &[0x00, 0x00, 0x7f, 0xff]);
round_trip!(coer, C, C::new(i32::MIN), &[0x80, 0x00, 0x00, 0x00]);
round_trip!(coer, C, C::new(i32::MAX), &[0x7f, 0xff, 0xff, 0xff]);
round_trip!(
coer,
D,
D::new(0),
&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
D,
D::new(-1),
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
D,
D::new(i32::MIN),
&[0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
D,
D::new(i32::MAX),
&[0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff]
);
round_trip!(
coer,
D,
D::new(i64::MIN),
&[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
round_trip!(
coer,
D,
D::new(i64::MAX),
&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
);
round_trip!(coer, E, E::new(4), &[0x04]);
round_trip!(coer, E, E::new(-4), &[0xfc]);
encode_error!(coer, A, A::new(i16::from(i8::MIN) - 1));
encode_error!(coer, B, B::new(i32::from(i16::MIN) - 1));
encode_error!(coer, C, C::new(i64::from(i32::MIN) - 1));
encode_error!(coer, D, D::new(i128::from(i64::MIN) - 1));
encode_error!(coer, A, A::new(i16::from(i8::MAX) + 1));
encode_error!(coer, B, B::new(i32::from(i16::MAX) + 1));
encode_error!(coer, C, C::new(i64::from(i32::MAX) + 1));
encode_error!(coer, D, D::new(i128::from(i64::MAX) + 1));
}
#[test]
fn test_integer_single_constraint() {
const CONSTRAINTS: Constraints = constraints!(value_constraint!(5));
round_trip_with_constraints!(coer, Integer, CONSTRAINTS, 5.into(), &[0x05]);
}
#[test]
fn test_enumerated() {
#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq)]
#[rasn(enumerated, crate_root = "crate")]
enum Enum1 {
Green,
Red,
Blue,
}
round_trip!(coer, Enum1, Enum1::Green, &[0x00]);
round_trip!(coer, Enum1, Enum1::Red, &[0x01]);
round_trip!(coer, Enum1, Enum1::Blue, &[0x02]);
#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq)]
#[rasn(enumerated, crate_root = "crate")]
#[allow(clippy::items_after_statements)]
enum Enum2 {
Red,
Blue,
Green,
#[rasn(extension_addition_group)]
Yellow,
Purple,
}
round_trip!(coer, Enum2, Enum2::Red, &[0x00]);
round_trip!(coer, Enum2, Enum2::Yellow, &[0x03]);
round_trip!(coer, Enum2, Enum2::Purple, &[0x04]);
#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq)]
#[rasn(enumerated, crate_root = "crate")]
#[allow(clippy::items_after_statements)]
enum Enum3 {
Red = 5,
Blue = 6,
Green = 7,
}
round_trip!(coer, Enum3, Enum3::Red, &[0x05]);
round_trip!(coer, Enum3, Enum3::Blue, &[0x06]);
round_trip!(coer, Enum3, Enum3::Green, &[0x07]);
#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq)]
#[rasn(enumerated, crate_root = "crate")]
#[allow(clippy::items_after_statements)]
enum Enum4 {
Yes = 1000,
No = -1000,
}
round_trip!(coer, Enum4, Enum4::Yes, &[0x82, 0x03, 0xe8]);
round_trip!(coer, Enum4, Enum4::No, &[0x82, 0xfc, 0x18]);
}
#[test]
fn test_bit_string() {
round_trip!(
coer,
BitString,
BitString::from_slice(&[0x01]),
&[0x02, 0x00, 0x01]
);
round_trip!(coer, BitString, BitString::from_slice(&[]), &[0x01, 0x00]);
let mut bv = bitvec![u8, Msb0;];
bv.extend_from_raw_slice(&[0xff]);
bv.push(false);
bv.push(true);
bv.extend([false; 4].iter());
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(14));
round_trip_with_constraints!(
coer,
BitString,
CONSTRAINT_1,
BitString::from_bitslice(&bv),
&[0b1111_1111, 0b0100_0000]
);
round_trip!(
coer,
BitString,
BitString::from_bitslice(&bv),
&[0x03u8, 0x02, 0b1111_1111, 0b0100_0000]
);
const CONSTRAINT_2: Constraints = constraints!(size_constraint!(15));
encode_error_with_constraints!(
coer,
BitString,
CONSTRAINT_2,
BitString::from_bitslice(&bv)
);
}
#[test]
fn test_octet_string() {
round_trip!(
coer,
OctetString,
OctetString::from_static(&[0x01]),
&[0x01, 0x01]
);
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(5));
round_trip_with_constraints!(
coer,
OctetString,
CONSTRAINT_1,
OctetString::from_static(&[0x01u8, 0x02, 0x03, 0x04, 0x05]),
&[0x01u8, 0x02, 0x03, 0x04, 0x05]
);
const CONSTRAINT_2: Constraints = constraints!(size_constraint!(3, 6));
round_trip_with_constraints!(
coer,
OctetString,
CONSTRAINT_2,
OctetString::from_static(&[0x01u8, 0x02, 0x03, 0x04, 0x05]),
&[0x05u8, 0x01, 0x02, 0x03, 0x04, 0x05]
);
const CONSTRAINT_3: Constraints = constraints!(size_constraint!(5));
encode_error_with_constraints!(
coer,
OctetString,
CONSTRAINT_3,
OctetString::from_static(&[0x01u8, 0x02, 0x03, 0x04])
);
const CONSTRAINT_4: Constraints = constraints!(size_constraint!(3, 6));
encode_error_with_constraints!(
coer,
OctetString,
CONSTRAINT_4,
OctetString::from_static(&[0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
);
const CONSTRAINT_5: Constraints = constraints!(size_constraint!(3, 6));
encode_error_with_constraints!(
coer,
OctetString,
CONSTRAINT_5,
OctetString::from_static(&[0x01u8, 0x02])
);
}
#[test]
fn test_object_identifier() {
round_trip!(
coer,
ObjectIdentifier,
ObjectIdentifier::new(vec![1u32, 2]).unwrap(),
&[0x01u8, 0x2a]
);
round_trip!(
coer,
ObjectIdentifier,
ObjectIdentifier::new(vec![1, 2, 3321]).unwrap(),
&[0x03u8, 0x2a, 0x99, 0x79]
);
}
#[test]
fn test_choice() {
#[derive(AsnType, Decode, Debug, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
enum Choice {
Normal(Integer),
High(Integer),
#[rasn(extension_addition)]
Medium(Integer),
}
round_trip!(
coer,
Choice,
Choice::Normal(333.into()),
&[0x80, 0x02, 0x01, 0x4d]
);
round_trip!(
coer,
Choice,
Choice::High(333.into()),
&[0x81, 0x02, 0x01, 0x4d]
);
round_trip!(
coer,
Choice,
Choice::Medium(333.into()),
&[0x82, 0x03, 0x02, 0x01, 0x4d]
);
#[derive(AsnType, Decode, Debug, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
enum BoolChoice {
A(bool),
#[rasn(extension_addition)]
B(bool),
C(Choice),
}
round_trip!(coer, BoolChoice, BoolChoice::A(true), &[0x80, 0xff]);
round_trip!(coer, BoolChoice, BoolChoice::B(true), &[0x81, 0x01, 0xff]);
round_trip!(
coer,
BoolChoice,
BoolChoice::C(Choice::Normal(333.into())),
&[0x82, 0x80, 0x02, 0x01, 0x4d]
);
#[derive(AsnType, Decode, Debug, Encode, PartialEq)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
enum TripleChoice {
A(bool),
B(BoolChoice),
}
round_trip!(coer, TripleChoice, TripleChoice::A(true), &[0x80, 0xff]);
round_trip!(
coer,
TripleChoice,
TripleChoice::B(BoolChoice::C(Choice::Normal(333.into()))),
&[0x81, 0x82, 0x80, 0x02, 0x01, 0x4d]
);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
enum TestChoice {
Number1(()),
Number2(bool),
Number3(Box<TopLevel>),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
#[rasn(automatic_tags)]
struct TopLevel {
#[rasn(value("1..=8"))]
pub test: u8,
pub choice: TestChoice,
}
impl TopLevel {
pub fn new(test: u8, choice: TestChoice) -> Self {
Self { test, choice }
}
}
let test_value = TopLevel::new(
1,
TestChoice::Number3(Box::new(TopLevel {
test: 2,
choice: TestChoice::Number1(()),
})),
);
round_trip!(coer, TopLevel, test_value, &[1, 130, 2, 128]);
}
#[test]
fn test_tag_max_size() {
#[derive(AsnType, Decode, Debug, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
#[rasn(choice)]
enum Choice {
#[rasn(tag(0))]
Normal(Integer),
#[rasn(tag(1))]
High(Integer),
#[rasn(tag(4_294_967_295u32))]
Medium(Integer),
}
round_trip!(
coer,
Choice,
Choice::Medium(333.into()),
&[191, 143, 255, 255, 255, 127, 2, 1, 77]
);
}
#[test]
fn test_numeric_string() {
round_trip!(
coer,
NumericString,
"123".try_into().unwrap(),
&[0x03, 0x31, 0x32, 0x33]
);
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(3));
round_trip_with_constraints!(
coer,
NumericString,
CONSTRAINT_1,
"123".try_into().unwrap(),
&[0x31, 0x32, 0x33]
);
const CONSTRAINT_2: Constraints = constraints!(size_constraint!(3, 7));
round_trip_with_constraints!(
coer,
NumericString,
CONSTRAINT_2,
"123".try_into().unwrap(),
&[0x03, 0x31, 0x32, 0x33]
);
}
#[test]
fn test_printable_string() {
round_trip!(
coer,
PrintableString,
"foo".try_into().unwrap(),
&[0x03, 0x66, 0x6f, 0x6f]
);
round_trip!(
coer,
PrintableString,
" '()+,-./:=?".try_into().unwrap(),
&[
0x0c, 0x20, 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x3a, 0x3d, 0x3f
]
);
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(3));
round_trip_with_constraints!(
coer,
PrintableString,
CONSTRAINT_1,
"foo".try_into().unwrap(),
&[0x66, 0x6f, 0x6f]
);
const CONSTRAINT_2: Constraints = constraints!(size_constraint!(3, 7));
round_trip_with_constraints!(
coer,
PrintableString,
CONSTRAINT_2,
"foo".try_into().unwrap(),
&[0x03, 0x66, 0x6f, 0x6f]
);
}
#[test]
fn test_visible_string() {
round_trip!(
coer,
VisibleString,
"foo".try_into().unwrap(),
&[0x03, 0x66, 0x6f, 0x6f]
);
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(3));
round_trip_with_constraints!(
coer,
VisibleString,
CONSTRAINT_1,
"foo".try_into().unwrap(),
&[0x66, 0x6f, 0x6f]
);
const CONSTRAINT_2: Constraints = constraints!(size_constraint!(3, 7));
round_trip_with_constraints!(
coer,
VisibleString,
CONSTRAINT_2,
"foo".try_into().unwrap(),
&[0x03, 0x66, 0x6f, 0x6f]
);
}
#[test]
fn test_ia5_string() {
round_trip!(
coer,
Ia5String,
"foo".try_into().unwrap(),
&[0x03, 0x66, 0x6f, 0x6f]
);
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(3));
round_trip_with_constraints!(
coer,
Ia5String,
CONSTRAINT_1,
"foo".try_into().unwrap(),
&[0x66, 0x6f, 0x6f]
);
const CONSTRAINT_2: Constraints = constraints!(size_constraint!(3, 7));
round_trip_with_constraints!(
coer,
Ia5String,
CONSTRAINT_2,
"foo".try_into().unwrap(),
&[0x03, 0x66, 0x6f, 0x6f]
);
}
#[test]
fn invalid_ia5_oer() {
let data = [4, 4, 4, 129, 233, 0, 1, 0, 2, 4, 130, 236, 236];
decode_error!(coer, Ia5String, &data);
}
#[test]
fn test_general_string() {
round_trip!(
coer,
GeneralString,
GeneralString::from_bytes("".as_bytes()).unwrap(),
&[0x00]
);
round_trip!(
coer,
GeneralString,
GeneralString::from_bytes("2".as_bytes()).unwrap(),
&[0x01, 0x32]
);
}
#[test]
fn test_utf8_string() {
round_trip!(coer, Utf8String, String::new(), &[0x00]);
round_trip!(coer, Utf8String, "2".into(), &[0x01, 0x32]);
round_trip!(
coer,
Utf8String,
"2".repeat(128),
&[0x81, 0x80]
.iter()
.chain("2".repeat(128).as_bytes().iter())
.copied()
.collect::<Vec<_>>()
);
round_trip!(
coer,
Utf8String,
"ÄÖÄÖÄÖÄÖ12e4Ä".into(),
&[
0x16, 0xc3, 0x84, 0xc3, 0x96, 0xc3, 0x84, 0xc3, 0x96, 0xc3, 0x84, 0xc3, 0x96, 0xc3,
0x84, 0xc3, 0x96, 0x31, 0x32, 0x65, 0x34, 0xc3, 0x84
]
);
const CONSTRAINT_1: Constraints = constraints!(size_constraint!(3));
round_trip_with_constraints!(
coer,
Utf8String,
CONSTRAINT_1,
"foo".into(),
&[0x66, 0x6f, 0x6f]
);
}
#[test]
fn test_teletext_string() {
round_trip!(
coer,
TeletexString,
TeletexString::from_bytes("1234".as_bytes()).unwrap(),
&[0x04, 0x31, 0x32, 0x33, 0x34]
);
}
#[test]
fn test_generalized_time() {
use chrono::NaiveDate;
let offset = chrono::FixedOffset::east_opt(0).unwrap();
let dt = NaiveDate::from_ymd_opt(2080, 10, 9)
.unwrap()
.and_hms_micro_opt(13, 0, 5, 342_000)
.unwrap()
.and_local_timezone(offset);
round_trip!(
coer,
GeneralizedTime,
GeneralizedTime::from(dt.unwrap(),),
&[
0x13, 0x32, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x39, 0x31, 0x33, 0x30, 0x30, 0x30,
0x35, 0x2e, 0x33, 0x34, 0x32, 0x5a
]
);
let data = [
24, 19, 43, 53, 49, 54, 49, 53, 32, 32, 48, 53, 50, 52, 48, 57, 52, 48, 50, 48, 90,
];
assert!(crate::der::decode::<crate::types::Open>(&data).is_err());
}
#[test]
fn test_utc_time() {
round_trip!(
coer,
UtcTime,
UtcTime::from(
chrono::NaiveDate::from_ymd_opt(2019, 10, 9)
.unwrap()
.and_hms_opt(13, 0, 5)
.unwrap()
.and_utc()
),
&[
0x0d, 0x31, 0x39, 0x31, 0x30, 0x30, 0x39, 0x31, 0x33, 0x30, 0x30, 0x30, 0x35, 0x5a
]
);
}
#[test]
fn test_sequence_no_extensions() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
struct Sequence1 {
a: Integer,
b: Integer,
}
round_trip!(
coer,
Sequence1,
Sequence1 {
a: 1.into(),
b: 2.into()
},
&[0x01, 0x01, 0x01, 0x02]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence2 {
a: bool,
}
round_trip!(coer, Sequence2, Sequence2 { a: true }, &[0xff]);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence3 {
a: bool,
b: Sequence1,
}
round_trip!(
coer,
Sequence3,
Sequence3 {
a: true,
b: Sequence1 {
a: 1.into(),
b: 2.into()
}
},
&[0xff, 0x01, 0x01, 0x01, 0x02]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(crate_root = "crate", choice, automatic_tags)]
enum Choice1 {
A(bool),
B(Sequence1),
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
struct Sequence4 {
a: Integer,
b: Choice1,
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(crate_root = "crate")]
struct Sequence5 {
a: bool,
b: Sequence4,
}
round_trip!(
coer,
Sequence5,
Sequence5 {
a: true,
b: Sequence4 {
a: 1.into(),
b: Choice1::B(Sequence1 {
a: 1.into(),
b: 2.into()
})
}
},
&[0xff, 0x01, 0x01, 0x81, 0x01, 0x01, 0x01, 0x02]
);
}
#[test]
fn test_sequence_default_option() {
fn default_a() -> Integer {
0.into()
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence1 {
#[rasn(default = "default_a")]
a: Integer,
}
round_trip!(coer, Sequence1, Sequence1 { a: 0.into() }, &[0x00]);
round_trip!(
coer,
Sequence1,
Sequence1 { a: 1.into() },
&[0b1000_0000, 0x01, 0x01]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence2 {
a: Integer,
b: Option<Integer>,
}
round_trip!(
coer,
Sequence2,
Sequence2 {
a: 1.into(),
b: Some(2.into())
},
&[0b1000_0000, 0x01, 0x01, 0x01, 0x02]
);
round_trip!(
coer,
Sequence2,
Sequence2 {
a: 1.into(),
b: None
},
&[0x00, 0x01, 0x01]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence4 {
#[rasn(default = "default_a")]
a: Integer, b: Option<Integer>,
}
round_trip!(
coer,
Sequence4,
Sequence4 {
a: 0.into(),
b: None
},
&[0x00]
);
round_trip!(
coer,
Sequence4,
Sequence4 {
a: 1.into(),
b: Some(3.into())
},
&[0b1100_0000, 0x01, 0x01, 0x01, 0x03]
);
}
#[test]
#[allow(clippy::too_many_lines)]
fn test_sequence_with_extensions() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
#[non_exhaustive]
struct Sequence1 {
a: bool,
}
round_trip!(coer, Sequence1, Sequence1 { a: true }, &[0x00, 0xff]);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
#[non_exhaustive]
struct Sequence2 {
a: bool,
#[rasn(extension_addition)]
b: Option<bool>,
#[rasn(extension_addition)]
c: Option<bool>,
}
round_trip!(
coer,
Sequence2,
Sequence2 {
a: true,
b: Some(true),
c: Some(true)
},
&[0x80, 0xff, 0x02, 0x06, 0xc0, 0x01, 0xff, 0x01, 0xff]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
#[non_exhaustive]
struct Sequence3 {
a: bool,
#[rasn(extension_addition_group)]
b: Option<Sequence4>,
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence4 {
a: bool,
}
round_trip!(
coer,
Sequence3,
Sequence3 { a: true, b: None },
&[0x00, 0xff]
);
round_trip!(
coer,
Sequence3,
Sequence3 {
a: true,
b: Some(Sequence4 { a: true })
},
&[0x80, 0xff, 0x02, 0x07, 0x80, 0x01, 0xff]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
#[non_exhaustive]
struct Sequence5 {
a: bool,
#[rasn(extension_addition)]
b: Option<bool>,
}
round_trip!(
coer,
Sequence5,
Sequence5 { a: true, b: None },
&[0x00, 0xff]
);
round_trip!(
coer,
Sequence5,
Sequence5 {
a: true,
b: Some(true)
},
&[0x80, 0xff, 0x02, 0x07, 0x80, 0x01, 0xff]
);
#[derive(AsnType, Debug, Decode, Encode, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct ExtendedOptional {
pub value: Integer,
pub integer1: Option<Integer>,
pub octet1: Option<OctetString>,
pub integer2: Option<Integer>,
pub octet2: Option<OctetString>,
pub integer3: Option<Integer>,
pub octet3: Option<OctetString>,
#[rasn(extension_addition)]
pub integer4: Option<Integer>,
#[rasn(extension_addition)]
pub octet4: Option<OctetString>,
#[rasn(extension_addition)]
pub integer5: Option<Integer>,
#[rasn(extension_addition)]
pub octet5: Option<OctetString>,
}
round_trip!(
coer,
ExtendedOptional,
ExtendedOptional {
value: 1.into(),
integer1: Some(1_230_066_625_199_609_624u64.into()),
octet1: None,
integer2: None,
octet2: None,
integer3: Some(1.into()),
octet3: None,
integer4: None,
octet4: None,
integer5: None,
octet5: None
},
&[68, 1, 1, 8, 17, 18, 19, 20, 21, 22, 23, 24, 1, 1]
);
round_trip!(
coer,
ExtendedOptional,
ExtendedOptional {
value: 0.into(),
integer1: Some(1.into()),
octet1: None,
integer2: Some(2.into()),
octet2: None,
integer3: Some(3.into()),
octet3: Some(vec![4, 5, 6].into()),
integer4: Some(7.into()),
octet4: None,
integer5: Some(8.into()),
octet5: None
},
&[
0b1101_0110, 0x01,
0x00,
0x01,
0x01,
0x01,
0x02,
0x01,
0x03,
0x03,
0x04,
0x05,
0x06,
0x02, 0x04, 0b1010_0000,
0x02, 0x01,
0x07,
0x02,
0x01,
0x08
]
);
#[derive(AsnType, Debug, Decode, Encode, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(automatic_tags)]
pub struct ManyOptional {
pub value: Integer,
pub integer1: Option<Integer>,
pub octet1: Option<OctetString>,
pub integer2: Option<Integer>,
pub octet2: Option<OctetString>,
pub integer3: Option<Integer>,
pub octet3: Option<OctetString>,
pub integer4: Option<Integer>,
pub integer5: Option<Integer>,
pub integer6: Option<Integer>,
pub integer7: Option<Integer>,
pub integer8: Option<Integer>,
pub integer9: Option<Integer>,
}
round_trip!(
coer,
ManyOptional,
ManyOptional {
value: 1.into(),
integer1: Some(1_230_066_625_199_609_624u64.into()),
octet1: None,
integer2: None,
octet2: None,
integer3: Some(1.into()),
octet3: None,
integer4: None,
integer5: None,
integer6: Some(1.into()),
integer7: None,
integer8: None,
integer9: None
},
&[
136, 128, 1, 1, 8, 17, 18, 19, 20, 21, 22, 23, 24, 1, 1, 1, 1
]
);
}
#[test]
fn test_sequence_of() {
round_trip!(
coer,
SequenceOf::<Integer>,
SequenceOf::<Integer>::from(vec![]),
&[0x01, 0x00]
);
round_trip!(
coer,
SequenceOf::<Integer>,
SequenceOf::<Integer>::from(vec![1.into(), 2.into()]),
&[0x01, 0x02, 0x01, 0x01, 0x01, 0x02]
);
}
#[test]
fn test_set() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(set, tag(application, 0))]
struct Foo {
#[rasn(tag(explicit(444)))]
a: Option<Integer>,
#[rasn(tag(explicit(5)))]
b: Option<Integer>,
#[rasn(tag(application, 9))]
c: Option<Integer>,
}
round_trip!(
coer,
Foo,
Foo {
a: Some(5.into()),
b: Some(6.into()),
c: Some(7.into()),
},
&[0b1110_0000, 0x01, 0x07, 0x01, 0x06, 0x01, 0x05]
);
round_trip!(
coer,
Foo,
Foo {
a: None,
b: None,
c: Some(1.into()),
},
&[0b1000_0000, 0x01, 0x01]
);
}
#[test]
fn test_sequence_with_nested_opt() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence1 {
a: Integer,
b: Option<Integer>,
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence2 {
a: Integer,
b: Option<Sequence1>,
}
round_trip!(
coer,
Sequence2,
Sequence2 {
a: 1.into(),
b: Some(Sequence1 {
a: 2.into(),
b: Some(3.into())
})
},
&[0x80, 0x01, 0x01, 0x80, 0x01, 0x02, 0x01, 0x03]
);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence3 {
a: Integer,
b: Sequence2,
}
round_trip!(
coer,
Sequence3,
Sequence3 {
a: 1.into(),
b: Sequence2 {
a: 2.into(),
b: Some(Sequence1 {
a: 3.into(),
b: Some(4.into())
})
}
},
&[0x01, 0x01, 0x80, 0x01, 0x02, 0x80, 0x01, 0x03, 0x01, 0x04]
);
}
#[test]
fn test_boxed_sequence() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence1 {
a: Integer,
b: Option<Integer>,
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence2 {
a: Integer,
b: Box<Sequence1>,
}
round_trip!(
coer,
Sequence2,
Sequence2 {
a: 1.into(),
b: Box::new(Sequence1 {
a: 2.into(),
b: Some(3.into())
})
},
&[0x01, 0x01, 0x80, 0x01, 0x02, 0x01, 0x03]
);
}
#[test]
fn test_nested_boxed_sequence() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(choice, automatic_tags)]
enum Choice1 {
A(bool),
B(Box<Sequence1>),
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence1 {
a: Option<Integer>,
b: Choice1,
}
round_trip!(
coer,
Sequence1,
Sequence1 {
a: Some(1.into()),
b: Choice1::B(Box::new(Sequence1 {
a: Some(2.into()),
b: Choice1::A(true)
}))
},
&[0x80, 0x01, 0x01, 0x81, 0x80, 0x01, 0x02, 0x80, 0xff]
);
}
#[test]
fn test_empty_sequence() {
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence1 {}
round_trip!(coer, Sequence1, Sequence1 {}, &[]);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence2 {
a: Option<Integer>,
b: Option<Integer>,
}
round_trip!(coer, Sequence2, Sequence2 { a: None, b: None }, &[0x00]);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(automatic_tags)]
struct Sequence3 {
#[rasn(default = "default_a")]
a: Integer,
#[rasn(default = "default_b")]
b: Integer,
}
fn default_a() -> Integer {
0.into()
}
fn default_b() -> Integer {
1.into()
}
round_trip!(
coer,
Sequence3,
Sequence3 {
a: 0.into(),
b: 1.into()
},
&[0x00]
);
}
#[test]
fn test_constrained_option() {
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(delegate, size("3"))]
pub struct HashedId3(pub OctetString);
#[derive(AsnType, Debug, Decode, Encode, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(automatic_tags)]
pub struct ConstrainedOptions {
pub a: Option<HashedId3>,
}
round_trip!(
coer,
ConstrainedOptions,
ConstrainedOptions { a: None },
&[0x00]
);
round_trip!(
coer,
ConstrainedOptions,
ConstrainedOptions {
a: Some(HashedId3(OctetString::from_static(&[0x01, 0x02, 0x03])))
},
&[0x80, 0x01, 0x02, 0x03]
);
}
#[test]
fn test_null_in_option() {
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct Omitted {
pub a: Option<OctetString>,
#[rasn(extension_addition)]
pub omitted: Option<()>,
}
round_trip!(
coer,
Omitted,
Omitted {
a: Some(OctetString::from_static(&[0x00, 0x01, 0x02])),
omitted: Some(())
},
&[192, 3, 0, 1, 2, 2, 7, 128, 0]
);
}
#[test]
fn test_untagged_duplicate_type_option_on_sequence() {
use crate as rasn;
#[derive(AsnType, Decode, Encode, Clone, Debug, PartialEq, Eq)]
pub struct SequenceOptionals {
pub it: Integer,
pub is: Option<OctetString>,
pub late: Option<Integer>,
}
round_trip!(
coer,
SequenceOptionals,
SequenceOptionals {
it: 1.into(),
is: Some(OctetString::from_static(&[0x01, 0x02, 0x03])),
late: None
},
&[0b1000_0000, 0x01, 0x01, 0x03, 0x01, 0x02, 0x03]
);
#[derive(AsnType, Decode, Encode, Clone, Debug, PartialEq, Eq)]
pub struct SequenceOptionalsExplicit {
#[rasn(tag(explicit(0)))]
pub it: Integer,
#[rasn(tag(explicit(1)))]
pub is: Option<OctetString>,
#[rasn(tag(explicit(2)))]
pub late: Option<Integer>,
}
round_trip!(
coer,
SequenceOptionalsExplicit,
SequenceOptionalsExplicit {
it: 42.into(),
is: None,
late: None
},
&[0, 1, 42]
);
#[derive(AsnType, Decode, Encode, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct SequenceDuplicatesExtended {
pub it: Integer,
pub is: Option<OctetString>,
pub late: Option<Integer>,
#[rasn(extension_addition)]
pub today: OctetString,
}
round_trip!(
coer,
SequenceDuplicatesExtended,
SequenceDuplicatesExtended {
it: 1.into(),
is: Some(OctetString::from_static(&[0x02, 0x03, 0x04])),
late: None,
today: OctetString::from_static(&[0x05, 0x06, 0x07])
},
&[
0b1100_0000,
0x01,
0x01,
0x03,
0x02,
0x03,
0x04,
0x02,
0x07,
0b1000_0000,
0x04,
0x03,
0x05,
0x06,
0x07
]
);
}
#[test]
fn test_decode_with_remainder() {
let bytes = vec![0x04, 0x01, 0x02, 0x03, 0x04, 0x05];
let (octets, remaining): (OctetString, &[u8]) =
rasn::coer::decode_with_remainder(&bytes).unwrap();
assert_eq!(octets, vec![0x01, 0x02, 0x03, 0x04]);
assert_eq!(remaining[0], 0x05);
let (octets, remaining): (OctetString, &[u8]) =
rasn::oer::decode_with_remainder(&bytes).unwrap();
assert_eq!(octets, vec![0x01, 0x02, 0x03, 0x04]);
assert_eq!(remaining[0], 0x05);
}
#[test]
fn test_delegate_constraints() {
#[derive(AsnType, Debug, Clone, Encode, Decode, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[rasn(delegate, value("0.."))]
pub struct Positive(pub Integer);
let negative = Positive(Integer::from(-1));
let encoded = rasn::coer::encode(&negative);
assert!(encoded.is_err());
#[derive(Debug, Clone, AsnType, Encode, Decode, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[rasn(delegate, value("..0"))]
pub struct Negative<T: AsnType + Encode + Decode>(T);
let positive = Negative(Positive(Integer::from(1)));
let encoded = rasn::coer::encode(&positive);
assert!(encoded.is_err());
let negative = Negative(Integer::from(-1));
let encoded = rasn::coer::encode(&negative);
assert!(encoded.is_ok());
}
}