Skip to main content

rasn/
aper.rs

1//! # Aligned Packed Encoding Rules
2//!
3//! Codec functions for APER, rasn provides a "basic" decoder, and canonical encoder.
4//! This means that users are able decode any valid APER value, and that rasn's
5//! encoding will always produce the same output for the same value.
6use crate::types::Constraints;
7
8pub use super::per::*;
9
10/// Attempts to decode `T` from `input` using APER-BASIC.
11pub fn decode<T: crate::Decode>(input: &[u8]) -> Result<T, crate::error::DecodeError> {
12    crate::per::decode(de::DecoderOptions::aligned(), input)
13}
14/// Attempts to decode `T` from `input` using APER-BASIC. Returns both `T` and reference to the remainder of the input.
15///
16/// # Errors
17/// Returns `DecodeError` if `input` is not valid APER-BASIC encoding specific to the expected type.
18pub fn decode_with_remainder<T: crate::Decode>(
19    input: &[u8],
20) -> Result<(T, &[u8]), crate::error::DecodeError> {
21    crate::per::decode_with_remainder(de::DecoderOptions::aligned(), input)
22}
23
24/// Attempts to encode `value` to APER-CANONICAL.
25pub fn encode<T: crate::Encode>(
26    value: &T,
27) -> Result<alloc::vec::Vec<u8>, crate::error::EncodeError> {
28    crate::per::encode(enc::EncoderOptions::aligned(), value)
29}
30
31/// Attempts to decode `T` from `input` using APER-BASIC.
32pub fn decode_with_constraints<T: crate::Decode>(
33    constraints: Constraints,
34    input: &[u8],
35) -> Result<T, crate::error::DecodeError> {
36    crate::per::decode_with_constraints(de::DecoderOptions::aligned(), constraints, input)
37}
38
39/// Attempts to encode `value` to APER-CANONICAL.
40pub fn encode_with_constraints<T: crate::Encode>(
41    constraints: Constraints,
42    value: &T,
43) -> Result<alloc::vec::Vec<u8>, crate::error::EncodeError> {
44    crate::per::encode_with_constraints(enc::EncoderOptions::aligned(), constraints, value)
45}
46
47#[cfg(test)]
48mod tests {
49    use crate::{
50        prelude::*,
51        types::{constraints::*, *},
52    };
53
54    #[test]
55    fn bitstring() {
56        use bitvec::prelude::*;
57        // B ::= BIT STRING (SIZE (9))
58        // C ::= BIT STRING (SIZE (5..7))
59
60        #[allow(dead_code)]
61        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
62        #[rasn(crate_root = "crate")]
63        struct D {
64            a: bool,
65            b: BitString,
66        }
67
68        #[allow(dead_code)]
69        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
70        #[rasn(crate_root = "crate")]
71        struct E {
72            a: bool,
73            #[rasn(size(1))]
74            b: BitString,
75            #[rasn(size(16))]
76            c: BitString,
77        }
78
79        #[allow(dead_code)]
80        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
81        #[rasn(crate_root = "crate")]
82        struct G {
83            a: BitString,
84            b: bool,
85        }
86
87        // H ::= SEQUENCE SIZE (0..2) OF BIT STRING (SIZE(1..255))
88        // I ::= SEQUENCE SIZE (0..2) OF BIT STRING (SIZE(1..256))
89        // J ::= SEQUENCE SIZE (0..2) OF BIT STRING (SIZE(2..256))
90        // K ::= SEQUENCE SIZE (0..2) OF BIT STRING (SIZE(2..257))
91        // L ::= BIT STRING (SIZE (1..160, ...))
92
93        #[allow(dead_code)]
94        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
95        #[rasn(crate_root = "crate")]
96        struct M {
97            a: bool,
98            #[rasn(size("1..=160", extensible))]
99            b: BitString,
100        }
101
102        // N ::= BIT STRING (SIZE(0..65535))
103        // O ::= BIT STRING (SIZE(0..65536))
104
105        round_trip!(
106            aper,
107            BitString,
108            bitvec::bitvec![u8, Msb0; 0, 1, 0, 0],
109            &[0x04, 0x40]
110        );
111        // round_trip!(
112        //     aper,
113        //     BitString,
114        //     BitString::from_vec({
115        //         let mut bytes = vec![0x55; 300];
116        //         bytes[299] = 0x54;
117        //         bytes
118        //     }),
119        //     &*{
120        //         let mut bytes = vec![0x89, 0x5f];
121        //         bytes.extend([0x55; 299]);
122        //         bytes.push(0x54);
123        //         bytes
124        //     }
125        // );
126        round_trip!(
127            aper,
128            BitString,
129            BitString::from_vec([0x55; 2048].into()),
130            &*{
131                let mut bytes = vec![0xc1];
132                bytes.extend([0x55; 2048]);
133                bytes.push(0x00);
134                bytes
135            }
136        );
137        // round_trip!(aper, B, (b'\x12\x80', 9), b'\x12\x80');
138        // round_trip!(aper, C, (b'\x34', 6), b'\x40\x34');
139        // round_trip!(aper, D, {'a': True, 'b': (b'\x40', 4)}, b'\x80\x04\x40');
140        // round_trip!(aper, E, {'a': True, 'b': (b'\x80', 1), 'c': (b'\x7f\x01', 16)}, b'\xdf\xc0\x40');
141        // round_trip!(aper, F, (b'\x80', 1), b'\x01\x80');
142        // round_trip!(aper, F, (b'\xe0', 3), b'\x03\xe0');
143        // round_trip!(aper, F, (b'\x01', 8), b'\x08\x01');
144        // round_trip!(aper, G, {'a': (b'\x80', 2), 'b': True}, b'\x02\xa0');
145        // round_trip!(aper, G, {'a': (b'', 0), 'b': True}, b'\x00\x80');
146        // round_trip!(aper, H, [(b'\x40', 2)], b'\x40\x40\x40');
147        // round_trip!(aper, I, [(b'\x40', 2)], b'\x40\x01\x40');
148        // round_trip!(aper, J, [(b'\x40', 2)], b'\x40\x00\x40');
149        // round_trip!(aper, K, [(b'\x40', 2)], b'\x40\x00\x40');
150        // round_trip!(aper, L, (b'\x80', 1), b'\x00\x00\x80');
151        // round_trip!(aper, M, {'a': True, 'b': (b'\xe0', 3)}, b'\x80\x80\xe0');
152        // round_trip!(aper, N, (b'', 0), b'\x00\x00');
153        // round_trip!(aper, O, (b'', 0), b'\x00');
154    }
155
156    #[test]
157    #[allow(clippy::too_many_lines)]
158    fn integer() {
159        type B = ConstrainedInteger<5, 99>;
160
161        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
162        #[rasn(crate_root = "crate")]
163        struct C {
164            a: bool,
165            b: Integer,
166            c: bool,
167            #[rasn(value("-10..=400"))]
168            d: Integer,
169        }
170
171        type D = ConstrainedInteger<0, 254>;
172        type E = ConstrainedInteger<0, 255>;
173        type F = ConstrainedInteger<0, 256>;
174        type G = ConstrainedInteger<0, 65535>;
175        type H = ConstrainedInteger<0, 65536>;
176        type I = ConstrainedInteger<0, 10_000_000_000>;
177
178        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
179        #[rasn(crate_root = "crate")]
180        struct J {
181            a: bool,
182            #[rasn(value("0..=254"))]
183            b: Integer,
184            #[rasn(value("0..=255"))]
185            c: Integer,
186            d: bool,
187            #[rasn(value("0..=256"))]
188            e: Integer,
189        }
190
191        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
192        #[rasn(crate_root = "crate")]
193        struct L {
194            #[rasn(value("7..=7"))]
195            a: Integer,
196        }
197
198        type N = ConstrainedInteger<0, 65535>;
199        type O = ConstrainedInteger<0, 65536>;
200        type P = ConstrainedInteger<0, 2_147_483_647>;
201        type Q = ConstrainedInteger<0, 4_294_967_295>;
202        type R = ConstrainedInteger<0, 4_294_967_296>;
203
204        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
205        #[rasn(crate_root = "crate")]
206        struct S {
207            a: bool,
208            #[rasn(value("-10000..=704000000000000001"))]
209            b: Integer,
210            c: bool,
211        }
212
213        round_trip!(aper, Integer, 32768.into(), &[0x03, 0x00, 0x80, 0x00]);
214        round_trip!(aper, Integer, 32767.into(), &[0x02, 0x7f, 0xff]);
215        round_trip!(aper, Integer, 256.into(), &[0x02, 0x01, 0x00]);
216        round_trip!(aper, Integer, 255.into(), &[0x02, 0x00, 0xff]);
217        round_trip!(aper, Integer, 128.into(), &[0x02, 0x00, 0x80]);
218        round_trip!(aper, Integer, 127.into(), &[0x01, 0x7f]);
219        round_trip!(aper, Integer, 1.into(), &[0x01, 0x01]);
220        round_trip!(aper, Integer, 0.into(), &[0x01, 0x00]);
221        round_trip!(aper, Integer, (-1).into(), &[0x01, 0xff]);
222        round_trip!(aper, Integer, (-128).into(), &[0x01, 0x80]);
223        round_trip!(aper, Integer, (-129).into(), &[0x02, 0xff, 0x7f]);
224        round_trip!(aper, Integer, (-256).into(), &[0x02, 0xff, 0x00]);
225        round_trip!(aper, Integer, (-32768).into(), &[0x02, 0x80, 0x00]);
226        round_trip!(aper, Integer, (-32769).into(), &[0x03, 0xff, 0x7f, 0xff]);
227        round_trip!(aper, B, B::new(5), &[0x00]);
228        round_trip!(aper, B, B::new(6), &[0x02]);
229        round_trip!(aper, B, B::new(99), &[0xbc]);
230        round_trip!(
231            aper,
232            C,
233            C {
234                a: true,
235                b: Integer::from(43_554_344_223_i64),
236                c: false,
237                d: Integer::from(-9)
238            },
239            &[0x80, 0x05, 0x0a, 0x24, 0x0a, 0x8d, 0x1f, 0x00, 0x00, 0x01]
240        );
241        round_trip!(aper, D, D::new(253), &[0xfd]);
242        round_trip!(aper, E, E::new(253), &[0xfd]);
243        round_trip!(aper, F, F::new(253), &[0x00, 0xfd]);
244        round_trip!(aper, G, G::new(253), &[0x00, 0xfd]);
245        round_trip!(aper, H, H::new(253), &[0x00, 0xfd]);
246        round_trip!(aper, H, H::new(256), &[0x40, 0x01, 0x00]);
247        round_trip!(aper, H, H::new(65536), &[0x80, 0x01, 0x00, 0x00]);
248        round_trip!(aper, I, I::new(0), &[0x00, 0x00]);
249        round_trip!(aper, I, I::new(1), &[0x00, 0x01]);
250        round_trip!(
251            aper,
252            I,
253            I::new(10_000_000_000_i64),
254            &[0x80, 0x02, 0x54, 0x0b, 0xe4, 0x00]
255        );
256        round_trip!(
257            aper,
258            J,
259            J {
260                a: false,
261                b: Integer::from(253),
262                c: Integer::from(253),
263                d: false,
264                e: Integer::from(253)
265            },
266            &[0x7e, 0x80, 0xfd, 0x00, 0x00, 0xfd]
267        );
268        round_trip!(
269            aper,
270            L,
271            L {
272                a: Integer::from(7)
273            },
274            &[]
275        );
276        // round_trip!(aper, M, 103.into(), &[0x80, 0x01, 0x67]);
277        round_trip!(aper, N, N::new(1), &[0x00, 0x01]);
278        round_trip!(aper, N, N::new(255), &[0x00, 0xff]);
279        round_trip!(aper, N, N::new(256), &[0x01, 0x00]);
280        round_trip!(aper, N, N::new(65535), &[0xff, 0xff]);
281        round_trip!(aper, O, O::new(1), &[0x00, 0x01]);
282        round_trip!(aper, O, O::new(255), &[0x00, 0xff]);
283        round_trip!(aper, O, O::new(256), &[0x40, 0x01, 0x00]);
284        round_trip!(aper, O, O::new(65535), &[0x40, 0xff, 0xff]);
285        round_trip!(aper, O, O::new(65536), &[0x80, 0x01, 0x00, 0x00]);
286        round_trip!(aper, P, P::new(1), &[0x00, 0x01]);
287        round_trip!(aper, P, P::new(255), &[0x00, 0xff]);
288        round_trip!(aper, P, P::new(256), &[0x40, 0x01, 0x00]);
289        round_trip!(aper, P, P::new(65535), &[0x40, 0xff, 0xff]);
290        round_trip!(aper, P, P::new(65536), &[0x80, 0x01, 0x00, 0x00]);
291        round_trip!(aper, P, P::new(16_777_215), &[0x80, 0xff, 0xff, 0xff]);
292        round_trip!(aper, P, P::new(16_777_216), &[0xc0, 0x01, 0x00, 0x00, 0x00]);
293        round_trip!(
294            aper,
295            P,
296            P::new(100_000_000),
297            &[0xc0, 0x05, 0xf5, 0xe1, 0x00]
298        );
299        round_trip!(
300            aper,
301            Q,
302            Q::new(4_294_967_295_u64),
303            &[0xc0, 0xff, 0xff, 0xff, 0xff]
304        );
305        round_trip!(
306            aper,
307            R,
308            R::new(4_294_967_296_u64),
309            &[0x80, 0x01, 0x00, 0x00, 0x00, 0x00]
310        );
311        round_trip!(
312            aper,
313            S,
314            S {
315                a: true,
316                b: 0.into(),
317                c: true
318            },
319            &[0x90, 0x27, 0x10, 0x80]
320        );
321    }
322
323    #[test]
324    fn visible_string() {
325        // B ::= VisibleString (SIZE (5))
326        // C ::= VisibleString (SIZE (19..1000))
327        // D ::= SEQUENCE {
328        //   a BOOLEAN,
329        //   b VisibleString (SIZE (1))
330        // }
331        // H ::= SEQUENCE {
332        //   a BOOLEAN,
333        //   b VisibleString (SIZE (0..2))
334        // }
335        // I ::= VisibleString (FROM (\a\..\z\)) (SIZE (1..255))
336
337        #[allow(dead_code)]
338        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
339        #[rasn(crate_root = "crate")]
340        struct D {
341            a: bool,
342            #[rasn(size(1))]
343            b: VisibleString,
344        }
345
346        #[allow(dead_code)]
347        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
348        #[rasn(crate_root = "crate")]
349        struct E {
350            a: bool,
351            #[rasn(size(2))]
352            b: VisibleString,
353        }
354
355        #[allow(dead_code)]
356        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
357        #[rasn(crate_root = "crate")]
358        struct F {
359            a: bool,
360            #[rasn(size(3))]
361            b: VisibleString,
362        }
363
364        #[allow(dead_code)]
365        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
366        #[rasn(crate_root = "crate")]
367        struct G {
368            a: bool,
369            #[rasn(size("0..=1"))]
370            b: VisibleString,
371        }
372
373        #[allow(dead_code)]
374        #[derive(Debug, AsnType, Decode, Encode, PartialEq)]
375        #[rasn(crate_root = "crate")]
376        struct H {
377            a: bool,
378            #[rasn(size("0..=2"))]
379            b: VisibleString,
380        }
381        // J ::= VisibleString (FROM (\a\))
382        // K ::= VisibleString (FROM (\a\..\a\))
383
384        // round_trip_with_constraints!(
385        //     aper,
386        //     VisibleString,
387        //     Constraints::new(&[Constraint::Size(Size::new(Bounded::new(19, 133)).into())]),
388        //     VisibleString::try_from("HejHoppHappHippAbcde").unwrap(),
389        //     &[
390        //         0x02, 0x48, 0x65, 0x6a, 0x48, 0x6f, 0x70, 0x70, 0x48, 0x61, 0x70, 0x70, 0x48, 0x69,
391        //         0x70, 0x70, 0x41, 0x62, 0x63, 0x64, 0x65
392        //     ]
393        // );
394        // round_trip_with_constraints!(
395        //     aper,
396        //     VisibleString,
397        //     Constraints::new(&[Constraint::Size(Size::new(Bounded::Single(5)).into())]),
398        //     VisibleString::try_from("Hejaa").unwrap(),
399        //     &[0x48, 0x65, 0x6a, 0x61, 0x61]
400        // );
401        // round_trip_with_constraints!(
402        //     aper,
403        //     VisibleString,
404        //     Constraints::new(&[Constraint::Size(Size::new(Bounded::new(19, 1000)).into())]),
405        //     VisibleString::try_from(str::repeat("HejHoppHappHippAbcde", 17)).unwrap(),
406        //     &*{
407        //         let mut bytes = vec![0x01, 0x41];
408        //         for _ in 0..17 {
409        //             bytes.extend([
410        //                 0x48, 0x65, 0x6a, 0x48, 0x6f, 0x70, 0x70, 0x48, 0x61,
411        //                 0x70, 0x70, 0x48, 0x69, 0x70, 0x70, 0x41, 0x62, 0x63,
412        //                 0x64, 0x65
413        //             ]);
414        //         }
415        //         bytes
416        //     }
417        // );
418        // round_trip!(aper, D, D { a: true, b: "1".try_into().unwrap() }, &[0x98, 0x80]);
419        // round_trip!(aper, E, E { a: true, b: "12".try_into().unwrap() }, &[0x98, 0x99, 0x00]);
420        // round_trip!(aper, F, F { a: true, b: "123".try_into().unwrap() }, &[0x80, 0x31, 0x32, 0x33]);
421        // round_trip!(aper, G, G { a: true, b: "1".try_into().unwrap() }, &[0xcc, 0x40]);
422        // round_trip!(aper, H, H { a: true, b: "1".try_into().unwrap() }, &[0xa0, 0x31]);
423        const PERMITTED_CONSTRAINT: Constraints = constraints!(
424            permitted_alphabet_constraint!(&[
425                b'a' as u32,
426                b'b' as u32,
427                b'c' as u32,
428                b'd' as u32,
429                b'e' as u32,
430                b'f' as u32,
431                b'g' as u32,
432                b'h' as u32,
433                b'i' as u32,
434                b'j' as u32,
435                b'k' as u32,
436                b'l' as u32,
437                b'm' as u32,
438                b'n' as u32,
439                b'o' as u32,
440                b'p' as u32,
441                b'q' as u32,
442                b'r' as u32,
443                b's' as u32,
444                b't' as u32,
445                b'u' as u32,
446                b'v' as u32,
447                b'w' as u32,
448                b'x' as u32,
449                b'y' as u32,
450                b'z' as u32,
451            ]),
452            size_constraint!(1, 255)
453        );
454        round_trip_with_constraints!(
455            aper,
456            VisibleString,
457            PERMITTED_CONSTRAINT,
458            VisibleString::try_from("hej").unwrap(),
459            &[0x02, 0x68, 0x65, 0x6a]
460        );
461        const PERMITTED_CONSTRAINT_2: Constraints =
462            constraints!(permitted_alphabet_constraint!(&[b'a' as u32]));
463        round_trip_with_constraints!(
464            aper,
465            VisibleString,
466            PERMITTED_CONSTRAINT_2,
467            VisibleString::try_from("a").unwrap(),
468            &[0x01]
469        );
470    }
471
472    #[test]
473    fn issue_192() {
474        // https://github.com/XAMPPRocky/rasn/issues/192
475        use crate as rasn;
476
477        use rasn::AsnType;
478
479        #[derive(rasn::AsnType, rasn::Encode, rasn::Decode, Debug, Clone, PartialEq, Eq)]
480        #[rasn(automatic_tags)]
481        #[non_exhaustive]
482        pub struct Updates {
483            pub updates: Vec<u8>,
484        }
485
486        #[derive(rasn::AsnType, rasn::Encode, rasn::Decode, Debug, Clone, PartialEq, Eq)]
487        #[rasn(automatic_tags)]
488        #[rasn(choice)]
489        #[non_exhaustive]
490        pub enum Message {
491            Updates(Updates),
492        }
493
494        let msg = Message::Updates(Updates { updates: vec![1] });
495
496        round_trip!(aper, Message, msg, &[0, 1, 1]);
497    }
498
499    #[test]
500    fn issue_201() {
501        use crate as rasn;
502        use crate::prelude::*;
503
504        const T124_IDENTIFIER_KEY: &Oid = Oid::const_new(&[0, 0, 20, 124, 0, 1]);
505        #[derive(Debug, AsnType, Encode, rasn::Decode)]
506        #[rasn(choice, automatic_tags)]
507        enum Key {
508            #[rasn(tag(explicit(5)))]
509            Object(ObjectIdentifier),
510            H221NonStandard(OctetString),
511        }
512
513        #[derive(Debug, AsnType, rasn::Encode, rasn::Decode)]
514        #[rasn(automatic_tags)]
515        struct ConnectData {
516            t124_identifier_key: Key,
517            connect_pdu: OctetString,
518        }
519
520        let connect_pdu: OctetString = vec![0u8, 1u8, 2u8, 3u8].into();
521        let connect_data = ConnectData {
522            t124_identifier_key: Key::Object(T124_IDENTIFIER_KEY.into()),
523            connect_pdu,
524        };
525
526        let encoded = rasn::aper::encode(&connect_data).expect("failed to encode");
527        assert_eq!(
528            encoded,
529            vec![
530                0x00, 0x05, 0x00, 0x14, 0x7C, 0x00, 0x01, 0x04, 0x00, 0x01, 0x02, 0x03
531            ]
532        );
533        let _: ConnectData = rasn::aper::decode(&encoded).expect("failed to decode");
534    }
535}