1 //! Tests for custom derive support.
2 //!
3 //! # Debugging with `cargo expand`
4 //!
5 //! To expand the Rust code generated by the proc macro when debugging
6 //! issues related to these tests, run:
7 //!
8 //! $ cargo expand --test derive --all-features
9 
10 #![cfg(all(feature = "derive", feature = "alloc"))]
11 
12 /// Custom derive test cases for the `Choice` macro.
13 mod choice {
14     /// `Choice` with `EXPLICIT` tagging.
15     mod explicit {
16         use der::{
17             asn1::{GeneralizedTime, UtcTime},
18             Choice, Decode, Encode, SliceWriter,
19         };
20         use hex_literal::hex;
21         use std::time::Duration;
22 
23         /// Custom derive test case for the `Choice` macro.
24         ///
25         /// Based on `Time` as defined in RFC 5280:
26         /// <https://tools.ietf.org/html/rfc5280#page-117>
27         ///
28         /// ```text
29         /// Time ::= CHOICE {
30         ///      utcTime        UTCTime,
31         ///      generalTime    GeneralizedTime }
32         /// ```
33         #[derive(Choice)]
34         pub enum Time {
35             #[asn1(type = "UTCTime")]
36             UtcTime(UtcTime),
37 
38             #[asn1(type = "GeneralizedTime")]
39             GeneralTime(GeneralizedTime),
40         }
41 
42         impl Time {
to_unix_duration(self) -> Duration43             fn to_unix_duration(self) -> Duration {
44                 match self {
45                     Time::UtcTime(t) => t.to_unix_duration(),
46                     Time::GeneralTime(t) => t.to_unix_duration(),
47                 }
48             }
49         }
50 
51         const UTC_TIMESTAMP_DER: &[u8] = &hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a");
52         const GENERAL_TIMESTAMP_DER: &[u8] =
53             &hex!("18 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a");
54 
55         #[test]
decode()56         fn decode() {
57             let utc_time = Time::from_der(UTC_TIMESTAMP_DER).unwrap();
58             assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
59 
60             let general_time = Time::from_der(GENERAL_TIMESTAMP_DER).unwrap();
61             assert_eq!(general_time.to_unix_duration().as_secs(), 673573540);
62         }
63 
64         #[test]
encode()65         fn encode() {
66             let mut buf = [0u8; 128];
67 
68             let utc_time = Time::from_der(UTC_TIMESTAMP_DER).unwrap();
69             let mut encoder = SliceWriter::new(&mut buf);
70             utc_time.encode(&mut encoder).unwrap();
71             assert_eq!(UTC_TIMESTAMP_DER, encoder.finish().unwrap());
72 
73             let general_time = Time::from_der(GENERAL_TIMESTAMP_DER).unwrap();
74             let mut encoder = SliceWriter::new(&mut buf);
75             general_time.encode(&mut encoder).unwrap();
76             assert_eq!(GENERAL_TIMESTAMP_DER, encoder.finish().unwrap());
77         }
78     }
79 
80     /// `Choice` with `IMPLICIT` tagging.
81     mod implicit {
82         use der::{
83             asn1::{BitStringRef, GeneralizedTime},
84             Choice, Decode, Encode, SliceWriter,
85         };
86         use hex_literal::hex;
87 
88         /// `Choice` macro test case for `IMPLICIT` tagging.
89         #[derive(Choice, Debug, Eq, PartialEq)]
90         #[asn1(tag_mode = "IMPLICIT")]
91         pub enum ImplicitChoice<'a> {
92             #[asn1(context_specific = "0", type = "BIT STRING")]
93             BitString(BitStringRef<'a>),
94 
95             #[asn1(context_specific = "1", type = "GeneralizedTime")]
96             Time(GeneralizedTime),
97 
98             #[asn1(context_specific = "2", type = "UTF8String")]
99             Utf8String(String),
100         }
101 
102         impl<'a> ImplicitChoice<'a> {
bit_string(&self) -> Option<BitStringRef<'a>>103             pub fn bit_string(&self) -> Option<BitStringRef<'a>> {
104                 match self {
105                     Self::BitString(bs) => Some(*bs),
106                     _ => None,
107                 }
108             }
109 
time(&self) -> Option<GeneralizedTime>110             pub fn time(&self) -> Option<GeneralizedTime> {
111                 match self {
112                     Self::Time(time) => Some(*time),
113                     _ => None,
114                 }
115             }
116         }
117 
118         const BITSTRING_DER: &[u8] = &hex!("80 04 00 01 02 03");
119         const TIME_DER: &[u8] = &hex!("81 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a");
120 
121         #[test]
decode()122         fn decode() {
123             let cs_bit_string = ImplicitChoice::from_der(BITSTRING_DER).unwrap();
124             assert_eq!(
125                 cs_bit_string.bit_string().unwrap().as_bytes().unwrap(),
126                 &[1, 2, 3]
127             );
128 
129             let cs_time = ImplicitChoice::from_der(TIME_DER).unwrap();
130             assert_eq!(
131                 cs_time.time().unwrap().to_unix_duration().as_secs(),
132                 673573540
133             );
134         }
135 
136         #[test]
encode()137         fn encode() {
138             let mut buf = [0u8; 128];
139 
140             let cs_bit_string = ImplicitChoice::from_der(BITSTRING_DER).unwrap();
141             let mut encoder = SliceWriter::new(&mut buf);
142             cs_bit_string.encode(&mut encoder).unwrap();
143             assert_eq!(BITSTRING_DER, encoder.finish().unwrap());
144 
145             let cs_time = ImplicitChoice::from_der(TIME_DER).unwrap();
146             let mut encoder = SliceWriter::new(&mut buf);
147             cs_time.encode(&mut encoder).unwrap();
148             assert_eq!(TIME_DER, encoder.finish().unwrap());
149         }
150     }
151 }
152 
153 /// Custom derive test cases for the `Enumerated` macro.
154 mod enumerated {
155     use der::{Decode, Encode, Enumerated, SliceWriter};
156     use hex_literal::hex;
157 
158     /// X.509 `CRLReason`.
159     #[derive(Enumerated, Copy, Clone, Debug, Eq, PartialEq)]
160     #[repr(u32)]
161     pub enum CrlReason {
162         Unspecified = 0,
163         KeyCompromise = 1,
164         CaCompromise = 2,
165         AffiliationChanged = 3,
166         Superseded = 4,
167         CessationOfOperation = 5,
168         CertificateHold = 6,
169         RemoveFromCrl = 8,
170         PrivilegeWithdrawn = 9,
171         AaCompromised = 10,
172     }
173 
174     const UNSPECIFIED_DER: &[u8] = &hex!("0a 01 00");
175     const KEY_COMPROMISE_DER: &[u8] = &hex!("0a 01 01");
176 
177     #[test]
decode()178     fn decode() {
179         let unspecified = CrlReason::from_der(UNSPECIFIED_DER).unwrap();
180         assert_eq!(CrlReason::Unspecified, unspecified);
181 
182         let key_compromise = CrlReason::from_der(KEY_COMPROMISE_DER).unwrap();
183         assert_eq!(CrlReason::KeyCompromise, key_compromise);
184     }
185 
186     #[test]
encode()187     fn encode() {
188         let mut buf = [0u8; 128];
189 
190         let mut encoder = SliceWriter::new(&mut buf);
191         CrlReason::Unspecified.encode(&mut encoder).unwrap();
192         assert_eq!(UNSPECIFIED_DER, encoder.finish().unwrap());
193 
194         let mut encoder = SliceWriter::new(&mut buf);
195         CrlReason::KeyCompromise.encode(&mut encoder).unwrap();
196         assert_eq!(KEY_COMPROMISE_DER, encoder.finish().unwrap());
197     }
198 }
199 
200 /// Custom derive test cases for the `Sequence` macro.
201 #[cfg(feature = "oid")]
202 mod sequence {
203     use core::marker::PhantomData;
204     use der::{
205         asn1::{AnyRef, ObjectIdentifier, SetOf},
206         Decode, Encode, Sequence, ValueOrd,
207     };
208     use hex_literal::hex;
209 
default_false_example() -> bool210     pub fn default_false_example() -> bool {
211         false
212     }
213 
214     // Issuing distribution point extension as defined in [RFC 5280 Section 5.2.5] and as identified by the [`PKIX_PE_SUBJECTINFOACCESS`](constant.PKIX_PE_SUBJECTINFOACCESS.html) OID.
215     //
216     // ```text
217     // IssuingDistributionPoint ::= SEQUENCE {
218     //      distributionPoint          [0] DistributionPointName OPTIONAL,
219     //      onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
220     //      onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
221     //      onlySomeReasons            [3] ReasonFlags OPTIONAL,
222     //      indirectCRL                [4] BOOLEAN DEFAULT FALSE,
223     //      onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
224     //      -- at most one of onlyContainsUserCerts, onlyContainsCACerts,
225     //      -- and onlyContainsAttributeCerts may be set to TRUE.
226     // ```
227     //
228     // [RFC 5280 Section 5.2.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5
229     #[derive(Sequence)]
230     pub struct IssuingDistributionPointExample {
231         // Omit distributionPoint and only_some_reasons because corresponding structs are not
232         // available here and are not germane to the example
233         // distributionPoint          [0] DistributionPointName OPTIONAL,
234         //#[asn1(context_specific="0", optional="true", tag_mode="IMPLICIT")]
235         //pub distribution_point: Option<DistributionPointName<'a>>,
236         /// onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
237         #[asn1(
238             context_specific = "1",
239             default = "default_false_example",
240             tag_mode = "IMPLICIT"
241         )]
242         pub only_contains_user_certs: bool,
243 
244         /// onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
245         #[asn1(
246             context_specific = "2",
247             default = "default_false_example",
248             tag_mode = "IMPLICIT"
249         )]
250         pub only_contains_cacerts: bool,
251 
252         // onlySomeReasons            [3] ReasonFlags OPTIONAL,
253         //#[asn1(context_specific="3", optional="true", tag_mode="IMPLICIT")]
254         //pub only_some_reasons: Option<ReasonFlags<'a>>,
255         /// indirectCRL                [4] BOOLEAN DEFAULT FALSE,
256         #[asn1(
257             context_specific = "4",
258             default = "default_false_example",
259             tag_mode = "IMPLICIT"
260         )]
261         pub indirect_crl: bool,
262 
263         /// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE
264         #[asn1(
265             context_specific = "5",
266             default = "default_false_example",
267             tag_mode = "IMPLICIT"
268         )]
269         pub only_contains_attribute_certs: bool,
270 
271         /// Test handling of PhantomData.
272         pub phantom: PhantomData<()>,
273     }
274 
275     // Extension as defined in [RFC 5280 Section 4.1.2.9].
276     //
277     // The ASN.1 definition for Extension objects is below. The extnValue type may be further parsed using a decoder corresponding to the extnID value.
278     //
279     // ```text
280     //    Extension  ::=  SEQUENCE  {
281     //         extnID      OBJECT IDENTIFIER,
282     //         critical    BOOLEAN DEFAULT FALSE,
283     //         extnValue   OCTET STRING
284     //                     -- contains the DER encoding of an ASN.1 value
285     //                     -- corresponding to the extension type identified
286     //                     -- by extnID
287     //         }
288     // ```
289     //
290     // [RFC 5280 Section 4.1.2.9]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.9
291     #[derive(Clone, Debug, Eq, PartialEq, Sequence)]
292     pub struct ExtensionExample<'a> {
293         /// extnID      OBJECT IDENTIFIER,
294         pub extn_id: ObjectIdentifier,
295 
296         /// critical    BOOLEAN DEFAULT FALSE,
297         #[asn1(default = "default_false_example")]
298         pub critical: bool,
299 
300         /// extnValue   OCTET STRING
301         #[asn1(type = "OCTET STRING")]
302         pub extn_value: &'a [u8],
303     }
304 
305     /// X.509 `AlgorithmIdentifier`
306     #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
307     pub struct AlgorithmIdentifier<'a> {
308         pub algorithm: ObjectIdentifier,
309         pub parameters: Option<AnyRef<'a>>,
310     }
311 
312     /// X.509 `SubjectPublicKeyInfo` (SPKI)
313     #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
314     pub struct SubjectPublicKeyInfo<'a> {
315         pub algorithm: AlgorithmIdentifier<'a>,
316         #[asn1(type = "BIT STRING")]
317         pub subject_public_key: &'a [u8],
318     }
319 
320     /// PKCS#8v2 `OneAsymmetricKey`
321     #[derive(Sequence)]
322     pub struct OneAsymmetricKey<'a> {
323         pub version: u8,
324         pub private_key_algorithm: AlgorithmIdentifier<'a>,
325         #[asn1(type = "OCTET STRING")]
326         pub private_key: &'a [u8],
327         #[asn1(context_specific = "0", extensible = "true", optional = "true")]
328         pub attributes: Option<SetOf<AnyRef<'a>, 1>>,
329         #[asn1(
330             context_specific = "1",
331             extensible = "true",
332             optional = "true",
333             type = "BIT STRING"
334         )]
335         pub public_key: Option<&'a [u8]>,
336     }
337 
338     /// X.509 extension
339     // TODO(tarcieri): tests for code derived with the `default` attribute
340     #[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
341     pub struct Extension<'a> {
342         extn_id: ObjectIdentifier,
343         #[asn1(default = "critical_default")]
344         critical: bool,
345         #[asn1(type = "OCTET STRING")]
346         extn_value: &'a [u8],
347     }
348 
349     /// Default value of the `critical` bit
critical_default() -> bool350     fn critical_default() -> bool {
351         false
352     }
353 
354     const ID_EC_PUBLIC_KEY_OID: ObjectIdentifier =
355         ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
356 
357     const PRIME256V1_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
358 
359     const ALGORITHM_IDENTIFIER_DER: &[u8] =
360         &hex!("30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a 86 48 ce 3d 03 01 07");
361 
362     #[derive(Sequence)]
363     #[asn1(tag_mode = "IMPLICIT")]
364     pub struct TypeCheckExpandedSequenceFieldAttributeCombinations<'a> {
365         pub simple: bool,
366         #[asn1(type = "BIT STRING")]
367         pub typed: &'a [u8],
368         #[asn1(context_specific = "0")]
369         pub context_specific: bool,
370         #[asn1(optional = "true")]
371         pub optional: Option<bool>,
372         #[asn1(default = "default_false_example")]
373         pub default: bool,
374         #[asn1(type = "BIT STRING", context_specific = "1")]
375         pub typed_context_specific: &'a [u8],
376         #[asn1(context_specific = "2", optional = "true")]
377         pub context_specific_optional: Option<bool>,
378         #[asn1(context_specific = "3", default = "default_false_example")]
379         pub context_specific_default: bool,
380         #[asn1(type = "BIT STRING", context_specific = "4", optional = "true")]
381         pub typed_context_specific_optional: Option<&'a [u8]>,
382     }
383 
384     #[test]
idp_test()385     fn idp_test() {
386         let idp = IssuingDistributionPointExample::from_der(&hex!("30038101FF")).unwrap();
387         assert_eq!(idp.only_contains_user_certs, true);
388         assert_eq!(idp.only_contains_cacerts, false);
389         assert_eq!(idp.indirect_crl, false);
390         assert_eq!(idp.only_contains_attribute_certs, false);
391 
392         let idp = IssuingDistributionPointExample::from_der(&hex!("30038201FF")).unwrap();
393         assert_eq!(idp.only_contains_user_certs, false);
394         assert_eq!(idp.only_contains_cacerts, true);
395         assert_eq!(idp.indirect_crl, false);
396         assert_eq!(idp.only_contains_attribute_certs, false);
397 
398         let idp = IssuingDistributionPointExample::from_der(&hex!("30038401FF")).unwrap();
399         assert_eq!(idp.only_contains_user_certs, false);
400         assert_eq!(idp.only_contains_cacerts, false);
401         assert_eq!(idp.indirect_crl, true);
402         assert_eq!(idp.only_contains_attribute_certs, false);
403 
404         let idp = IssuingDistributionPointExample::from_der(&hex!("30038501FF")).unwrap();
405         assert_eq!(idp.only_contains_user_certs, false);
406         assert_eq!(idp.only_contains_cacerts, false);
407         assert_eq!(idp.indirect_crl, false);
408         assert_eq!(idp.only_contains_attribute_certs, true);
409     }
410 
411     // demonstrates default field that is not context specific
412     #[test]
extension_test()413     fn extension_test() {
414         let ext1 = ExtensionExample::from_der(&hex!(
415             "300F"        //  0  15: SEQUENCE {
416             "0603551D13"  //  2   3:   OBJECT IDENTIFIER basicConstraints (2 5 29 19)
417             "0101FF"      //  7   1:   BOOLEAN TRUE
418             "0405"        //  10   5:   OCTET STRING, encapsulates {
419             "3003"        //  12   3:     SEQUENCE {
420             "0101FF"      //  14   1:       BOOLEAN TRUE
421         ))
422         .unwrap();
423         assert_eq!(ext1.critical, true);
424 
425         let ext2 = ExtensionExample::from_der(&hex!(
426             "301F"                                            //  0  31: SEQUENCE {
427             "0603551D23"                                      //  2   3:   OBJECT IDENTIFIER authorityKeyIdentifier (2 5 29 35)
428             "0418"                                            //  7  24:   OCTET STRING, encapsulates {
429             "3016"                                            //  9  22:     SEQUENCE {
430             "8014E47D5FD15C9586082C05AEBE75B665A7D95DA866"    // 11  20:       [0] E4 7D 5F D1 5C 95 86 08 2C 05 AE BE 75 B6 65 A7 D9 5D A8 66
431         ))
432         .unwrap();
433         assert_eq!(ext2.critical, false);
434     }
435 
436     #[test]
decode()437     fn decode() {
438         let algorithm_identifier = AlgorithmIdentifier::from_der(ALGORITHM_IDENTIFIER_DER).unwrap();
439 
440         assert_eq!(ID_EC_PUBLIC_KEY_OID, algorithm_identifier.algorithm);
441         assert_eq!(
442             PRIME256V1_OID,
443             ObjectIdentifier::try_from(algorithm_identifier.parameters.unwrap()).unwrap()
444         );
445     }
446 
447     #[test]
encode()448     fn encode() {
449         let parameters_oid = PRIME256V1_OID;
450 
451         let algorithm_identifier = AlgorithmIdentifier {
452             algorithm: ID_EC_PUBLIC_KEY_OID,
453             parameters: Some(AnyRef::from(&parameters_oid)),
454         };
455 
456         assert_eq!(
457             ALGORITHM_IDENTIFIER_DER,
458             algorithm_identifier.to_der().unwrap()
459         );
460     }
461 }
462