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(¶meters_oid)), 454 }; 455 456 assert_eq!( 457 ALGORITHM_IDENTIFIER_DER, 458 algorithm_identifier.to_der().unwrap() 459 ); 460 } 461 } 462