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                // Compare the contents octets: the inner TLV for EXPLICIT, the inner value for
282                // IMPLICIT. (`self.der_cmp` / `self.value_cmp` here would recurse forever.)
283                match self.tag_mode {
284                    TagMode::Explicit => self.value.der_cmp(&other.value),
285                    TagMode::Implicit => self.value.value_cmp(&other.value),
286                }
287            }
288        }
289    };
290}
291
292macro_rules! impl_custom_class_ref {
293    ($ref_class_type_name: ident, $class_type_name: ident, $asn1_class_name: literal, $class_bits_str: literal) => {
294        #[doc = concat!("`", $asn1_class_name, "` field reference.")]
295        ///
296        ///
297        /// This type encodes a field which is specific to a particular context
298        /// and is identified by a [`TagNumber`].
299        #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
300        pub struct $ref_class_type_name<'a, T> {
301            #[doc = concat!("`", $asn1_class_name, "` tag number sans the leading `", $class_bits_str, "` class")]
302            /// identifier bit and `0b100000` constructed flag.
303            pub tag_number: TagNumber,
304
305            /// Tag mode: `EXPLICIT` VS `IMPLICIT`.
306            pub tag_mode: TagMode,
307
308            /// Value of the field.
309            pub value: &'a T,
310        }
311
312        impl<'a, T> $ref_class_type_name<'a, T> {
313            /// Convert to a [`EncodeValue`] object using [`EncodeValueRef`].
314            fn encoder(&self) -> $class_type_name<EncodeValueRef<'a, T>> {
315                $class_type_name {
316                    tag_number: self.tag_number,
317                    tag_mode: self.tag_mode,
318                    value: EncodeValueRef(self.value),
319                }
320            }
321        }
322
323        impl<T> EncodeValue for $ref_class_type_name<'_, T>
324        where
325            T: EncodeValue + Tagged,
326        {
327            fn value_len(&self) -> Result<Length, Error> {
328                self.encoder().value_len()
329            }
330
331            fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
332                self.encoder().encode_value(writer)
333            }
334        }
335
336        impl<T> Tagged for $ref_class_type_name<'_, T>
337        where
338            T: Tagged,
339        {
340            fn tag(&self) -> Tag {
341                self.encoder().tag()
342            }
343        }
344    };
345}