Skip to content

Commit

Permalink
x509-cert: introduce a builder::AsyncBuilder trait
Browse files Browse the repository at this point in the history
  • Loading branch information
baloo committed Jan 20, 2024
1 parent 345ac96 commit a8afdae
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 1 deletion.
101 changes: 101 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ tls_codec_derive = { path = "./tls_codec/derive" }
x509-tsp = { path = "./x509-tsp" }
x509-cert = { path = "./x509-cert" }
x509-ocsp = { path = "./x509-ocsp" }

async-signature = { git = "https://github.com/baloo/traits.git", branch = "baloo/async-signature/v0.6.0-pre.0" }
2 changes: 2 additions & 0 deletions x509-cert/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ spki = { version = "=0.8.0-pre.0", features = ["alloc"] }

# optional dependencies
arbitrary = { version = "1.3", features = ["derive"], optional = true }
async-signature = { version = "=0.6.0-pre.0", features = ["digest", "rand_core"], optional = true }
sha1 = { version = "0.11.0-pre.2", optional = true }
signature = { version = "=2.3.0-pre.2", features = ["rand_core"], optional = true }
tls_codec = { version = "0.4.0", default-features = false, features = ["derive"], optional = true }
Expand All @@ -34,6 +35,7 @@ p256 = "=0.14.0-pre.0"
rstest = "0.18"
sha2 = { version = "=0.11.0-pre.2", features = ["oid"] }
tempfile = "3.5.0"
tokio = { version = "1.35.0", features = ["macros", "rt"] }
x509-cert-test-support = { path = "./test-support" }

[features]
Expand Down
84 changes: 84 additions & 0 deletions x509-cert/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! X509 Certificate builder

use alloc::vec;
use async_signature::{AsyncRandomizedSigner, AsyncSigner};
use core::fmt;
use der::{asn1::BitString, referenced::OwnedToRef, Encode};
use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer};
Expand Down Expand Up @@ -538,3 +539,86 @@ impl Builder for RequestBuilder {
})
}
}

/// Trait for async X509 builders
///
/// This trait defines the interface between builder and the signers.
///
/// This is the async counterpart of [`Builder`].
#[allow(async_fn_in_trait)]
pub trait AsyncBuilder: Sized {
/// Type built by this builder
type Output: Sized;

/// Assemble the final object from signature.
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<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.
async fn build_async<S, Signature: 'static>(mut self, signer: &S) -> Result<Self::Output>
where
S: AsyncSigner<Signature>,
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
Signature: SignatureBitStringEncoding,
{
let blob = self.finalize(signer)?;

let signature = signer.sign_async(&blob).await?.to_bitstring()?;

self.assemble(signature, signer)
}

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

let signature = signer
.try_sign_with_rng_async(rng, &blob)
.await?
.to_bitstring()?;

self.assemble(signature, signer)
}
}

impl<T> AsyncBuilder for T
where
T: Builder,
{
type Output = <T as Builder>::Output;

fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
<T as Builder>::assemble(self, signature, signer)
}

fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
<T as Builder>::finalize(self, signer)
}
}
28 changes: 27 additions & 1 deletion x509-cert/tests/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use sha2::Sha256;
use spki::SubjectPublicKeyInfoOwned;
use std::{str::FromStr, time::Duration};
use x509_cert::{
builder::{Builder, CertificateBuilder, Profile, RequestBuilder},
builder::{AsyncBuilder, Builder, CertificateBuilder, Profile, RequestBuilder},
ext::pkix::{
name::{DirectoryString, GeneralName},
SubjectAltName,
Expand Down Expand Up @@ -333,3 +333,29 @@ fn dynamic_signer() {

println!("{}", csr_pem);
}

#[tokio::test]
async fn async_builder() {
let serial_number = SerialNumber::from(42u32);
let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
let profile = Profile::Root;
let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US")
.unwrap()
.to_der()
.unwrap();
let subject = Name::from_der(&subject).unwrap();
let pub_key =
SubjectPublicKeyInfoOwned::try_from(PKCS8_PUBLIC_KEY_DER).expect("get ecdsa pub key");

let signer = ecdsa_signer();
let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key)
.expect("Create certificate");

let certificate = builder
.build_async::<_, DerSignature>(&signer)
.await
.unwrap();

let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
println!("{}", openssl::check_certificate(pem.as_bytes()));
}

0 comments on commit a8afdae

Please sign in to comment.