Skip to content

Commit

Permalink
x509-cert: don't bind builder with signer early
Browse files Browse the repository at this point in the history
This is mostly a draft after discussion in #1160

Signed-off-by: Arthur Gautier <[email protected]>
  • Loading branch information
baloo committed Jan 19, 2024
1 parent ea0d5f7 commit 2a0156e
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 108 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions x509-cert/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ rand = "0.8.5"
rsa = { version = "0.9.6", features = ["sha2"] }
ecdsa = { version = "0.16.8", features = ["digest", "pem"] }
p256 = "0.13.0"
p384 = "0.13.0"
rstest = "0.18"
sha2 = { version = "0.10", features = ["oid"] }
tempfile = "3.5.0"
Expand Down
186 changes: 104 additions & 82 deletions x509-cert/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use core::fmt;
use der::{asn1::BitString, referenced::OwnedToRef, Encode};
use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer};
use spki::{
DynSignatureAlgorithmIdentifier, EncodePublicKey, SignatureBitStringEncoding,
SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef,
AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier,
SignatureBitStringEncoding, SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef,
};

use crate::{
Expand All @@ -23,6 +23,8 @@ use crate::{
time::Validity,
};

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

/// Error type
#[derive(Debug)]
#[non_exhaustive]
Expand Down Expand Up @@ -68,7 +70,8 @@ impl From<signature::Error> for Error {
}
}

type Result<T> = core::result::Result<T, Error>;
/// Result type
pub type Result<T> = core::result::Result<T, Error>;

/// The type of certificate to build
#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -219,7 +222,7 @@ impl Profile {
/// ```
/// use der::Decode;
/// use x509_cert::spki::SubjectPublicKeyInfoOwned;
/// use x509_cert::builder::{CertificateBuilder, Profile};
/// use x509_cert::builder::{CertificateBuilder, Profile, Builder};
/// use x509_cert::name::Name;
/// use x509_cert::serial_number::SerialNumber;
/// use x509_cert::time::Validity;
Expand Down Expand Up @@ -251,34 +254,31 @@ impl Profile {
/// validity,
/// subject,
/// pub_key,
/// &signer,
/// )
/// .expect("Create certificate");
/// .expect("Create certificate builder");
///
/// let cert = builder.build(&signer).expect("Create certificate");
/// ```
pub struct CertificateBuilder<'s, S> {
pub struct CertificateBuilder {
tbs: TbsCertificate,
extensions: Extensions,
cert_signer: &'s S,
profile: Profile,
}

impl<'s, S> CertificateBuilder<'s, S>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
impl CertificateBuilder {
/// Creates a new certificate builder
pub fn new(
profile: Profile,
serial_number: SerialNumber,
mut validity: Validity,
subject: Name,
subject_public_key_info: SubjectPublicKeyInfoOwned,
cert_signer: &'s S,
) -> Result<Self> {
let verifying_key = cert_signer.verifying_key();
let signer_pub = SubjectPublicKeyInfoOwned::from_key(verifying_key)?;
let signature_alg = AlgorithmIdentifier {
oid: NULL_OID,
parameters: None,
};

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

validity.not_before.rfc5280_adjust_utc_time()?;
Expand All @@ -303,15 +303,11 @@ where
subject_unique_id: None,
};

let extensions = profile.build_extensions(
tbs.subject_public_key_info.owned_to_ref(),
signer_pub.owned_to_ref(),
&tbs,
)?;
let extensions = Extensions::default();
Ok(Self {
tbs,
extensions,
cert_signer,
profile,
})
}

Expand Down Expand Up @@ -344,31 +340,34 @@ where
/// let subject = Name::from_str("CN=service.domination.world").unwrap();
///
/// let signer = ecdsa_signer();
/// let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request");
/// 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>().unwrap();
/// let cert_req = builder.build::<_, DerSignature>(&signer).unwrap();
/// ```
pub struct RequestBuilder<'s, S> {
pub struct RequestBuilder {
info: CertReqInfo,
extension_req: ExtensionReq,
req_signer: &'s S,
}

impl<'s, S> RequestBuilder<'s, S>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
impl RequestBuilder {
/// Creates a new certificate request builder
pub fn new(subject: Name, req_signer: &'s S) -> Result<Self> {
pub fn new(subject: Name) -> Result<Self> {
let version = Default::default();
let verifying_key = req_signer.verifying_key();
let public_key = SubjectPublicKeyInfoOwned::from_key(verifying_key)?;

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

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

Expand All @@ -380,7 +379,6 @@ where
attributes,
},
extension_req,
req_signer,
})
}

Expand All @@ -406,64 +404,79 @@ where
///
/// 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;

/// 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>;
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey;

/// Finalize and return a serialization of the object for signature.
fn finalize(&mut self) -> der::Result<vec::Vec<u8>>;
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey;

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

let signature = self.signer().try_sign(&blob)?.to_bitstring()?;
let signature = signer.try_sign(&blob)?.to_bitstring()?;

self.assemble(signature)
self.assemble(signature, signer)
}

/// Run the object through the signer and build it.
fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Self::Output>
fn build_with_rng<S, Signature>(
mut self,
signer: &S,
rng: &mut impl CryptoRngCore,
) -> Result<Self::Output>
where
Self::Signer: RandomizedSigner<Signature>,
S: RandomizedSigner<Signature>,
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
Signature: SignatureBitStringEncoding,
{
let blob = self.finalize()?;
let blob = self.finalize(signer)?;

let signature = self
.signer()
.try_sign_with_rng(rng, &blob)?
.to_bitstring()?;
let signature = signer.try_sign_with_rng(rng, &blob)?.to_bitstring()?;

self.assemble(signature)
self.assemble(signature, signer)
}
}

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

fn signer(&self) -> &Self::Signer {
self.cert_signer
}
fn finalize<S>(&mut self, cert_signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let verifying_key = cert_signer.verifying_key();
let signer_pub = verifying_key
.to_public_key_der()?
.decode_msg::<SubjectPublicKeyInfoOwned>()?;

self.tbs.signature = cert_signer.signature_algorithm_identifier()?;

let mut default_extensions = self.profile.build_extensions(
self.tbs.subject_public_key_info.owned_to_ref(),
signer_pub.owned_to_ref(),
&self.tbs,
)?;

self.extensions.append(&mut default_extensions);

fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
if !self.extensions.is_empty() {
self.tbs.extensions = Some(self.extensions.clone());
}
Expand All @@ -476,10 +489,14 @@ where
}
}

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

fn assemble(self, signature: BitString) -> Result<Self::Output> {
fn assemble<S>(self, signature: BitString, _signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let signature_algorithm = self.tbs.signature.clone();

Ok(Certificate {
Expand All @@ -490,28 +507,33 @@ where
}
}

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

fn signer(&self) -> &Self::Signer {
self.req_signer
}
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 = verifying_key
.to_public_key_der()?
.decode_msg::<SubjectPublicKeyInfoOwned>()?;
self.info.public_key = public_key;

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

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

fn assemble(self, signature: BitString) -> Result<Self::Output> {
let algorithm = self.req_signer.signature_algorithm_identifier()?;
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,
Expand Down
Loading

0 comments on commit 2a0156e

Please sign in to comment.