der/asn1/
context_specific.rs

1//! Context-specific field.
2
3use crate::{
4    asn1::AnyRef, Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error,
5    Header, Length, Reader, Result, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
6};
7use core::cmp::Ordering;
8
9/// Context-specific field which wraps an owned inner value.
10///
11/// This type decodes/encodes a field which is specific to a particular context
12/// and is identified by a [`TagNumber`].
13#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
14pub struct ContextSpecific<T> {
15    /// Context-specific tag number sans the leading `0b10000000` class
16    /// identifier bit and `0b100000` constructed flag.
17    pub tag_number: TagNumber,
18
19    /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
20    pub tag_mode: TagMode,
21
22    /// Value of the field.
23    pub value: T,
24}
25
26impl<T> ContextSpecific<T> {
27    /// Attempt to decode an `EXPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
28    /// provided [`TagNumber`].
29    ///
30    /// This method has the following behavior which is designed to simplify
31    /// handling of extension fields, which are denoted in an ASN.1 schema
32    /// using the `...` ellipsis extension marker:
33    ///
34    /// - Skips over [`ContextSpecific`] fields with a tag number lower than
35    ///   the current one, consuming and ignoring them.
36    /// - Returns `Ok(None)` if a [`ContextSpecific`] field with a higher tag
37    ///   number is encountered. These fields are not consumed in this case,
38    ///   allowing a field with a lower tag number to be omitted, then the
39    ///   higher numbered field consumed as a follow-up.
40    /// - Returns `Ok(None)` if anything other than a [`ContextSpecific`] field
41    ///   is encountered.
42    pub fn decode_explicit<'a, R: Reader<'a>>(
43        reader: &mut R,
44        tag_number: TagNumber,
45    ) -> Result<Option<Self>>
46    where
47        T: Decode<'a>,
48    {
49        Self::decode_with(reader, tag_number, |reader| Self::decode(reader))
50    }
51
52    /// Attempt to decode an `IMPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
53    /// provided [`TagNumber`].
54    ///
55    /// This method otherwise behaves the same as `decode_explicit`,
56    /// but should be used in cases where the particular fields are `IMPLICIT`
57    /// as opposed to `EXPLICIT`.
58    pub fn decode_implicit<'a, R: Reader<'a>>(
59        reader: &mut R,
60        tag_number: TagNumber,
61    ) -> Result<Option<Self>>
62    where
63        T: DecodeValue<'a> + Tagged,
64    {
65        Self::decode_with(reader, tag_number, |reader| {
66            let header = Header::decode(reader)?;
67            let value = T::decode_value(reader, header)?;
68
69            if header.tag.is_constructed() != value.tag().is_constructed() {
70                return Err(header.tag.non_canonical_error());
71            }
72
73            Ok(Self {
74                tag_number,
75                tag_mode: TagMode::Implicit,
76                value,
77            })
78        })
79    }
80
81    /// Attempt to decode a context-specific field with the given
82    /// helper callback.
83    fn decode_with<'a, F, R: Reader<'a>>(
84        reader: &mut R,
85        tag_number: TagNumber,
86        f: F,
87    ) -> Result<Option<Self>>
88    where
89        F: FnOnce(&mut R) -> Result<Self>,
90    {
91        while let Some(octet) = reader.peek_byte() {
92            let tag = Tag::try_from(octet)?;
93
94            if !tag.is_context_specific() || (tag.number() > tag_number) {
95                break;
96            } else if tag.number() == tag_number {
97                return Some(f(reader)).transpose();
98            } else {
99                AnyRef::decode(reader)?;
100            }
101        }
102
103        Ok(None)
104    }
105}
106
107impl<'a, T> Choice<'a> for ContextSpecific<T>
108where
109    T: Decode<'a> + Tagged,
110{
111    fn can_decode(tag: Tag) -> bool {
112        tag.is_context_specific()
113    }
114}
115
116impl<'a, T> Decode<'a> for ContextSpecific<T>
117where
118    T: Decode<'a>,
119{
120    fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
121        let header = Header::decode(reader)?;
122
123        match header.tag {
124            Tag::ContextSpecific {
125                number,
126                constructed: true,
127            } => Ok(Self {
128                tag_number: number,
129                tag_mode: TagMode::default(),
130                value: reader.read_nested(header.length, |reader| T::decode(reader))?,
131            }),
132            tag => Err(tag.unexpected_error(None)),
133        }
134    }
135}
136
137impl<T> EncodeValue for ContextSpecific<T>
138where
139    T: EncodeValue + Tagged,
140{
141    fn value_len(&self) -> Result<Length> {
142        match self.tag_mode {
143            TagMode::Explicit => self.value.encoded_len(),
144            TagMode::Implicit => self.value.value_len(),
145        }
146    }
147
148    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
149        match self.tag_mode {
150            TagMode::Explicit => self.value.encode(writer),
151            TagMode::Implicit => self.value.encode_value(writer),
152        }
153    }
154}
155
156impl<T> Tagged for ContextSpecific<T>
157where
158    T: Tagged,
159{
160    fn tag(&self) -> Tag {
161        let constructed = match self.tag_mode {
162            TagMode::Explicit => true,
163            TagMode::Implicit => self.value.tag().is_constructed(),
164        };
165
166        Tag::ContextSpecific {
167            number: self.tag_number,
168            constructed,
169        }
170    }
171}
172
173impl<'a, T> TryFrom<AnyRef<'a>> for ContextSpecific<T>
174where
175    T: Decode<'a>,
176{
177    type Error = Error;
178
179    fn try_from(any: AnyRef<'a>) -> Result<ContextSpecific<T>> {
180        match any.tag() {
181            Tag::ContextSpecific {
182                number,
183                constructed: true,
184            } => Ok(Self {
185                tag_number: number,
186                tag_mode: TagMode::default(),
187                value: T::from_der(any.value())?,
188            }),
189            tag => Err(tag.unexpected_error(None)),
190        }
191    }
192}
193
194impl<T> ValueOrd for ContextSpecific<T>
195where
196    T: EncodeValue + ValueOrd + Tagged,
197{
198    fn value_cmp(&self, other: &Self) -> Result<Ordering> {
199        match self.tag_mode {
200            TagMode::Explicit => self.der_cmp(other),
201            TagMode::Implicit => self.value_cmp(other),
202        }
203    }
204}
205
206/// Context-specific field reference.
207///
208/// This type encodes a field which is specific to a particular context
209/// and is identified by a [`TagNumber`].
210#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
211pub struct ContextSpecificRef<'a, T> {
212    /// Context-specific tag number sans the leading `0b10000000` class
213    /// identifier bit and `0b100000` constructed flag.
214    pub tag_number: TagNumber,
215
216    /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
217    pub tag_mode: TagMode,
218
219    /// Value of the field.
220    pub value: &'a T,
221}
222
223impl<'a, T> ContextSpecificRef<'a, T> {
224    /// Convert to a [`ContextSpecific`].
225    fn encoder(&self) -> ContextSpecific<EncodeValueRef<'a, T>> {
226        ContextSpecific {
227            tag_number: self.tag_number,
228            tag_mode: self.tag_mode,
229            value: EncodeValueRef(self.value),
230        }
231    }
232}
233
234impl<'a, T> EncodeValue for ContextSpecificRef<'a, T>
235where
236    T: EncodeValue + Tagged,
237{
238    fn value_len(&self) -> Result<Length> {
239        self.encoder().value_len()
240    }
241
242    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
243        self.encoder().encode_value(writer)
244    }
245}
246
247impl<'a, T> Tagged for ContextSpecificRef<'a, T>
248where
249    T: Tagged,
250{
251    fn tag(&self) -> Tag {
252        self.encoder().tag()
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::ContextSpecific;
259    use crate::{asn1::BitStringRef, Decode, Encode, SliceReader, TagMode, TagNumber};
260    use hex_literal::hex;
261
262    // Public key data from `pkcs8` crate's `ed25519-pkcs8-v2.der`
263    const EXAMPLE_BYTES: &[u8] =
264        &hex!("A123032100A3A7EAE3A8373830BC47E1167BC50E1DB551999651E0E2DC587623438EAC3F31");
265
266    #[test]
267    fn round_trip() {
268        let field = ContextSpecific::<BitStringRef<'_>>::from_der(EXAMPLE_BYTES).unwrap();
269        assert_eq!(field.tag_number.value(), 1);
270        assert_eq!(
271            field.value,
272            BitStringRef::from_bytes(&EXAMPLE_BYTES[5..]).unwrap()
273        );
274
275        let mut buf = [0u8; 128];
276        let encoded = field.encode_to_slice(&mut buf).unwrap();
277        assert_eq!(encoded, EXAMPLE_BYTES);
278    }
279
280    #[test]
281    fn context_specific_with_explicit_field() {
282        let tag_number = TagNumber::new(0);
283
284        // Empty message
285        let mut reader = SliceReader::new(&[]).unwrap();
286        assert_eq!(
287            ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
288            None
289        );
290
291        // Message containing a non-context-specific type
292        let mut reader = SliceReader::new(&hex!("020100")).unwrap();
293        assert_eq!(
294            ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number).unwrap(),
295            None
296        );
297
298        // Message containing an EXPLICIT context-specific field
299        let mut reader = SliceReader::new(&hex!("A003020100")).unwrap();
300        let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag_number)
301            .unwrap()
302            .unwrap();
303
304        assert_eq!(field.tag_number, tag_number);
305        assert_eq!(field.tag_mode, TagMode::Explicit);
306        assert_eq!(field.value, 0);
307    }
308
309    #[test]
310    fn context_specific_with_implicit_field() {
311        // From RFC8410 Section 10.3:
312        // <https://datatracker.ietf.org/doc/html/rfc8410#section-10.3>
313        //
314        //    81  33:   [1] 00 19 BF 44 09 69 84 CD FE 85 41 BA C1 67 DC 3B
315        //                  96 C8 50 86 AA 30 B6 B6 CB 0C 5C 38 AD 70 31 66
316        //                  E1
317        let context_specific_implicit_bytes =
318            hex!("81210019BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
319
320        let tag_number = TagNumber::new(1);
321
322        let mut reader = SliceReader::new(&context_specific_implicit_bytes).unwrap();
323        let field = ContextSpecific::<BitStringRef<'_>>::decode_implicit(&mut reader, tag_number)
324            .unwrap()
325            .unwrap();
326
327        assert_eq!(field.tag_number, tag_number);
328        assert_eq!(field.tag_mode, TagMode::Implicit);
329        assert_eq!(
330            field.value.as_bytes().unwrap(),
331            &context_specific_implicit_bytes[3..]
332        );
333    }
334
335    #[test]
336    fn context_specific_skipping_unknown_field() {
337        let tag = TagNumber::new(1);
338        let mut reader = SliceReader::new(&hex!("A003020100A103020101")).unwrap();
339        let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag)
340            .unwrap()
341            .unwrap();
342        assert_eq!(field.value, 1);
343    }
344
345    #[test]
346    fn context_specific_returns_none_on_greater_tag_number() {
347        let tag = TagNumber::new(0);
348        let mut reader = SliceReader::new(&hex!("A103020101")).unwrap();
349        assert_eq!(
350            ContextSpecific::<u8>::decode_explicit(&mut reader, tag).unwrap(),
351            None
352        );
353    }
354}