From 778c0e70a16c53f07a341f1b7bf4bdf576a993f2 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Thu, 26 Sep 2024 20:38:46 -0700 Subject: [PATCH] cms: adds support for AsyncSigner --- Cargo.lock | 2 ++ cms/Cargo.toml | 4 ++- cms/src/builder.rs | 48 ++++++++++++++++++++++++++++- cms/tests/builder.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98ca4ce8e..64e6f9e6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,6 +363,7 @@ name = "cms" version = "0.3.0-pre" dependencies = [ "aes", + "async-signature", "cbc", "cipher", "const-oid", @@ -380,6 +381,7 @@ dependencies = [ "sha3", "signature", "spki", + "tokio", "x509-cert", "zeroize", ] diff --git a/cms/Cargo.toml b/cms/Cargo.toml index e8acd71c4..7cdfbff7a 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -22,6 +22,7 @@ const-oid = { version = "0.10.0-rc.0", features = ["db"] } # optional dependencies aes = { version = "=0.9.0-pre.1", optional = true } +async-signature = { version = "=0.6.0-pre.4", features = ["digest", "rand_core"], optional = true } cbc = { version = "=0.2.0-pre.1", optional = true } cipher = { version = "=0.5.0-pre.6", features = ["alloc", "block-padding", "rand_core"], optional = true } rsa = { version = "=0.10.0-pre.2", optional = true } @@ -40,11 +41,12 @@ rand = "0.8.5" rsa = { version = "=0.10.0-pre.2", features = ["sha2"] } ecdsa = { version = "=0.17.0-pre.9", features = ["digest", "pem"] } p256 = "=0.14.0-pre.2" +tokio = { version = "1.40.0", features = ["macros", "rt"] } [features] alloc = ["der/alloc"] std = ["der/std", "spki/std"] -builder = ["aes", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder", "zeroize"] +builder = ["aes", "async-signature", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder", "zeroize"] pem = ["alloc", "der/pem"] [package.metadata.docs.rs] diff --git a/cms/src/builder.rs b/cms/src/builder.rs index 02d1bb8fd..5f49fd31a 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -19,6 +19,7 @@ use alloc::borrow::ToOwned; use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; +use async_signature::{AsyncRandomizedSigner, AsyncSigner}; use cipher::{ block_padding::Pkcs7, rand_core::{self, CryptoRng, CryptoRngCore, RngCore}, @@ -43,7 +44,7 @@ use spki::{ use std::time::SystemTime; use std::vec; use x509_cert::attr::{Attribute, AttributeValue, Attributes}; -use x509_cert::builder::{self, Builder}; +use x509_cert::builder::{self, AsyncBuilder, Builder}; use zeroize::Zeroize; /// Error type @@ -426,6 +427,51 @@ impl<'s> SignedDataBuilder<'s> { Ok(self) } + /// Add a signer info. The signature will be calculated. Note that the encapsulated content + /// must not be changed after the first signer info was added. + pub async fn add_signer_info_async( + &mut self, + signer_info_builder: SignerInfoBuilder<'_>, + signer: &S, + ) -> Result<&mut Self> + where + S: Keypair + DynSignatureAlgorithmIdentifier, + S: AsyncSigner, + S::VerifyingKey: EncodePublicKey, + Signature: SignatureBitStringEncoding + 'static, + { + let signer_info = signer_info_builder + .build_async::(signer) + .await + .map_err(|_| der::Error::from(ErrorKind::Failed))?; + self.signer_infos.push(signer_info); + + Ok(self) + } + + /// Add a signer info. The signature will be calculated. Note that the encapsulated content + /// must not be changed after the first signer info was added. + pub async fn add_signer_info_with_rng_async( + &mut self, + signer_info_builder: SignerInfoBuilder<'_>, + signer: &S, + rng: &mut impl CryptoRngCore, + ) -> Result<&mut Self> + where + S: Keypair + DynSignatureAlgorithmIdentifier, + S: AsyncRandomizedSigner, + S::VerifyingKey: EncodePublicKey, + Signature: SignatureBitStringEncoding + 'static, + { + let signer_info = signer_info_builder + .build_with_rng_async::(signer, rng) + .await + .map_err(|_| der::Error::from(ErrorKind::Failed))?; + self.signer_infos.push(signer_info); + + Ok(self) + } + /// This method returns a `ContentInfo` of type `signedData`. pub fn build(&mut self) -> Result { let digest_algorithms = diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 4b53db315..b126c32e9 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -595,3 +595,75 @@ fn test_create_signing_attribute() { "Invalid tag number in signing time attribute value" ); } + +#[tokio::test] +async fn async_builder() { + // Make some content + let content = EncapsulatedContentInfo { + econtent_type: const_oid::db::rfc5911::ID_DATA, + econtent: Some( + Any::new( + Tag::OctetString, + OctetString::new(vec![48]).unwrap().to_der().unwrap(), + ) + .unwrap(), + ), + }; + // Create multiple signer infos + let signer_1 = rsa_pkcs1v15_signer(); + let digest_algorithm = AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5912::ID_SHA_256, + parameters: None, + }; + let external_message_digest = None; + let signer_info_builder_1 = SignerInfoBuilder::new( + signer_identifier(1), + digest_algorithm.clone(), + &content, + external_message_digest, + ) + .expect("Could not create RSA SignerInfoBuilder"); + + let signer_3 = rsa_pss_signer(); + let digest_algorithm = AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5912::ID_SHA_256, + parameters: None, + }; + let external_message_digest = None; + let signer_info_builder_3 = SignerInfoBuilder::new( + signer_identifier(3), + digest_algorithm.clone(), + &content, + external_message_digest, + ) + .expect("Could not create RSA SignerInfoBuilder"); + + let mut builder = SignedDataBuilder::new(&content); + + let signed_data_pkcs7 = builder + .add_digest_algorithm(digest_algorithm) + .expect("could not add a digest algorithm") + .add_signer_info_async::, rsa::pkcs1v15::Signature>( + signer_info_builder_1, + &signer_1, + ) + .await + .expect("error adding PKCS1v15 RSA signer info") + .add_signer_info_with_rng_async::, pss::Signature>( + signer_info_builder_3, + &signer_3, + &mut OsRng, + ) + .await + .expect("error adding PKCS1v15 RSA signer info") + .build() + .expect("building signed data failed"); + let signed_data_pkcs7_der = signed_data_pkcs7 + .to_der() + .expect("conversion of signed data to DER failed."); + println!( + "{}", + pem_rfc7468::encode_string("PKCS7", LineEnding::LF, &signed_data_pkcs7_der) + .expect("PEM encoding of signed data DER failed") + ); +}