diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 8fd4a6c0..9093952b 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -172,7 +172,7 @@ impl CertificateParams { /// See [`from_ca_cert_der`](Self::from_ca_cert_der) for more details. #[cfg(all(feature = "pem", feature = "x509-parser"))] pub fn from_ca_cert_pem(pem_str: &str) -> Result { - let certificate = pem::parse(pem_str).or(Err(Error::CouldNotParseCertificate))?; + let certificate = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificate)?; Self::from_ca_cert_der(&certificate.contents().into()) } @@ -200,208 +200,22 @@ impl CertificateParams { #[cfg(feature = "x509-parser")] pub fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) - .or(Err(Error::CouldNotParseCertificate))?; - - let dn = DistinguishedName::from_name(&x509.tbs_certificate.subject)?; - let is_ca = Self::convert_x509_is_ca(&x509)?; - let validity = x509.validity(); - let subject_alt_names = Self::convert_x509_subject_alternative_name(&x509)?; - let key_usages = Self::convert_x509_key_usages(&x509)?; - let extended_key_usages = Self::convert_x509_extended_key_usages(&x509)?; - let name_constraints = Self::convert_x509_name_constraints(&x509)?; - let serial_number = Some(x509.serial.to_bytes_be().into()); - - let key_identifier_method = - x509.iter_extensions() - .find_map(|ext| match ext.parsed_extension() { - x509_parser::extensions::ParsedExtension::SubjectKeyIdentifier(key_id) => { - Some(KeyIdMethod::PreSpecified(key_id.0.into())) - }, - _ => None, - }); - - let key_identifier_method = match key_identifier_method { - Some(method) => method, - None => { - #[cfg(not(feature = "crypto"))] - return Err(Error::UnsupportedSignatureAlgorithm); - #[cfg(feature = "crypto")] - KeyIdMethod::Sha256 - }, - }; + .map_err(|_| Error::CouldNotParseCertificate)?; Ok(CertificateParams { - is_ca, - subject_alt_names, - key_usages, - extended_key_usages, - name_constraints, - serial_number, - key_identifier_method, - distinguished_name: dn, - not_before: validity.not_before.to_datetime(), - not_after: validity.not_after.to_datetime(), + is_ca: IsCa::from_x509(&x509)?, + subject_alt_names: SanType::from_x509(&x509)?, + key_usages: KeyUsagePurpose::from_x509(&x509)?, + extended_key_usages: ExtendedKeyUsagePurpose::from_x509(&x509)?, + name_constraints: NameConstraints::from_x509(&x509)?, + serial_number: Some(x509.serial.to_bytes_be().into()), + key_identifier_method: KeyIdMethod::from_x509(&x509)?, + distinguished_name: DistinguishedName::from_name(&x509.tbs_certificate.subject)?, + not_before: x509.validity().not_before.to_datetime(), + not_after: x509.validity().not_after.to_datetime(), ..Default::default() }) } - #[cfg(feature = "x509-parser")] - fn convert_x509_is_ca( - x509: &x509_parser::certificate::X509Certificate<'_>, - ) -> Result { - use x509_parser::extensions::BasicConstraints as B; - - let basic_constraints = x509 - .basic_constraints() - .or(Err(Error::CouldNotParseCertificate))? - .map(|ext| ext.value); - - let is_ca = match basic_constraints { - Some(B { - ca: true, - path_len_constraint: Some(n), - }) if *n <= u8::MAX as u32 => IsCa::Ca(BasicConstraints::Constrained(*n as u8)), - Some(B { - ca: true, - path_len_constraint: Some(_), - }) => return Err(Error::CouldNotParseCertificate), - Some(B { - ca: true, - path_len_constraint: None, - }) => IsCa::Ca(BasicConstraints::Unconstrained), - Some(B { ca: false, .. }) => IsCa::ExplicitNoCa, - None => IsCa::NoCa, - }; - - Ok(is_ca) - } - #[cfg(feature = "x509-parser")] - fn convert_x509_subject_alternative_name( - x509: &x509_parser::certificate::X509Certificate<'_>, - ) -> Result, Error> { - let sans = x509 - .subject_alternative_name() - .or(Err(Error::CouldNotParseCertificate))? - .map(|ext| &ext.value.general_names); - - if let Some(sans) = sans { - let mut subject_alt_names = Vec::with_capacity(sans.len()); - for san in sans { - subject_alt_names.push(SanType::try_from_general(san)?); - } - Ok(subject_alt_names) - } else { - Ok(Vec::new()) - } - } - #[cfg(feature = "x509-parser")] - fn convert_x509_key_usages( - x509: &x509_parser::certificate::X509Certificate<'_>, - ) -> Result, Error> { - let key_usage = x509 - .key_usage() - .or(Err(Error::CouldNotParseCertificate))? - .map(|ext| ext.value); - // This x509 parser stores flags in reversed bit BIT STRING order - let flags = key_usage.map_or(0u16, |k| k.flags).reverse_bits(); - Ok(KeyUsagePurpose::from_u16(flags)) - } - #[cfg(feature = "x509-parser")] - fn convert_x509_extended_key_usages( - x509: &x509_parser::certificate::X509Certificate<'_>, - ) -> Result, Error> { - let extended_key_usage = x509 - .extended_key_usage() - .or(Err(Error::CouldNotParseCertificate))? - .map(|ext| ext.value); - - let mut extended_key_usages = Vec::new(); - if let Some(extended_key_usage) = extended_key_usage { - if extended_key_usage.any { - extended_key_usages.push(ExtendedKeyUsagePurpose::Any); - } - if extended_key_usage.server_auth { - extended_key_usages.push(ExtendedKeyUsagePurpose::ServerAuth); - } - if extended_key_usage.client_auth { - extended_key_usages.push(ExtendedKeyUsagePurpose::ClientAuth); - } - if extended_key_usage.code_signing { - extended_key_usages.push(ExtendedKeyUsagePurpose::CodeSigning); - } - if extended_key_usage.email_protection { - extended_key_usages.push(ExtendedKeyUsagePurpose::EmailProtection); - } - if extended_key_usage.time_stamping { - extended_key_usages.push(ExtendedKeyUsagePurpose::TimeStamping); - } - if extended_key_usage.ocsp_signing { - extended_key_usages.push(ExtendedKeyUsagePurpose::OcspSigning); - } - } - Ok(extended_key_usages) - } - #[cfg(feature = "x509-parser")] - fn convert_x509_name_constraints( - x509: &x509_parser::certificate::X509Certificate<'_>, - ) -> Result, Error> { - let constraints = x509 - .name_constraints() - .or(Err(Error::CouldNotParseCertificate))? - .map(|ext| ext.value); - - if let Some(constraints) = constraints { - let permitted_subtrees = if let Some(permitted) = &constraints.permitted_subtrees { - Self::convert_x509_general_subtrees(permitted)? - } else { - Vec::new() - }; - - let excluded_subtrees = if let Some(excluded) = &constraints.excluded_subtrees { - Self::convert_x509_general_subtrees(excluded)? - } else { - Vec::new() - }; - - let name_constraints = NameConstraints { - permitted_subtrees, - excluded_subtrees, - }; - - Ok(Some(name_constraints)) - } else { - Ok(None) - } - } - #[cfg(feature = "x509-parser")] - fn convert_x509_general_subtrees( - subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], - ) -> Result, Error> { - use x509_parser::extensions::GeneralName; - - let mut result = Vec::new(); - for subtree in subtrees { - let subtree = match &subtree.base { - GeneralName::RFC822Name(s) => GeneralSubtree::Rfc822Name(s.to_string()), - GeneralName::DNSName(s) => GeneralSubtree::DnsName(s.to_string()), - GeneralName::DirectoryName(n) => { - GeneralSubtree::DirectoryName(DistinguishedName::from_name(n)?) - }, - GeneralName::IPAddress(bytes) if bytes.len() == 8 => { - let addr: [u8; 4] = bytes[..4].try_into().unwrap(); - let mask: [u8; 4] = bytes[4..].try_into().unwrap(); - GeneralSubtree::IpAddress(CidrSubnet::V4(addr, mask)) - }, - GeneralName::IPAddress(bytes) if bytes.len() == 32 => { - let addr: [u8; 16] = bytes[..16].try_into().unwrap(); - let mask: [u8; 16] = bytes[16..].try_into().unwrap(); - GeneralSubtree::IpAddress(CidrSubnet::V6(addr, mask)) - }, - _ => continue, - }; - result.push(subtree); - } - Ok(result) - } /// Write a CSR extension request attribute as defined in [RFC 2985]. /// @@ -987,6 +801,41 @@ pub enum ExtendedKeyUsagePurpose { } impl ExtendedKeyUsagePurpose { + #[cfg(feature = "x509-parser")] + fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { + let extended_key_usage = x509 + .extended_key_usage() + .map_err(|_| Error::CouldNotParseCertificate)? + .map(|ext| ext.value); + + let mut extended_key_usages = Vec::new(); + if let Some(extended_key_usage) = extended_key_usage { + if extended_key_usage.any { + extended_key_usages.push(Self::Any); + } + if extended_key_usage.server_auth { + extended_key_usages.push(Self::ServerAuth); + } + if extended_key_usage.client_auth { + extended_key_usages.push(Self::ClientAuth); + } + if extended_key_usage.code_signing { + extended_key_usages.push(Self::CodeSigning); + } + if extended_key_usage.email_protection { + extended_key_usages.push(Self::EmailProtection); + } + if extended_key_usage.time_stamping { + extended_key_usages.push(Self::TimeStamping); + } + if extended_key_usage.ocsp_signing { + extended_key_usages.push(Self::OcspSigning); + } + } + + Ok(extended_key_usages) + } + fn oid(&self) -> &[u64] { use ExtendedKeyUsagePurpose::*; match self { @@ -1017,6 +866,37 @@ pub struct NameConstraints { } impl NameConstraints { + #[cfg(feature = "x509-parser")] + fn from_x509( + x509: &x509_parser::certificate::X509Certificate<'_>, + ) -> Result, Error> { + let constraints = x509 + .name_constraints() + .map_err(|_| Error::CouldNotParseCertificate)? + .map(|ext| ext.value); + + let Some(constraints) = constraints else { + return Ok(None); + }; + + let permitted_subtrees = if let Some(permitted) = &constraints.permitted_subtrees { + GeneralSubtree::from_x509(permitted)? + } else { + Vec::new() + }; + + let excluded_subtrees = if let Some(excluded) = &constraints.excluded_subtrees { + GeneralSubtree::from_x509(excluded)? + } else { + Vec::new() + }; + + Ok(Some(Self { + permitted_subtrees, + excluded_subtrees, + })) + } + fn is_empty(&self) -> bool { self.permitted_subtrees.is_empty() && self.excluded_subtrees.is_empty() } @@ -1039,6 +919,38 @@ pub enum GeneralSubtree { } impl GeneralSubtree { + #[cfg(feature = "x509-parser")] + fn from_x509( + subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], + ) -> Result, Error> { + use x509_parser::extensions::GeneralName; + + let mut result = Vec::new(); + for subtree in subtrees { + let subtree = match &subtree.base { + GeneralName::RFC822Name(s) => Self::Rfc822Name(s.to_string()), + GeneralName::DNSName(s) => Self::DnsName(s.to_string()), + GeneralName::DirectoryName(n) => { + Self::DirectoryName(DistinguishedName::from_name(n)?) + }, + GeneralName::IPAddress(bytes) if bytes.len() == 8 => { + let addr: [u8; 4] = bytes[..4].try_into().unwrap(); + let mask: [u8; 4] = bytes[4..].try_into().unwrap(); + Self::IpAddress(CidrSubnet::V4(addr, mask)) + }, + GeneralName::IPAddress(bytes) if bytes.len() == 32 => { + let addr: [u8; 16] = bytes[..16].try_into().unwrap(); + let mask: [u8; 16] = bytes[16..].try_into().unwrap(); + Self::IpAddress(CidrSubnet::V6(addr, mask)) + }, + _ => continue, + }; + result.push(subtree); + } + + Ok(result) + } + fn tag(&self) -> u64 { // Defined in the GeneralName list in // https://tools.ietf.org/html/rfc5280#page-38 @@ -1178,6 +1090,35 @@ pub enum IsCa { Ca(BasicConstraints), } +impl IsCa { + #[cfg(feature = "x509-parser")] + fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { + use x509_parser::extensions::BasicConstraints as B; + + let basic_constraints = x509 + .basic_constraints() + .map_err(|_| Error::CouldNotParseCertificate)? + .map(|ext| ext.value); + + Ok(match basic_constraints { + Some(B { + ca: true, + path_len_constraint: Some(n), + }) if *n <= u8::MAX as u32 => Self::Ca(BasicConstraints::Constrained(*n as u8)), + Some(B { + ca: true, + path_len_constraint: Some(_), + }) => return Err(Error::CouldNotParseCertificate), + Some(B { + ca: true, + path_len_constraint: None, + }) => Self::Ca(BasicConstraints::Unconstrained), + Some(B { ca: false, .. }) => Self::ExplicitNoCa, + None => Self::NoCa, + }) + } +} + /// The path length constraint (only relevant for CA certificates) /// /// Sets an optional upper limit on the length of the intermediate certificate chain diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index a6f0d816..c6d6cb15 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -80,7 +80,7 @@ impl CertificateSigningRequestParams { /// See [`from_der`](Self::from_der) for more details. #[cfg(all(feature = "pem", feature = "x509-parser"))] pub fn from_pem(pem_str: &str) -> Result { - let csr = pem::parse(pem_str).or(Err(Error::CouldNotParseCertificationRequest))?; + let csr = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificationRequest)?; Self::from_der(&csr.contents().into()) } diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index f0190bdd..2bbc7c1d 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -177,6 +177,26 @@ pub enum SanType { OtherName((Vec, OtherNameValue)), } +impl SanType { + #[cfg(feature = "x509-parser")] + fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { + let sans = x509 + .subject_alternative_name() + .map_err(|_| Error::CouldNotParseCertificate)? + .map(|ext| &ext.value.general_names); + + let Some(sans) = sans else { + return Ok(Vec::new()); + }; + + let mut subject_alt_names = Vec::with_capacity(sans.len()); + for san in sans { + subject_alt_names.push(Self::try_from_general(san)?); + } + Ok(subject_alt_names) + } +} + /// An `OtherName` value, defined in [RFC 5280ยง4.1.2.4]. /// /// While the standard specifies this could be any ASN.1 type rcgen limits @@ -449,6 +469,17 @@ pub enum KeyUsagePurpose { } impl KeyUsagePurpose { + #[cfg(feature = "x509-parser")] + fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { + let key_usage = x509 + .key_usage() + .map_err(|_| Error::CouldNotParseCertificate)? + .map(|ext| ext.value); + // This x509 parser stores flags in reversed bit BIT STRING order + let flags = key_usage.map_or(0u16, |k| k.flags).reverse_bits(); + Ok(Self::from_u16(flags)) + } + /// Encode a key usage as the value of a BIT STRING as defined by RFC 5280. /// [`u16`] is sufficient to encode the largest possible key usage value (two bytes). fn to_u16(&self) -> u16 { @@ -519,6 +550,28 @@ pub enum KeyIdMethod { } impl KeyIdMethod { + #[cfg(feature = "x509-parser")] + fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { + let key_identifier_method = + x509.iter_extensions() + .find_map(|ext| match ext.parsed_extension() { + x509_parser::extensions::ParsedExtension::SubjectKeyIdentifier(key_id) => { + Some(KeyIdMethod::PreSpecified(key_id.0.into())) + }, + _ => None, + }); + + Ok(match key_identifier_method { + Some(method) => method, + None => { + #[cfg(not(feature = "crypto"))] + return Err(Error::UnsupportedSignatureAlgorithm); + #[cfg(feature = "crypto")] + KeyIdMethod::Sha256 + }, + }) + } + /// Derive a key identifier for the provided subject public key info using the key ID method. /// /// Typically this is a truncated hash over the raw subject public key info, but may diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 53e7dc62..4cf24ac7 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -250,8 +250,8 @@ impl KeyPairAlgorithm { let rng = ring_like::rand::SystemRandom::new(); let alg = &rcgen::PKCS_ED25519; - let pkcs8_bytes = - Ed25519KeyPair::generate_pkcs8(&rng).or(Err(rcgen::Error::RingUnspecified))?; + let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng) + .map_err(|_| rcgen::Error::RingUnspecified)?; rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) }, @@ -263,7 +263,7 @@ impl KeyPairAlgorithm { let alg = &rcgen::PKCS_ECDSA_P256_SHA256; let pkcs8_bytes = EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, &rng) - .or(Err(rcgen::Error::RingUnspecified))?; + .map_err(|_| rcgen::Error::RingUnspecified)?; rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) }, KeyPairAlgorithm::EcdsaP384 => { @@ -274,7 +274,7 @@ impl KeyPairAlgorithm { let alg = &rcgen::PKCS_ECDSA_P384_SHA384; let pkcs8_bytes = EcdsaKeyPair::generate_pkcs8(&ECDSA_P384_SHA384_ASN1_SIGNING, &rng) - .or(Err(rcgen::Error::RingUnspecified))?; + .map_err(|_| rcgen::Error::RingUnspecified)?; rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) }, @@ -287,7 +287,7 @@ impl KeyPairAlgorithm { let alg = &rcgen::PKCS_ECDSA_P521_SHA512; let pkcs8_bytes = EcdsaKeyPair::generate_pkcs8(&ECDSA_P521_SHA512_ASN1_SIGNING, &rng) - .or(Err(rcgen::Error::RingUnspecified))?; + .map_err(|_| rcgen::Error::RingUnspecified)?; rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) },