Skip to content

Distribute methods for parsing params elements from x509 #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
337 changes: 139 additions & 198 deletions rcgen/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, Error> {
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())
}

Expand Down Expand Up @@ -200,208 +200,22 @@ impl CertificateParams {
#[cfg(feature = "x509-parser")]
pub fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result<Self, Error> {
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<IsCa, Error> {
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<Vec<SanType>, 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<Vec<KeyUsagePurpose>, 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<Vec<ExtendedKeyUsagePurpose>, 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<Option<NameConstraints>, 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<Vec<GeneralSubtree>, 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].
///
Expand Down Expand Up @@ -987,6 +801,41 @@ pub enum ExtendedKeyUsagePurpose {
}

impl ExtendedKeyUsagePurpose {
#[cfg(feature = "x509-parser")]
fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Vec<Self>, 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 {
Expand Down Expand Up @@ -1017,6 +866,37 @@ pub struct NameConstraints {
}

impl NameConstraints {
#[cfg(feature = "x509-parser")]
fn from_x509(
x509: &x509_parser::certificate::X509Certificate<'_>,
) -> Result<Option<Self>, 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()
}
Expand All @@ -1039,6 +919,38 @@ pub enum GeneralSubtree {
}

impl GeneralSubtree {
#[cfg(feature = "x509-parser")]
fn from_x509(
subtrees: &[x509_parser::extensions::GeneralSubtree<'_>],
) -> Result<Vec<Self>, 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
Expand Down Expand Up @@ -1178,6 +1090,35 @@ pub enum IsCa {
Ca(BasicConstraints),
}

impl IsCa {
#[cfg(feature = "x509-parser")]
fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Self, Error> {
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
Expand Down
2 changes: 1 addition & 1 deletion rcgen/src/csr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, Error> {
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())
}

Expand Down
Loading
Loading