From f2ab4565a4b899bce00863215a283e73dbb16535 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Sat, 5 Feb 2022 14:48:05 -0500 Subject: [PATCH 01/18] add Display impl for Time --- x501/src/time.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/x501/src/time.rs b/x501/src/time.rs index 3dfa11b67..745f0774b 100644 --- a/x501/src/time.rs +++ b/x501/src/time.rs @@ -1,5 +1,7 @@ //! X.501 time types as defined in RFC 5280 +use alloc::string::ToString; +use core::fmt; use core::time::Duration; use der::asn1::{GeneralizedTime, UtcTime}; use der::{Choice, Decodable, Error, Result, Sequence}; @@ -51,6 +53,15 @@ impl Time { } } +impl fmt::Display for Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { + match self { + Time::UtcTime(t) => f.write_str(t.to_date_time().to_string().as_str()), + Time::GeneralTime(t) => f.write_str(t.to_date_time().to_string().as_str()), + } + } +} + impl From for Time { fn from(time: UtcTime) -> Time { Time::UtcTime(time) From 213e9a2adf25e62dc877ecdfeeca7c9153bf3175 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Sat, 5 Feb 2022 15:09:31 -0500 Subject: [PATCH 02/18] refactor Display to use new to_date_time --- x501/src/time.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/x501/src/time.rs b/x501/src/time.rs index 745f0774b..57857cbe2 100644 --- a/x501/src/time.rs +++ b/x501/src/time.rs @@ -1,10 +1,9 @@ //! X.501 time types as defined in RFC 5280 -use alloc::string::ToString; use core::fmt; use core::time::Duration; use der::asn1::{GeneralizedTime, UtcTime}; -use der::{Choice, Decodable, Error, Result, Sequence}; +use der::{Choice, DateTime, Decodable, Error, Result, Sequence}; #[cfg(feature = "std")] use std::time::SystemTime; @@ -42,6 +41,14 @@ impl Time { } } + /// Get Time as DateTime + pub fn to_date_time(&self) -> DateTime { + match self { + Time::UtcTime(t) => t.to_date_time(), + Time::GeneralTime(t) => t.to_date_time(), + } + } + /// Convert to [`SystemTime`]. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] @@ -55,10 +62,7 @@ impl Time { impl fmt::Display for Time { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { - match self { - Time::UtcTime(t) => f.write_str(t.to_date_time().to_string().as_str()), - Time::GeneralTime(t) => f.write_str(t.to_date_time().to_string().as_str()), - } + write!(f, "{}", self.to_date_time()) } } From 945bfe9391a4fe5a82598cd7eda855819255c55b Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 05:58:32 -0500 Subject: [PATCH 03/18] move algorithm OID definitions to x509 from certval --- x509/src/pkix_oids.rs | 162 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/x509/src/pkix_oids.rs b/x509/src/pkix_oids.rs index 14b7d0a4d..5d2d29961 100644 --- a/x509/src/pkix_oids.rs +++ b/x509/src/pkix_oids.rs @@ -188,3 +188,165 @@ pub const PKIX_OCSP_NOCHECK: ObjectIdentifier = ObjectIdentifier::new("1.3.6.1.5 /// OID for PIV NACI extension: 2.16.840.1.101.3.6.9.1. See [`PivNaciIndicator`](type.PivNaciIndicator.html). pub const PIV_NACI_INDICATOR: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.6.9.1"); + +// ------------------------------------------------------------------------------------------------- +// OIDs from PKIXAlgs-2009 +// ------------------------------------------------------------------------------------------------- + +/// rsaEncryption OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) +/// pkcs-1(1) 1 } +pub const PKIXALG_RSA_ENCRYPTION: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.1"); + +/// id-ecPublicKey OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } +pub const PKIXALG_EC_PUBLIC_KEY: ObjectIdentifier = ObjectIdentifier::new("1.2.840.10045.2.1"); + +/// id-ecDH OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) schemes(1) +/// ecdh(12) } +pub const PKIXALG_DH: ObjectIdentifier = ObjectIdentifier::new("1.3.132.1.12"); + +/// secp192r1 OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +/// prime(1) 1 } +pub const PKIXALG_SECP192R1: ObjectIdentifier = ObjectIdentifier::new("1.2.840.10045.3.1.1"); + +/// sect163k1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 1 } +pub const PKIXALG_SECP163K1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.1"); + +/// sect163r2 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 15 } +pub const PKIXALG_SECP163R2: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.15"); + +/// secp224r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +pub const PKIXALG_SECP224R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.33"); + +/// sect233k1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 26 } +pub const PKIXALG_SECP233K1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.26"); + +/// sect233r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 27 } +pub const PKIXALG_SECP233R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.27"); + +/// secp256r1 OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +/// prime(1) 7 } +pub const PKIXALG_SECP256R1: ObjectIdentifier = ObjectIdentifier::new("1.2.840.10045.3.1.7"); + +/// sect283k1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 16 } +pub const PKIXALG_SECP283K1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.16"); + +/// sect283r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 17 } +pub const PKIXALG_SECP283R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.17"); + +/// secp384r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +pub const PKIXALG_SECP384R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.34"); + +/// sect409k1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 36 } +pub const PKIXALG_SECP409K1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.36"); + +/// sect409r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 37 } +pub const PKIXALG_SECP409R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.37"); + +/// secp521r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +pub const PKIXALG_SECP521R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.35"); + +/// sect571k1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 38 } +pub const PKIXALG_SECP571K1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.38"); + +/// sect571r1 OBJECT IDENTIFIER ::= { +/// iso(1) identified-organization(3) certicom(132) curve(0) 39 } +pub const PKIXALG_SECP571R1: ObjectIdentifier = ObjectIdentifier::new("1.3.132.0.39"); + +/// ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) +/// ecdsa-with-SHA2(3) 1 } +pub const PKIXALG_ECDSA_WITH_SHA224: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.10045.4.3.1"); + +/// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) +/// ecdsa-with-SHA2(3) 2 } +pub const PKIXALG_ECDSA_WITH_SHA256: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.10045.4.3.2"); + +/// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) +/// ecdsa-with-SHA2(3) 3 } +pub const PKIXALG_ECDSA_WITH_SHA384: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.10045.4.3.3"); + +/// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { +/// iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) +/// ecdsa-with-SHA2(3) 4 } +pub const PKIXALG_ECDSA_WITH_SHA512: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.10045.4.3.4"); + +// ------------------------------------------------------------------------------------------------- +// OIDs from PKIX1-PSS-OAEP-Algorithms-2009 +// ------------------------------------------------------------------------------------------------- +// pkcs-1 OBJECT IDENTIFIER ::= +// { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + +/// sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 } +pub const PKIXALG_SHA224_WITH_RSA_ENCRYPTION: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.113549.1.1.14"); + +/// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } +pub const PKIXALG_SHA256_WITH_RSA_ENCRYPTION: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.113549.1.1.11"); + +/// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } +pub const PKIXALG_SHA384_WITH_RSA_ENCRYPTION: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.113549.1.1.12"); + +/// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +pub const PKIXALG_SHA512_WITH_RSA_ENCRYPTION: ObjectIdentifier = + ObjectIdentifier::new("1.2.840.113549.1.1.13"); + +/// id-RSAES-OAEP OBJECT IDENTIFIER ::= { pkcs-1 7 } +pub const PKIXALG_RSAES_OAEP: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.7"); + +/// id-pSpecified OBJECT IDENTIFIER ::= { pkcs-1 9 } +pub const PKIXALG_PSPECIFIED: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.9"); + +/// id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 } +pub const PKIXALG_MGF1: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.8"); + +/// id-RSASSA-PSS OBJECT IDENTIFIER ::= { pkcs-1 10 } +pub const PKIXALG_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.10"); + +/// sha-1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) +/// oiw(14) secsig(3) algorithm(2) 26 } +pub const PKIXALG_SHA1: ObjectIdentifier = ObjectIdentifier::new("1.3.14.3.2.26"); + +/// id-sha224 OBJECT IDENTIFIER ::= +/// { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) +/// csor(3) algorithms(4) hashalgs(2) 4 } +pub const PKIXALG_SHA224: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.2.4"); + +/// id-sha256 OBJECT IDENTIFIER ::= +/// { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) +/// csor(3) algorithms(4) hashalgs(2) 1 } +pub const PKIXALG_SHA256: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.2.1"); + +/// id-sha384 OBJECT IDENTIFIER ::= +/// { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) +/// csor(3) algorithms(4) hashalgs(2) 2 } +pub const PKIXALG_SHA384: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.2.2"); + +/// id-sha512 OBJECT IDENTIFIER ::= +/// { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) +/// csor(3) algorithms(4) hashalgs(2) 3 } +pub const PKIXALG_SHA512: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.2.3"); From c2d3b40ae3b643c7568e7932c4cda85ea78d74fc Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 06:53:11 -0500 Subject: [PATCH 04/18] move OID lookup from certval (and yubikey), refactor to have a &'static str option, and expand to cover more OIDs --- x509/src/pkix_oids.rs | 118 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/x509/src/pkix_oids.rs b/x509/src/pkix_oids.rs index 5d2d29961..fb7012d6c 100644 --- a/x509/src/pkix_oids.rs +++ b/x509/src/pkix_oids.rs @@ -1,5 +1,6 @@ //! Object identifier values from PKIX1Implicit and PKIX1Explicit ASN.1 modules use crate::ObjectIdentifier; +use alloc::string::{String, ToString}; /// OID for CPS qualifier: 1.3.6.1.5.5.7.2.1 pub const PKIX_QT_CPS: ObjectIdentifier = ObjectIdentifier::new("1.3.6.1.5.5.7.2.1"); @@ -148,7 +149,7 @@ pub const PKIX_CE_CERTIFICATEISSUER: ObjectIdentifier = ObjectIdentifier::new("2 /// OID for holdInstructionCode extension: 2.5.29.23 pub const PKIX_CE_HOLDINSTRUCTIONCODE: ObjectIdentifier = ObjectIdentifier::new("2.5.29.23"); -/// OID forholdinstruction-callissuer attribute: 2.2.840.10040.2.2 +/// OID for holdinstruction-callissuer attribute: 2.2.840.10040.2.2 pub const PKIX_HI_HOLDINSTRUCTION_CALLISSUER: ObjectIdentifier = ObjectIdentifier::new("2.2.840.10040.2.2"); @@ -177,7 +178,7 @@ pub const PKIX_CE_AUTHORITY_KEY_IDENTIFIER: ObjectIdentifier = ObjectIdentifier: /// OID for policyConstraints extension: 2.5.29.36. See [`PolicyConstraints`](struct.PolicyConstraints.html). pub const PKIX_CE_POLICY_CONSTRAINTS: ObjectIdentifier = ObjectIdentifier::new("2.5.29.36"); -/// OID for policyConstraints extension: 2.5.29.46. See [`PolicyConstraints`](type.FreshestCRL.html). +/// OID for freshestCrl extension: 2.5.29.46. See [`FreshestCrl`](type.FreshestCRL.html). pub const PKIX_CE_FRESHEST_CRL: ObjectIdentifier = ObjectIdentifier::new("2.5.29.46"); /// OID for inhibitAnyPolicy extension: 2.5.29.54. See [`InhibitAnyPolicy`](type.InhibitAnyPolicy.html). @@ -350,3 +351,116 @@ pub const PKIXALG_SHA384: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.1 /// { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) /// csor(3) algorithms(4) hashalgs(2) 3 } pub const PKIXALG_SHA512: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.2.3"); + +/// oid_to_string takes an ObjectIdentifier and returns a String containing a friendly name or a +/// dot notation representation of the OID, i.e., 1.2.3.4, if no friendly name is found. +pub fn oid_to_string(oid: &ObjectIdentifier) -> String { + let s = oid_to_str(oid); + if s.is_empty() { + return oid.to_string(); + } + s.to_string() +} + +/// oid_to_str takes an ObjectIdentifier and returns a str containing a friendly name or an empty +/// string if no friendly name is found. +pub fn oid_to_str(oid: &ObjectIdentifier) -> &'static str { + match *oid { + PKIX_QT_CPS => "qtCps", + PKIX_QT_UNOTICE => "qtUnotice", + PKIX_AD_OCSP => "adOcsp", + PKIX_AD_CA_ISSUERS => "adCaIssuers", + PKIX_AD_TIME_STAMPING => "adTimeStamping", + PKIX_AD_CA_REPOSITORY => "adCaRepository", + PKIX_AT_NAME => "name", + PKIX_AT_SURNAME => "sn", + PKIX_AT_GIVENNAME => "givenName", + PKIX_AT_INITIALS => "initials", + PKIX_AT_GENERATION_QUALIFIER => "generationQualifier", + PKIX_AT_COMMON_NAME => "cn", + PKIX_AT_LOCALITY_NAME => "l", + PKIX_AT_STATEORPROVINCENAME => "st", + PKIX_AT_STREET => "street", + PKIX_AT_ORGANIZATIONNAME => "ou", + PKIX_AT_ORGANIZATIONALUNITNAME => "sn", + PKIX_AT_TITLE => "title", + PKIX_AT_DNQUALIFIER => "dnQualifier", + PKIX_AT_COUNTRYNAME => "c", + PKIX_AT_SERIALNUMBER => "serialNumber", + PKIX_AT_PSEUDONYM => "pseudonym", + PKIX_DOMAINCOMPONENT => "dc", + PKIX_EMAILADDRESS => "emailAddress", + PKIX_CE_ANYPOLICY => "anyPolicy", + PKIX_CE_EXTKEYUSAGE => "extKeyUsage", + PKIX_CE_ANYEXTENDEDKEYUSAGE => "anyExtendedKeyUsage", + PKIX_KP_SERVERAUTH => "serverAuth", + PKIX_KP_CLIENTAUTH => "clientAuth", + PKIX_KP_CODESIGNING => "codeSigning", + PKIX_KP_EMAILPROTECTION => "emailProtection", + PKIX_KP_TIMESTAMPING => "timeStamping", + PKIX_KP_OCSPSIGNING => "OCSPSigning", + PKIX_PE_AUTHORITYINFOACCESS => "authorityInfoAccess", + PKIX_PE_SUBJECTINFOACCESS => "subjectInfoAccess", + PKIX_CE_SUBJECT_DIRECTORY_ATTRIBUTES => "subjectDirectoryAttributes", + PKIX_CE_SUBJECT_KEY_IDENTIFIER => "subjectKeyIdentifier", + PKIX_CE_KEY_USAGE => "keyUsage", + PKIX_CE_PRIVATE_KEY_USAGE_PERIOD => "privateKeyUsagePeriod", + PKIX_CE_SUBJECT_ALT_NAME => "subjectAltName", + PKIX_CE_ISSUER_ALT_NAME => "issuerAltName", + PKIX_CE_BASIC_CONSTRAINTS => "basicConstraints", + PKIX_CE_CRLNUMBER => "cRLNumber", + PKIX_CE_CRLREASONS => "cRLReasons", + PKIX_CE_ISSUINGDISTRIBUTIONPOINT => "issuingDistributionPoint", + PKIX_CE_DELTACRLINDICATOR => "deltaCRLIndicator", + PKIX_CE_CERTIFICATEISSUER => "certificateIssuer", + PKIX_CE_HOLDINSTRUCTIONCODE => "holdInstructionCode", + PKIX_HI_HOLDINSTRUCTION_CALLISSUER => "holdinstruction-callissuer", + PKIX_HI_HOLDINSTRUCTION_REJECT => "holdinstruction-reject", + PKIX_CE_INVALIDITYDATE => "invalidityDate", + PKIX_CE_NAME_CONSTRAINTS => "nameConstraints", + PKIX_CE_CRL_DISTRIBUTION_POINTS => "cRLDistributionPoints", + PKIX_CE_CERTIFICATE_POLICIES => "certificatePolicies", + PKIX_CE_POLICY_MAPPINGS => "policyMappings", + PKIX_CE_AUTHORITY_KEY_IDENTIFIER => "authorityKeyIdentifier", + PKIX_CE_POLICY_CONSTRAINTS => "policyConstraints", + PKIX_CE_FRESHEST_CRL => "freshestCrl", + PKIX_CE_INHIBIT_ANY_POLICY => "inhibitAnyPolicy", + PKIX_OCSP_NOCHECK => "ocspNoCheck", + PIV_NACI_INDICATOR => "pivNaciIdendicator", + PKIXALG_RSA_ENCRYPTION => "rsaEncryption", + PKIXALG_EC_PUBLIC_KEY => "ecPublicKey", + PKIXALG_DH => "ecDH", + PKIXALG_SECP192R1 => "secp192r1", + PKIXALG_SECP163K1 => "sect163k1", + PKIXALG_SECP163R2 => "sect163r2", + PKIXALG_SECP224R1 => "secp224r1", + PKIXALG_SECP233K1 => "sect233k1", + PKIXALG_SECP233R1 => "sect233r1", + PKIXALG_SECP256R1 => "secp256r1", + PKIXALG_SECP283K1 => "sect283k1", + PKIXALG_SECP283R1 => "sect283r1", + PKIXALG_SECP384R1 => "secp384r1", + PKIXALG_SECP409K1 => "sect409k1", + PKIXALG_SECP409R1 => "sect409r1", + PKIXALG_SECP521R1 => "secp521r1", + PKIXALG_SECP571K1 => "sect571k1", + PKIXALG_SECP571R1 => "sect571r1", + PKIXALG_ECDSA_WITH_SHA224 => "ecdsa-with-SHA224", + PKIXALG_ECDSA_WITH_SHA256 => "ecdsa-with-SHA256", + PKIXALG_ECDSA_WITH_SHA384 => "ecdsa-with-SHA384", + PKIXALG_ECDSA_WITH_SHA512 => "ecdsa-with-SHA512", + PKIXALG_SHA224_WITH_RSA_ENCRYPTION => "sha224WithRSAEncryption", + PKIXALG_SHA256_WITH_RSA_ENCRYPTION => "sha256WithRSAEncryption", + PKIXALG_SHA384_WITH_RSA_ENCRYPTION => "sha384WithRSAEncryption", + PKIXALG_SHA512_WITH_RSA_ENCRYPTION => "sha512WithRSAEncryption", + PKIXALG_RSAES_OAEP => "RSAES-OAEP", + PKIXALG_PSPECIFIED => "pSpecified", + PKIXALG_MGF1 => "MFG1", + PKIXALG_RSASSA_PSS => "sha1", + PKIXALG_SHA224 => "sha224", + PKIXALG_SHA256 => "sha256", + PKIXALG_SHA384 => "sha384", + PKIXALG_SHA512 => "sha512", + _ => "", + } +} From 40cf4aa4289799324b0fc0601d20f89075f3c740 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 12:16:39 -0500 Subject: [PATCH 05/18] add version enum and add constraints to govern decode/encode relative to version number --- x509/Cargo.toml | 2 + x509/src/certificate.rs | 117 ++++++++++++++++++--- x509/src/certificate_document.rs | 159 +++++++++++++++++++++++++++++ x509/src/certificate_traits.rs | 97 ++++++++++++++++++ x509/src/lib.rs | 6 +- x509/tests/certificate.rs | 2 +- x509/tests/certificate_document.rs | 151 +++++++++++++++++++++++++++ x509/tests/examples/amazon.der | Bin 0 -> 2245 bytes x509/tests/examples/amazon.pem | 49 +++++++++ x509/tests/pkix_extensions.rs | 2 +- 10 files changed, 564 insertions(+), 21 deletions(-) create mode 100644 x509/src/certificate_document.rs create mode 100644 x509/src/certificate_traits.rs create mode 100644 x509/tests/certificate_document.rs create mode 100644 x509/tests/examples/amazon.der create mode 100644 x509/tests/examples/amazon.pem diff --git a/x509/Cargo.toml b/x509/Cargo.toml index f87d03c88..2d5624d76 100644 --- a/x509/Cargo.toml +++ b/x509/Cargo.toml @@ -23,7 +23,9 @@ x501 = { version = "=0.1.0-pre.0", path = "../x501" } hex-literal = "0.3" [features] +alloc = ["der/alloc"] std = ["der/std"] +pem = ["alloc", "der/pem"] [package.metadata.docs.rs] all-features = true diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 5fb269557..e2e26d604 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -1,7 +1,9 @@ //! Certificate [`Certificate`] and TBSCertificate [`TBSCertificate`] as defined in RFC 5280 use der::asn1::{BitString, ContextSpecific, ObjectIdentifier, UIntBytes}; -use der::{Sequence, TagMode, TagNumber}; +use der::{ + Decodable, Decoder, Encodable, Encoder, Error, FixedTag, Sequence, Tag, TagMode, TagNumber, +}; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; use x501::name::Name; use x501::time::Validity; @@ -25,6 +27,57 @@ pub fn default_zero() -> u32 { /// Version ::= INTEGER { v1(0), v2(1), v3(2) } pub const X509_CERT_VERSION: u8 = 2; +/// Version identifier for X.509 certificates. In practice, only v3 is used. +#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd)] +pub enum X509Version { + /// Denotes X.509 v1 + V1 = 0, + + /// Denotes X.509 v2 - added issuerUniqueID and subjectUniqueID (both of which have been deprecated) + V2 = 1, + + /// Denotes X.509 v3 - add extensions + V3 = 2, +} + +impl Decodable<'_> for X509Version { + fn decode(decoder: &mut Decoder<'_>) -> der::Result { + X509Version::try_from(u8::decode(decoder)?).map_err(|_| Self::TAG.value_error()) + } +} + +impl Encodable for X509Version { + fn encoded_len(&self) -> der::Result { + der::Length::from(1u8).for_tlv() + } + + fn encode(&self, encoder: &mut Encoder<'_>) -> der::Result<()> { + u8::from(*self).encode(encoder) + } +} + +impl FixedTag for X509Version { + const TAG: Tag = Tag::Integer; +} + +impl From for u8 { + fn from(version: X509Version) -> Self { + version as u8 + } +} + +impl TryFrom for X509Version { + type Error = Error; + fn try_from(byte: u8) -> Result { + match byte { + 0 => Ok(X509Version::V1), + 1 => Ok(X509Version::V2), + 2 => Ok(X509Version::V3), + _ => Err(Self::TAG.value_error()), + } + } +} + /// X.509 `TBSCertificate` as defined in [RFC 5280 Section 4.1.2.5] /// /// ASN.1 structure containing the names of the subject and issuer, a public key associated @@ -54,7 +107,7 @@ pub const X509_CERT_VERSION: u8 = 2; pub struct TBSCertificate<'a> { /// version [0] Version DEFAULT v1, //#[asn1(context_specific = "0", default = "default_zero_u8")] - pub version: u8, + pub version: X509Version, /// serialNumber CertificateSerialNumber, pub serial_number: UIntBytes<'a>, /// signature AlgorithmIdentifier{SIGNATURE-ALGORITHM, {SignatureAlgorithms}}, @@ -80,25 +133,37 @@ pub struct TBSCertificate<'a> { impl<'a> ::der::Decodable<'a> for TBSCertificate<'a> { fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { decoder.sequence(|decoder| { - let version = + let version_int = ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N0)? .map(|cs| cs.value) .unwrap_or_else(default_zero_u8); + let version = X509Version::try_from(version_int)?; 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 = + + let issuer_unique_id = if X509Version::V2 <= version { ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - let subject_unique_id = + .map(|cs| cs.value) + } else { + None + }; + let subject_unique_id = if X509Version::V2 <= version { ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N2)? - .map(|cs| cs.value); - let extensions = + .map(|cs| cs.value) + } else { + None + }; + let extensions = if X509Version::V3 <= version { ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N3)? - .map(|cs| cs.value); + .map(|cs| cs.value) + } else { + None + }; + Ok(Self { version, serial_number, @@ -121,13 +186,35 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { where F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, { + let version_value: u8 = self.version.try_into()?; + + let issuer_unique_id = if X509Version::V2 <= self.version { + &self.issuer_unique_id + } else { + &None + }; + let subject_unique_id = if X509Version::V2 <= self.version { + &self.subject_unique_id + } else { + &None + }; + let extensions = if X509Version::V3 <= self.version { + self.extensions.as_ref().map(|exts| ContextSpecific { + tag_number: EXTENSIONS_TAG, + tag_mode: TagMode::Explicit, + value: exts.clone(), + }) + } else { + None + }; + #[allow(unused_imports)] use core::convert::TryFrom; f(&[ &ContextSpecific { tag_number: VERSION_TAG, tag_mode: TagMode::Explicit, - value: self.version, + value: version_value, }, &self.serial_number, &self.signature, @@ -135,13 +222,9 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { &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(), - }), + issuer_unique_id, + subject_unique_id, + &extensions, ]) } } diff --git a/x509/src/certificate_document.rs b/x509/src/certificate_document.rs new file mode 100644 index 000000000..83b5ac8ff --- /dev/null +++ b/x509/src/certificate_document.rs @@ -0,0 +1,159 @@ +//! CertificateDocument implementation + +use crate::certificate_traits::*; +use crate::Certificate; +use der::{Error, Result}; + +use alloc::vec::Vec; +use core::fmt; +use der::{Decodable, Document}; + +#[cfg(feature = "std")] +use std::path::Path; + +#[cfg(feature = "pem")] +use { + alloc::string::String, + core::str::FromStr, + der::pem::{self, LineEnding}, +}; + +/// Certificate document. +/// +/// This type provides storage for [`Certificate`] encoded as ASN.1 +/// DER with the invariant that the contained-document is "well-formed", i.e. +/// it will parse successfully according to this crate's parsing rules. +#[derive(Clone)] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub struct CertificateDocument(Vec); + +impl<'a> TryFrom<&'a [u8]> for Certificate<'a> { + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result { + Self::from_der(bytes) + } +} + +impl<'a> Document<'a> for CertificateDocument { + type Message = Certificate<'a>; + const SENSITIVE: bool = false; +} + +impl DecodeCertificate for CertificateDocument { + fn from_certificate_der(bytes: &[u8]) -> Result { + Self::from_der(bytes) + } + + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + fn from_certificate_pem(s: &str) -> Result { + Ok(Self::from_pem(s)?) + } + + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn read_certificate_der_file(path: impl AsRef) -> Result { + Ok(Self::read_der_file(path)?) + } + + #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] + fn read_certificate_pem_file(path: impl AsRef) -> Result { + Ok(Self::read_pem_file(path)?) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +impl EncodeCertificate for CertificateDocument { + fn to_certificate_der(&self) -> Result { + Ok(self.clone()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { + Ok(self.to_pem(line_ending)?) + } + + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { + Ok(self.write_der_file(path)?) + } + + #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] + fn write_certificate_pem_file( + &self, + path: impl AsRef, + line_ending: LineEnding, + ) -> Result<()> { + Ok(self.write_pem_file(path, line_ending)?) + } +} + +impl AsRef<[u8]> for CertificateDocument { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl TryFrom<&[u8]> for CertificateDocument { + type Error = Error; + + fn try_from(bytes: &[u8]) -> Result { + Self::from_der(bytes) + } +} + +impl TryFrom> for CertificateDocument { + type Error = Error; + + fn try_from(cert: Certificate<'_>) -> Result { + Self::try_from(&cert) + } +} + +impl TryFrom<&Certificate<'_>> for CertificateDocument { + type Error = Error; + + fn try_from(cert: &Certificate<'_>) -> Result { + Self::from_msg(cert) + } +} + +impl TryFrom> for CertificateDocument { + type Error = der::Error; + + fn try_from(bytes: Vec) -> der::Result { + // Ensure document is well-formed + Certificate::from_der(bytes.as_slice())?; + Ok(Self(bytes)) + } +} + +impl fmt::Debug for CertificateDocument { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_tuple("CertificateDocument") + .field(&self.decode()) + .finish() + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl FromStr for CertificateDocument { + type Err = Error; + + fn from_str(s: &str) -> Result { + Self::from_certificate_pem(s) + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl pem::PemLabel for CertificateDocument { + const TYPE_LABEL: &'static str = "CERTIFICATE"; +} diff --git a/x509/src/certificate_traits.rs b/x509/src/certificate_traits.rs new file mode 100644 index 000000000..d3cbf81c8 --- /dev/null +++ b/x509/src/certificate_traits.rs @@ -0,0 +1,97 @@ +//! CertificateDocument implementation + +#[cfg(feature = "alloc")] +use crate::certificate_document::CertificateDocument; + +use crate::Certificate; +use der::{Error, Result}; + +#[cfg(feature = "alloc")] +use der::Document; + +#[cfg(feature = "std")] +use std::path::Path; + +#[cfg(feature = "pem")] +use {alloc::string::String, der::pem::LineEnding}; + +/// Parse a public key object from an encoded Certificate document. +pub trait DecodeCertificate: for<'a> TryFrom, Error = Error> + Sized { + /// Deserialize object from ASN.1 DER-encoded [`Certificate`] + /// (binary format). + fn from_certificate_der(bytes: &[u8]) -> Result { + Self::try_from(Certificate::try_from(bytes)?) + } + + /// Deserialize certificate from a [`CertificateDocument`]. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn from_certificate_doc(doc: &CertificateDocument) -> Result { + Self::try_from(doc.decode()) + } + + /// Deserialize PEM-encoded [`Certificate`]. + /// + /// Keys in this format begin with the following delimiter: + /// + /// ```text + /// -----BEGIN CERTIFICATE----- + /// ``` + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + fn from_certificate_pem(s: &str) -> Result { + CertificateDocument::from_certificate_pem(s) + .and_then(|doc| Self::from_certificate_doc(&doc)) + } + + /// Load public key object from an ASN.1 DER-encoded file on the local + /// filesystem (binary format). + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn read_certificate_der_file(path: impl AsRef) -> Result { + CertificateDocument::read_certificate_der_file(path) + .and_then(|doc| Self::from_certificate_doc(&doc)) + } + + /// Load public key object from a PEM-encoded file on the local filesystem. + #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] + fn read_certificate_pem_file(path: impl AsRef) -> Result { + CertificateDocument::read_certificate_pem_file(path) + .and_then(|doc| Self::from_certificate_doc(&doc)) + } +} + +/// Serialize a certificate object to a DER-encoded document. +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub trait EncodeCertificate { + /// Serialize a [`CertificateDocument`] containing a Certificate. + fn to_certificate_der(&self) -> Result; + + /// Serialize this public key as PEM-encoded Certificate with the given [`LineEnding`]. + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { + self.to_certificate_der()?.to_certificate_pem(line_ending) + } + + /// Write ASN.1 DER-encoded public key to the given path + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { + self.to_certificate_der()?.write_certificate_der_file(path) + } + + /// Write ASN.1 DER-encoded public key to the given path + #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] + fn write_certificate_pem_file( + &self, + path: impl AsRef, + line_ending: LineEnding, + ) -> Result<()> { + self.to_certificate_der()? + .write_certificate_pem_file(path, line_ending) + } +} diff --git a/x509/src/lib.rs b/x509/src/lib.rs index aadf4848b..68ce24ff6 100644 --- a/x509/src/lib.rs +++ b/x509/src/lib.rs @@ -14,9 +14,11 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -mod certificate; +pub mod certificate; +pub mod certificate_document; +pub mod certificate_traits; pub mod extensions_utils; -mod general_name; +pub mod general_name; pub mod pkix_extensions; pub mod pkix_oids; pub mod trust_anchor_format; diff --git a/x509/tests/certificate.rs b/x509/tests/certificate.rs index 774a15186..f044077b1 100644 --- a/x509/tests/certificate.rs +++ b/x509/tests/certificate.rs @@ -194,7 +194,7 @@ fn decode_cert() { let result = Certificate::from_der(der_encoded_cert); let cert: Certificate = result.unwrap(); - assert_eq!(cert.tbs_certificate.version, 2); + assert_eq!(cert.tbs_certificate.version, X509Version::V3); let target_serial: [u8; 16] = [ 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x49, 0xCF, 0x70, 0x66, 0x4D, 0x00, 0x00, 0x00, 0x02, diff --git a/x509/tests/certificate_document.rs b/x509/tests/certificate_document.rs new file mode 100644 index 000000000..29c45740c --- /dev/null +++ b/x509/tests/certificate_document.rs @@ -0,0 +1,151 @@ +//! Certificate document tests +use crate::certificate_document::CertificateDocument; +use der::Document; +use x509::certificate_traits::DecodeCertificate; + +#[cfg(feature = "alloc")] +use x509::certificate_traits::EncodeCertificate; + +#[cfg(feature = "alloc")] +use der::Encodable; + +use x509::*; + +#[cfg(feature = "std")] +use std::path::Path; + +#[cfg(feature = "pem")] +use der::pem::LineEnding; + +/// `Certificate` encoded as ASN.1 DER +const CERT_DER_EXAMPLE: &[u8] = include_bytes!("examples/amazon.der"); + +/// `Certificate` encoded as PEM +#[cfg(all(feature = "pem"))] +const CERT_PEM_EXAMPLE: &str = include_str!("examples/amazon.pem"); + +#[test] +#[cfg(all(feature = "pem", feature = "std"))] +fn decode_cert_pem_file() { + let doc: CertificateDocument = + CertificateDocument::read_certificate_pem_file(Path::new("tests/examples/amazon.pem")) + .unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); +} + +#[test] +#[cfg(all(feature = "std", feature = "alloc"))] +fn decode_cert_der_file() { + use crate::certificate_document::CertificateDocument; + let doc: CertificateDocument = + CertificateDocument::read_certificate_der_file(Path::new("tests/examples/amazon.der")) + .unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); +} + +#[test] +#[cfg(all(feature = "pem"))] +fn decode_cert_pem() { + let doc: CertificateDocument = CERT_PEM_EXAMPLE.parse().unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + + // Ensure `CertificateDocument` parses successfully + let cert = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); + assert_eq!(doc.decode(), cert); + assert_eq!( + doc.to_certificate_pem(LineEnding::default()).unwrap(), + CERT_PEM_EXAMPLE + ); + + let doc: CertificateDocument = + CertificateDocument::from_certificate_pem(CERT_PEM_EXAMPLE).unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + + // Ensure `CertificateDocument` parses successfully + let cert = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); + assert_eq!(doc.decode(), cert); + assert_eq!( + doc.to_certificate_pem(LineEnding::default()).unwrap(), + CERT_PEM_EXAMPLE + ); +} + +#[test] +fn decode_cert_der() { + let doc: CertificateDocument = + CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + + // Ensure `CertificateDocument` parses successfully + let cert = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); + assert_eq!(doc.decode(), cert); +} + +#[test] +#[cfg(all(feature = "alloc"))] +fn encode_cert_der() { + let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); + let pk_encoded = pk.to_vec().unwrap(); + assert_eq!(CERT_DER_EXAMPLE, pk_encoded.as_slice()); +} + +#[test] +#[cfg(all(feature = "pem"))] +fn encode_cert_pem() { + let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); + let pk_encoded = CertificateDocument::try_from(pk) + .unwrap() + .to_certificate_pem(Default::default()) + .unwrap(); + + assert_eq!(CERT_PEM_EXAMPLE, pk_encoded); +} + +#[test] +#[cfg(all(feature = "std", feature = "alloc"))] +fn write_cert_der() { + let doc: CertificateDocument = + CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + + let r = doc.write_certificate_der_file(Path::new("tests/examples/amazon.der.regen")); + if r.is_err() { + panic!("Failed to write file") + } + + let doc: CertificateDocument = CertificateDocument::read_certificate_der_file(Path::new( + "tests/examples/amazon.der.regen", + )) + .unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + let r = std::fs::remove_file("tests/examples/amazon.der.regen"); + if r.is_err() {} +} + +#[test] +#[cfg(all(feature = "std", feature = "alloc", feature = "pem"))] +fn write_cert_pem() { + let doc: CertificateDocument = + CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + + let r = doc.write_certificate_pem_file( + Path::new("tests/examples/amazon.pem.regen"), + LineEnding::default(), + ); + if r.is_err() { + panic!("Failed to write file") + } + + let doc: CertificateDocument = CertificateDocument::read_certificate_pem_file(Path::new( + "tests/examples/amazon.pem.regen", + )) + .unwrap(); + assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + let r = std::fs::remove_file("tests/examples/amazon.pem.regen"); + if r.is_err() {} +} diff --git a/x509/tests/examples/amazon.der b/x509/tests/examples/amazon.der new file mode 100644 index 0000000000000000000000000000000000000000..cc30bd69b4642cb531b4d7c4f14049dd10ea1b80 GIT binary patch literal 2245 zcma)7X;2eq7~b7%B7_9QK)_MCK|Baw5~&ReLO?D@lp}x#LK310kfV?g1BkFi6h%Z7 z4^%+#R;+bwr7Gfy;87J3#j1E##fy3X>R7wWWg`A)W-_zyv(NiI@3rp-Fr$+I8?w8C zgunKnn~)*k&o%+Z6p{d?Fc3im+Y&Gc zgUX=!%NEG|Bq?gUV1<}%14a@siD5Ya2PP{;!el!?U%NmKu(6_Z*lYlP06o9ixXA%L zHjl&g;Cb+718X)DSoEAgqmN~&BnwzVPC84hlmiTQ&<_bFLnI6~gK(URU@#2Ty?(_# zosiM)7qL5PYmrIp$4e)&>V_;nW)XNP1Nyk3ja7F06j*HQ6&j$7d#WK9zmVp57q~|3 zbzypwn|!+XHm2c4mh$>xtvhvFW-d4-%?wpG-{u~^7ZqQB^X=VzzL9(0|KeiW_Hwap zvhR^`nR6nWw4c0hd9ro_h+oYuKcQ_85#3lCW4|MQ2%Y%{e5>)$W0OFqs@rLYS6+={ z%*q*=D0aeC!bx~t9K9em!Db2Iv^fXe0?jwUA{14%KS4 zhKQ9i1s@KD+n^VQy~U^l=g2W5p*)?Ckz?lHipVj#CiSa=95XdISdP(p?LL1Cu`)%a zOcAOj`ab3&jZUOoXaLDnQl-LxMc1kH8D>(Y+~B?C_cD`X%wCvdn6VtQ7iq*v5}m{? zMWPnUlHIftQL4-^u3=J5LPD~{AV1^2x+F+2x?ZZdX)#zINA;SNsKWZa@kf1c-2A3f zIP(2Y5!A6Pp-}%r>_J3>5U~MbJvcnPqb#8YGuO+b55grx=?3ofuk%3o^quxiRauED zU`het5qJPDg|Z-r1|u*S14j49 zhQQ1beW4KbF^I%(W&}|86JZ#Yg89Iy-v)4=b)TbBoT@VL1l|KIB#+adsgOu9)qv>! z-DK5(MnugZs*3<((PmMocu`TfMTejWiopu}p9o8#G-!m$gRDt`2)+JXvZwW6?tmy1? zoZ+JFY<{&cj{oEyV#gs>l)6wOg}II?jP`uD(-%{Wd}_%KA@=5tid}C66_6GRt2*%h zGpySjC9j#da20J^`o$5WFLaeHhxr;JQVVd~JRYUK77{`Y_}7 zx4c3g%*jde7DP%8tyq85d#|Pjr~KTF+50lKuLd32?mqD~7)cdiT>ENc>7Ag-%we)C zyC4nZ+i?EZHiv+aq4irmjrmLGg|b(SAU%IR?VPluNVJa9*(Y4yBOCzy5xZzs^!@Rg zk8A5KN*=a{TNO_+pD9?r_>t$jHRLmE)@ny0c9ZOzRSkE%>RO)6txTJDnl!wt)<2VZ za<&!eZ7|oy=eGX!hu_Hf^#?-@2PHL%Y|FB^C|c~avhLvZ8S4BqM`TyRjH|n+yp{)a zdvq7x-xfPrMyb5IbjQ`GE?P`sr*lYBlp=#y_Yx`HuwHik!i4u558Z6cj?~WL{ACN| zX7`RY)cW|n=`4EYSt1C$-+bUl+TJpP>xPEla$Suh#-xgOO ZIC{n3ahXI=eel)JDI3mhrj%U!@E@F;H{$>R literal 0 HcmV?d00001 diff --git a/x509/tests/examples/amazon.pem b/x509/tests/examples/amazon.pem new file mode 100644 index 000000000..8b41dea1f --- /dev/null +++ b/x509/tests/examples/amazon.pem @@ -0,0 +1,49 @@ +-----BEGIN CERTIFICATE----- +MIIIwTCCB6mgAwIBAgIQDkI5q4Xi5qJ8Usbem5B42TANBgkqhkiG9w0BAQsFADBE +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMR4wHAYDVQQDExVE +aWdpQ2VydCBHbG9iYWwgQ0EgRzIwHhcNMjExMDA2MDAwMDAwWhcNMjIwOTE5MjM1 +OTU5WjAYMRYwFAYDVQQDDA0qLnBlZy5hMnouY29tMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv+/uM8Nke+pDU6lWoZALXfrNwH6/B3+8FEfNewD6mN8u +ntzCMH8fPU5Gb1/odQWS7GiBPowoU6smFj4F0kD3Qh4OUpAVbcYS2ad5nVBnwmh8 +Tm/U3DO34FZgxtjz3qxBVKr1ryYO3+1/H2xBuit8W1TSd/s+2joupzAAQq0zn8B3 +6kpi14dYIaZgBw0WuQHaybTlC0cko9x2t43RXxNZgRxqYyh+I+MK19ZOAZgCPAoo +JXyQiUIohTdOw8gnDY5gI4zhHyzvuhifSBeII1WA6MfGdiKV+K0R9LLr0oHYV3F7 +yCQoDN29ZVgXj4UZu64IxIQnuHRN6X2otR3qgUjAoQIDAQABo4IF2TCCBdUwHwYD +VR0jBBgwFoAUJG4rLdBqklFRJWkBqppHponnQCAwHQYDVR0OBBYEFJVFFD46QB6V +FvCCrEVzglhtm/B0MIICowYDVR0RBIICmjCCApaCDGFtYXpvbi5jby51a4ITdWVk +YXRhLmFtYXpvbi5jby51a4IQd3d3LmFtYXpvbi5jby51a4IXb3JpZ2luLXd3dy5h +bWF6b24uY28udWuCDSoucGVnLmEyei5jb22CCmFtYXpvbi5jb22CCGFtem4uY29t +ghF1ZWRhdGEuYW1hem9uLmNvbYINdXMuYW1hem9uLmNvbYIOd3d3LmFtYXpvbi5j +b22CDHd3dy5hbXpuLmNvbYIUY29ycG9yYXRlLmFtYXpvbi5jb22CEWJ1eWJveC5h +bWF6b24uY29tghFpcGhvbmUuYW1hem9uLmNvbYINeXAuYW1hem9uLmNvbYIPaG9t +ZS5hbWF6b24uY29tghVvcmlnaW4td3d3LmFtYXpvbi5jb22CFm9yaWdpbjItd3d3 +LmFtYXpvbi5jb22CIWJ1Y2tleWUtcmV0YWlsLXdlYnNpdGUuYW1hem9uLmNvbYIS +aHVkZGxlcy5hbWF6b24uY29tgglhbWF6b24uZGWCDXd3dy5hbWF6b24uZGWCFG9y +aWdpbi13d3cuYW1hem9uLmRlggxhbWF6b24uY28uanCCCWFtYXpvbi5qcIINd3d3 +LmFtYXpvbi5qcIIQd3d3LmFtYXpvbi5jby5qcIIXb3JpZ2luLXd3dy5hbWF6b24u +Y28uanCCECouYWEucGVnLmEyei5jb22CECouYWIucGVnLmEyei5jb22CECouYWMu +cGVnLmEyei5jb22CGG9yaWdpbi13d3cuYW1hem9uLmNvbS5hdYIRd3d3LmFtYXpv +bi5jb20uYXWCECouYnoucGVnLmEyei5jb22CDWFtYXpvbi5jb20uYXWCGG9yaWdp +bjItd3d3LmFtYXpvbi5jby5qcDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwz +LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbENBRzIuY3JsMDWgM6Axhi9odHRw +Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxDQUcyLmNybDA+BgNV +HSAENzA1MDMGBmeBDAECATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2lj +ZXJ0LmNvbS9DUFMwdAYIKwYBBQUHAQEEaDBmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC5kaWdpY2VydC5jb20wPgYIKwYBBQUHMAKGMmh0dHA6Ly9jYWNlcnRzLmRp +Z2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbENBRzIuY3J0MAwGA1UdEwEB/wQCMAAw +ggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2ACl5vvCeOTkh8FZzn2Old+W+V32c +YAr4+U1dJlwlXceEAAABfFOKWLsAAAQDAEcwRQIhAOIjvEg/ozDhjhiV0fbaYc83 +oPb2I08md/bU7nhfQufgAiAyA6CaJgpUYYQjchPqiS3DzHIIQL0FIkohEcluqPJV +oAB3AFGjsPX9AXmcVm24N3iPDKR6zBsny/eeiEKaDf7UiwXlAAABfFOKWOQAAAQD +AEgwRgIhAJA7QR62SZNgoU0SCfXaPUriW4FrPlVUZbSLl7s+q3W4AiEAg/iCIUET +pZEw8IAvQGC4ggNwRgm97pma3Ug8FhJp0KgAdQBByMqx3yJGShDGoToJQodeTjGL +GwPr60vHaPCQYpYG9gAAAXxTilifAAAEAwBGMEQCIFcuV+EsdfqUxhSV4+pSF5I/ +EVBVin/kOpaTBcSTlHccAiA7IdRwyN09v9bnXKJ2XsMDGfe9RHwWwVoXA/NJMx5A +3DANBgkqhkiG9w0BAQsFAAOCAQEAyLJluG6AFZ5fVgxdS574SZd7dInEumnQUQmt ++D/vbUb4NfiO4aRdPGkGotGHptFW9wxYjvYlSmtWbns5v+0CmpiXadXLNPWZtNjJ +gFR3WTLbHzBtD+C8yL1AQ/L2kOk9PVpq50leD7+dZimurjX3k7CbxaItjHnKAq3V +klINM2LLV//ZqnQxBlHeUiTfnUjKR/0FRDM4zr247HX0BH5CUl3wT5/e6mBqkXkK +Vvl+zzVfQjkeL40KjJJjQ4+N0gsPS8+rBPGHEVXFGPtlbI6pyS3Wo2rTkaIDwd+i +7ckVQviFKPooe4SVfp+kX4xVu9BEI4hlVa2y7qc/mMecBpXT+Q== +-----END CERTIFICATE----- diff --git a/x509/tests/pkix_extensions.rs b/x509/tests/pkix_extensions.rs index 262147468..b3d38e283 100644 --- a/x509/tests/pkix_extensions.rs +++ b/x509/tests/pkix_extensions.rs @@ -613,7 +613,7 @@ fn decode_cert() { let result = Certificate::from_der(der_encoded_cert); let cert: Certificate = result.unwrap(); - assert_eq!(cert.tbs_certificate.version, 2); + assert_eq!(cert.tbs_certificate.version, X509Version::V3); let target_serial: [u8; 1] = [2]; assert_eq!( cert.tbs_certificate.serial_number, From 750410174c4e4c75383b51c5050e5ccd7bc7032b Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 12:30:39 -0500 Subject: [PATCH 06/18] clippy suggestions --- x509/src/certificate_document.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/x509/src/certificate_document.rs b/x509/src/certificate_document.rs index 83b5ac8ff..330c32c8b 100644 --- a/x509/src/certificate_document.rs +++ b/x509/src/certificate_document.rs @@ -48,19 +48,19 @@ impl DecodeCertificate for CertificateDocument { #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] fn from_certificate_pem(s: &str) -> Result { - Ok(Self::from_pem(s)?) + Self::from_pem(s) } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn read_certificate_der_file(path: impl AsRef) -> Result { - Ok(Self::read_der_file(path)?) + Self::read_der_file(path) } #[cfg(all(feature = "pem", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] fn read_certificate_pem_file(path: impl AsRef) -> Result { - Ok(Self::read_pem_file(path)?) + Self::read_pem_file(path) } } @@ -74,13 +74,13 @@ impl EncodeCertificate for CertificateDocument { #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { - Ok(self.to_pem(line_ending)?) + self.to_pem(line_ending) } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { - Ok(self.write_der_file(path)?) + self.write_der_file(path) } #[cfg(all(feature = "pem", feature = "std"))] @@ -90,7 +90,7 @@ impl EncodeCertificate for CertificateDocument { path: impl AsRef, line_ending: LineEnding, ) -> Result<()> { - Ok(self.write_pem_file(path, line_ending)?) + self.write_pem_file(path, line_ending) } } From 30c77d050eeb3823c73cb021d2c1d29ed634c75c Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 13:14:41 -0500 Subject: [PATCH 07/18] address unused import on alloc feature --- x509/tests/certificate_document.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x509/tests/certificate_document.rs b/x509/tests/certificate_document.rs index 29c45740c..2d8c60996 100644 --- a/x509/tests/certificate_document.rs +++ b/x509/tests/certificate_document.rs @@ -3,7 +3,7 @@ use crate::certificate_document::CertificateDocument; use der::Document; use x509::certificate_traits::DecodeCertificate; -#[cfg(feature = "alloc")] +#[cfg(all(feature = "std", feature = "alloc"))] use x509::certificate_traits::EncodeCertificate; #[cfg(feature = "alloc")] From 66e6b9af4c1c6ae001e8d1edcb460b40080524e1 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 13:18:47 -0500 Subject: [PATCH 08/18] add links to doc comment --- x501/src/time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x501/src/time.rs b/x501/src/time.rs index 57857cbe2..a83d15093 100644 --- a/x501/src/time.rs +++ b/x501/src/time.rs @@ -41,7 +41,7 @@ impl Time { } } - /// Get Time as DateTime + /// Get [`Time`] as [`DateTime`] pub fn to_date_time(&self) -> DateTime { match self { Time::UtcTime(t) => t.to_date_time(), From 89dc7502cccd6b74a32bfdc7dc6b0e674c37fcda Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 13:32:09 -0500 Subject: [PATCH 09/18] use const length of 1 vs calculated --- x509/src/certificate.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index e2e26d604..9346c96cf 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -2,7 +2,8 @@ use der::asn1::{BitString, ContextSpecific, ObjectIdentifier, UIntBytes}; use der::{ - Decodable, Decoder, Encodable, Encoder, Error, FixedTag, Sequence, Tag, TagMode, TagNumber, + Decodable, Decoder, Encodable, Encoder, Error, FixedTag, Length, Sequence, Tag, TagMode, + TagNumber, }; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; use x501::name::Name; @@ -48,7 +49,7 @@ impl Decodable<'_> for X509Version { impl Encodable for X509Version { fn encoded_len(&self) -> der::Result { - der::Length::from(1u8).for_tlv() + Length::ONE.for_tlv() } fn encode(&self, encoder: &mut Encoder<'_>) -> der::Result<()> { From bc391a7a460b9ec78ac70c56e44ebf78233d5df7 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 16:09:26 -0500 Subject: [PATCH 10/18] address cargo-hack findings --- x509/src/certificate_document.rs | 2 +- x509/src/certificate_traits.rs | 15 ++++------ x509/tests/certificate_document.rs | 45 +++++++++++++++++------------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/x509/src/certificate_document.rs b/x509/src/certificate_document.rs index 330c32c8b..9323ecb8e 100644 --- a/x509/src/certificate_document.rs +++ b/x509/src/certificate_document.rs @@ -64,7 +64,7 @@ impl DecodeCertificate for CertificateDocument { } } -#[cfg(feature = "alloc")] +#[cfg(any(feature = "alloc", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl EncodeCertificate for CertificateDocument { fn to_certificate_der(&self) -> Result { diff --git a/x509/src/certificate_traits.rs b/x509/src/certificate_traits.rs index d3cbf81c8..ef4c50d94 100644 --- a/x509/src/certificate_traits.rs +++ b/x509/src/certificate_traits.rs @@ -1,13 +1,10 @@ //! CertificateDocument implementation -#[cfg(feature = "alloc")] -use crate::certificate_document::CertificateDocument; - use crate::Certificate; use der::{Error, Result}; -#[cfg(feature = "alloc")] -use der::Document; +#[cfg(any(feature = "alloc", feature = "std"))] +use {crate::certificate_document::CertificateDocument, der::Document}; #[cfg(feature = "std")] use std::path::Path; @@ -24,8 +21,8 @@ pub trait DecodeCertificate: for<'a> TryFrom, Error = Error> + S } /// Deserialize certificate from a [`CertificateDocument`]. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + #[cfg(any(feature = "alloc", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] fn from_certificate_doc(doc: &CertificateDocument) -> Result { Self::try_from(doc.decode()) } @@ -63,8 +60,8 @@ pub trait DecodeCertificate: for<'a> TryFrom, Error = Error> + S } /// Serialize a certificate object to a DER-encoded document. -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] pub trait EncodeCertificate { /// Serialize a [`CertificateDocument`] containing a Certificate. fn to_certificate_der(&self) -> Result; diff --git a/x509/tests/certificate_document.rs b/x509/tests/certificate_document.rs index 2d8c60996..0523472f0 100644 --- a/x509/tests/certificate_document.rs +++ b/x509/tests/certificate_document.rs @@ -3,10 +3,7 @@ use crate::certificate_document::CertificateDocument; use der::Document; use x509::certificate_traits::DecodeCertificate; -#[cfg(all(feature = "std", feature = "alloc"))] -use x509::certificate_traits::EncodeCertificate; - -#[cfg(feature = "alloc")] +#[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] use der::Encodable; use x509::*; @@ -44,8 +41,10 @@ fn decode_cert_der_file() { } #[test] -#[cfg(all(feature = "pem"))] +#[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] fn decode_cert_pem() { + use x509::certificate_traits::EncodeCertificate; + let doc: CertificateDocument = CERT_PEM_EXAMPLE.parse().unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); @@ -82,7 +81,7 @@ fn decode_cert_der() { } #[test] -#[cfg(all(feature = "alloc"))] +#[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] fn encode_cert_der() { let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); let pk_encoded = pk.to_vec().unwrap(); @@ -90,20 +89,10 @@ fn encode_cert_der() { } #[test] -#[cfg(all(feature = "pem"))] -fn encode_cert_pem() { - let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); - let pk_encoded = CertificateDocument::try_from(pk) - .unwrap() - .to_certificate_pem(Default::default()) - .unwrap(); - - assert_eq!(CERT_PEM_EXAMPLE, pk_encoded); -} - -#[test] -#[cfg(all(feature = "std", feature = "alloc"))] +#[cfg(feature = "std")] fn write_cert_der() { + use x509::certificate_traits::EncodeCertificate; + let doc: CertificateDocument = CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); @@ -125,8 +114,24 @@ fn write_cert_der() { } #[test] -#[cfg(all(feature = "std", feature = "alloc", feature = "pem"))] +#[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] +fn encode_cert_pem() { + use x509::certificate_traits::EncodeCertificate; + + let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); + let pk_encoded = CertificateDocument::try_from(pk) + .unwrap() + .to_certificate_pem(Default::default()) + .unwrap(); + + assert_eq!(CERT_PEM_EXAMPLE, pk_encoded); +} + +#[test] +#[cfg(all(feature = "std", feature = "pem"))] fn write_cert_pem() { + use x509::certificate_traits::EncodeCertificate; + let doc: CertificateDocument = CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); From 1e44e559d3b29fb67fdb42968ade797bbfbf8cc8 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 16:18:51 -0500 Subject: [PATCH 11/18] rename new version enum, add a default, delete an unused const --- x509/src/certificate.rs | 49 +++++++++++++++++++---------------- x509/tests/certificate.rs | 2 +- x509/tests/pkix_extensions.rs | 2 +- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 9346c96cf..93c2acdf6 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -24,13 +24,12 @@ pub fn default_zero() -> u32 { 0 } -/// only support v3 certificates +/// only supporting v3 certificates, but all versions are listed here /// Version ::= INTEGER { v1(0), v2(1), v3(2) } -pub const X509_CERT_VERSION: u8 = 2; /// Version identifier for X.509 certificates. In practice, only v3 is used. #[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd)] -pub enum X509Version { +pub enum Version { /// Denotes X.509 v1 V1 = 0, @@ -41,13 +40,19 @@ pub enum X509Version { V3 = 2, } -impl Decodable<'_> for X509Version { +impl Default for Version { + fn default() -> Self { + Version::V3 + } +} + +impl Decodable<'_> for Version { fn decode(decoder: &mut Decoder<'_>) -> der::Result { - X509Version::try_from(u8::decode(decoder)?).map_err(|_| Self::TAG.value_error()) + Version::try_from(u8::decode(decoder)?).map_err(|_| Self::TAG.value_error()) } } -impl Encodable for X509Version { +impl Encodable for Version { fn encoded_len(&self) -> der::Result { Length::ONE.for_tlv() } @@ -57,23 +62,23 @@ impl Encodable for X509Version { } } -impl FixedTag for X509Version { +impl FixedTag for Version { const TAG: Tag = Tag::Integer; } -impl From for u8 { - fn from(version: X509Version) -> Self { +impl From for u8 { + fn from(version: Version) -> Self { version as u8 } } -impl TryFrom for X509Version { +impl TryFrom for Version { type Error = Error; - fn try_from(byte: u8) -> Result { + fn try_from(byte: u8) -> Result { match byte { - 0 => Ok(X509Version::V1), - 1 => Ok(X509Version::V2), - 2 => Ok(X509Version::V3), + 0 => Ok(Version::V1), + 1 => Ok(Version::V2), + 2 => Ok(Version::V3), _ => Err(Self::TAG.value_error()), } } @@ -108,7 +113,7 @@ impl TryFrom for X509Version { pub struct TBSCertificate<'a> { /// version [0] Version DEFAULT v1, //#[asn1(context_specific = "0", default = "default_zero_u8")] - pub version: X509Version, + pub version: Version, /// serialNumber CertificateSerialNumber, pub serial_number: UIntBytes<'a>, /// signature AlgorithmIdentifier{SIGNATURE-ALGORITHM, {SignatureAlgorithms}}, @@ -138,7 +143,7 @@ impl<'a> ::der::Decodable<'a> for TBSCertificate<'a> { ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N0)? .map(|cs| cs.value) .unwrap_or_else(default_zero_u8); - let version = X509Version::try_from(version_int)?; + let version = Version::try_from(version_int)?; let serial_number = decoder.decode()?; let signature = decoder.decode()?; let issuer = decoder.decode()?; @@ -146,19 +151,19 @@ impl<'a> ::der::Decodable<'a> for TBSCertificate<'a> { let subject = decoder.decode()?; let subject_public_key_info = decoder.decode()?; - let issuer_unique_id = if X509Version::V2 <= version { + let issuer_unique_id = if Version::V2 <= version { ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? .map(|cs| cs.value) } else { None }; - let subject_unique_id = if X509Version::V2 <= version { + let subject_unique_id = if Version::V2 <= version { ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N2)? .map(|cs| cs.value) } else { None }; - let extensions = if X509Version::V3 <= version { + let extensions = if Version::V3 <= version { ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N3)? .map(|cs| cs.value) } else { @@ -189,17 +194,17 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { { let version_value: u8 = self.version.try_into()?; - let issuer_unique_id = if X509Version::V2 <= self.version { + let issuer_unique_id = if Version::V2 <= self.version { &self.issuer_unique_id } else { &None }; - let subject_unique_id = if X509Version::V2 <= self.version { + let subject_unique_id = if Version::V2 <= self.version { &self.subject_unique_id } else { &None }; - let extensions = if X509Version::V3 <= self.version { + let extensions = if Version::V3 <= self.version { self.extensions.as_ref().map(|exts| ContextSpecific { tag_number: EXTENSIONS_TAG, tag_mode: TagMode::Explicit, diff --git a/x509/tests/certificate.rs b/x509/tests/certificate.rs index f044077b1..db4a25d9f 100644 --- a/x509/tests/certificate.rs +++ b/x509/tests/certificate.rs @@ -194,7 +194,7 @@ fn decode_cert() { let result = Certificate::from_der(der_encoded_cert); let cert: Certificate = result.unwrap(); - assert_eq!(cert.tbs_certificate.version, X509Version::V3); + assert_eq!(cert.tbs_certificate.version, Version::V3); let target_serial: [u8; 16] = [ 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x49, 0xCF, 0x70, 0x66, 0x4D, 0x00, 0x00, 0x00, 0x02, diff --git a/x509/tests/pkix_extensions.rs b/x509/tests/pkix_extensions.rs index b3d38e283..f953e8ea2 100644 --- a/x509/tests/pkix_extensions.rs +++ b/x509/tests/pkix_extensions.rs @@ -613,7 +613,7 @@ fn decode_cert() { let result = Certificate::from_der(der_encoded_cert); let cert: Certificate = result.unwrap(); - assert_eq!(cert.tbs_certificate.version, X509Version::V3); + assert_eq!(cert.tbs_certificate.version, Version::V3); let target_serial: [u8; 1] = [2]; assert_eq!( cert.tbs_certificate.serial_number, From fb203b6d8d7c4705c2eeba670b562ad3b9e56bc0 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 7 Feb 2022 18:17:01 -0500 Subject: [PATCH 12/18] reorder derive attributes on version --- x509/src/certificate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 93c2acdf6..df806edb4 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -28,7 +28,7 @@ pub fn default_zero() -> u32 { /// Version ::= INTEGER { v1(0), v2(1), v3(2) } /// Version identifier for X.509 certificates. In practice, only v3 is used. -#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] pub enum Version { /// Denotes X.509 v1 V1 = 0, From ee9f1a3102dcfd68cc56e25a6676b5ff722b22c4 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Tue, 8 Feb 2022 06:16:58 -0500 Subject: [PATCH 13/18] use EncodeValue/DecodeValue instead of Encodable/Decodable for Version --- x509/src/certificate.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index df806edb4..37767bb8e 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -2,8 +2,8 @@ use der::asn1::{BitString, ContextSpecific, ObjectIdentifier, UIntBytes}; use der::{ - Decodable, Decoder, Encodable, Encoder, Error, FixedTag, Length, Sequence, Tag, TagMode, - TagNumber, + DecodeValue, Decoder, EncodeValue, Encoder, Error, FixedTag, Header, Length, Sequence, Tag, + TagMode, TagNumber, }; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; use x501::name::Name; @@ -46,19 +46,19 @@ impl Default for Version { } } -impl Decodable<'_> for Version { - fn decode(decoder: &mut Decoder<'_>) -> der::Result { - Version::try_from(u8::decode(decoder)?).map_err(|_| Self::TAG.value_error()) +impl DecodeValue<'_> for Version { + fn decode_value(decoder: &mut Decoder<'_>, header: Header) -> der::Result { + Version::try_from(u8::decode_value(decoder, header)?).map_err(|_| Self::TAG.value_error()) } } -impl Encodable for Version { - fn encoded_len(&self) -> der::Result { +impl EncodeValue for Version { + fn value_len(&self) -> der::Result { Length::ONE.for_tlv() } - fn encode(&self, encoder: &mut Encoder<'_>) -> der::Result<()> { - u8::from(*self).encode(encoder) + fn encode_value(&self, encoder: &mut Encoder<'_>) -> der::Result<()> { + u8::from(*self).encode_value(encoder) } } From c226d37a77913bad7a0a674f679b7b85a4ecb280 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Tue, 8 Feb 2022 06:33:55 -0500 Subject: [PATCH 14/18] fix bug in value_len (was still implemented as if encoded_len). drop conversion to u8 when encoding context specific tag for version field. --- x509/src/certificate.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 37767bb8e..5c81d585a 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -54,7 +54,7 @@ impl DecodeValue<'_> for Version { impl EncodeValue for Version { fn value_len(&self) -> der::Result { - Length::ONE.for_tlv() + Ok(Length::ONE) } fn encode_value(&self, encoder: &mut Encoder<'_>) -> der::Result<()> { @@ -192,8 +192,6 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { where F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, { - let version_value: u8 = self.version.try_into()?; - let issuer_unique_id = if Version::V2 <= self.version { &self.issuer_unique_id } else { @@ -220,7 +218,7 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { &ContextSpecific { tag_number: VERSION_TAG, tag_mode: TagMode::Explicit, - value: version_value, + value: self.version, }, &self.serial_number, &self.signature, From b600b9982e63c0a39a13edd8bf0f29bd0969ba83 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Tue, 8 Feb 2022 07:21:49 -0500 Subject: [PATCH 15/18] refactor version handling (including adding proper support for v1), add a v1 test case, add an invalid version (v7) test case --- x509/src/certificate.rs | 27 ++++++++++++++---------- x509/tests/certificate.rs | 25 ++++++++++++++++++++-- x509/tests/examples/invalid_version.der | Bin 0 -> 914 bytes x509/tests/examples/v1_cert.der | Bin 0 -> 351 bytes 4 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 x509/tests/examples/invalid_version.der create mode 100644 x509/tests/examples/v1_cert.der diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 5c81d585a..f05cfaeaa 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -10,8 +10,8 @@ use x501::name::Name; use x501::time::Validity; /// returns false in support of integer DEFAULT fields set to 0 -pub fn default_zero_u8() -> u8 { - 0 +pub fn default_v1() -> Version { + Version::V1 } /// returns false in support of boolean DEFAULT fields @@ -112,7 +112,7 @@ impl TryFrom for Version { #[derive(Clone, Eq, PartialEq)] pub struct TBSCertificate<'a> { /// version [0] Version DEFAULT v1, - //#[asn1(context_specific = "0", default = "default_zero_u8")] + //#[asn1(context_specific = "0", default = "default_v1")] pub version: Version, /// serialNumber CertificateSerialNumber, pub serial_number: UIntBytes<'a>, @@ -139,11 +139,10 @@ pub struct TBSCertificate<'a> { impl<'a> ::der::Decodable<'a> for TBSCertificate<'a> { fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { decoder.sequence(|decoder| { - let version_int = + let version = ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N0)? .map(|cs| cs.value) - .unwrap_or_else(default_zero_u8); - let version = Version::try_from(version_int)?; + .unwrap_or_else(default_v1); let serial_number = decoder.decode()?; let signature = decoder.decode()?; let issuer = decoder.decode()?; @@ -192,6 +191,12 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { where F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, { + let version = &ContextSpecific { + tag_number: VERSION_TAG, + tag_mode: TagMode::Explicit, + value: self.version, + }; + let issuer_unique_id = if Version::V2 <= self.version { &self.issuer_unique_id } else { @@ -215,11 +220,11 @@ impl<'a> ::der::Sequence<'a> for TBSCertificate<'a> { #[allow(unused_imports)] use core::convert::TryFrom; f(&[ - &ContextSpecific { - tag_number: VERSION_TAG, - tag_mode: TagMode::Explicit, - value: self.version, - }, + &::der::asn1::OptionalRef(if self.version == Version::V1 { + None + } else { + Some(version) + }), &self.serial_number, &self.signature, &self.issuer, diff --git a/x509/tests/certificate.rs b/x509/tests/certificate.rs index db4a25d9f..b27de6382 100644 --- a/x509/tests/certificate.rs +++ b/x509/tests/certificate.rs @@ -1,6 +1,6 @@ //! Certificate tests use der::asn1::{BitString, UIntBytes}; -use der::{Decodable, Decoder, Encodable, Tag, Tagged}; +use der::{Decodable, Decoder, Encodable, ErrorKind, Tag, Tagged}; use hex_literal::hex; use x509::Certificate; use x509::*; @@ -67,7 +67,9 @@ pub struct DeferDecodeTBSCertificate<'a> { /// Defer decoded field pub extensions: &'a [u8], } - +pub fn default_zero_u8() -> u8 { + 0 +} impl<'a> Decodable<'a> for DeferDecodeTBSCertificate<'a> { fn decode(decoder: &mut Decoder<'a>) -> der::Result> { decoder.sequence(|decoder| { @@ -100,6 +102,25 @@ impl<'a> Decodable<'a> for DeferDecodeTBSCertificate<'a> { } } +#[test] +fn unusual_x509_versions() { + let der_encoded_cert = include_bytes!("examples/v1_cert.der"); + let defer_cert = DeferDecodeCertificate::from_der(der_encoded_cert).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); + + let der_encoded_cert = include_bytes!("examples/invalid_version.der"); + let defer_cert = DeferDecodeCertificate::from_der(der_encoded_cert).unwrap(); + + let r = TBSCertificate::from_der(defer_cert.tbs_certificate); + assert!(r.is_err()); + if let Err(e) = r { + assert_eq!(e.kind(), ErrorKind::Value { tag: Tag::Integer }); + } +} + #[test] fn reencode_cert() { let der_encoded_cert = diff --git a/x509/tests/examples/invalid_version.der b/x509/tests/examples/invalid_version.der new file mode 100644 index 0000000000000000000000000000000000000000..020ae8459b35e591676d75a1aa438aabf853692a GIT binary patch literal 914 zcmXqLV(v3&Vk%p}%*4pfB+M4IR)~X1p!1vDZILriyzV%jzihzE#;Mij(e|B}k&%^^ z!64j_+klgeIh2J>m?<>akk5bz#Nps!4N0v?EH>l^st5AfdDt9Fi%T-|40#Q>K>}Pn zECH3yj)oEjVjwOv55IF!WkE@PdQoCQMx}zYqk)__uc3*dp^=%PiHV7+d6YP>F^Fqu zWB}&U&f+FUC1g)BvNA9?G4eAQG%<29H8Ct0+jj7m12 zX0@Q$z1g(GH(`#+hrd(Ag+)UZXGl5v-s)2N+B$!)>DBc!Bbv1$emJiC_aVl;?bZIs zSC9T_yS5x>*^zD6X|5W1{`i%1o_8KBH{+Bhv3I$ZKl@d6#^Pei zGR1_f1FHh8-ZLL9{i>t<@2GxqT7V$0?$j0br|p+T``*hadHQR@i5+G6X<_9T1)Y9> z6BcSUetScpde)<5x0S`$btm;Sj1RF)}Gs-ZGHc)%1=N4jUj1A;L(#kBrIB&qN z05xp^gPoBCyjm=bjmc+6yw0xt~1^a=*v7Q@Y+Isph->Ara2TF7Z`WHH0xFMMVHgg5@U8oKFB;SR5Dn-!m_H9mhLUDQx`J7UkG z8K$drUmln*++^2z?9k-CaFb(gVSL877N5Vn?Av{#Lj_aHHm3yemwDbaKmTH}vn6jr zz;20Fv(~3mtl8I2_Wfv}68G!>&FW>$5}ax|&MVa}vY0+U!1MUatnG@H6*=>H58vir Ws$X{Djs9h!wGK_U7e5KydH?{l&|SFz literal 0 HcmV?d00001 diff --git a/x509/tests/examples/v1_cert.der b/x509/tests/examples/v1_cert.der new file mode 100644 index 0000000000000000000000000000000000000000..6541c4ed3c8a751ca3b61011edaacfc75cfb7f78 GIT binary patch literal 351 zcmXqLVvIIuVq{}tlrZ3BgwPj*v zWMDMlVdH=V0TV0GXh#N?$a6;u;x!lN-?U!I%gwZ3LGc$~+XLN~49`Nk*H6^nr6oB} iE%RAmF*BcCvhjttMV;s3-4mJ(84Zh8YBYbdHUt3C7iHl9 literal 0 HcmV?d00001 From 6fe650f02debf2de21a2fcf3a6d7dbc98910ecb7 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Tue, 8 Feb 2022 09:02:28 -0500 Subject: [PATCH 16/18] fix visibility on version default function --- x509/src/certificate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index f05cfaeaa..756d35bd4 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -10,7 +10,7 @@ use x501::name::Name; use x501::time::Validity; /// returns false in support of integer DEFAULT fields set to 0 -pub fn default_v1() -> Version { +fn default_v1() -> Version { Version::V1 } From a9dab3e497be889adb89e4b4f2f928a8dd0731ab Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Tue, 8 Feb 2022 09:09:26 -0500 Subject: [PATCH 17/18] drop traits for certificate as unnecessary. move PEM support to impls for Certificate type. --- x509/src/certificate_document.rs | 29 +++++---- x509/src/certificate_traits.rs | 94 ------------------------------ x509/src/lib.rs | 1 - x509/tests/certificate_document.rs | 9 --- 4 files changed, 18 insertions(+), 115 deletions(-) delete mode 100644 x509/src/certificate_traits.rs diff --git a/x509/src/certificate_document.rs b/x509/src/certificate_document.rs index 9323ecb8e..c2a870b2c 100644 --- a/x509/src/certificate_document.rs +++ b/x509/src/certificate_document.rs @@ -1,6 +1,5 @@ //! CertificateDocument implementation -use crate::certificate_traits::*; use crate::Certificate; use der::{Error, Result}; @@ -40,52 +39,60 @@ impl<'a> Document<'a> for CertificateDocument { const SENSITIVE: bool = false; } -impl DecodeCertificate for CertificateDocument { - fn from_certificate_der(bytes: &[u8]) -> Result { +impl CertificateDocument { + /// Parse [`Certificate`] from buffer containing DER-encoded certificate + pub fn from_certificate_der(bytes: &[u8]) -> Result { Self::from_der(bytes) } #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] - fn from_certificate_pem(s: &str) -> Result { + /// Parse [`Certificate`] from buffer containing PEM-encoded certificate + pub fn from_certificate_pem(s: &str) -> Result { Self::from_pem(s) } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - fn read_certificate_der_file(path: impl AsRef) -> Result { + /// Read DER-encdoed [`Certificate`] from file + pub fn read_certificate_der_file(path: impl AsRef) -> Result { Self::read_der_file(path) } #[cfg(all(feature = "pem", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] - fn read_certificate_pem_file(path: impl AsRef) -> Result { + /// Read PEM-encdoed [`Certificate`] from file + pub fn read_certificate_pem_file(path: impl AsRef) -> Result { Self::read_pem_file(path) } } #[cfg(any(feature = "alloc", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -impl EncodeCertificate for CertificateDocument { - fn to_certificate_der(&self) -> Result { +impl CertificateDocument { + /// Generate DER-encoded [`Certificate`] + pub fn to_certificate_der(&self) -> Result { Ok(self.clone()) } #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] - fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { + /// Generate PEM-encoded [`Certificate`] + pub fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { self.to_pem(line_ending) } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { + /// Write DER-encoded [`Certificate`] to a file + pub fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { self.write_der_file(path) } #[cfg(all(feature = "pem", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] - fn write_certificate_pem_file( + /// Write PEM-encoded [`Certificate`] to a file + pub fn write_certificate_pem_file( &self, path: impl AsRef, line_ending: LineEnding, diff --git a/x509/src/certificate_traits.rs b/x509/src/certificate_traits.rs deleted file mode 100644 index ef4c50d94..000000000 --- a/x509/src/certificate_traits.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! CertificateDocument implementation - -use crate::Certificate; -use der::{Error, Result}; - -#[cfg(any(feature = "alloc", feature = "std"))] -use {crate::certificate_document::CertificateDocument, der::Document}; - -#[cfg(feature = "std")] -use std::path::Path; - -#[cfg(feature = "pem")] -use {alloc::string::String, der::pem::LineEnding}; - -/// Parse a public key object from an encoded Certificate document. -pub trait DecodeCertificate: for<'a> TryFrom, Error = Error> + Sized { - /// Deserialize object from ASN.1 DER-encoded [`Certificate`] - /// (binary format). - fn from_certificate_der(bytes: &[u8]) -> Result { - Self::try_from(Certificate::try_from(bytes)?) - } - - /// Deserialize certificate from a [`CertificateDocument`]. - #[cfg(any(feature = "alloc", feature = "std"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] - fn from_certificate_doc(doc: &CertificateDocument) -> Result { - Self::try_from(doc.decode()) - } - - /// Deserialize PEM-encoded [`Certificate`]. - /// - /// Keys in this format begin with the following delimiter: - /// - /// ```text - /// -----BEGIN CERTIFICATE----- - /// ``` - #[cfg(feature = "pem")] - #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] - fn from_certificate_pem(s: &str) -> Result { - CertificateDocument::from_certificate_pem(s) - .and_then(|doc| Self::from_certificate_doc(&doc)) - } - - /// Load public key object from an ASN.1 DER-encoded file on the local - /// filesystem (binary format). - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - fn read_certificate_der_file(path: impl AsRef) -> Result { - CertificateDocument::read_certificate_der_file(path) - .and_then(|doc| Self::from_certificate_doc(&doc)) - } - - /// Load public key object from a PEM-encoded file on the local filesystem. - #[cfg(all(feature = "pem", feature = "std"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] - fn read_certificate_pem_file(path: impl AsRef) -> Result { - CertificateDocument::read_certificate_pem_file(path) - .and_then(|doc| Self::from_certificate_doc(&doc)) - } -} - -/// Serialize a certificate object to a DER-encoded document. -#[cfg(any(feature = "alloc", feature = "std"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] -pub trait EncodeCertificate { - /// Serialize a [`CertificateDocument`] containing a Certificate. - fn to_certificate_der(&self) -> Result; - - /// Serialize this public key as PEM-encoded Certificate with the given [`LineEnding`]. - #[cfg(feature = "pem")] - #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] - fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { - self.to_certificate_der()?.to_certificate_pem(line_ending) - } - - /// Write ASN.1 DER-encoded public key to the given path - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { - self.to_certificate_der()?.write_certificate_der_file(path) - } - - /// Write ASN.1 DER-encoded public key to the given path - #[cfg(all(feature = "pem", feature = "std"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] - fn write_certificate_pem_file( - &self, - path: impl AsRef, - line_ending: LineEnding, - ) -> Result<()> { - self.to_certificate_der()? - .write_certificate_pem_file(path, line_ending) - } -} diff --git a/x509/src/lib.rs b/x509/src/lib.rs index 68ce24ff6..6035c27ac 100644 --- a/x509/src/lib.rs +++ b/x509/src/lib.rs @@ -16,7 +16,6 @@ extern crate std; pub mod certificate; pub mod certificate_document; -pub mod certificate_traits; pub mod extensions_utils; pub mod general_name; pub mod pkix_extensions; diff --git a/x509/tests/certificate_document.rs b/x509/tests/certificate_document.rs index 0523472f0..231ccf47a 100644 --- a/x509/tests/certificate_document.rs +++ b/x509/tests/certificate_document.rs @@ -1,7 +1,6 @@ //! Certificate document tests use crate::certificate_document::CertificateDocument; use der::Document; -use x509::certificate_traits::DecodeCertificate; #[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] use der::Encodable; @@ -43,8 +42,6 @@ fn decode_cert_der_file() { #[test] #[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] fn decode_cert_pem() { - use x509::certificate_traits::EncodeCertificate; - let doc: CertificateDocument = CERT_PEM_EXAMPLE.parse().unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); @@ -91,8 +88,6 @@ fn encode_cert_der() { #[test] #[cfg(feature = "std")] fn write_cert_der() { - use x509::certificate_traits::EncodeCertificate; - let doc: CertificateDocument = CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); @@ -116,8 +111,6 @@ fn write_cert_der() { #[test] #[cfg(all(feature = "pem", any(feature = "alloc", feature = "std")))] fn encode_cert_pem() { - use x509::certificate_traits::EncodeCertificate; - let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); let pk_encoded = CertificateDocument::try_from(pk) .unwrap() @@ -130,8 +123,6 @@ fn encode_cert_pem() { #[test] #[cfg(all(feature = "std", feature = "pem"))] fn write_cert_pem() { - use x509::certificate_traits::EncodeCertificate; - let doc: CertificateDocument = CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); From b7a19dec599cb9c28e37af333fde14e7f7271d39 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Tue, 8 Feb 2022 20:11:48 -0500 Subject: [PATCH 18/18] remove redundant impls --- x509/src/certificate_document.rs | 73 +----------------------------- x509/tests/certificate_document.rs | 54 ++++++++-------------- 2 files changed, 21 insertions(+), 106 deletions(-) diff --git a/x509/src/certificate_document.rs b/x509/src/certificate_document.rs index c2a870b2c..15ce62096 100644 --- a/x509/src/certificate_document.rs +++ b/x509/src/certificate_document.rs @@ -7,15 +7,8 @@ use alloc::vec::Vec; use core::fmt; use der::{Decodable, Document}; -#[cfg(feature = "std")] -use std::path::Path; - #[cfg(feature = "pem")] -use { - alloc::string::String, - core::str::FromStr, - der::pem::{self, LineEnding}, -}; +use {core::str::FromStr, der::pem}; /// Certificate document. /// @@ -39,68 +32,6 @@ impl<'a> Document<'a> for CertificateDocument { const SENSITIVE: bool = false; } -impl CertificateDocument { - /// Parse [`Certificate`] from buffer containing DER-encoded certificate - pub fn from_certificate_der(bytes: &[u8]) -> Result { - Self::from_der(bytes) - } - - #[cfg(feature = "pem")] - #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] - /// Parse [`Certificate`] from buffer containing PEM-encoded certificate - pub fn from_certificate_pem(s: &str) -> Result { - Self::from_pem(s) - } - - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - /// Read DER-encdoed [`Certificate`] from file - pub fn read_certificate_der_file(path: impl AsRef) -> Result { - Self::read_der_file(path) - } - - #[cfg(all(feature = "pem", feature = "std"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] - /// Read PEM-encdoed [`Certificate`] from file - pub fn read_certificate_pem_file(path: impl AsRef) -> Result { - Self::read_pem_file(path) - } -} - -#[cfg(any(feature = "alloc", feature = "std"))] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -impl CertificateDocument { - /// Generate DER-encoded [`Certificate`] - pub fn to_certificate_der(&self) -> Result { - Ok(self.clone()) - } - - #[cfg(feature = "pem")] - #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] - /// Generate PEM-encoded [`Certificate`] - pub fn to_certificate_pem(&self, line_ending: LineEnding) -> Result { - self.to_pem(line_ending) - } - - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - /// Write DER-encoded [`Certificate`] to a file - pub fn write_certificate_der_file(&self, path: impl AsRef) -> Result<()> { - self.write_der_file(path) - } - - #[cfg(all(feature = "pem", feature = "std"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] - /// Write PEM-encoded [`Certificate`] to a file - pub fn write_certificate_pem_file( - &self, - path: impl AsRef, - line_ending: LineEnding, - ) -> Result<()> { - self.write_pem_file(path, line_ending) - } -} - impl AsRef<[u8]> for CertificateDocument { fn as_ref(&self) -> &[u8] { self.0.as_ref() @@ -155,7 +86,7 @@ impl FromStr for CertificateDocument { type Err = Error; fn from_str(s: &str) -> Result { - Self::from_certificate_pem(s) + Self::from_pem(s) } } diff --git a/x509/tests/certificate_document.rs b/x509/tests/certificate_document.rs index 231ccf47a..33ea5f45e 100644 --- a/x509/tests/certificate_document.rs +++ b/x509/tests/certificate_document.rs @@ -24,8 +24,7 @@ const CERT_PEM_EXAMPLE: &str = include_str!("examples/amazon.pem"); #[cfg(all(feature = "pem", feature = "std"))] fn decode_cert_pem_file() { let doc: CertificateDocument = - CertificateDocument::read_certificate_pem_file(Path::new("tests/examples/amazon.pem")) - .unwrap(); + CertificateDocument::read_pem_file(Path::new("tests/examples/amazon.pem")).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); } @@ -34,8 +33,7 @@ fn decode_cert_pem_file() { fn decode_cert_der_file() { use crate::certificate_document::CertificateDocument; let doc: CertificateDocument = - CertificateDocument::read_certificate_der_file(Path::new("tests/examples/amazon.der")) - .unwrap(); + CertificateDocument::read_der_file(Path::new("tests/examples/amazon.der")).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); } @@ -48,28 +46,20 @@ fn decode_cert_pem() { // Ensure `CertificateDocument` parses successfully let cert = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.decode(), cert); - assert_eq!( - doc.to_certificate_pem(LineEnding::default()).unwrap(), - CERT_PEM_EXAMPLE - ); + assert_eq!(doc.to_pem(LineEnding::default()).unwrap(), CERT_PEM_EXAMPLE); - let doc: CertificateDocument = - CertificateDocument::from_certificate_pem(CERT_PEM_EXAMPLE).unwrap(); + let doc: CertificateDocument = CertificateDocument::from_pem(CERT_PEM_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); // Ensure `CertificateDocument` parses successfully let cert = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.decode(), cert); - assert_eq!( - doc.to_certificate_pem(LineEnding::default()).unwrap(), - CERT_PEM_EXAMPLE - ); + assert_eq!(doc.to_pem(LineEnding::default()).unwrap(), CERT_PEM_EXAMPLE); } #[test] fn decode_cert_der() { - let doc: CertificateDocument = - CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); + let doc: CertificateDocument = CertificateDocument::from_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); // Ensure `CertificateDocument` parses successfully @@ -88,22 +78,19 @@ fn encode_cert_der() { #[test] #[cfg(feature = "std")] fn write_cert_der() { - let doc: CertificateDocument = - CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); + let doc: CertificateDocument = CertificateDocument::from_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); - assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_der().as_ref(), CERT_DER_EXAMPLE); - let r = doc.write_certificate_der_file(Path::new("tests/examples/amazon.der.regen")); + let r = doc.write_der_file(Path::new("tests/examples/amazon.der.regen")); if r.is_err() { panic!("Failed to write file") } - let doc: CertificateDocument = CertificateDocument::read_certificate_der_file(Path::new( - "tests/examples/amazon.der.regen", - )) - .unwrap(); + let doc: CertificateDocument = + CertificateDocument::read_der_file(Path::new("tests/examples/amazon.der.regen")).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); - assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_der().as_ref(), CERT_DER_EXAMPLE); let r = std::fs::remove_file("tests/examples/amazon.der.regen"); if r.is_err() {} } @@ -114,7 +101,7 @@ fn encode_cert_pem() { let pk = Certificate::try_from(CERT_DER_EXAMPLE).unwrap(); let pk_encoded = CertificateDocument::try_from(pk) .unwrap() - .to_certificate_pem(Default::default()) + .to_pem(Default::default()) .unwrap(); assert_eq!(CERT_PEM_EXAMPLE, pk_encoded); @@ -123,12 +110,11 @@ fn encode_cert_pem() { #[test] #[cfg(all(feature = "std", feature = "pem"))] fn write_cert_pem() { - let doc: CertificateDocument = - CertificateDocument::from_certificate_der(CERT_DER_EXAMPLE).unwrap(); + let doc: CertificateDocument = CertificateDocument::from_der(CERT_DER_EXAMPLE).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); - assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_der().as_ref(), CERT_DER_EXAMPLE); - let r = doc.write_certificate_pem_file( + let r = doc.write_pem_file( Path::new("tests/examples/amazon.pem.regen"), LineEnding::default(), ); @@ -136,12 +122,10 @@ fn write_cert_pem() { panic!("Failed to write file") } - let doc: CertificateDocument = CertificateDocument::read_certificate_pem_file(Path::new( - "tests/examples/amazon.pem.regen", - )) - .unwrap(); + let doc: CertificateDocument = + CertificateDocument::read_pem_file(Path::new("tests/examples/amazon.pem.regen")).unwrap(); assert_eq!(doc.as_ref(), CERT_DER_EXAMPLE); - assert_eq!(doc.to_certificate_der().unwrap().as_ref(), CERT_DER_EXAMPLE); + assert_eq!(doc.to_der().as_ref(), CERT_DER_EXAMPLE); let r = std::fs::remove_file("tests/examples/amazon.pem.regen"); if r.is_err() {} }