Skip to main content

der/asn1/
context_specific.rs

1//! Context-specific field.
2
3use crate::{
4    Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error, Header,
5    Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
6    tag::IsConstructed,
7};
8use core::cmp::Ordering;
9
10#[cfg(doc)]
11use crate::ErrorKind;
12
13impl_custom_class!(
14    ContextSpecific,
15    ContextSpecific,
16    "CONTEXT-SPECIFIC",
17    "0b10000000"
18);
19impl_custom_class_ref!(
20    ContextSpecificRef,
21    ContextSpecific,
22    "CONTEXT-SPECIFIC",
23    "0b10000000"
24);
25
26#[cfg(test)]
27#[allow(clippy::unwrap_used)]
28mod tests {
29    use super::ContextSpecific;
30    use crate::{Decode, Encode, SliceReader, TagMode, TagNumber, asn1::BitStringRef};
31    use hex_literal::hex;
32
33    #[cfg(feature = "heapless")]
34    use crate::asn1::{ContextSpecificRef, SetOf, Utf8StringRef};
35
36    #[test]
37    fn value_ord() {
38        use crate::{DerOrd, ValueOrd};
39        use core::cmp::Ordering;
40
41        for tag_mode in [TagMode::Explicit, TagMode::Implicit] {
42            let field = |value: u8| ContextSpecific {
43                tag_number: TagNumber(1),
44                tag_mode,
45                value,
46            };
47            assert_eq!(field(5).value_cmp(&field(6)), Ok(Ordering::Less));
48            assert_eq!(field(6).der_cmp(&field(5)), Ok(Ordering::Greater));
49            assert_eq!(field(5).der_cmp(&field(5)), Ok(Ordering::Equal));
50        }
51    }
52
53    // Public key data from `pkcs8` crate's `ed25519-pkcs8-v2.der`
54    const EXAMPLE_BYTES: &[u8] =
55        &hex!("A123032100A3A7EAE3A8373830BC47E1167BC50E1DB551999651E0E2DC587623438EAC3F31");
56
57    #[test]
58    fn round_trip() {
59        let field = ContextSpecific::<BitStringRef<'_>>::from_der(EXAMPLE_BYTES).unwrap();
60        assert_eq!(field.tag_number.value(), 1);
61        assert_eq!(
62            field.value,
63            BitStringRef::from_bytes(&EXAMPLE_BYTES[5..]).unwrap()
64        );
65
66        let mut buf = [0u8; 128];
67        let encoded = field.encode_to_slice(&mut buf).unwrap();
68        assert_eq!(encoded, EXAMPLE_BYTES);
69    }
70
71    #[test]
72    fn context_specific_with_explicit_field() {
73        let tag_number = TagNumber(0);
74
75        // Empty message
76        let mut reader = SliceReader::new(&[]).unwrap();
77        assert_eq!(
78            ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
79            None
80        );
81
82        // Message containing a non-context-specific type
83        let mut reader = SliceReader::new(&hex!("020100")).unwrap();
84        assert_eq!(
85            ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
86            None
87        );
88
89        // Message containing an EXPLICIT context-specific field
90        let mut reader = SliceReader::new(&hex!("A003020100")).unwrap();
91        let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number)
92            .unwrap()
93            .unwrap();
94
95        assert_eq!(field.tag_number, tag_number);
96        assert_eq!(field.tag_mode, TagMode::Explicit);
97        assert_eq!(field.value, 0);
98    }
99
100    #[test]
101    fn context_specific_with_implicit_field() {
102        // From RFC8410 Section 10.3:
103        // <https://datatracker.ietf.org/doc/html/rfc8410#section-10.3>
104        //
105        //    81  33:   [1] 00 19 BF 44 09 69 84 CD FE 85 41 BA C1 67 DC 3B
106        //                  96 C8 50 86 AA 30 B6 B6 CB 0C 5C 38 AD 70 31 66
107        //                  E1
108        let context_specific_implicit_bytes =
109            hex!("81210019BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
110
111        let tag_number = TagNumber(1);
112
113        let mut reader = SliceReader::new(&context_specific_implicit_bytes).unwrap();
114        let field = ContextSpecific::<BitStringRef<'_>>::decode_implicit(&mut reader, tag_number)
115            .unwrap()
116            .unwrap();
117
118        assert_eq!(field.tag_number, tag_number);
119        assert_eq!(field.tag_mode, TagMode::Implicit);
120        assert_eq!(
121            field.value.as_bytes().unwrap(),
122            &context_specific_implicit_bytes[3..]
123        );
124    }
125
126    #[test]
127    fn context_specific_not_skipping_unknown_field() {
128        let tag = TagNumber(1);
129        let mut reader = SliceReader::new(&hex!("A003020100A103020101")).unwrap();
130        let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag).unwrap();
131        assert_eq!(field, None);
132    }
133
134    #[test]
135    fn context_specific_returns_none_on_greater_tag_number() {
136        let tag = TagNumber(0);
137        let mut reader = SliceReader::new(&hex!("A103020101")).unwrap();
138        assert_eq!(
139            ContextSpecific::<u8>::decode_explicit(&mut reader, tag).unwrap(),
140            None
141        );
142    }
143
144    #[test]
145    #[cfg(feature = "heapless")]
146    fn context_specific_explicit_ref() {
147        let mut set = SetOf::new();
148        set.insert(8u16).unwrap();
149        set.insert(7u16).unwrap();
150
151        let field = ContextSpecificRef::<SetOf<u16, 2>> {
152            value: &set,
153            tag_number: TagNumber(2),
154            tag_mode: TagMode::Explicit,
155        };
156
157        let mut buf = [0u8; 16];
158        let encoded = field.encode_to_slice(&mut buf).unwrap();
159        assert_eq!(
160            encoded,
161            &[
162                /* CONTEXT-SPECIFIC [2] */ 0xA2, 0x08, /* SET 0x11 | 0x20 */ 0x31, 0x06,
163                /* INTEGER */ 0x02, 0x01, 0x07, /* INTEGER */ 0x02, 0x01, 0x08
164            ]
165        );
166
167        let mut reader = SliceReader::new(encoded).unwrap();
168        let field = ContextSpecific::<SetOf<u16, 2>>::decode_explicit(&mut reader, TagNumber(2))
169            .unwrap()
170            .unwrap();
171
172        assert_eq!(field.value.len(), 2);
173        assert_eq!(field.value.get(0).cloned(), Some(7));
174        assert_eq!(field.value.get(1).cloned(), Some(8));
175    }
176
177    #[test]
178    #[cfg(feature = "heapless")]
179    fn context_specific_implicit_ref() {
180        let hello = Utf8StringRef::new("Hello").unwrap();
181        let world = Utf8StringRef::new("world").unwrap();
182
183        let mut set = SetOf::new();
184        set.insert(hello).unwrap();
185        set.insert(world).unwrap();
186
187        let field = ContextSpecificRef::<SetOf<Utf8StringRef<'_>, 2>> {
188            value: &set,
189            tag_number: TagNumber(2),
190            tag_mode: TagMode::Implicit,
191        };
192
193        let mut buf = [0u8; 16];
194        let encoded = field.encode_to_slice(&mut buf).unwrap();
195        assert_eq!(
196            encoded,
197            &[
198                0xA2, 0x0E, // CONTEXT-SPECIFIC [2]
199                0x0C, 0x05, b'H', b'e', b'l', b'l', b'o', // UTF8String "Hello"
200                0x0C, 0x05, b'w', b'o', b'r', b'l', b'd', // UTF8String "world"
201            ]
202        );
203
204        let mut reader = SliceReader::new(encoded).unwrap();
205        let field = ContextSpecific::<SetOf<Utf8StringRef<'_>, 2>>::decode_implicit(
206            &mut reader,
207            TagNumber(2),
208        )
209        .unwrap()
210        .unwrap();
211
212        assert_eq!(field.value.len(), 2);
213        assert_eq!(field.value.get(0).cloned(), Some(hello));
214        assert_eq!(field.value.get(1).cloned(), Some(world));
215    }
216}