Skip to content

Commit a8afdae

Browse files
committed
x509-cert: introduce a builder::AsyncBuilder trait
1 parent 345ac96 commit a8afdae

File tree

5 files changed

+216
-1
lines changed

5 files changed

+216
-1
lines changed

Cargo.lock

Lines changed: 101 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,5 @@ tls_codec_derive = { path = "./tls_codec/derive" }
5858
x509-tsp = { path = "./x509-tsp" }
5959
x509-cert = { path = "./x509-cert" }
6060
x509-ocsp = { path = "./x509-ocsp" }
61+
62+
async-signature = { git = "https://github.com/baloo/traits.git", branch = "baloo/async-signature/v0.6.0-pre.0" }

x509-cert/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ spki = { version = "=0.8.0-pre.0", features = ["alloc"] }
2121

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

3941
[features]

x509-cert/src/builder.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! X509 Certificate builder
22
33
use alloc::vec;
4+
use async_signature::{AsyncRandomizedSigner, AsyncSigner};
45
use core::fmt;
56
use der::{asn1::BitString, referenced::OwnedToRef, Encode};
67
use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer};
@@ -538,3 +539,86 @@ impl Builder for RequestBuilder {
538539
})
539540
}
540541
}
542+
543+
/// Trait for async X509 builders
544+
///
545+
/// This trait defines the interface between builder and the signers.
546+
///
547+
/// This is the async counterpart of [`Builder`].
548+
#[allow(async_fn_in_trait)]
549+
pub trait AsyncBuilder: Sized {
550+
/// Type built by this builder
551+
type Output: Sized;
552+
553+
/// Assemble the final object from signature.
554+
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
555+
where
556+
S: Keypair + DynSignatureAlgorithmIdentifier,
557+
S::VerifyingKey: EncodePublicKey;
558+
559+
/// Finalize and return a serialization of the object for signature.
560+
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
561+
where
562+
S: Keypair + DynSignatureAlgorithmIdentifier,
563+
S::VerifyingKey: EncodePublicKey;
564+
565+
/// Run the object through the signer and build it.
566+
async fn build_async<S, Signature: 'static>(mut self, signer: &S) -> Result<Self::Output>
567+
where
568+
S: AsyncSigner<Signature>,
569+
S: Keypair + DynSignatureAlgorithmIdentifier,
570+
S::VerifyingKey: EncodePublicKey,
571+
Signature: SignatureBitStringEncoding,
572+
{
573+
let blob = self.finalize(signer)?;
574+
575+
let signature = signer.sign_async(&blob).await?.to_bitstring()?;
576+
577+
self.assemble(signature, signer)
578+
}
579+
580+
/// Run the object through the signer and build it.
581+
async fn build_with_rng_async<S, Signature: 'static>(
582+
mut self,
583+
signer: &S,
584+
rng: &mut impl CryptoRngCore,
585+
) -> Result<Self::Output>
586+
where
587+
S: AsyncRandomizedSigner<Signature>,
588+
S: Keypair + DynSignatureAlgorithmIdentifier,
589+
S::VerifyingKey: EncodePublicKey,
590+
Signature: SignatureBitStringEncoding,
591+
{
592+
let blob = self.finalize(signer)?;
593+
594+
let signature = signer
595+
.try_sign_with_rng_async(rng, &blob)
596+
.await?
597+
.to_bitstring()?;
598+
599+
self.assemble(signature, signer)
600+
}
601+
}
602+
603+
impl<T> AsyncBuilder for T
604+
where
605+
T: Builder,
606+
{
607+
type Output = <T as Builder>::Output;
608+
609+
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
610+
where
611+
S: Keypair + DynSignatureAlgorithmIdentifier,
612+
S::VerifyingKey: EncodePublicKey,
613+
{
614+
<T as Builder>::assemble(self, signature, signer)
615+
}
616+
617+
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
618+
where
619+
S: Keypair + DynSignatureAlgorithmIdentifier,
620+
S::VerifyingKey: EncodePublicKey,
621+
{
622+
<T as Builder>::finalize(self, signer)
623+
}
624+
}

x509-cert/tests/builder.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use sha2::Sha256;
99
use spki::SubjectPublicKeyInfoOwned;
1010
use std::{str::FromStr, time::Duration};
1111
use x509_cert::{
12-
builder::{Builder, CertificateBuilder, Profile, RequestBuilder},
12+
builder::{AsyncBuilder, Builder, CertificateBuilder, Profile, RequestBuilder},
1313
ext::pkix::{
1414
name::{DirectoryString, GeneralName},
1515
SubjectAltName,
@@ -333,3 +333,29 @@ fn dynamic_signer() {
333333

334334
println!("{}", csr_pem);
335335
}
336+
337+
#[tokio::test]
338+
async fn async_builder() {
339+
let serial_number = SerialNumber::from(42u32);
340+
let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
341+
let profile = Profile::Root;
342+
let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US")
343+
.unwrap()
344+
.to_der()
345+
.unwrap();
346+
let subject = Name::from_der(&subject).unwrap();
347+
let pub_key =
348+
SubjectPublicKeyInfoOwned::try_from(PKCS8_PUBLIC_KEY_DER).expect("get ecdsa pub key");
349+
350+
let signer = ecdsa_signer();
351+
let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key)
352+
.expect("Create certificate");
353+
354+
let certificate = builder
355+
.build_async::<_, DerSignature>(&signer)
356+
.await
357+
.unwrap();
358+
359+
let pem = certificate.to_pem(LineEnding::LF).expect("generate pem");
360+
println!("{}", openssl::check_certificate(pem.as_bytes()));
361+
}

0 commit comments

Comments
 (0)