der/asn1/
context_specific.rs

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