1 //! Context-specific field.
2 
3 use crate::{
4     asn1::AnyRef, Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error,
5     Header, Length, Reader, Result, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer,
6 };
7 use 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)]
14 pub 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 
26 impl<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.
decode_explicit<'a, R: Reader<'a>>( reader: &mut R, tag_number: TagNumber, ) -> Result<Option<Self>> where T: Decode<'a>,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`.
decode_implicit<'a, R: Reader<'a>>( reader: &mut R, tag_number: TagNumber, ) -> Result<Option<Self>> where T: DecodeValue<'a> + Tagged,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.
decode_with<'a, F, R: Reader<'a>>( reader: &mut R, tag_number: TagNumber, f: F, ) -> Result<Option<Self>> where F: FnOnce(&mut R) -> Result<Self>,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 
107 impl<'a, T> Choice<'a> for ContextSpecific<T>
108 where
109     T: Decode<'a> + Tagged,
110 {
can_decode(tag: Tag) -> bool111     fn can_decode(tag: Tag) -> bool {
112         tag.is_context_specific()
113     }
114 }
115 
116 impl<'a, T> Decode<'a> for ContextSpecific<T>
117 where
118     T: Decode<'a>,
119 {
decode<R: Reader<'a>>(reader: &mut R) -> Result<Self>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 
137 impl<T> EncodeValue for ContextSpecific<T>
138 where
139     T: EncodeValue + Tagged,
140 {
value_len(&self) -> Result<Length>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 
encode_value(&self, writer: &mut impl Writer) -> Result<()>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 
156 impl<T> Tagged for ContextSpecific<T>
157 where
158     T: Tagged,
159 {
tag(&self) -> Tag160     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 
173 impl<'a, T> TryFrom<AnyRef<'a>> for ContextSpecific<T>
174 where
175     T: Decode<'a>,
176 {
177     type Error = Error;
178 
try_from(any: AnyRef<'a>) -> Result<ContextSpecific<T>>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 
194 impl<T> ValueOrd for ContextSpecific<T>
195 where
196     T: EncodeValue + ValueOrd + Tagged,
197 {
value_cmp(&self, other: &Self) -> Result<Ordering>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)]
211 pub 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 
223 impl<'a, T> ContextSpecificRef<'a, T> {
224     /// Convert to a [`ContextSpecific`].
encoder(&self) -> ContextSpecific<EncodeValueRef<'a, T>>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 
234 impl<'a, T> EncodeValue for ContextSpecificRef<'a, T>
235 where
236     T: EncodeValue + Tagged,
237 {
value_len(&self) -> Result<Length>238     fn value_len(&self) -> Result<Length> {
239         self.encoder().value_len()
240     }
241 
encode_value(&self, writer: &mut impl Writer) -> Result<()>242     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
243         self.encoder().encode_value(writer)
244     }
245 }
246 
247 impl<'a, T> Tagged for ContextSpecificRef<'a, T>
248 where
249     T: Tagged,
250 {
tag(&self) -> Tag251     fn tag(&self) -> Tag {
252         self.encoder().tag()
253     }
254 }
255 
256 #[cfg(test)]
257 mod 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]
round_trip()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]
context_specific_with_explicit_field()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]
context_specific_with_implicit_field()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]
context_specific_skipping_unknown_field()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]
context_specific_returns_none_on_greater_tag_number()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 }
355