From 9698764e60052dd5c59be6bac832c34e6a859728 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 10:11:45 -0500 Subject: [PATCH 01/11] x509: derive(Sequence, Debug) for TbsCertificate Signed-off-by: Nathaniel McCallum --- x509/src/certificate.rs | 182 +++++++++----------------------------- x509/tests/certificate.rs | 2 +- 2 files changed, 41 insertions(+), 143 deletions(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 9c18c610a..1f44a53c9 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -1,7 +1,7 @@ //! Certificate [`Certificate`] and TBSCertificate [`TBSCertificate`] as defined in RFC 5280 -use der::asn1::{BitString, ContextSpecific, ObjectIdentifier, UIntBytes}; -use der::{Enumerated, Sequence, TagMode, TagNumber}; +use der::asn1::{BitString, ObjectIdentifier, UIntBytes}; +use der::{Enumerated, Sequence}; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; use x501::name::Name; use x501::time::Validity; @@ -33,160 +33,58 @@ impl Default for Version { } } -/// X.509 `TBSCertificate` as defined in [RFC 5280 Section 4.1.2.5] +/// X.509 `TBSCertificate` as defined in [RFC 5280 Section 4.1] /// -/// ASN.1 structure containing the names of the subject and issuer, a public key associated -/// with the subject, a validity period, and other associated information. +/// ASN.1 structure containing the names of the subject and issuer, a public +/// key associated with the subject, a validity period, and other associated +/// information. /// /// ```text -/// TBSCertificate ::= SEQUENCE { -/// version [0] Version DEFAULT v1, -/// serialNumber CertificateSerialNumber, -/// signature AlgorithmIdentifier{SIGNATURE-ALGORITHM, {SignatureAlgorithms}}, -/// issuer Name, -/// validity Validity, -/// subject Name, -/// subjectPublicKeyInfo SubjectPublicKeyInfo, -/// ... , -/// [[2: -- If present, version MUST be v2 -/// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -/// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL -/// ]], -/// [[3: -- If present, version MUST be v3 -- -/// extensions [3] Extensions{{CertExtensions}} OPTIONAL -/// ]], ... } +/// TBSCertificate ::= SEQUENCE { +/// version [0] EXPLICIT Version DEFAULT v1, +/// serialNumber CertificateSerialNumber, +/// signature AlgorithmIdentifier, +/// issuer Name, +/// validity Validity, +/// subject Name, +/// subjectPublicKeyInfo SubjectPublicKeyInfo, +/// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, +/// -- If present, version MUST be v2 or v3 +/// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, +/// -- If present, version MUST be v2 or v3 +/// extensions [3] Extensions OPTIONAL +/// -- If present, version MUST be v3 -- +/// } /// ``` /// -/// [RFC 5280 Section 4.1.2.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5 -#[derive(Clone, Eq, PartialEq)] -pub struct TBSCertificate<'a> { - /// version [0] Version DEFAULT v1, - //#[asn1(context_specific = "0", default = "Default::default")] +/// [RFC 5280 Section 4.1.2.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] +pub struct TbsCertificate<'a> { + /// The certificate version + /// + /// Note that this value defaults to Version 1 per the RFC. However, + /// fields such as `issuer_unique_id`, `subject_unique_id` and `extensions` + /// require later versions. Care should be taken in order to ensure + /// standards compliance. + #[asn1(context_specific = "0", default = "Default::default")] pub version: Version, - /// serialNumber CertificateSerialNumber, + pub serial_number: UIntBytes<'a>, - /// signature AlgorithmIdentifier{SIGNATURE-ALGORITHM, {SignatureAlgorithms}}, pub signature: AlgorithmIdentifier<'a>, - /// issuer Name, pub issuer: Name<'a>, - /// validity Validity, pub validity: Validity, - /// subject Name, pub subject: Name<'a>, - /// subjectPublicKeyInfo SubjectPublicKeyInfo, pub subject_public_key_info: SubjectPublicKeyInfo<'a>, - /// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - //#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")] + + #[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")] pub issuer_unique_id: Option>, - /// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL - //#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")] + + #[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")] pub subject_unique_id: Option>, - /// extensions [3] Extensions{{CertExtensions}} OPTIONAL - //#[asn1(context_specific = "3", optional = "true", tag_mode = "EXPLICIT")] - pub extensions: Option>, -} -impl<'a> ::der::Decodable<'a> for TBSCertificate<'a> { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let version = - ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N0)? - .map(|cs| cs.value) - .unwrap_or_else(Default::default); - let serial_number = decoder.decode()?; - let signature = decoder.decode()?; - let issuer = decoder.decode()?; - let validity = decoder.decode()?; - let subject = decoder.decode()?; - let subject_public_key_info = decoder.decode()?; - let issuer_unique_id = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - let subject_unique_id = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N2)? - .map(|cs| cs.value); - let extensions = - ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N3)? - .map(|cs| cs.value); - Ok(Self { - version, - serial_number, - signature, - issuer, - validity, - subject, - subject_public_key_info, - issuer_unique_id, - subject_unique_id, - extensions, - }) - }) - } -} -const VERSION_TAG: TagNumber = TagNumber::new(0); -const EXTENSIONS_TAG: TagNumber = TagNumber::new(3); -impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - #[allow(unused_imports)] - use core::convert::TryFrom; - f(&[ - &ContextSpecific { - tag_number: VERSION_TAG, - tag_mode: TagMode::Explicit, - value: self.version, - }, - &self.serial_number, - &self.signature, - &self.issuer, - &self.validity, - &self.subject, - &self.subject_public_key_info, - &self.issuer_unique_id, - &self.subject_unique_id, - &self.extensions.as_ref().map(|exts| ContextSpecific { - tag_number: EXTENSIONS_TAG, - tag_mode: TagMode::Explicit, - value: exts.clone(), - }), - ]) - } -} -impl<'a> ::core::fmt::Debug for TBSCertificate<'a> { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?; - f.write_fmt(format_args!("\tSerial Number: 0x"))?; - for b in self.serial_number.as_bytes() { - f.write_fmt(format_args!("{:02X?}", b))?; - } - f.write_fmt(format_args!("\n"))?; - f.write_fmt(format_args!("\tSignature: {:?}\n", self.signature))?; - f.write_fmt(format_args!("\tIssuer: {:?}\n", self.issuer))?; - f.write_fmt(format_args!("\tValidity: {:?}\n", self.validity))?; - f.write_fmt(format_args!("\tSubject: {:?}\n", self.subject))?; - f.write_fmt(format_args!( - "\tSubject Public Key Info: {:?}\n", - self.subject_public_key_info - ))?; - f.write_fmt(format_args!( - "\tIssuer Unique ID: {:?}\n", - self.issuer_unique_id - ))?; - f.write_fmt(format_args!( - "\tSubject Unique ID: {:?}\n", - self.subject_unique_id - ))?; - if let Some(exts) = self.extensions.as_ref() { - for (i, e) in exts.iter().enumerate() { - f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?; - } - } else { - f.write_fmt(format_args!("\tExtensions: None\n"))?; - } - Ok(()) - } + #[asn1(context_specific = "3", optional = "true", tag_mode = "EXPLICIT")] + pub extensions: Option>, } /// X.509 certificates are defined in [RFC 5280 Section 4.1]. @@ -204,7 +102,7 @@ impl<'a> ::core::fmt::Debug for TBSCertificate<'a> { #[derive(Clone, Debug, Eq, PartialEq, Sequence)] pub struct Certificate<'a> { /// tbsCertificate TBSCertificate, - pub tbs_certificate: TBSCertificate<'a>, + pub tbs_certificate: TbsCertificate<'a>, /// signatureAlgorithm AlgorithmIdentifier, pub signature_algorithm: AlgorithmIdentifier<'a>, /// signature BIT STRING diff --git a/x509/tests/certificate.rs b/x509/tests/certificate.rs index 60f0ba101..0e5bbdaeb 100644 --- a/x509/tests/certificate.rs +++ b/x509/tests/certificate.rs @@ -106,7 +106,7 @@ fn reencode_cert() { include_bytes!("examples/026EDA6FA1EDFA8C253936C75B5EEBD954BFF452.fake.der"); let defer_cert = DeferDecodeCertificate::from_der(der_encoded_cert).unwrap(); - let parsed_tbs = TBSCertificate::from_der(defer_cert.tbs_certificate).unwrap(); + let parsed_tbs = TbsCertificate::from_der(defer_cert.tbs_certificate).unwrap(); let reencoded_tbs = parsed_tbs.to_vec().unwrap(); assert_eq!(defer_cert.tbs_certificate, reencoded_tbs); From 256f9d8e81eaa5ed3e87c3823ae34d0cc5ddd00a Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 10:25:44 -0500 Subject: [PATCH 02/11] x509: derive(Sequence) for PolicyConstraints Signed-off-by: Nathaniel McCallum --- x509/src/pkix_extensions.rs | 60 ++++++------------------------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/x509/src/pkix_extensions.rs b/x509/src/pkix_extensions.rs index 067d4ae16..71b7c70b7 100644 --- a/x509/src/pkix_extensions.rs +++ b/x509/src/pkix_extensions.rs @@ -440,70 +440,28 @@ impl<'a> ::der::Sequence<'a> for GeneralSubtree<'a> { } } -/// Policy constraints extension as defined in [RFC 5280 Section 4.2.1.11] and as identified by the [`PKIX_CE_POLICY_CONSTRAINTS`](constant.PKIX_CE_POLICY_CONSTRAINTS.html) OID. +/// Policy constraints extension as defined in [RFC 5280 Section 4.2.1.11]. +/// +/// This extension is identified by the [`PKIX_CE_POLICY_CONSTRAINTS`](constant.PKIX_CE_POLICY_CONSTRAINTS.html) OID. /// /// ```text /// PolicyConstraints ::= SEQUENCE { /// requireExplicitPolicy [0] SkipCerts OPTIONAL, -/// inhibitPolicyMapping [1] SkipCerts OPTIONAL } +/// inhibitPolicyMapping [1] SkipCerts OPTIONAL +/// } /// ``` /// /// [RFC 5280 Section 4.2.1.11]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.11 -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] pub struct PolicyConstraints { - /// requireExplicitPolicy [0] SkipCerts OPTIONAL, + #[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")] pub require_explicit_policy: Option, - /// inhibitPolicyMapping [1] SkipCerts OPTIONAL } + #[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")] pub inhibit_policy_mapping: Option, } -impl<'a> ::der::Decodable<'a> for PolicyConstraints { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let require_explicit_policy = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? - .map(|cs| cs.value); - let inhibit_policy_mapping = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - Ok(Self { - require_explicit_policy, - inhibit_policy_mapping, - }) - }) - } -} - -const REQUIRE_EXPLICIT_POLICY_TAG: TagNumber = TagNumber::new(0); -const INHIBIT_POLICY_MAPPING_TAG: TagNumber = TagNumber::new(1); - -impl<'a> ::der::Sequence<'a> for PolicyConstraints { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - f(&[ - &self - .require_explicit_policy - .as_ref() - .map(|elem| ContextSpecific { - tag_number: REQUIRE_EXPLICIT_POLICY_TAG, - tag_mode: TagMode::Implicit, - value: *elem, - }), - &self - .inhibit_policy_mapping - .as_ref() - .map(|elem| ContextSpecific { - tag_number: INHIBIT_POLICY_MAPPING_TAG, - tag_mode: TagMode::Implicit, - value: *elem, - }), - ]) - } -} - /// Inhibit any policy extension as defined in [RFC 5280 Section 4.2.1.14] and as identified by the [`PKIX_CE_INHIBIT_ANY_POLICY`](constant.PKIX_CE_INHIBIT_ANY_POLICY.html) OID. /// /// ```text From cf22659ea9e8fa13913fe60cde143b77c06e1210 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 10:26:58 -0500 Subject: [PATCH 03/11] x509: derive(Sequence) for AccessDescription Signed-off-by: Nathaniel McCallum --- x509/src/pkix_extensions.rs | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/x509/src/pkix_extensions.rs b/x509/src/pkix_extensions.rs index 71b7c70b7..7e9deba55 100644 --- a/x509/src/pkix_extensions.rs +++ b/x509/src/pkix_extensions.rs @@ -481,46 +481,24 @@ pub type InhibitAnyPolicy = u32; /// [RFC 5280 Section 4.2.2.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.2.1 pub type AuthorityInfoAccessSyntax<'a> = Vec>; -/// AccessDescription as defined in [RFC 5280 Section 4.2.2.1] in support of the Authority Information Access extension (and referenced by Subject Information Access extension). +/// AccessDescription as defined in [RFC 5280 Section 4.2.2.1]. /// /// ```text /// AccessDescription ::= SEQUENCE { -/// accessMethod OBJECT IDENTIFIER, -/// accessLocation GeneralName } +/// accessMethod OBJECT IDENTIFIER, +/// accessLocation GeneralName +/// } /// ``` /// /// [RFC 5280 Section 4.2.2.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.2.1 -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] pub struct AccessDescription<'a> { - /// accessMethod OBJECT IDENTIFIER, pub access_method: ObjectIdentifier, - /// accessLocation GeneralName pub access_location: GeneralName<'a>, } -impl<'a> ::der::Decodable<'a> for AccessDescription<'a> { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let access_method = decoder.decode()?; - let access_location = decoder.decode()?; - Ok(Self { - access_method, - access_location, - }) - }) - } -} - -impl<'a> ::der::Sequence<'a> for AccessDescription<'a> { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - f(&[&self.access_method, &self.access_location]) - } -} - /// Subject information access extension as defined in [RFC 5280 Section 4.2.2.2] and as identified by the [`PKIX_PE_SUBJECTINFOACCESS`](constant.PKIX_PE_SUBJECTINFOACCESS.html) OID. /// /// ```text From cc656f685b9e2e7027703d2acdf347d1aa8e2765 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 10:29:11 -0500 Subject: [PATCH 04/11] x509: derive(Sequence) for AuthorityKeyIdentifier Signed-off-by: Nathaniel McCallum --- x509/src/pkix_extensions.rs | 83 +++++++------------------------------ 1 file changed, 14 insertions(+), 69 deletions(-) diff --git a/x509/src/pkix_extensions.rs b/x509/src/pkix_extensions.rs index 7e9deba55..599009527 100644 --- a/x509/src/pkix_extensions.rs +++ b/x509/src/pkix_extensions.rs @@ -569,88 +569,33 @@ pub enum CRLReason { AaCompromise = 10, } -/// Authority key identifier extension as defined in [RFC 5280 Section 4.2.1.1] and as identified by the [`PKIX_CE_AUTHORITY_KEY_IDENTIFIER`](constant.PKIX_CE_AUTHORITY_KEY_IDENTIFIER.html) OID. +/// AuthorityKeyIdentifier as defined in [RFC 5280 Section 4.2.1.1]. +/// +/// This extension is identified by the [`PKIX_CE_AUTHORITY_KEY_IDENTIFIER`](constant.PKIX_CE_AUTHORITY_KEY_IDENTIFIER.html) OID. /// /// ```text -/// AuthorityKeyIdentifier ::= SEQUENCE { -/// keyIdentifier [0] KeyIdentifier OPTIONAL, -/// authorityCertIssuer [1] GeneralNames OPTIONAL, -/// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } +/// AuthorityKeyIdentifier ::= SEQUENCE { +/// keyIdentifier [0] KeyIdentifier OPTIONAL, +/// authorityCertIssuer [1] GeneralNames OPTIONAL, +/// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL +/// } /// -/// KeyIdentifier ::= OCTET STRING +/// KeyIdentifier ::= OCTET STRING /// ``` /// /// [RFC 5280 Section 4.2.1.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1 -//#[derive(Clone, Debug, Eq, PartialEq, Sequence)] -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] pub struct AuthorityKeyIdentifier<'a> { - /// keyIdentifier [0] KeyIdentifier OPTIONAL, - //#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")] pub key_identifier: Option>, - /// authorityCertIssuer [1] GeneralNames OPTIONAL, - //#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] pub authority_cert_issuer: Option>, - /// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - //#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] pub authority_cert_serial_number: Option>, } -impl<'a> ::der::Decodable<'a> for AuthorityKeyIdentifier<'a> { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let key_identifier = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? - .map(|cs| cs.value); - let authority_cert_issuer = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - let authority_cert_serial_number = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N2)? - .map(|cs| cs.value); - Ok(Self { - key_identifier, - authority_cert_issuer, - authority_cert_serial_number, - }) - }) - } -} - -const KEY_IDENTIFIER_TAG: TagNumber = TagNumber::new(0); -const AUTHORITY_CERT_ISSUER_TAG: TagNumber = TagNumber::new(1); -const AUTHORITY_CERT_SERIAL_NUMBER: TagNumber = TagNumber::new(2); - -impl<'a> ::der::Sequence<'a> for AuthorityKeyIdentifier<'a> { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - f(&[ - &self.key_identifier.as_ref().map(|elem| ContextSpecific { - tag_number: KEY_IDENTIFIER_TAG, - tag_mode: TagMode::Implicit, - value: *elem, - }), - &self - .authority_cert_issuer - .as_ref() - .map(|elem| ContextSpecific { - tag_number: AUTHORITY_CERT_ISSUER_TAG, - tag_mode: TagMode::Implicit, - value: elem.clone(), - }), - &self - .authority_cert_serial_number - .as_ref() - .map(|elem| ContextSpecific { - tag_number: AUTHORITY_CERT_SERIAL_NUMBER, - tag_mode: TagMode::Implicit, - value: *elem, - }), - ]) - } -} flags! { /// Reason flags as defined in [RFC 5280 Section 4.2.1.13]. From a6830f534090c7c4e5e5f05d87be9d3a75f17c29 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 13:41:48 -0500 Subject: [PATCH 05/11] x509: derive(Sequence) for PrivateKeyUsagePeriod Signed-off-by: Nathaniel McCallum --- x509/src/pkix_extensions.rs | 131 +++++++----------------------------- 1 file changed, 24 insertions(+), 107 deletions(-) diff --git a/x509/src/pkix_extensions.rs b/x509/src/pkix_extensions.rs index 599009527..25844afe8 100644 --- a/x509/src/pkix_extensions.rs +++ b/x509/src/pkix_extensions.rs @@ -213,67 +213,31 @@ pub struct PolicyQualifierInfo<'a> { pub qualifier: Option>, } -/// Private key usage extension as defined in [RFC 3280 Section 4.2.1.4] and as identified by the [`PKIX_CE_PRIVATE_KEY_USAGE_PERIOD`](constant.PKIX_CE_PRIVATE_KEY_USAGE_PERIOD.html) OID. +/// PrivateKeyUsagePeriod as defined in [RFC 3280 Section 4.2.1.4]. +/// +/// This extension is by the [`PKIX_CE_PRIVATE_KEY_USAGE_PERIOD`](constant.PKIX_CE_PRIVATE_KEY_USAGE_PERIOD.html) OID. /// /// RFC 5280 states "use of this ISO standard extension is neither deprecated nor recommended for use in the Internet PKI." /// /// ```text /// PrivateKeyUsagePeriod ::= SEQUENCE { -/// notBefore [0] GeneralizedTime OPTIONAL, -/// notAfter [1] GeneralizedTime OPTIONAL } -/// -- either notBefore or notAfter MUST be present +/// notBefore [0] GeneralizedTime OPTIONAL, +/// notAfter [1] GeneralizedTime OPTIONAL +/// -- either notBefore or notAfter MUST be present +/// } /// ``` /// /// [RFC 3280 Section 4.2.1.12]: https://datatracker.ietf.org/doc/html/rfc3280#section-4.2.1.4 -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Sequence)] +#[allow(missing_docs)] pub struct PrivateKeyUsagePeriod { - /// notBefore [0] GeneralizedTime OPTIONAL, + #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")] pub not_before: Option, - /// notAfter [1] GeneralizedTime OPTIONAL + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] pub not_after: Option, } -impl<'a> ::der::Decodable<'a> for PrivateKeyUsagePeriod { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let not_before = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? - .map(|cs| cs.value); - let not_after = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - Ok(Self { - not_before, - not_after, - }) - }) - } -} - -const NOT_BEFORE_TAG: TagNumber = TagNumber::new(0); -const NOT_AFTER_TAG: TagNumber = TagNumber::new(1); - -impl<'a> ::der::Sequence<'a> for PrivateKeyUsagePeriod { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - f(&[ - &self.not_before.as_ref().map(|elem| ContextSpecific { - tag_number: NOT_BEFORE_TAG, - tag_mode: TagMode::Implicit, - value: *elem, - }), - &self.not_after.as_ref().map(|elem| ContextSpecific { - tag_number: NOT_AFTER_TAG, - tag_mode: TagMode::Implicit, - value: *elem, - }), - ]) - } -} - /// NoticeReference as defined in [RFC 5280 Section 4.2.1.4] in support of the Certificate Policies extension. /// /// ```text @@ -366,80 +330,33 @@ pub struct NameConstraints<'a> { /// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10 pub type GeneralSubtrees<'a> = Vec>; -/// GeneralSubtree as defined in [RFC 5280 Section 4.2.1.10] in support of the Name Constraints extension. +/// GeneralSubtree as defined in [RFC 5280 Section 4.2.1.10]. /// /// ```text /// GeneralSubtree ::= SEQUENCE { -/// base GeneralName, -/// minimum [0] BaseDistance DEFAULT 0, -/// maximum [1] BaseDistance OPTIONAL } +/// base GeneralName, +/// minimum [0] BaseDistance DEFAULT 0, +/// maximum [1] BaseDistance OPTIONAL +/// } /// ``` /// /// [RFC 5280 Section 4.2.1.10]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10 -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] pub struct GeneralSubtree<'a> { - /// base GeneralName, pub base: GeneralName<'a>, - /// minimum [0] BaseDistance DEFAULT 0, + #[asn1( + context_specific = "0", + tag_mode = "IMPLICIT", + default = "Default::default" + )] pub minimum: u32, - /// maximum [1] BaseDistance OPTIONAL } + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] pub maximum: Option, } -impl<'a> ::der::Decodable<'a> for GeneralSubtree<'a> { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let base = decoder.decode()?; - - let minimum = - match decoder.context_specific::(GS_MINIMUM_TAG, TagMode::Implicit)? { - Some(v) => v, - _ => 0, - }; - - let maximum = decoder.context_specific::(GS_MAXIMUM_TAG, TagMode::Implicit)?; - - Ok(Self { - base, - minimum, - maximum, - }) - }) - } -} -const GS_MINIMUM_TAG: TagNumber = TagNumber::new(0); -const GS_MAXIMUM_TAG: TagNumber = TagNumber::new(1); - -impl<'a> ::der::Sequence<'a> for GeneralSubtree<'a> { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - //f(&[&self.base, &self.minimum, &self.maximum]) - let cs_min = ContextSpecific { - tag_number: GS_MINIMUM_TAG, - tag_mode: TagMode::Implicit, - value: self.minimum, - }; - - f(&[ - &self.base, - &::der::asn1::OptionalRef(if self.minimum == Default::default() { - None - } else { - Some(&cs_min) - }), - &self.maximum.as_ref().map(|exts| ContextSpecific { - tag_number: GS_MAXIMUM_TAG, - tag_mode: TagMode::Implicit, - value: *exts, - }), - ]) - } -} - /// Policy constraints extension as defined in [RFC 5280 Section 4.2.1.11]. /// /// This extension is identified by the [`PKIX_CE_POLICY_CONSTRAINTS`](constant.PKIX_CE_POLICY_CONSTRAINTS.html) OID. From d034c7f389ee13fbb562c9ab078d9bfc24aec8e3 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 16:19:17 -0500 Subject: [PATCH 06/11] x509: derive(Sequence) for remaining extensions The remaining types had interrelated dependencies and needed to be performed as a set. These types are: * DistributionPoint * DistributionPointName * IssuingDistributionPoint Signed-off-by: Nathaniel McCallum --- x509/src/pkix_extensions.rs | 280 +++++++--------------------------- x509/tests/pkix_extensions.rs | 6 +- 2 files changed, 61 insertions(+), 225 deletions(-) diff --git a/x509/src/pkix_extensions.rs b/x509/src/pkix_extensions.rs index 25844afe8..4725cf63a 100644 --- a/x509/src/pkix_extensions.rs +++ b/x509/src/pkix_extensions.rs @@ -4,15 +4,9 @@ use crate::general_name::GeneralName; use crate::general_name::GeneralNames; use alloc::vec::Vec; -use der::asn1::{ - Any, ContextSpecific, GeneralizedTime, Ia5String, Null, ObjectIdentifier, OctetString, - UIntBytes, Utf8String, -}; -use der::Header; -use der::{ - Choice, Decodable, DecodeValue, Decoder, Encodable, EncodeValue, Enumerated, ErrorKind, - FixedTag, Sequence, Tag, TagMode, TagNumber, -}; + +use der::asn1::*; +use der::{Choice, Enumerated, Sequence}; use flagset::{flags, FlagSet}; use x501::attr::AttributeTypeAndValue; use x501::name::RelativeDistinguishedName; @@ -559,174 +553,50 @@ pub type ReasonFlags = FlagSet; /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13 pub type CRLDistributionPoints<'a> = Vec>; -/// DistributionPoint as defined in [RFC 5280 Section 4.2.1.13] in support of the CRL distribution points extension. +/// DistributionPoint as defined in [RFC 5280 Section 4.2.1.13]. /// /// ```text /// DistributionPoint ::= SEQUENCE { -/// distributionPoint [0] DistributionPointName OPTIONAL, -/// reasons [1] ReasonFlags OPTIONAL, -/// cRLIssuer [2] GeneralNames OPTIONAL } +/// distributionPoint [0] DistributionPointName OPTIONAL, +/// reasons [1] ReasonFlags OPTIONAL, +/// cRLIssuer [2] GeneralNames OPTIONAL +/// } /// ``` /// /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13 -//#[derive(Sequence)] +#[derive(Clone, Debug, PartialEq, Eq, Sequence)] +#[allow(missing_docs)] pub struct DistributionPoint<'a> { - /// distributionPoint [0] DistributionPointName OPTIONAL, - //#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")] pub distribution_point: Option>, - /// reasons [1] ReasonFlags OPTIONAL, - //#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] pub reasons: Option, - /// cRLIssuer [2] GeneralNames OPTIONAL } - //#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] pub crl_issuer: Option>, } -const CRLDP_DISTRIBUTION_POINT_TAG: TagNumber = TagNumber::new(0); -const REASONS_TAG: TagNumber = TagNumber::new(1); -const CRL_ISSUER_TAG: TagNumber = TagNumber::new(2); - -impl<'a> ::der::Decodable<'a> for DistributionPoint<'a> { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let distribution_point = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? - .map(|cs| cs.value); - let reasons = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - let crl_issuer = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N2)? - .map(|cs| cs.value); - Ok(Self { - distribution_point, - reasons, - crl_issuer, - }) - }) - } -} - -impl<'a> ::der::Sequence<'a> for DistributionPoint<'a> { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - f(&[ - &self - .distribution_point - .as_ref() - .map(|elem| ContextSpecific { - tag_number: CRLDP_DISTRIBUTION_POINT_TAG, - tag_mode: TagMode::Implicit, - value: elem.clone(), - }), - &self.reasons.as_ref().map(|elem| ContextSpecific { - tag_number: REASONS_TAG, - tag_mode: TagMode::Implicit, - value: *elem, - }), - &self.crl_issuer.as_ref().map(|elem| ContextSpecific { - tag_number: CRL_ISSUER_TAG, - tag_mode: TagMode::Implicit, - value: elem.clone(), - }), - ]) - } -} - -/// DistributionPointName as defined in [RFC 5280 Section 4.2.1.13] in support of the CRL distribution points extension. +/// DistributionPointName as defined in [RFC 5280 Section 4.2.1.13]. /// /// ```text /// DistributionPointName ::= CHOICE { -/// fullName [0] GeneralNames, -/// nameRelativeToCRLIssuer [1] RelativeDistinguishedName } +/// fullName [0] GeneralNames, +/// nameRelativeToCRLIssuer [1] RelativeDistinguishedName +/// } /// ``` /// /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13 -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Choice)] +#[allow(missing_docs)] pub enum DistributionPointName<'a> { - /// fullName [0] GeneralNames, + #[asn1(context_specific = "0", tag_mode = "IMPLICIT", constructed = "true")] FullName(GeneralNames<'a>), - /// nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", constructed = "true")] NameRelativeToCRLIssuer(RelativeDistinguishedName<'a>), } -const FULL_NAME_TAG: TagNumber = TagNumber::new(0); -const NAME_RELATIVE_TO_ISSUER_TAG: TagNumber = TagNumber::new(1); - -impl<'a> DecodeValue<'a> for DistributionPointName<'a> { - fn decode_value(decoder: &mut Decoder<'a>, _header: Header) -> der::Result { - let t = decoder.peek_tag()?; - let o = t.octet(); - // Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here - match o { - 0xA0 => { - let on = decoder - .context_specific::>(FULL_NAME_TAG, TagMode::Implicit)?; - match on { - Some(on) => Ok(DistributionPointName::FullName(on)), - _ => Err(ErrorKind::Failed.into()), - } - } - 0xA1 => { - let on = decoder.context_specific::>( - NAME_RELATIVE_TO_ISSUER_TAG, - TagMode::Implicit, - )?; - match on { - Some(on) => Ok(DistributionPointName::NameRelativeToCRLIssuer(on)), - _ => Err(ErrorKind::Failed.into()), - } - } - _ => Err(ErrorKind::TagUnknown { byte: o }.into()), - } - } -} - -impl<'a> EncodeValue for DistributionPointName<'a> { - fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> { - match self { - Self::FullName(variant) => ContextSpecific { - tag_number: FULL_NAME_TAG, - tag_mode: TagMode::Implicit, - value: variant.clone(), - } - .encode(encoder), - Self::NameRelativeToCRLIssuer(variant) => ContextSpecific { - tag_number: NAME_RELATIVE_TO_ISSUER_TAG, - tag_mode: TagMode::Implicit, - value: (*variant).clone(), - } - .encode(encoder), - } - } - fn value_len(&self) -> ::der::Result<::der::Length> { - match self { - Self::FullName(variant) => ContextSpecific { - tag_number: FULL_NAME_TAG, - tag_mode: TagMode::Implicit, - value: variant.clone(), - } - .encoded_len(), - Self::NameRelativeToCRLIssuer(variant) => ContextSpecific { - tag_number: NAME_RELATIVE_TO_ISSUER_TAG, - tag_mode: TagMode::Implicit, - value: variant.clone(), - } - .encoded_len(), - } - } -} - -//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag -impl<'a> FixedTag for DistributionPointName<'a> { - const TAG: Tag = ::der::Tag::Sequence; -} - /// Freshest CRL extension as defined in [RFC 5280 Section 5.2.6] and as identified by the [`PKIX_CE_FRESHEST_CRL`](constant.PKIX_CE_FRESHEST_CRL.html) OID. /// /// ```text @@ -736,96 +606,62 @@ impl<'a> FixedTag for DistributionPointName<'a> { /// [RFC 5280 Section 5.2.6]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.6 pub type FreshestCRL<'a> = CRLDistributionPoints<'a>; -/// 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. +/// IssuingDistributionPoint as defined in [RFC 5280 Section 5.2.5]. +/// +/// This extension is identified by the [`PKIX_PE_SUBJECTINFOACCESS`](constant.PKIX_PE_SUBJECTINFOACCESS.html) OID. /// /// ```text /// IssuingDistributionPoint ::= SEQUENCE { -/// distributionPoint [0] DistributionPointName OPTIONAL, -/// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, -/// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, -/// onlySomeReasons [3] ReasonFlags OPTIONAL, -/// indirectCRL [4] BOOLEAN DEFAULT FALSE, -/// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } -/// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, -/// -- and onlyContainsAttributeCerts may be set to TRUE. +/// distributionPoint [0] DistributionPointName OPTIONAL, +/// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, +/// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, +/// onlySomeReasons [3] ReasonFlags OPTIONAL, +/// indirectCRL [4] BOOLEAN DEFAULT FALSE, +/// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE +/// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, +/// -- and onlyContainsAttributeCerts may be set to TRUE. +/// } /// ``` /// /// [RFC 5280 Section 5.2.5]: https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5 -//#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] pub struct IssuingDistributionPoint<'a> { - /// distributionPoint [0] DistributionPointName OPTIONAL, + #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")] pub distribution_point: Option>, - /// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + #[asn1( + context_specific = "1", + tag_mode = "IMPLICIT", + default = "Default::default" + )] pub only_contains_user_certs: bool, - /// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, - pub only_contains_cacerts: bool, + #[asn1( + context_specific = "2", + tag_mode = "IMPLICIT", + default = "Default::default" + )] + pub only_contains_ca_certs: bool, - /// onlySomeReasons [3] ReasonFlags OPTIONAL, + #[asn1(context_specific = "3", tag_mode = "IMPLICIT", optional = "true")] pub only_some_reasons: Option, - /// indirectCRL [4] BOOLEAN DEFAULT FALSE, + #[asn1( + context_specific = "4", + tag_mode = "IMPLICIT", + default = "Default::default" + )] pub indirect_crl: bool, - /// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE + #[asn1( + context_specific = "5", + tag_mode = "IMPLICIT", + default = "Default::default" + )] pub only_contains_attribute_certs: bool, } -const DISTRIBUTION_POINT_TAG: TagNumber = TagNumber::new(0); -const ONLY_CONTAINS_USER_CERTS_TAG: TagNumber = TagNumber::new(1); -const ONLY_CONTAINS_CA_CERTS_TAG: TagNumber = TagNumber::new(2); -const ONLY_SOME_REASONS_TAG: TagNumber = TagNumber::new(3); -const INDIRECT_TAG: TagNumber = TagNumber::new(4); -const ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG: TagNumber = TagNumber::new(5); - -impl<'a> Decodable<'a> for IssuingDistributionPoint<'a> { - fn decode(decoder: &mut Decoder<'a>) -> der::Result { - decoder.sequence(|decoder| { - let distribution_point = decoder.context_specific::>( - DISTRIBUTION_POINT_TAG, - TagMode::Implicit, - )?; - - // for each of the BOOLEAN fields, assign the DEFAULT value upon None - let mut only_contains_user_certs = decoder - .context_specific::(ONLY_CONTAINS_USER_CERTS_TAG, TagMode::Implicit)?; - if None == only_contains_user_certs { - only_contains_user_certs = Some(false); - } - - let mut only_contains_cacerts = - decoder.context_specific::(ONLY_CONTAINS_CA_CERTS_TAG, TagMode::Implicit)?; - if None == only_contains_cacerts { - only_contains_cacerts = Some(false); - } - - let only_some_reasons = decoder - .context_specific::(ONLY_SOME_REASONS_TAG, TagMode::Implicit)?; - - let mut indirect_crl = - decoder.context_specific::(INDIRECT_TAG, TagMode::Implicit)?; - if None == indirect_crl { - indirect_crl = Some(false); - } - - let mut only_contains_attribute_certs = decoder - .context_specific::(ONLY_CONTAINS_ATTRIBUTE_CERTS_TAG, TagMode::Implicit)?; - if None == only_contains_attribute_certs { - only_contains_attribute_certs = Some(false); - } - Ok(IssuingDistributionPoint { - distribution_point, - only_contains_user_certs: only_contains_user_certs.unwrap(), - only_contains_cacerts: only_contains_cacerts.unwrap(), - only_some_reasons, - indirect_crl: indirect_crl.unwrap(), - only_contains_attribute_certs: only_contains_attribute_certs.unwrap(), - }) - }) - } -} - /// The PIV NACI extension is defined in [FIPS 201-2 Appendix B] and is identified by the [`PIV_NACI_INDICATOR`](constant.PIV_NACI_INDICATOR.html) OID. /// /// ```text diff --git a/x509/tests/pkix_extensions.rs b/x509/tests/pkix_extensions.rs index d97ae25fc..5778207f7 100644 --- a/x509/tests/pkix_extensions.rs +++ b/x509/tests/pkix_extensions.rs @@ -872,7 +872,7 @@ fn decode_idp() { // IDP from 04A8739769B3C090A11DCDFABA3CF33F4BEF21F3.crl in PKITS 2048 in ficam-scvp-testing repo let idp = IssuingDistributionPoint::from_der(&hex!("30038201FF")).unwrap(); - assert_eq!(idp.only_contains_cacerts, true); + assert_eq!(idp.only_contains_ca_certs, true); assert_eq!(idp.only_contains_attribute_certs, false); assert_eq!(idp.only_contains_user_certs, false); assert_eq!(idp.indirect_crl, false); @@ -977,7 +977,7 @@ fn decode_idp() { // IDP from 54B0D2A6F6AA4780771CC4F9F076F623CEB0F57E.crl in PKITS 2048 in ficam-scvp-testing repo let idp = IssuingDistributionPoint::from_der(&hex!("3067A060A05EA45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C8303079F80")).unwrap(); - assert_eq!(idp.only_contains_cacerts, false); + assert_eq!(idp.only_contains_ca_certs, false); assert_eq!(idp.only_contains_attribute_certs, false); assert_eq!(idp.only_contains_user_certs, false); assert_eq!(idp.indirect_crl, false); @@ -1097,7 +1097,7 @@ fn decode_idp() { // IDP from 959528526E54B646AF895E2362D3AD20F4B3284D.crl in PKITS 2048 in ficam-scvp-testing repo let idp = IssuingDistributionPoint::from_der(&hex!("30820168A0820161A082015DA4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434136A4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434137A46D306B310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353121301F0603550403131843524C3120666F7220696E64697265637443524C204341358401FF")).unwrap(); - assert_eq!(idp.only_contains_cacerts, false); + assert_eq!(idp.only_contains_ca_certs, false); assert_eq!(idp.only_contains_attribute_certs, false); assert_eq!(idp.only_contains_user_certs, false); assert_eq!(idp.indirect_crl, true); From 8f95e6213a2fbbb13b5c2f54467cf0fad18d1607 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 11 Feb 2022 13:42:10 -0500 Subject: [PATCH 07/11] x509: derive(Sequence, Debug) for TrustAnchorInfo Signed-off-by: Nathaniel McCallum --- x509/src/trust_anchor_format.rs | 128 +++++--------------------------- 1 file changed, 19 insertions(+), 109 deletions(-) diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs index 16e2c7d03..1cdf4dd2c 100644 --- a/x509/src/trust_anchor_format.rs +++ b/x509/src/trust_anchor_format.rs @@ -1,7 +1,7 @@ //! Trust anchor-related structures as defined in RFC 5914 use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints}; -use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String}; +use der::asn1::{BitString, OctetString, Utf8String}; use der::{ DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Header, Sequence, Tag, TagMode, TagNumber, @@ -9,134 +9,44 @@ use der::{ use spki::SubjectPublicKeyInfo; use x501::name::Name; +/// ```text /// TrustAnchorInfo ::= SEQUENCE { -/// version TrustAnchorInfoVersion DEFAULT v1, -/// pubKey SubjectPublicKeyInfo, -/// keyId KeyIdentifier, -/// taTitle TrustAnchorTitle OPTIONAL, -/// certPath CertPathControls OPTIONAL, -/// exts \[1\] EXPLICIT Extensions OPTIONAL, -/// taTitleLangTag \[2\] UTF8String OPTIONAL } +/// version TrustAnchorInfoVersion DEFAULT v1, +/// pubKey SubjectPublicKeyInfo, +/// keyId KeyIdentifier, +/// taTitle TrustAnchorTitle OPTIONAL, +/// certPath CertPathControls OPTIONAL, +/// exts [1] EXPLICIT Extensions OPTIONAL, +/// taTitleLangTag [2] UTF8String OPTIONAL +/// } /// /// TrustAnchorInfoVersion ::= INTEGER { v1(1) } /// /// TrustAnchorTitle ::= UTF8String (SIZE (1..64)) -#[derive(Clone, Eq, PartialEq)] +/// ``` +#[derive(Clone, Debug, PartialEq, Eq, Sequence)] +#[allow(missing_docs)] pub struct TrustAnchorInfo<'a> { - /// version TrustAnchorInfoVersion DEFAULT v1, - pub version: Option, + #[asn1(default = "Default::default")] + pub version: u8, - /// pubKey SubjectPublicKeyInfo, pub pub_key: SubjectPublicKeyInfo<'a>, - /// keyId KeyIdentifier, pub key_id: OctetString<'a>, - /// taTitle TrustAnchorTitle OPTIONAL, + #[asn1(optional = "true")] pub ta_title: Option>, - /// certPath CertPathControls OPTIONAL, + #[asn1(optional = "true")] pub cert_path: Option>, - /// exts \[1\] EXPLICIT Extensions OPTIONAL, + #[asn1(context_specific = "1", tag_mode = "EXPLICIT", optional = "true")] pub extensions: Option>, - /// taTitleLangTag \[2\] UTF8String OPTIONAL } + #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] pub ta_title_lang_tag: Option>, } -// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> { -// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { -impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> { - fn decode_value(decoder: &mut Decoder<'a>, _header: Header) -> der::Result { - let version = match decoder.decode()? { - Some(v) => Some(v), - _ => Some(1), - }; - - let pub_key = decoder.decode()?; - let key_id = decoder.decode()?; - let ta_title = decoder.decode()?; - let cert_path = decoder.decode()?; - let extensions = - ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - let ta_title_lang_tag = - ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)? - .map(|cs| cs.value); - Ok(Self { - version, - pub_key, - key_id, - ta_title, - cert_path, - extensions, - ta_title_lang_tag, - }) - } -} - -const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1); -const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0); -impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> { - fn fields(&self, f: F) -> ::der::Result - where - F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, - { - #[allow(unused_imports)] - use core::convert::TryFrom; - f(&[ - &::der::asn1::OptionalRef(if self.version == Some(1) { - None - } else { - Some(&self.version) - }), - &self.pub_key, - &self.key_id, - &self.ta_title, - &self.cert_path, - &self.extensions.as_ref().map(|exts| ContextSpecific { - tag_number: TAF_EXTENSIONS_TAG, - tag_mode: TagMode::Explicit, - value: exts.clone(), - }), - &self - .ta_title_lang_tag - .as_ref() - .map(|ta_title_lang_tag| ContextSpecific { - tag_number: TA_TITLE_LANG_TAG, - tag_mode: TagMode::Implicit, - value: *ta_title_lang_tag, - }), - ]) - } -} - -impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?; - f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?; - f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?; - f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?; - f.write_fmt(format_args!( - "\tTA title language tag: {:?}\n", - self.ta_title_lang_tag - ))?; - f.write_fmt(format_args!( - "\tCertificate path controls: {:?}\n", - self.cert_path - ))?; - if let Some(exts) = self.extensions.as_ref() { - for (i, e) in exts.iter().enumerate() { - f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?; - } - } else { - f.write_fmt(format_args!("\tExtensions: None\n"))?; - } - Ok(()) - } -} - /// CertPathControls ::= SEQUENCE { /// taName Name, /// certificate \[0\] Certificate OPTIONAL, From 4439fa2f7897547ec5668f8407b187b303ad599e Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 11 Feb 2022 13:43:40 -0500 Subject: [PATCH 08/11] x509: clean up CertPathControls Signed-off-by: Nathaniel McCallum --- x509/src/trust_anchor_format.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs index 1cdf4dd2c..3c5eecd7e 100644 --- a/x509/src/trust_anchor_format.rs +++ b/x509/src/trust_anchor_format.rs @@ -47,36 +47,34 @@ pub struct TrustAnchorInfo<'a> { pub ta_title_lang_tag: Option>, } +/// ```text /// CertPathControls ::= SEQUENCE { -/// taName Name, -/// certificate \[0\] Certificate OPTIONAL, -/// policySet \[1\] CertificatePolicies OPTIONAL, -/// policyFlags \[2\] CertPolicyFlags OPTIONAL, -/// nameConstr \[3\] NameConstraints OPTIONAL, -/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL} +/// taName Name, +/// certificate [0] Certificate OPTIONAL, +/// policySet [1] CertificatePolicies OPTIONAL, +/// policyFlags [2] CertPolicyFlags OPTIONAL, +/// nameConstr [3] NameConstraints OPTIONAL, +/// pathLenConstraint [4] INTEGER (0..MAX) OPTIONAL +/// } +/// ``` #[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[allow(missing_docs)] pub struct CertPathControls<'a> { - /// taName Name, pub ta_name: Name<'a>, - /// certificate \[0\] Certificate OPTIONAL, - #[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")] pub certificate: Option>, - /// policySet \[1\] CertificatePolicies OPTIONAL, - #[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] pub policy_set: Option>, - /// policyFlags \[2\] CertPolicyFlags OPTIONAL, - #[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] pub policy_flags: Option>, - /// nameConstr \[3\] NameConstraints OPTIONAL, - #[asn1(context_specific = "3", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "3", tag_mode = "IMPLICIT", optional = "true")] pub name_constr: Option>, - /// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL} - #[asn1(context_specific = "4", optional = "true", tag_mode = "IMPLICIT")] + #[asn1(context_specific = "4", tag_mode = "IMPLICIT", optional = "true")] pub path_len_constraint: Option, } From fa1bef4e4e89b79c071b7c22672e25b5554af7f6 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 11 Feb 2022 13:44:52 -0500 Subject: [PATCH 09/11] x509: convert CertPolicyFlags to use FlagSet Signed-off-by: Nathaniel McCallum --- x509/src/trust_anchor_format.rs | 32 +++++++++++++++++++++++++------ x509/tests/trust_anchor_format.rs | 22 +++++++-------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs index 3c5eecd7e..0e28210c0 100644 --- a/x509/src/trust_anchor_format.rs +++ b/x509/src/trust_anchor_format.rs @@ -1,11 +1,12 @@ //! Trust anchor-related structures as defined in RFC 5914 use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints}; -use der::asn1::{BitString, OctetString, Utf8String}; +use der::asn1::{OctetString, Utf8String}; use der::{ DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Header, Sequence, Tag, TagMode, TagNumber, }; +use flagset::{flags, FlagSet}; use spki::SubjectPublicKeyInfo; use x501::name::Name; @@ -78,11 +79,30 @@ pub struct CertPathControls<'a> { pub path_len_constraint: Option, } -/// CertPolicyFlags ::= BIT STRING { -/// inhibitPolicyMapping (0), -/// requireExplicitPolicy (1), -/// inhibitAnyPolicy (2) } -pub type CertPolicyFlags<'a> = BitString<'a>; +flags! { + /// Certificate policies as defined in [RFC 5280 Section 4.2.1.13]. + /// + /// ```text + /// CertPolicyFlags ::= BIT STRING { + /// inhibitPolicyMapping (0), + /// requireExplicitPolicy (1), + /// inhibitAnyPolicy (2) + /// } + /// ``` + /// + /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13 + #[allow(missing_docs)] + pub enum CertPolicies: u8 { + InhibitPolicyMapping = 1 << 0, + RequireExplicitPolicy = 1 << 1, + InhibitAnyPolicy = 1 << 2, + } +} + +/// Certificate policy flags as defined in [RFC 5280 Section 4.2.1.13]. +/// +/// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13 +pub type CertPolicyFlags<'a> = FlagSet; /// TrustAnchorChoice ::= CHOICE { /// certificate Certificate, diff --git a/x509/tests/trust_anchor_format.rs b/x509/tests/trust_anchor_format.rs index 35e9179a0..51bf7e268 100644 --- a/x509/tests/trust_anchor_format.rs +++ b/x509/tests/trust_anchor_format.rs @@ -1,7 +1,7 @@ use der::Decoder; use hex_literal::hex; use x509::der::{DecodeValue, Encodable}; -use x509::trust_anchor_format::TrustAnchorChoice; +use x509::trust_anchor_format::{CertPolicies, TrustAnchorChoice}; use x509::*; #[test] @@ -253,20 +253,12 @@ fn decode_ta3() { let cert_path = tai.cert_path.as_ref().unwrap(); - let cpf = cert_path.policy_flags.unwrap(); - let b = cpf.raw_bytes(); - if 0x80 != 0x80 & b[0] { - panic!("Missing policy flag bit 0") - } - if 0x40 != 0x40 & b[0] { - panic!("Missing policy flag bit 1") - } - if 0x20 != 0x20 & b[0] { - panic!("Missing policy flag bit 2") - } - if cpf.unused_bits() != 5 { - panic!("Wrong unused bits for policy flags") - } + assert_eq!( + CertPolicies::InhibitPolicyMapping + | CertPolicies::RequireExplicitPolicy + | CertPolicies::InhibitAnyPolicy, + cert_path.policy_flags.unwrap() + ); let mut counter = 0; let i = cert_path.ta_name.iter(); From 470b5d5e2954da29a4b88b31cee05191bd714790 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 11 Feb 2022 13:46:35 -0500 Subject: [PATCH 10/11] x509: derive(Choice) for TrustAnchorChoice Signed-off-by: Nathaniel McCallum --- x509/src/trust_anchor_format.rs | 100 ++++-------------------------- x509/tests/trust_anchor_format.rs | 16 ++--- 2 files changed, 19 insertions(+), 97 deletions(-) diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs index 0e28210c0..f744113c0 100644 --- a/x509/src/trust_anchor_format.rs +++ b/x509/src/trust_anchor_format.rs @@ -1,11 +1,8 @@ //! Trust anchor-related structures as defined in RFC 5914 -use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints}; +use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints, TbsCertificate}; use der::asn1::{OctetString, Utf8String}; -use der::{ - DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Header, Sequence, Tag, - TagMode, TagNumber, -}; +use der::{Choice, Sequence}; use flagset::{flags, FlagSet}; use spki::SubjectPublicKeyInfo; use x501::name::Name; @@ -104,93 +101,22 @@ flags! { /// [RFC 5280 Section 4.2.1.13]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13 pub type CertPolicyFlags<'a> = FlagSet; +/// ```text /// TrustAnchorChoice ::= CHOICE { /// certificate Certificate, -/// tbsCert \[1\] EXPLICIT TBSCertificate, -/// taInfo \[2\] EXPLICIT TrustAnchorInfo } -#[derive(Clone, Debug, Eq, PartialEq)] +/// tbsCert [1] EXPLICIT TBSCertificate, +/// taInfo [2] EXPLICIT TrustAnchorInfo +/// } +/// ``` +#[derive(Clone, Debug, PartialEq, Eq, Choice)] #[allow(clippy::large_enum_variant)] +#[allow(missing_docs)] pub enum TrustAnchorChoice<'a> { - /// certificate Certificate, Certificate(Certificate<'a>), - // Not supporting TBSCertificate option - // tbsCert \[1\] EXPLICIT TBSCertificate, - //TbsCertificate(TBSCertificate<'a>), - /// taInfo \[2\] EXPLICIT TrustAnchorInfo } - TaInfo(TrustAnchorInfo<'a>), -} - -//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1); -const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2); - -impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> { - fn decode_value(decoder: &mut Decoder<'a>, _header: Header) -> der::Result { - let t = decoder.peek_tag()?; - let o = t.octet(); - // Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here - match o { - 0x30 => { - let cert = decoder.decode()?; - Ok(TrustAnchorChoice::Certificate(cert)) - } - // TODO - need DecodeValue on TBSCertificate to support this - // 0xA1 => { - // let on = decoder - // .context_specific::>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?; - // match on { - // Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)), - // _ => Err(ErrorKind::Failed.into()), - // } - // } - 0xA2 => { - let on = decoder - .context_specific::>(TAC_TA_INFO_TAG, TagMode::Explicit)?; - match on { - Some(on) => Ok(TrustAnchorChoice::TaInfo(on)), - _ => Err(ErrorKind::Failed.into()), - } - } - _ => Err(ErrorKind::TagUnknown { byte: o }.into()), - } - } -} -impl<'a> EncodeValue for TrustAnchorChoice<'a> { - fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> { - match self { - Self::Certificate(certificate) => certificate.encode(encoder), - // Self::TbsCertificate(variant) => ContextSpecific { - // tag_number: TAC_TBS_CERTIFICATE_TAG, - // tag_mode: TagMode::Explicit, - // value: variant.clone(), - // }.encode(encoder), - Self::TaInfo(variant) => variant.encode(encoder), - } - } - fn value_len(&self) -> ::der::Result<::der::Length> { - match self { - Self::Certificate(certificate) => certificate.encoded_len(), - // Self::TbsCertificate(variant) => ContextSpecific { - // tag_number: TAC_TBS_CERTIFICATE_TAG, - // tag_mode: TagMode::Explicit, - // value: variant.clone(), - // }.encoded_len(), - Self::TaInfo(variant) => variant.encoded_len(), - } - } -} + #[asn1(context_specific = "1", tag_mode = "EXPLICIT", constructed = "true")] + TbsCertificate(TbsCertificate<'a>), -//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag -impl<'a> FixedTag for TrustAnchorChoice<'a> { - const TAG: Tag = ::der::Tag::ContextSpecific { - constructed: true, - number: TAC_TA_INFO_TAG, - }; + #[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")] + TaInfo(TrustAnchorInfo<'a>), } - -// Not supporting these structures -// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice -// -// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1) -// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) -// id-smime(16) id-ct(1) 34 } diff --git a/x509/tests/trust_anchor_format.rs b/x509/tests/trust_anchor_format.rs index 51bf7e268..5f46980c8 100644 --- a/x509/tests/trust_anchor_format.rs +++ b/x509/tests/trust_anchor_format.rs @@ -1,6 +1,6 @@ -use der::Decoder; +use der::{Decodable, Decoder}; use hex_literal::hex; -use x509::der::{DecodeValue, Encodable}; +use x509::der::Encodable; use x509::trust_anchor_format::{CertPolicies, TrustAnchorChoice}; use x509::*; @@ -12,8 +12,7 @@ fn decode_ta1() { let der_encoded_cert = include_bytes!("examples/eca.der"); let mut decoder = Decoder::new(der_encoded_tac).unwrap(); - let header = decoder.peek_header().unwrap(); - let tac = TrustAnchorChoice::decode_value(&mut decoder, header).unwrap(); + let tac = TrustAnchorChoice::decode(&mut decoder).unwrap(); let reencoded_tac = tac.to_vec().unwrap(); println!("Original : {:02X?}", der_encoded_cert); println!("Reencoded: {:02X?}", reencoded_tac); @@ -128,8 +127,7 @@ fn decode_ta2() { let der_encoded_cert = include_bytes!("examples/entrust.der"); let mut decoder = Decoder::new(der_encoded_tac).unwrap(); - let header = decoder.peek_header().unwrap(); - let tac = TrustAnchorChoice::decode_value(&mut decoder, header).unwrap(); + let tac = TrustAnchorChoice::decode(&mut decoder).unwrap(); let reencoded_tac = tac.to_vec().unwrap(); println!("Original : {:02X?}", der_encoded_cert); println!("Reencoded: {:02X?}", reencoded_tac); @@ -232,8 +230,7 @@ fn decode_ta3() { let der_encoded_cert = include_bytes!("examples/exostar.der"); let mut decoder = Decoder::new(der_encoded_tac).unwrap(); - let header = decoder.peek_header().unwrap(); - let tac = TrustAnchorChoice::decode_value(&mut decoder, header).unwrap(); + let tac = TrustAnchorChoice::decode(&mut decoder).unwrap(); let reencoded_tac = tac.to_vec().unwrap(); println!("Original : {:02X?}", der_encoded_cert); println!("Reencoded: {:02X?}", reencoded_tac); @@ -343,8 +340,7 @@ fn decode_ta4() { let der_encoded_cert = include_bytes!("examples/raytheon.der"); let mut decoder = Decoder::new(der_encoded_tac).unwrap(); - let header = decoder.peek_header().unwrap(); - let tac = TrustAnchorChoice::decode_value(&mut decoder, header).unwrap(); + let tac = TrustAnchorChoice::decode(&mut decoder).unwrap(); let reencoded_tac = tac.to_vec().unwrap(); println!("Original : {:02X?}", der_encoded_cert); println!("Reencoded: {:02X?}", reencoded_tac); From 277715b312c819a16a64dd0e36d4590032de3d7a Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 11 Feb 2022 14:39:00 -0500 Subject: [PATCH 11/11] x509: add Version type for TrustAnchorInfo Signed-off-by: Nathaniel McCallum --- x509/src/trust_anchor_format.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs index f744113c0..eda8d1888 100644 --- a/x509/src/trust_anchor_format.rs +++ b/x509/src/trust_anchor_format.rs @@ -2,11 +2,26 @@ use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints, TbsCertificate}; use der::asn1::{OctetString, Utf8String}; -use der::{Choice, Sequence}; +use der::{Choice, Enumerated, Sequence}; use flagset::{flags, FlagSet}; use spki::SubjectPublicKeyInfo; use x501::name::Name; +/// Version identifier for TrustAnchorInfo +#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)] +#[asn1(type = "INTEGER")] +#[repr(u8)] +pub enum Version { + /// Version 1 (default) + V1 = 0, +} + +impl Default for Version { + fn default() -> Self { + Version::V1 + } +} + /// ```text /// TrustAnchorInfo ::= SEQUENCE { /// version TrustAnchorInfoVersion DEFAULT v1, @@ -26,7 +41,7 @@ use x501::name::Name; #[allow(missing_docs)] pub struct TrustAnchorInfo<'a> { #[asn1(default = "Default::default")] - pub version: u8, + pub version: Version, pub pub_key: SubjectPublicKeyInfo<'a>,