Skip to main content

der/asn1/
internal_macros.rs

1macro_rules! impl_any_conversions {
2    ($type: ty) => {
3        impl_any_conversions!($type, );
4    };
5    ($type: ty, $($li: lifetime)?) => {
6        impl<'__der: $($li),*, $($li),*> TryFrom<$crate::AnyRef<'__der>> for $type {
7            type Error = $crate::Error;
8
9            fn try_from(any: $crate::AnyRef<'__der>) -> $crate::Result<$type> {
10                any.decode_as()
11            }
12        }
13
14        #[cfg(feature = "alloc")]
15        impl<'__der: $($li),*, $($li),*> TryFrom<&'__der $crate::Any> for $type {
16            type Error = $crate::Error;
17
18            fn try_from(any: &'__der $crate::Any) -> $crate::Result<$type> {
19                any.decode_as()
20            }
21        }
22    };
23}
24
25macro_rules! impl_string_type {
26    ($type: ty, $($li: lifetime)?) => {
27        impl_any_conversions!($type, $($li),*);
28
29        mod __impl_string {
30            use super::*;
31
32            use crate::{
33                ord::OrdIsValueOrd, BytesRef, DecodeValue, EncodeValue, Header, Length, Reader,
34                Result, Writer,
35            };
36            use core::{fmt, str};
37
38            impl<$($li),*> AsRef<str> for $type {
39                fn as_ref(&self) -> &str {
40                    self.as_str()
41                }
42            }
43
44            impl<$($li),*> AsRef<[u8]> for $type {
45                fn as_ref(&self) -> &[u8] {
46                    self.as_bytes()
47                }
48            }
49
50            impl<'__der: $($li),*, $($li),*> DecodeValue<'__der> for $type {
51                type Error = $crate::Error;
52
53                fn decode_value<R: Reader<'__der>>(reader: &mut R, header: Header) -> $crate::Result<Self> {
54                    Self::new(<&'__der BytesRef>::decode_value(reader, header)?.as_slice())
55                }
56            }
57
58            impl<$($li),*> EncodeValue for $type {
59                fn value_len(&self) -> Result<Length> {
60                    self.inner.value_len()
61                }
62
63                fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
64                    self.inner.encode_value(writer)
65                }
66            }
67
68            impl<$($li),*> OrdIsValueOrd for $type {}
69
70            impl<$($li),*> fmt::Display for $type {
71                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72                    f.write_str(self.as_str())
73                }
74            }
75        }
76    };
77}
78
79macro_rules! impl_custom_class {
80    ($class_type_name: ident, $class_enum_name: ident, $asn1_class_name: literal, $class_bits_str: literal) => {
81        #[doc = concat!("`", $asn1_class_name, "` field which wraps an owned inner value.")]
82        ///
83        /// This type decodes/encodes a field which is specific to a particular context
84        /// and is identified by a [`TagNumber`].
85        #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
86        pub struct $class_type_name<T> {
87            #[doc = concat!("`", $asn1_class_name, "` tag number sans the leading `", $class_bits_str, "` class")]
88            /// identifier bit and `0b100000` constructed flag.
89            pub tag_number: TagNumber,
90
91            /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
92            pub tag_mode: TagMode,
93
94            /// Value of the field.
95            pub value: T,
96        }
97
98        impl<T> $class_type_name<T> {
99            #[doc = concat!("Attempt to decode an `EXPLICIT` ASN.1 `", $asn1_class_name, "` field with the")]
100            /// provided [`TagNumber`].
101            ///
102            /// This method has the following behavior which decodes tag numbers one by one
103            /// in extension fields, which are denoted in an ASN.1 schema using
104            /// the `...` ellipsis extension marker:
105            ///
106            /// - Returns `Ok(Some(..))` if tag number matches.
107            #[doc = concat!("- Returns `Ok(None)` if class other than [`Class::", stringify!($class_enum_name), "`] tag")]
108            ///   is encountered.
109            /// - Returns `Ok(None)` if a field with a different tag number is encountered.
110            ///   These fields are not consumed in this case.
111            ///
112            /// # Errors
113            /// Returns [`ErrorKind::Noncanonical`] if constructed bit is primitive.
114            pub fn decode_explicit<'a, R: Reader<'a>>(
115                reader: &mut R,
116                tag_number: TagNumber,
117            ) -> Result<Option<Self>, T::Error>
118            where
119                T: Decode<'a>,
120            {
121                if !Tag::peek_matches(reader, Class::$class_enum_name, tag_number)? {
122                    return Ok(None);
123                }
124                Ok(Some(Self::decode(reader)?))
125            }
126
127            #[doc = concat!("Attempt to decode an `IMPLICIT` ASN.1 `", $asn1_class_name, "` field with the")]
128            /// provided [`TagNumber`].
129            ///
130            /// This method otherwise behaves the same as `decode_explicit`,
131            /// but should be used in cases where the particular fields are `IMPLICIT`
132            /// as opposed to `EXPLICIT`.
133            ///
134            /// Differences from `EXPLICIT`:
135            /// - Returns [`ErrorKind::Noncanonical`] if constructed bit
136            ///   does not match constructed bit of the base encoding.
137            ///
138            /// # Errors
139            /// Returns `T::Error` in the event of a decoding error.
140            pub fn decode_implicit<'a, R: Reader<'a>>(
141                reader: &mut R,
142                tag_number: TagNumber,
143            ) -> Result<Option<Self>, T::Error>
144            where
145                T: DecodeValue<'a> + IsConstructed,
146            {
147                // Peek tag number
148                if !Tag::peek_matches(reader, Class::$class_enum_name, tag_number)? {
149                    return Ok(None);
150                }
151
152                // Decode IMPLICIT header
153                let header = Header::decode(reader)?;
154
155                // the encoding shall be constructed if the base encoding is constructed
156                if header.tag().is_constructed() != T::CONSTRUCTED
157                    && reader.encoding_rules().is_der() {
158                    return Err(reader.error(header.tag().non_canonical_error()).into());
159                }
160
161                // read_value checks if header matches decoded length
162                let value = crate::reader::read_value(reader, header, T::decode_value)?;
163
164                Ok(Some(Self {
165                    tag_number,
166                    tag_mode: TagMode::Implicit,
167                    value,
168                }))
169            }
170        }
171
172        impl<'a, T> Choice<'a> for $class_type_name<T>
173        where
174            T: Decode<'a> + Tagged,
175        {
176            fn can_decode(tag: Tag) -> bool {
177                tag.class() == Class::$class_enum_name
178            }
179        }
180
181        impl<'a, T> Decode<'a> for $class_type_name<T>
182        where
183            T: Decode<'a>,
184        {
185            type Error = T::Error;
186
187            fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self, Self::Error> {
188                // Decode EXPLICIT header
189                let header = Header::decode(reader)?;
190
191                // encoding shall be constructed
192                if !header.tag().is_constructed() {
193                    return Err(reader.error(header.tag().non_canonical_error()).into());
194                }
195                match header.tag() {
196                    Tag::$class_enum_name { number, .. } => Ok(Self {
197                        tag_number: number,
198                        tag_mode: TagMode::default(),
199                        value: crate::reader::read_value(reader, header, |reader, _| {
200                            // Decode inner tag-length-value of EXPLICIT
201                            T::decode(reader)
202                        })?,
203                    }),
204                    tag => Err(reader.error(tag.unexpected_error(None)).into())
205                }
206            }
207        }
208
209        impl<T> EncodeValue for $class_type_name<T>
210        where
211            T: EncodeValue + Tagged,
212        {
213            fn value_len(&self) -> Result<Length, Error> {
214                match self.tag_mode {
215                    TagMode::Explicit => self.value.encoded_len(),
216                    TagMode::Implicit => self.value.value_len(),
217                }
218            }
219
220            fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
221                match self.tag_mode {
222                    TagMode::Explicit => self.value.encode(writer),
223                    TagMode::Implicit => self.value.encode_value(writer),
224                }
225            }
226        }
227
228        impl<T> Tagged for $class_type_name<T>
229        where
230            T: Tagged,
231        {
232            fn tag(&self) -> Tag {
233                let constructed = match self.tag_mode {
234                    // ISO/IEC 8825-1:2021
235                    // 8.14.3 If implicit tagging (see Rec. ITU-T X.680 | ISO/IEC 8824-1, 31.2.7) was not used in the definition of the type, the
236                    // encoding shall be constructed and the contents octets shall be the complete base encoding [Encode].
237                    TagMode::Explicit => true,
238
239                    // ISO/IEC 8825-1:2021
240                    // 8.14.4 If implicit tagging was used in the definition of the type, then:
241                    // a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise; and
242                    // b) the contents octets shall be the same as the contents octets [EncodeValue] of the base encoding.
243                    //
244                    // TODO(dishmaker): use IsConstructed trait for IMPLICIT
245                    TagMode::Implicit => self.value.tag().is_constructed(),
246                };
247
248                Tag::$class_enum_name {
249                    number: self.tag_number,
250                    constructed,
251                }
252            }
253        }
254
255        impl<'a, T> TryFrom<AnyRef<'a>> for $class_type_name<T>
256        where
257            T: Decode<'a>,
258        {
259            type Error = T::Error;
260
261            fn try_from(any: AnyRef<'a>) -> Result<$class_type_name<T>, Self::Error> {
262                match any.tag() {
263                    Tag::$class_enum_name {
264                        number,
265                        constructed: true,
266                    } => Ok(Self {
267                        tag_number: number,
268                        tag_mode: TagMode::default(),
269                        value: T::from_der(any.value())?,
270                    }),
271                    tag => Err(tag.unexpected_error(None).to_error().into()),
272                }
273            }
274        }
275
276        impl<T> ValueOrd for $class_type_name<T>
277        where
278            T: EncodeValue + ValueOrd + Tagged,
279        {
280            fn value_cmp(&self, other: &Self) -> Result<Ordering, Error> {
281                match self.tag_mode {
282                    TagMode::Explicit => self.der_cmp(other),
283                    TagMode::Implicit => self.value_cmp(other),
284                }
285            }
286        }
287    };
288}
289
290macro_rules! impl_custom_class_ref {
291    ($ref_class_type_name: ident, $class_type_name: ident, $asn1_class_name: literal, $class_bits_str: literal) => {
292        #[doc = concat!("`", $asn1_class_name, "` field reference.")]
293        ///
294        ///
295        /// This type encodes a field which is specific to a particular context
296        /// and is identified by a [`TagNumber`].
297        #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
298        pub struct $ref_class_type_name<'a, T> {
299            #[doc = concat!("`", $asn1_class_name, "` tag number sans the leading `", $class_bits_str, "` class")]
300            /// identifier bit and `0b100000` constructed flag.
301            pub tag_number: TagNumber,
302
303            /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
304            pub tag_mode: TagMode,
305
306            /// Value of the field.
307            pub value: &'a T,
308        }
309
310        impl<'a, T> $ref_class_type_name<'a, T> {
311            /// Convert to a [`EncodeValue`] object using [`EncodeValueRef`].
312            fn encoder(&self) -> $class_type_name<EncodeValueRef<'a, T>> {
313                $class_type_name {
314                    tag_number: self.tag_number,
315                    tag_mode: self.tag_mode,
316                    value: EncodeValueRef(self.value),
317                }
318            }
319        }
320
321        impl<T> EncodeValue for $ref_class_type_name<'_, T>
322        where
323            T: EncodeValue + Tagged,
324        {
325            fn value_len(&self) -> Result<Length, Error> {
326                self.encoder().value_len()
327            }
328
329            fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
330                self.encoder().encode_value(writer)
331            }
332        }
333
334        impl<T> Tagged for $ref_class_type_name<'_, T>
335        where
336            T: Tagged,
337        {
338            fn tag(&self) -> Tag {
339                self.encoder().tag()
340            }
341        }
342    };
343}