Skip to content

Commit

Permalink
x509-cert: move CSR builder to x509_cert::request (#1581)
Browse files Browse the repository at this point in the history
  • Loading branch information
baloo authored Oct 21, 2024
1 parent 75edabf commit 486a5a6
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 121 deletions.
128 changes: 7 additions & 121 deletions x509-cert/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ use spki::{
use crate::{
certificate::{Certificate, TbsCertificate, Version},
ext::{AsExtension, Extensions},
name::Name,
request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq},
serial_number::SerialNumber,
time::Validity,
AlgorithmIdentifier, SubjectPublicKeyInfo,
Expand All @@ -29,7 +27,13 @@ use self::profile::BuilderProfile;
)]
pub use self::profile::BuilderProfile as Profile;

const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");
#[deprecated(
since = "0.3.0",
note = "please use `x509_cert::request::RequestBuilder` instead"
)]
pub use crate::request::RequestBuilder;

pub(crate) const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");

/// Error type
#[derive(Debug)]
Expand Down Expand Up @@ -212,90 +216,6 @@ where
}
}

/// Builder for X509 Certificate Requests
///
/// ```
/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../tests/examples/p256-priv.der");
/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
/// # let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
/// # ecdsa::SigningKey::from(secret_key)
/// # }
/// use x509_cert::{
/// builder::{Builder, RequestBuilder},
/// ext::pkix::{name::GeneralName, SubjectAltName},
/// name::Name,
/// };
/// use std::str::FromStr;
///
/// use std::net::{IpAddr, Ipv4Addr};
/// let subject = Name::from_str("CN=service.domination.world").unwrap();
///
/// let signer = ecdsa_signer();
/// let mut builder = RequestBuilder::new(subject).expect("Create certificate request");
/// builder
/// .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
/// Ipv4Addr::new(192, 0, 2, 0),
/// ))]))
/// .unwrap();
///
/// let cert_req = builder.build::<_, DerSignature>(&signer).unwrap();
/// ```
pub struct RequestBuilder {
info: CertReqInfo,
extension_req: ExtensionReq,
}

impl RequestBuilder {
/// Creates a new certificate request builder
pub fn new(subject: Name) -> Result<Self> {
let version = Default::default();

let algorithm = AlgorithmIdentifier {
oid: NULL_OID,
parameters: None,
};
let public_key = SubjectPublicKeyInfo {
algorithm,
subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"),
};

let attributes = Default::default();
let extension_req = Default::default();

Ok(Self {
info: CertReqInfo {
version,
subject,
public_key,
attributes,
},
extension_req,
})
}

/// Add an extension to this certificate request
///
/// Extensions need to implement [`AsExtension`], examples may be found in
/// in [`AsExtension` documentation](../ext/trait.AsExtension.html#examples) or
/// [the implementors](../ext/trait.AsExtension.html#implementors).
pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;

self.extension_req.0.push(ext);

Ok(())
}

/// Add an attribute to this certificate request
pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
let attr = attribute.to_attribute()?;

self.info.attributes.insert(attr)?;
Ok(())
}
}

/// Trait for X509 builders
///
/// This trait defines the interface between builder and the signers.
Expand Down Expand Up @@ -404,40 +324,6 @@ where
}
}

impl Builder for RequestBuilder {
type Output = CertReq;

fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let verifying_key = signer.verifying_key();
let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?;
self.info.public_key = public_key;

self.info
.attributes
.insert(self.extension_req.clone().try_into()?)?;

self.info.to_der().map_err(Error::from)
}

fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let algorithm = signer.signature_algorithm_identifier()?;

Ok(CertReq {
info: self.info,
algorithm,
signature,
})
}
}

/// Trait for async X509 builders
///
/// This trait defines the interface between builder and the signers.
Expand Down
6 changes: 6 additions & 0 deletions x509-cert/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ use der::{
#[cfg(feature = "pem")]
use der::pem::PemLabel;

#[cfg(feature = "builder")]
mod builder;

#[cfg(feature = "builder")]
pub use self::builder::RequestBuilder;

/// Version identifier for certification request information.
///
/// (RFC 2986 designates `0` as the only valid version)
Expand Down
132 changes: 132 additions & 0 deletions x509-cert/src/request/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use alloc::vec;

use der::{asn1::BitString, Encode};
use signature::Keypair;
use spki::{
AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfo,
};

use crate::{
builder::{Builder, Error, Result, NULL_OID},
ext::AsExtension,
name::Name,
request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq},
};

/// Builder for X509 Certificate Requests (CSR)
///
/// ```
/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../../tests/examples/p256-priv.der");
/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
/// # let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
/// # ecdsa::SigningKey::from(secret_key)
/// # }
/// use x509_cert::{
/// builder::{Builder, RequestBuilder},
/// ext::pkix::{name::GeneralName, SubjectAltName},
/// name::Name,
/// };
/// use std::str::FromStr;
///
/// use std::net::{IpAddr, Ipv4Addr};
/// let subject = Name::from_str("CN=service.domination.world").unwrap();
///
/// let signer = ecdsa_signer();
/// let mut builder = RequestBuilder::new(subject).expect("Create certificate request");
/// builder
/// .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
/// Ipv4Addr::new(192, 0, 2, 0),
/// ))]))
/// .unwrap();
///
/// let cert_req = builder.build::<_, DerSignature>(&signer).unwrap();
/// ```
pub struct RequestBuilder {
info: CertReqInfo,
extension_req: ExtensionReq,
}

impl RequestBuilder {
/// Creates a new certificate request builder
pub fn new(subject: Name) -> Result<Self> {
let version = Default::default();

let algorithm = AlgorithmIdentifier {
oid: NULL_OID,
parameters: None,
};
let public_key = SubjectPublicKeyInfo {
algorithm,
subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"),
};

let attributes = Default::default();
let extension_req = Default::default();

Ok(Self {
info: CertReqInfo {
version,
subject,
public_key,
attributes,
},
extension_req,
})
}

/// Add an extension to this certificate request
///
/// Extensions need to implement [`AsExtension`], examples may be found in
/// in [`AsExtension` documentation](../ext/trait.AsExtension.html#examples) or
/// [the implementors](../ext/trait.AsExtension.html#implementors).
pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;

self.extension_req.0.push(ext);

Ok(())
}

/// Add an attribute to this certificate request
pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
let attr = attribute.to_attribute()?;

self.info.attributes.insert(attr)?;
Ok(())
}
}

impl Builder for RequestBuilder {
type Output = CertReq;

fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let verifying_key = signer.verifying_key();
let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?;
self.info.public_key = public_key;

self.info
.attributes
.insert(self.extension_req.clone().try_into()?)?;

self.info.to_der().map_err(Error::from)
}

fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let algorithm = signer.signature_algorithm_identifier()?;

Ok(CertReq {
info: self.info,
algorithm,
signature,
})
}
}

0 comments on commit 486a5a6

Please sign in to comment.