• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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: &'static [u8] =
52             &hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a");
53         const GENERAL_TIMESTAMP_DER: &'static [u8] =
54             &hex!("18 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a");
55 
56         #[test]
decode()57         fn decode() {
58             let utc_time = Time::from_der(UTC_TIMESTAMP_DER).unwrap();
59             assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
60 
61             let general_time = Time::from_der(GENERAL_TIMESTAMP_DER).unwrap();
62             assert_eq!(general_time.to_unix_duration().as_secs(), 673573540);
63         }
64 
65         #[test]
encode()66         fn encode() {
67             let mut buf = [0u8; 128];
68 
69             let utc_time = Time::from_der(UTC_TIMESTAMP_DER).unwrap();
70             let mut encoder = SliceWriter::new(&mut buf);
71             utc_time.encode(&mut encoder).unwrap();
72             assert_eq!(UTC_TIMESTAMP_DER, encoder.finish().unwrap());
73 
74             let general_time = Time::from_der(GENERAL_TIMESTAMP_DER).unwrap();
75             let mut encoder = SliceWriter::new(&mut buf);
76             general_time.encode(&mut encoder).unwrap();
77             assert_eq!(GENERAL_TIMESTAMP_DER, encoder.finish().unwrap());
78         }
79     }
80 
81     /// `Choice` with `IMPLICIT` tagging.
82     mod implicit {
83         use der::{
84             asn1::{BitStringRef, GeneralizedTime},
85             Choice, Decode, Encode, SliceWriter,
86         };
87         use hex_literal::hex;
88 
89         /// `Choice` macro test case for `IMPLICIT` tagging.
90         #[derive(Choice, Debug, Eq, PartialEq)]
91         #[asn1(tag_mode = "IMPLICIT")]
92         pub enum ImplicitChoice<'a> {
93             #[asn1(context_specific = "0", type = "BIT STRING")]
94             BitString(BitStringRef<'a>),
95 
96             #[asn1(context_specific = "1", type = "GeneralizedTime")]
97             Time(GeneralizedTime),
98 
99             #[asn1(context_specific = "2", type = "UTF8String")]
100             Utf8String(String),
101         }
102 
103         impl<'a> ImplicitChoice<'a> {
bit_string(&self) -> Option<BitStringRef<'a>>104             pub fn bit_string(&self) -> Option<BitStringRef<'a>> {
105                 match self {
106                     Self::BitString(bs) => Some(*bs),
107                     _ => None,
108                 }
109             }
110 
time(&self) -> Option<GeneralizedTime>111             pub fn time(&self) -> Option<GeneralizedTime> {
112                 match self {
113                     Self::Time(time) => Some(*time),
114                     _ => None,
115                 }
116             }
117         }
118 
119         const BITSTRING_DER: &'static [u8] = &hex!("80 04 00 01 02 03");
120         const TIME_DER: &'static [u8] = &hex!("81 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a");
121 
122         #[test]
decode()123         fn decode() {
124             let cs_bit_string = ImplicitChoice::from_der(BITSTRING_DER).unwrap();
125             assert_eq!(
126                 cs_bit_string.bit_string().unwrap().as_bytes().unwrap(),
127                 &[1, 2, 3]
128             );
129 
130             let cs_time = ImplicitChoice::from_der(TIME_DER).unwrap();
131             assert_eq!(
132                 cs_time.time().unwrap().to_unix_duration().as_secs(),
133                 673573540
134             );
135         }
136 
137         #[test]
encode()138         fn encode() {
139             let mut buf = [0u8; 128];
140 
141             let cs_bit_string = ImplicitChoice::from_der(BITSTRING_DER).unwrap();
142             let mut encoder = SliceWriter::new(&mut buf);
143             cs_bit_string.encode(&mut encoder).unwrap();
144             assert_eq!(BITSTRING_DER, encoder.finish().unwrap());
145 
146             let cs_time = ImplicitChoice::from_der(TIME_DER).unwrap();
147             let mut encoder = SliceWriter::new(&mut buf);
148             cs_time.encode(&mut encoder).unwrap();
149             assert_eq!(TIME_DER, encoder.finish().unwrap());
150         }
151     }
152 }
153 
154 /// Custom derive test cases for the `Enumerated` macro.
155 mod enumerated {
156     use der::{Decode, Encode, Enumerated, SliceWriter};
157     use hex_literal::hex;
158 
159     /// X.509 `CRLReason`.
160     #[derive(Enumerated, Copy, Clone, Debug, Eq, PartialEq)]
161     #[repr(u32)]
162     pub enum CrlReason {
163         Unspecified = 0,
164         KeyCompromise = 1,
165         CaCompromise = 2,
166         AffiliationChanged = 3,
167         Superseded = 4,
168         CessationOfOperation = 5,
169         CertificateHold = 6,
170         RemoveFromCrl = 8,
171         PrivilegeWithdrawn = 9,
172         AaCompromised = 10,
173     }
174 
175     const UNSPECIFIED_DER: &[u8] = &hex!("0a 01 00");
176     const KEY_COMPROMISE_DER: &[u8] = &hex!("0a 01 01");
177 
178     #[test]
decode()179     fn decode() {
180         let unspecified = CrlReason::from_der(UNSPECIFIED_DER).unwrap();
181         assert_eq!(CrlReason::Unspecified, unspecified);
182 
183         let key_compromise = CrlReason::from_der(KEY_COMPROMISE_DER).unwrap();
184         assert_eq!(CrlReason::KeyCompromise, key_compromise);
185     }
186 
187     #[test]
encode()188     fn encode() {
189         let mut buf = [0u8; 128];
190 
191         let mut encoder = SliceWriter::new(&mut buf);
192         CrlReason::Unspecified.encode(&mut encoder).unwrap();
193         assert_eq!(UNSPECIFIED_DER, encoder.finish().unwrap());
194 
195         let mut encoder = SliceWriter::new(&mut buf);
196         CrlReason::KeyCompromise.encode(&mut encoder).unwrap();
197         assert_eq!(KEY_COMPROMISE_DER, encoder.finish().unwrap());
198     }
199 }
200 
201 /// Custom derive test cases for the `Sequence` macro.
202 #[cfg(feature = "oid")]
203 mod sequence {
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 
272     // Extension as defined in [RFC 5280 Section 4.1.2.9].
273     //
274     // The ASN.1 definition for Extension objects is below. The extnValue type may be further parsed using a decoder corresponding to the extnID value.
275     //
276     // ```text
277     //    Extension  ::=  SEQUENCE  {
278     //         extnID      OBJECT IDENTIFIER,
279     //         critical    BOOLEAN DEFAULT FALSE,
280     //         extnValue   OCTET STRING
281     //                     -- contains the DER encoding of an ASN.1 value
282     //                     -- corresponding to the extension type identified
283     //                     -- by extnID
284     //         }
285     // ```
286     //
287     // [RFC 5280 Section 4.1.2.9]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.9
288     #[derive(Clone, Debug, Eq, PartialEq, Sequence)]
289     pub struct ExtensionExample<'a> {
290         /// extnID      OBJECT IDENTIFIER,
291         pub extn_id: ObjectIdentifier,
292 
293         /// critical    BOOLEAN DEFAULT FALSE,
294         #[asn1(default = "default_false_example")]
295         pub critical: bool,
296 
297         /// extnValue   OCTET STRING
298         #[asn1(type = "OCTET STRING")]
299         pub extn_value: &'a [u8],
300     }
301 
302     /// X.509 `AlgorithmIdentifier`
303     #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
304     pub struct AlgorithmIdentifier<'a> {
305         pub algorithm: ObjectIdentifier,
306         pub parameters: Option<AnyRef<'a>>,
307     }
308 
309     /// X.509 `SubjectPublicKeyInfo` (SPKI)
310     #[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
311     pub struct SubjectPublicKeyInfo<'a> {
312         pub algorithm: AlgorithmIdentifier<'a>,
313         #[asn1(type = "BIT STRING")]
314         pub subject_public_key: &'a [u8],
315     }
316 
317     /// PKCS#8v2 `OneAsymmetricKey`
318     #[derive(Sequence)]
319     pub struct OneAsymmetricKey<'a> {
320         pub version: u8,
321         pub private_key_algorithm: AlgorithmIdentifier<'a>,
322         #[asn1(type = "OCTET STRING")]
323         pub private_key: &'a [u8],
324         #[asn1(context_specific = "0", extensible = "true", optional = "true")]
325         pub attributes: Option<SetOf<AnyRef<'a>, 1>>,
326         #[asn1(
327             context_specific = "1",
328             extensible = "true",
329             optional = "true",
330             type = "BIT STRING"
331         )]
332         pub public_key: Option<&'a [u8]>,
333     }
334 
335     /// X.509 extension
336     // TODO(tarcieri): tests for code derived with the `default` attribute
337     #[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
338     pub struct Extension<'a> {
339         extn_id: ObjectIdentifier,
340         #[asn1(default = "critical_default")]
341         critical: bool,
342         #[asn1(type = "OCTET STRING")]
343         extn_value: &'a [u8],
344     }
345 
346     /// Default value of the `critical` bit
critical_default() -> bool347     fn critical_default() -> bool {
348         false
349     }
350 
351     const ID_EC_PUBLIC_KEY_OID: ObjectIdentifier =
352         ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
353 
354     const PRIME256V1_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
355 
356     const ALGORITHM_IDENTIFIER_DER: &[u8] =
357         &hex!("30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a 86 48 ce 3d 03 01 07");
358 
359     #[derive(Sequence)]
360     #[asn1(tag_mode = "IMPLICIT")]
361     pub struct TypeCheckExpandedSequenceFieldAttributeCombinations<'a> {
362         pub simple: bool,
363         #[asn1(type = "BIT STRING")]
364         pub typed: &'a [u8],
365         #[asn1(context_specific = "0")]
366         pub context_specific: bool,
367         #[asn1(optional = "true")]
368         pub optional: Option<bool>,
369         #[asn1(default = "default_false_example")]
370         pub default: bool,
371         #[asn1(type = "BIT STRING", context_specific = "1")]
372         pub typed_context_specific: &'a [u8],
373         #[asn1(context_specific = "2", optional = "true")]
374         pub context_specific_optional: Option<bool>,
375         #[asn1(context_specific = "3", default = "default_false_example")]
376         pub context_specific_default: bool,
377         #[asn1(type = "BIT STRING", context_specific = "4", optional = "true")]
378         pub typed_context_specific_optional: Option<&'a [u8]>,
379     }
380 
381     #[test]
idp_test()382     fn idp_test() {
383         let idp = IssuingDistributionPointExample::from_der(&hex!("30038101FF")).unwrap();
384         assert_eq!(idp.only_contains_user_certs, true);
385         assert_eq!(idp.only_contains_cacerts, false);
386         assert_eq!(idp.indirect_crl, false);
387         assert_eq!(idp.only_contains_attribute_certs, false);
388 
389         let idp = IssuingDistributionPointExample::from_der(&hex!("30038201FF")).unwrap();
390         assert_eq!(idp.only_contains_user_certs, false);
391         assert_eq!(idp.only_contains_cacerts, true);
392         assert_eq!(idp.indirect_crl, false);
393         assert_eq!(idp.only_contains_attribute_certs, false);
394 
395         let idp = IssuingDistributionPointExample::from_der(&hex!("30038401FF")).unwrap();
396         assert_eq!(idp.only_contains_user_certs, false);
397         assert_eq!(idp.only_contains_cacerts, false);
398         assert_eq!(idp.indirect_crl, true);
399         assert_eq!(idp.only_contains_attribute_certs, false);
400 
401         let idp = IssuingDistributionPointExample::from_der(&hex!("30038501FF")).unwrap();
402         assert_eq!(idp.only_contains_user_certs, false);
403         assert_eq!(idp.only_contains_cacerts, false);
404         assert_eq!(idp.indirect_crl, false);
405         assert_eq!(idp.only_contains_attribute_certs, true);
406     }
407 
408     // demonstrates default field that is not context specific
409     #[test]
extension_test()410     fn extension_test() {
411         let ext1 = ExtensionExample::from_der(&hex!(
412             "300F"        //  0  15: SEQUENCE {
413             "0603551D13"  //  2   3:   OBJECT IDENTIFIER basicConstraints (2 5 29 19)
414             "0101FF"      //  7   1:   BOOLEAN TRUE
415             "0405"        //  10   5:   OCTET STRING, encapsulates {
416             "3003"        //  12   3:     SEQUENCE {
417             "0101FF"      //  14   1:       BOOLEAN TRUE
418         ))
419         .unwrap();
420         assert_eq!(ext1.critical, true);
421 
422         let ext2 = ExtensionExample::from_der(&hex!(
423             "301F"                                            //  0  31: SEQUENCE {
424             "0603551D23"                                      //  2   3:   OBJECT IDENTIFIER authorityKeyIdentifier (2 5 29 35)
425             "0418"                                            //  7  24:   OCTET STRING, encapsulates {
426             "3016"                                            //  9  22:     SEQUENCE {
427             "8014E47D5FD15C9586082C05AEBE75B665A7D95DA866"    // 11  20:       [0] E4 7D 5F D1 5C 95 86 08 2C 05 AE BE 75 B6 65 A7 D9 5D A8 66
428         ))
429         .unwrap();
430         assert_eq!(ext2.critical, false);
431     }
432 
433     #[test]
decode()434     fn decode() {
435         let algorithm_identifier =
436             AlgorithmIdentifier::from_der(&ALGORITHM_IDENTIFIER_DER).unwrap();
437 
438         assert_eq!(ID_EC_PUBLIC_KEY_OID, algorithm_identifier.algorithm);
439         assert_eq!(
440             PRIME256V1_OID,
441             ObjectIdentifier::try_from(algorithm_identifier.parameters.unwrap()).unwrap()
442         );
443     }
444 
445     #[test]
encode()446     fn encode() {
447         let parameters_oid = PRIME256V1_OID;
448 
449         let algorithm_identifier = AlgorithmIdentifier {
450             algorithm: ID_EC_PUBLIC_KEY_OID,
451             parameters: Some(AnyRef::from(&parameters_oid)),
452         };
453 
454         assert_eq!(
455             ALGORITHM_IDENTIFIER_DER,
456             algorithm_identifier.to_vec().unwrap()
457         );
458     }
459 }
460