Skip to content

Commit

Permalink
x509-cert: adds a Builder trait for Certificate and CertReq
Browse files Browse the repository at this point in the history
  • Loading branch information
baloo committed May 1, 2023
1 parent 9f19495 commit be6d26f
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 93 deletions.
200 changes: 108 additions & 92 deletions x509-cert/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ impl Profile {
pub struct CertificateBuilder<'s, S> {
tbs: TbsCertificate,
extensions: Extensions,
signer: &'s S,
cert_signer: &'s S,
}

impl<'s, S> CertificateBuilder<'s, S>
Expand All @@ -273,14 +273,14 @@ where
mut validity: Validity,
subject: Name,
subject_public_key_info: SubjectPublicKeyInfoOwned,
signer: &'s S,
cert_signer: &'s S,
) -> Result<Self> {
let verifying_key = signer.verifying_key();
let verifying_key = cert_signer.verifying_key();
let signer_pub = verifying_key
.to_public_key_der()?
.decode_msg::<SubjectPublicKeyInfoOwned>()?;

let signature_alg = signer.signature_algorithm_identifier()?;
let signature_alg = cert_signer.signature_algorithm_identifier()?;
let issuer = profile.get_issuer(&subject);

validity.not_before.rfc5280_adjust_utc_time()?;
Expand Down Expand Up @@ -313,7 +313,7 @@ where
Ok(Self {
tbs,
extensions,
signer,
cert_signer,
})
}

Expand All @@ -324,66 +324,13 @@ where

Ok(())
}

fn finalize(&mut self) {
if !self.extensions.is_empty() {
self.tbs.extensions = Some(self.extensions.clone());
}

if self.tbs.extensions.is_none() {
if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
self.tbs.version = Version::V2;
} else {
self.tbs.version = Version::V1;
}
}
}

/// Run the certificate through the signer and build the end certificate.
pub fn build<Signature>(mut self) -> Result<Certificate>
where
S: Signer<Signature>,
Signature: SignatureEncoding,
{
self.finalize();

let signature = self.signer.try_sign(&self.tbs.to_der()?)?;
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;

let cert = Certificate {
tbs_certificate: self.tbs.clone(),
signature_algorithm: self.tbs.signature,
signature,
};

Ok(cert)
}

/// Run the certificate through the signer and build the end certificate.
pub fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Certificate>
where
S: RandomizedSigner<Signature>,
Signature: SignatureEncoding,
{
self.finalize();

let signature = self.signer.try_sign_with_rng(rng, &self.tbs.to_der()?)?;
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;

let cert = Certificate {
tbs_certificate: self.tbs.clone(),
signature_algorithm: self.tbs.signature,
signature,
};

Ok(cert)
}
}

/// Builder for X509 Certificate Requests
pub struct RequestBuilder<'s, S> {
info: CertReqInfo,
extension_req: ExtensionReq,
signer: &'s S,
req_signer: &'s S,
}

impl<'s, S> RequestBuilder<'s, S>
Expand All @@ -392,9 +339,9 @@ where
S::VerifyingKey: EncodePublicKey,
{
/// Creates a new certificate request builder
pub fn new(subject: Name, signer: &'s S) -> Result<Self> {
pub fn new(subject: Name, req_signer: &'s S) -> Result<Self> {
let version = Default::default();
let verifying_key = signer.verifying_key();
let verifying_key = req_signer.verifying_key();
let public_key = verifying_key
.to_public_key_der()?
.decode_msg::<SubjectPublicKeyInfoOwned>()?;
Expand All @@ -409,7 +356,7 @@ where
attributes,
},
extension_req,
signer,
req_signer,
})
}

Expand All @@ -421,53 +368,122 @@ where

Ok(())
}
}

fn finalize(&mut self) -> Result<()> {
self.info
.attributes
.add(self.extension_req.clone().try_into()?);
Ok(())
}
/// Trait for X509 builders
///
/// This trait defines the interface between builder and the signers.
pub trait Builder: Sized {
/// The builder's object signer
type Signer;

/// Type built by this builder
type Output: Sized;

/// Run the certificate through the signer and build the end certificate.
pub fn build<Signature>(mut self) -> Result<CertReq>
/// Return a reference to the signer.
fn signer(&self) -> &Self::Signer;

/// Assemble the final object from signature.
fn assemble(self, signature: BitString) -> Result<Self::Output>;

/// Finalize and return a serialization of the object for signature.
fn finalize(&mut self) -> der::Result<vec::Vec<u8>>;

/// Run the object through the signer and build it.
fn build<Signature>(mut self) -> Result<Self::Output>
where
S: Signer<Signature>,
Self::Signer: Signer<Signature>,
Signature: SignatureEncoding,
{
self.finalize()?;
let blob = self.finalize()?;

let algorithm = self.signer.signature_algorithm_identifier()?;
let signature = self.signer.try_sign(&self.info.to_der()?)?;
let signature = self.signer().try_sign(&blob)?;
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;

let req = CertReq {
info: self.info,
algorithm,
signature,
};

Ok(req)
self.assemble(signature)
}

/// Run the certificate through the signer and build the end certificate.
pub fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<CertReq>
/// Run the object through the signer and build it.
fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Self::Output>
where
S: RandomizedSigner<Signature>,
Self::Signer: RandomizedSigner<Signature>,
Signature: SignatureEncoding,
{
self.finalize()?;
let blob = self.finalize()?;

let algorithm = self.signer.signature_algorithm_identifier()?;
let signature = self.signer.try_sign_with_rng(rng, &self.info.to_der()?)?;
let signature = self.signer().try_sign_with_rng(rng, &blob)?;
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;

let req = CertReq {
self.assemble(signature)
}
}

impl<'s, S> Builder for CertificateBuilder<'s, S>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
type Signer = S;
type Output = Certificate;

fn signer(&self) -> &Self::Signer {
self.cert_signer
}

fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
if !self.extensions.is_empty() {
self.tbs.extensions = Some(self.extensions.clone());
}

if self.tbs.extensions.is_none() {
if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
self.tbs.version = Version::V2;
} else {
self.tbs.version = Version::V1;
}
}

self.tbs.to_der()
}

fn assemble(self, signature: BitString) -> Result<Self::Output> {
let signature_algorithm = self.tbs.signature.clone();

Ok(Certificate {
tbs_certificate: self.tbs,
signature_algorithm,
signature,
})
}
}

impl<'s, S> Builder for RequestBuilder<'s, S>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
type Signer = S;
type Output = CertReq;

fn signer(&self) -> &Self::Signer {
self.req_signer
}

fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
self.info
.attributes
.add(self.extension_req.clone().try_into()?)?;

self.info.to_der()
}

fn assemble(self, signature: BitString) -> Result<Self::Output> {
let algorithm = self.req_signer.signature_algorithm_identifier()?;

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

Ok(req)
})
}
}
2 changes: 1 addition & 1 deletion x509-cert/tests/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use sha2::Sha256;
use spki::SubjectPublicKeyInfoOwned;
use std::{str::FromStr, time::Duration};
use x509_cert::{
builder::{CertificateBuilder, Profile, RequestBuilder},
builder::{Builder, CertificateBuilder, Profile, RequestBuilder},
ext::pkix::{name::GeneralName, SubjectAltName},
name::Name,
serial_number::SerialNumber,
Expand Down

0 comments on commit be6d26f

Please sign in to comment.