From 6aa7758c61ef6c5d6babb9ae65567b0fd4122935 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Wed, 14 Jun 2023 17:22:02 +0200 Subject: [PATCH 01/22] Implemented `EnvelopedDataBuilder` (untested) --- Cargo.lock | 102 +++++++-- cms/Cargo.toml | 7 +- cms/src/builder.rs | 534 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 622 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d6f2f31b..c321743f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "aes" version = "0.8.2" @@ -13,6 +23,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.0.1" @@ -249,13 +273,17 @@ dependencies = [ "der", "hex-literal", "spki", - "x509-cert 0.2.2", + "x509-cert", ] [[package]] name = "cms" version = "0.2.1" dependencies = [ + "aes", + "aes-gcm", + "cbc", + "cipher", "const-oid 0.9.2", "der", "ecdsa", @@ -269,7 +297,8 @@ dependencies = [ "sha3", "signature", "spki", - "x509-cert 0.2.3", + "x509-cert", + "zeroize", ] [[package]] @@ -342,7 +371,7 @@ dependencies = [ "const-oid 0.9.2", "der", "spki", - "x509-cert 0.2.2", + "x509-cert", ] [[package]] @@ -407,9 +436,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "der" version = "0.7.6" @@ -676,6 +715,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "glob" version = "0.3.1" @@ -700,7 +749,7 @@ dependencies = [ "der", "hex-literal", "spki", - "x509-cert 0.2.2", + "x509-cert", ] [[package]] @@ -948,6 +997,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "p256" version = "0.13.2" @@ -1028,7 +1083,7 @@ dependencies = [ "der", "hex-literal", "spki", - "x509-cert 0.2.2", + "x509-cert", ] [[package]] @@ -1072,6 +1127,18 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1718,6 +1785,16 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "version_check" version = "0.9.4" @@ -2006,19 +2083,6 @@ dependencies = [ "x509-cert-test-support", ] -[[package]] -name = "x509-cert" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "077a1672d8ecfa5c1fe69fa7c5d043962e4e32866d4375495536261c0b4781f5" -dependencies = [ - "const-oid 0.9.2", - "der", - "sha1", - "signature", - "spki", -] - [[package]] name = "x509-cert-test-support" version = "0.1.0" @@ -2036,7 +2100,7 @@ dependencies = [ "der", "hex-literal", "spki", - "x509-cert 0.2.2", + "x509-cert", ] [[package]] diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 3c89de56d..f24a7b84f 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -20,10 +20,15 @@ x509-cert = { version = "0.2.3", default-features = false, features = ["pem"] } const-oid = { version = "0.9", features = ["db"] } # TODO: path = "../const-oid" # optional dependencies +aes = { version = "0.8.2", optional = true} +aes-gcm = { version = "0.10.2", optional = true} +cbc = { version = "0.1.2", optional = true} +cipher = { version = "0.4.4", features = ["alloc", "block-padding"], optional = true} sha1 = { version = "0.10", optional = true} sha2 = { version = "0.10", optional = true} sha3 = { version = "0.10", optional = true} signature = { version = "2.1.0", features = ["digest", "alloc"], optional = true} +zeroize = { version = "1.6.0", optional = true} [dev-dependencies] hex-literal = "0.4" @@ -36,7 +41,7 @@ p256 = "0.13.0" [features] alloc = ["der/alloc"] std = ["der/std", "spki/std"] -builder = ["sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder"] +builder = ["aes", "aes-gcm", "cbc", "cipher", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder"] pem = ["alloc", "der/pem"] [package.metadata.docs.rs] diff --git a/cms/src/builder.rs b/cms/src/builder.rs index 8a03bd9de..fc2521a92 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -4,15 +4,25 @@ use crate::cert::CertificateChoices; use crate::content_info::{CmsVersion, ContentInfo}; +use crate::enveloped_data::{ + EncryptedContentInfo, EncryptedKey, EnvelopedData, KekIdentifier, KeyTransRecipientInfo, + OriginatorIdentifierOrKey, OriginatorInfo, RecipientIdentifier, RecipientInfo, RecipientInfos, + UserKeyingMaterial, +}; use crate::revocation::{RevocationInfoChoice, RevocationInfoChoices}; use crate::signed_data::{ CertificateSet, DigestAlgorithmIdentifiers, EncapsulatedContentInfo, SignatureValue, SignedAttributes, SignedData, SignerIdentifier, SignerInfo, SignerInfos, UnsignedAttributes, }; +use aes::{Aes128, Aes192, Aes256}; use alloc::borrow::ToOwned; use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; +use cipher::block_padding::Pkcs7; +use cipher::crypto_common::rand_core::{CryptoRng, OsRng, RngCore}; +use cipher::{BlockEncryptMut, BlockSizeUser}; +use cipher::{Key, KeyIvInit, KeySizeUser}; use const_oid::ObjectIdentifier; use core::cmp::Ordering; use core::fmt; @@ -27,10 +37,12 @@ use spki::{ AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, EncodePublicKey, SignatureBitStringEncoding, }; +use std::marker::PhantomData; use std::time::SystemTime; use std::vec; -use x509_cert::attr::{Attribute, AttributeValue}; +use x509_cert::attr::{Attribute, AttributeValue, Attributes}; use x509_cert::builder::Builder; +use zeroize::Zeroize; /// Error type #[derive(Debug)] @@ -506,6 +518,448 @@ impl<'s> SignedDataBuilder<'s> { } } +/// This trait must be implemented for the 5 recipient info types defined in RFC 5652 § 6: +pub enum RecipientInfoType { + /// KeyTransRecipientInfo + Ktri, + /// KeyAgreeRecipientInfo + Kari, + /// KekRecipientInfo + Kekri, + /// PasswordRecipientInfo + Pwri, + /// OtherRecipientInfo + Ori, +} + +/// Trait for builders of a `RecipientInfo`. RFC 5652 § 6 defines 5 different `RecipientInfo` +/// formats. All implementations must implement this trait. +pub trait RecipientInfoBuilder { + /// Return the recipient info type + fn recipient_info_type(&self) -> RecipientInfoType; + + /// Return the recipient info version + fn recipient_info_version(&self) -> CmsVersion; + + /// Encrypt the `content_encryption_key` using a method, that is specific for the implementing + /// builder type. Finally return a `RecipientInfo`. + fn build(&self, content_encryption_key: &[u8]) -> Result; +} + +/// Builds a `KeyTransRecipientInfo` according to RFC 5652 § 6. +/// This type uses the recipient's public key to encrypt the content-encryption key. +pub struct KeyTransRecipientInfoBuilder<'k> { + /// Identifies the recipient + pub rid: RecipientIdentifier, + /// Encryption algorithm to be used for key encryption + pub key_enc_alg: AlgorithmIdentifierOwned, + /// Recipient's public, which will be used to encrypt the content-encryption key. + pub recipient_public_key: &'k [u8], +} + +impl<'k> KeyTransRecipientInfoBuilder<'k> { + /// Creates a `KeyTransRecipientInfoBuilder` + pub fn new( + rid: RecipientIdentifier, + key_enc_alg: AlgorithmIdentifierOwned, + recipient_public_key: &'k [u8], + ) -> Result> { + Ok(KeyTransRecipientInfoBuilder { + rid, + key_enc_alg, + recipient_public_key, + }) + } +} + +impl<'k> RecipientInfoBuilder for KeyTransRecipientInfoBuilder<'k> { + fn recipient_info_type(&self) -> RecipientInfoType { + RecipientInfoType::Ktri + } + + fn recipient_info_version(&self) -> CmsVersion { + match self.rid { + RecipientIdentifier::IssuerAndSerialNumber(_) => CmsVersion::V0, + RecipientIdentifier::SubjectKeyIdentifier(_) => CmsVersion::V2, + } + } + + /// Build a `KeyTransRecipientInfo`. See RFC 5652 § 6.2.1 + /// `content_encryption_key` will be encrypted with the recipient's public key. + fn build(&self, content_encryption_key: &[u8]) -> Result { + // Encrypt key + let (encrypted_key, _) = encrypt_data( + content_encryption_key, + &self.key_enc_alg, + Some(&self.recipient_public_key), + &mut OsRng, + )?; + let enc_key = EncryptedKey::new(encrypted_key)?; + + Ok(RecipientInfo::Ktri(KeyTransRecipientInfo { + version: self.recipient_info_version(), + rid: self.rid.clone(), + key_enc_alg: self.key_enc_alg.clone(), + enc_key, + })) + } +} + +/// Builds a `KeyAgreeRecipientInfo` according to RFC 5652 § 6. +/// This type uses key agreement: the recipient's public key and the sender's +/// private key are used to generate a pairwise symmetric key, then +/// the content-encryption key is encrypted in the pairwise symmetric key. +pub struct KeyAgreeRecipientInfoBuilder { + /// A CHOICE with three alternatives specifying the sender's key agreement public key. + pub originator: OriginatorIdentifierOrKey, + /// Optional information which helps generating different keys every time. + pub ukm: Option, + /// Encryption algorithm to be used for key encryption + pub key_enc_alg: AlgorithmIdentifierOwned, +} + +impl KeyAgreeRecipientInfoBuilder { + /// Creates a `KeyAgreeRecipientInfoBuilder` + pub fn new( + originator: OriginatorIdentifierOrKey, + ukm: Option, + key_enc_alg: AlgorithmIdentifierOwned, + ) -> Result { + Ok(KeyAgreeRecipientInfoBuilder { + originator, + ukm, + key_enc_alg, + }) + } +} + +impl RecipientInfoBuilder for KeyAgreeRecipientInfoBuilder { + /// Returns the RecipientInfoType + fn recipient_info_type(&self) -> RecipientInfoType { + RecipientInfoType::Kari + } + + /// Returns the `CMSVersion` for this `RecipientInfo` + fn recipient_info_version(&self) -> CmsVersion { + CmsVersion::V3 + } + + /// Build a `KeyAgreeRecipientInfoBuilder`. See RFC 5652 § 6.2.1 + fn build(&self, _content_encryption_key: &[u8]) -> Result { + Err(Error::Builder(String::from( + "Building KeyAgreeRecipientInfo is not implemented, yet.", + ))) + } +} + +/// Builds a `KekRecipientInfo` according to RFC 5652 § 6. +/// Uses symmetric key-encryption keys: the content-encryption key is +/// encrypted in a previously distributed symmetric key-encryption key. +pub struct KekRecipientInfoBuilder { + /// Specifies a symmetric key-encryption key that was previously distributed to the sender and + /// one or more recipients. + pub kek_id: KekIdentifier, + /// Encryption algorithm to be used for key encryption + pub key_enc_alg: AlgorithmIdentifierOwned, +} + +impl KekRecipientInfoBuilder { + /// Creates a `KekRecipientInfoBuilder` + pub fn new( + kek_id: KekIdentifier, + key_enc_alg: AlgorithmIdentifierOwned, + ) -> Result { + Ok(KekRecipientInfoBuilder { + kek_id, + key_enc_alg, + }) + } +} + +impl RecipientInfoBuilder for KekRecipientInfoBuilder { + /// Returns the RecipientInfoType + fn recipient_info_type(&self) -> RecipientInfoType { + RecipientInfoType::Kekri + } + + /// Returns the `CMSVersion` for this `RecipientInfo` + fn recipient_info_version(&self) -> CmsVersion { + CmsVersion::V4 + } + + /// Build a `KekRecipientInfoBuilder`. See RFC 5652 § 6.2.1 + fn build(&self, _content_encryption_key: &[u8]) -> Result { + Err(Error::Builder(String::from( + "Building KekRecipientInfo is not implemented, yet.", + ))) + } +} + +/// Builds a `PasswordRecipientInfo` according to RFC 5652 § 6. +/// Uses a password or shared secret value to encrypt the content-encryption key. +pub struct PasswordRecipientInfoBuilder { + /// Identifies the key-derivation algorithm, and any associated parameters, used to derive the + /// key-encryption key from the password or shared secret value. If this field is `None`, + /// the key-encryption key is supplied from an external source, for example a hardware crypto + /// token such as a smart card. + pub key_derivation_alg: Option, + /// Encryption algorithm to be used for key encryption + pub key_enc_alg: AlgorithmIdentifierOwned, +} + +impl PasswordRecipientInfoBuilder { + /// Creates a `PasswordRecipientInfoBuilder` + pub fn new( + key_derivation_alg: Option, + key_enc_alg: AlgorithmIdentifierOwned, + ) -> Result { + Ok(PasswordRecipientInfoBuilder { + key_derivation_alg, + key_enc_alg, + }) + } +} + +impl RecipientInfoBuilder for PasswordRecipientInfoBuilder { + /// Returns the RecipientInfoType + fn recipient_info_type(&self) -> RecipientInfoType { + RecipientInfoType::Pwri + } + + /// Returns the `CMSVersion` for this `RecipientInfo` + fn recipient_info_version(&self) -> CmsVersion { + CmsVersion::V0 + } + + /// Build a `PasswordRecipientInfoBuilder`. See RFC 5652 § 6.2.1 + fn build(&self, _content_encryption_key: &[u8]) -> Result { + Err(Error::Builder(String::from( + "Building PasswordRecipientInfo is not implemented, yet.", + ))) + } +} + +/// Builds an `OtherRecipientInfo` according to RFC 5652 § 6. +/// This type makes no assumption about the encryption method or the needed information. +pub struct OtherRecipientInfoBuilder { + /// Identifies the key management technique. + pub ori_type: ObjectIdentifier, + /// Contains the protocol data elements needed by a recipient using the identified key + /// management technique + pub ori_value: Any, +} + +impl OtherRecipientInfoBuilder { + /// Creates a `OtherRecipientInfoBuilder` + pub fn new(ori_type: ObjectIdentifier, ori_value: Any) -> Result { + Ok(OtherRecipientInfoBuilder { + ori_type, + ori_value, + }) + } +} + +impl RecipientInfoBuilder for OtherRecipientInfoBuilder { + /// Returns the RecipientInfoType + fn recipient_info_type(&self) -> RecipientInfoType { + RecipientInfoType::Ori + } + + /// Returns the `CMSVersion` for this `RecipientInfo` + fn recipient_info_version(&self) -> CmsVersion { + // TODO bk + panic!("Ori has no CMSVersion") + } + + /// Build a `OtherRecipientInfoBuilder`. See RFC 5652 § 6.2.1 + fn build(&self, _content_encryption_key: &[u8]) -> Result { + // TODO bk + panic!("Ori has no common build method.") + } +} + +/// Builds CMS `EnvelopedData` according to RFC 5652 § 6. +pub struct EnvelopedDataBuilder<'c, E> +where + E: BlockEncryptMut + KeySizeUser + BlockSizeUser, +{ + originator_info: Option, + recipient_infos: Vec>, + unencrypted_content: &'c [u8], + // TODO bk Not good to offer both, `content_encryptor` and `content_encryption_algorithm`. + // We should + // (1) either derive `content_encryption_algorithm` from `content_encryptor` (but this is not + // yet supported by RustCrypto), + // (2) or pass `content_encryption_algorithm` and create an encryptor for it. + // In the first case, we might need a new trait here, e.g. `DynEncryptionAlgorithmIdentifier` in + // analogy to `DynSignatureAlgorithmIdentifier`. + // Going for (2) + // content_encryptor: E, + content_encryption_algorithm: AlgorithmIdentifierOwned, + unprotected_attributes: Option, + phantom: PhantomData, +} + +impl<'c, E> EnvelopedDataBuilder<'c, E> +where + E: BlockEncryptMut + KeySizeUser + BlockSizeUser, +{ + /// Create a new builder for `EnvelopedData` + pub fn new( + originator_info: Option, + unencrypted_content: &'c [u8], + content_encryption_algorithm: AlgorithmIdentifierOwned, + unprotected_attributes: Option, + ) -> Result> { + Ok(EnvelopedDataBuilder { + originator_info, + recipient_infos: Vec::new(), + unencrypted_content, + content_encryption_algorithm, + unprotected_attributes, + phantom: Default::default(), + }) + } + + /// Add recipient info. A builder is used, which generates a `RecipientInfo` according to + /// RFC 5652 § 6.2, when `EnvelopedData` is built. + pub fn add_recipient_info( + &mut self, + recipient_info_builder: impl RecipientInfoBuilder + 'static, + ) -> Result<&mut Self> { + self.recipient_infos.push(Box::new(recipient_info_builder)); + + Ok(self) + } + + /// Generate an `EnvelopedData` object according to RFC 5652 § 6. + pub fn build(&mut self) -> Result { + // Generate encryption key + // Encrypt content + // Build recipient infos + // Make sure, key is securely destroyed + let (encrypted_content, mut content_encryption_key) = encrypt_data( + &self.unencrypted_content, + &self.content_encryption_algorithm, + None, + &mut OsRng, + )?; + let encrypted_content_octetstring = der::asn1::OctetString::new(encrypted_content)?; + let encrypted_content_info = EncryptedContentInfo { + content_type: const_oid::db::rfc5911::ID_DATA, // TODO bk should this be configurable? + content_enc_alg: self.content_encryption_algorithm.clone(), + encrypted_content: Some(encrypted_content_octetstring), // TODO bk `None` (external content) should also be possible + }; + + let recipient_infos_vec = self + .recipient_infos + .iter() + .map(|ri| Ok(ri.build(&content_encryption_key)?)) + .collect::>>()?; + content_encryption_key.zeroize(); + let recip_infos = RecipientInfos::try_from(recipient_infos_vec).unwrap(); + + Ok(EnvelopedData { + version: self.calculate_version(), + originator_info: self.originator_info.clone(), + recip_infos, + encrypted_content: encrypted_content_info, + unprotected_attrs: self.unprotected_attributes.clone(), + }) + } + + /// Calculate the `CMSVersion` of the `EnvelopedData` according to RFC 5652 § 6.1 + fn calculate_version(&self) -> CmsVersion { + // IF (originatorInfo is present) AND + // ((any certificates with a type of other are present) OR + // (any crls with a type of other are present)) + // THEN version is 4 + // ELSE + // IF ((originatorInfo is present) AND + // (any version 2 attribute certificates are present)) OR + // (any RecipientInfo structures include pwri) OR + // (any RecipientInfo structures include ori) + // THEN version is 3 + // ELSE + // IF (originatorInfo is absent) AND + // (unprotectedAttrs is absent) AND + // (all RecipientInfo structures are version 0) + // THEN version is 0 + // ELSE version is 2 + let originator_info_present = self.originator_info.is_some(); + let other_certificates_present = if let Some(originator_info) = &self.originator_info { + if let Some(certificates) = &originator_info.certs { + certificates + .0 + .iter() + .any(|certificate| matches!(certificate, CertificateChoices::Other(_))) + } else { + false + } + } else { + false + }; + let other_crls_present = if let Some(originator_info) = &self.originator_info { + if let Some(crls) = &originator_info.crls { + crls.0 + .iter() + .any(|crl| matches!(crl, RevocationInfoChoice::Other(_))) + } else { + false + } + } else { + false + }; + // v2 certificates currently not supported + // let v2_certificates_present = if let Some(certificate_option) = &self.originator_info { + // if let Some(certificates) = certificate_option { + // certificates + // .iter() + // .any(|certificate| matches!(certificate, CertificateChoices::V2AttrCert)) + // } else { + // false + // } + // } else { + // false + // }; + let v2_certificates_present = false; + let pwri_recipient_info_present = self.recipient_infos.iter().any(|recipient_info| { + matches!( + recipient_info.recipient_info_type(), + RecipientInfoType::Pwri + ) + }); + let ori_recipient_info_present = self.recipient_infos.iter().any(|recipient_info| { + matches!(recipient_info.recipient_info_type(), RecipientInfoType::Ori) + }); + let unprotected_attributes_present = self.unprotected_attributes.is_some(); + let all_recipient_infos_are_v0 = self + .recipient_infos + .iter() + .all(|ri| ri.recipient_info_version() == CmsVersion::V0); + + if originator_info_present && (other_certificates_present || other_crls_present) { + CmsVersion::V4 + } else { + if (originator_info_present && v2_certificates_present) + || pwri_recipient_info_present + || ori_recipient_info_present + { + CmsVersion::V3 + } else { + if !originator_info_present + && !unprotected_attributes_present + && all_recipient_infos_are_v0 + { + CmsVersion::V0 + } else { + CmsVersion::V2 + } + } + } + } +} + /// Get a hasher for a given digest algorithm fn get_hasher( digest_algorithm_identifier: &AlgorithmIdentifierOwned, @@ -525,6 +979,84 @@ fn get_hasher( } } +/// Helps encrypting. +#[macro_export] +macro_rules! encrypt_block_mode { + ($data:expr, $block_mode:ident::$typ:ident<$alg:ident>, $key:expr, $rng:expr) => {{ + let (key, iv) = match $key { + None => $block_mode::$typ::<$alg>::generate_key_iv($rng), + Some(key) => { + if key.len() != $alg::key_size() { + return Err(Error::Builder(String::from( + "Invalid key size for chosen algorithm", + ))); + } + ( + Key::<$block_mode::$typ<$alg>>::from_slice(key).to_owned(), + $block_mode::$typ::<$alg>::generate_iv($rng), + ) + } + }; + let encryptor = $block_mode::$typ::<$alg>::new(&key.into(), &iv.into()); + Ok(( + encryptor.encrypt_padded_vec_mut::($data), + key.to_vec(), + )) + }}; +} + +// TODO bk this must be refactored so we can select the encryption algorithm +/// Get an encryptor for a given encryption algorithm. Currently, only AES-CBC is supported. +/// If `key` is `Some`, it's length must fit the chosen encryption algorithm. Otherwise the +/// conversion `Key::::from_slice(key)` panics. +/// +/// TODO bk add CFB, CTR, OFB and others? Not GCM, as AEAD is not necessary for CMS? +fn encrypt_data( + data: &[u8], + encryption_algorithm_identifier: &AlgorithmIdentifierOwned, + key: Option<&[u8]>, + rng: &mut R, +) -> Result<(Vec, Vec)> +where + R: CryptoRng + RngCore, +{ + let encryption_algorithm_name = + DB.by_oid(&encryption_algorithm_identifier.oid) + .ok_or(Error::Builder(String::from( + "Encryption algorithm oid not found", + )))?; + match encryption_algorithm_name { + "id-aes128-CBC" => encrypt_block_mode!(data, cbc::Encryptor, key, rng), + "id-aes192-CBC" => encrypt_block_mode!(data, cbc::Encryptor, key, rng), + "id-aes256-CBC" => encrypt_block_mode!(data, cbc::Encryptor, key, rng), + // TODO bk remove following test code + "test-for-debugging" => Ok({ + let (key, iv) = match key { + None => cbc::Encryptor::::generate_key_iv(rng), + Some(key) => { + if key.len() != ::key_size() { + return Err(Error::Builder(String::from( + "Invalid key size for chosen algorithm", + ))); + } + ( + Key::>::from_slice(key).to_owned(), + cbc::Encryptor::::generate_iv(rng), + ) + } + }; + let encryptor = cbc::Encryptor::::new(&key.into(), &iv.into()); + ( + encryptor.encrypt_padded_vec_mut::(data), + key.to_vec(), + ) + }), + _ => Err(Error::Builder(String::from( + "Unsupported encryption algorithm", + ))), + } +} + /// Create a content-type attribute according to /// [RFC 5652 § 11.1](https://datatracker.ietf.org/doc/html/rfc5652#section-11.1) pub fn create_content_type_attribute(content_type: ObjectIdentifier) -> Result { From 10e48580555b2ab0886a1d6bdda98bfbe5e839ab Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Wed, 21 Jun 2023 17:03:49 +0200 Subject: [PATCH 02/22] `KeyTransRecipientInfo` with RSA encryption --- cms/Cargo.toml | 3 +- cms/src/builder.rs | 96 ++++++++++++++++++++++---------------------- cms/tests/builder.rs | 67 ++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 49 deletions(-) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index f24a7b84f..abe4255f0 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -24,6 +24,7 @@ aes = { version = "0.8.2", optional = true} aes-gcm = { version = "0.10.2", optional = true} cbc = { version = "0.1.2", optional = true} cipher = { version = "0.4.4", features = ["alloc", "block-padding"], optional = true} +rsa = { version = "0.9.2", optional = true } sha1 = { version = "0.10", optional = true} sha2 = { version = "0.10", optional = true} sha3 = { version = "0.10", optional = true} @@ -41,7 +42,7 @@ p256 = "0.13.0" [features] alloc = ["der/alloc"] std = ["der/std", "spki/std"] -builder = ["aes", "aes-gcm", "cbc", "cipher", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder"] +builder = ["aes", "aes-gcm", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder"] pem = ["alloc", "der/pem"] [package.metadata.docs.rs] diff --git a/cms/src/builder.rs b/cms/src/builder.rs index fc2521a92..a7a21edb5 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -21,7 +21,7 @@ use alloc::string::String; use alloc::vec::Vec; use cipher::block_padding::Pkcs7; use cipher::crypto_common::rand_core::{CryptoRng, OsRng, RngCore}; -use cipher::{BlockEncryptMut, BlockSizeUser}; +use cipher::BlockEncryptMut; use cipher::{Key, KeyIvInit, KeySizeUser}; use const_oid::ObjectIdentifier; use core::cmp::Ordering; @@ -30,6 +30,7 @@ use der::asn1::{BitString, OctetStringRef, SetOfVec}; use der::oid::db::DB; use der::{Any, AnyRef, DateTime, Decode, Encode, ErrorKind, Tag}; use digest::Digest; +use rsa::Pkcs1v15Encrypt; use sha2::digest; use signature::digest::DynDigest; use signature::{Keypair, Signer}; @@ -37,7 +38,6 @@ use spki::{ AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, EncodePublicKey, SignatureBitStringEncoding, }; -use std::marker::PhantomData; use std::time::SystemTime; use std::vec; use x509_cert::attr::{Attribute, AttributeValue, Attributes}; @@ -518,7 +518,22 @@ impl<'s> SignedDataBuilder<'s> { } } -/// This trait must be implemented for the 5 recipient info types defined in RFC 5652 § 6: +/// Trait for builders of a `RecipientInfo`. RFC 5652 § 6 defines 5 different `RecipientInfo` +/// formats. All implementations must implement this trait. +pub trait RecipientInfoBuilder { + /// Return the recipient info type + fn recipient_info_type(&self) -> RecipientInfoType; + + /// Return the recipient info version + fn recipient_info_version(&self) -> CmsVersion; + + /// Encrypt the `content_encryption_key` using a method, that is specific for the implementing + /// builder type. Finally return a `RecipientInfo`. + fn build(&self, content_encryption_key: &[u8]) -> Result; +} + +/// `RecipientInfoBuilder` must be implemented for these 5 recipient info types +/// as defined in RFC 5652 § 6: pub enum RecipientInfoType { /// KeyTransRecipientInfo Ktri, @@ -532,47 +547,36 @@ pub enum RecipientInfoType { Ori, } -/// Trait for builders of a `RecipientInfo`. RFC 5652 § 6 defines 5 different `RecipientInfo` -/// formats. All implementations must implement this trait. -pub trait RecipientInfoBuilder { - /// Return the recipient info type - fn recipient_info_type(&self) -> RecipientInfoType; - - /// Return the recipient info version - fn recipient_info_version(&self) -> CmsVersion; - - /// Encrypt the `content_encryption_key` using a method, that is specific for the implementing - /// builder type. Finally return a `RecipientInfo`. - fn build(&self, content_encryption_key: &[u8]) -> Result; +/// Contains information required to encrypt the content encryption key with a specific method +pub enum KeyEncryptionInfo { + /// Encrypt key with RSA + Rsa(rsa::RsaPublicKey), + // to be extended here with other asymmetric encryption algorithms } /// Builds a `KeyTransRecipientInfo` according to RFC 5652 § 6. /// This type uses the recipient's public key to encrypt the content-encryption key. -pub struct KeyTransRecipientInfoBuilder<'k> { +pub struct KeyTransRecipientInfoBuilder { /// Identifies the recipient pub rid: RecipientIdentifier, - /// Encryption algorithm to be used for key encryption - pub key_enc_alg: AlgorithmIdentifierOwned, - /// Recipient's public, which will be used to encrypt the content-encryption key. - pub recipient_public_key: &'k [u8], + /// Info for key encryption + pub key_encryption_info: KeyEncryptionInfo, } -impl<'k> KeyTransRecipientInfoBuilder<'k> { +impl KeyTransRecipientInfoBuilder { /// Creates a `KeyTransRecipientInfoBuilder` pub fn new( rid: RecipientIdentifier, - key_enc_alg: AlgorithmIdentifierOwned, - recipient_public_key: &'k [u8], - ) -> Result> { + key_encryption_info: KeyEncryptionInfo, + ) -> Result { Ok(KeyTransRecipientInfoBuilder { rid, - key_enc_alg, - recipient_public_key, + key_encryption_info, }) } } -impl<'k> RecipientInfoBuilder for KeyTransRecipientInfoBuilder<'k> { +impl RecipientInfoBuilder for KeyTransRecipientInfoBuilder { fn recipient_info_type(&self) -> RecipientInfoType { RecipientInfoType::Ktri } @@ -588,18 +592,24 @@ impl<'k> RecipientInfoBuilder for KeyTransRecipientInfoBuilder<'k> { /// `content_encryption_key` will be encrypted with the recipient's public key. fn build(&self, content_encryption_key: &[u8]) -> Result { // Encrypt key - let (encrypted_key, _) = encrypt_data( - content_encryption_key, - &self.key_enc_alg, - Some(&self.recipient_public_key), - &mut OsRng, - )?; + let (encrypted_key, key_enc_alg) = match &self.key_encryption_info { + // RSA encryption + KeyEncryptionInfo::Rsa(recipient_public_key) => ( + recipient_public_key + .encrypt(&mut OsRng, Pkcs1v15Encrypt, content_encryption_key) + .map_err(|_| Error::Builder(String::from("Could not encrypt key")))?, + AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5912::RSA_ENCRYPTION, + parameters: None, + }, + ), + }; let enc_key = EncryptedKey::new(encrypted_key)?; Ok(RecipientInfo::Ktri(KeyTransRecipientInfo { version: self.recipient_info_version(), rid: self.rid.clone(), - key_enc_alg: self.key_enc_alg.clone(), + key_enc_alg, enc_key, })) } @@ -779,10 +789,7 @@ impl RecipientInfoBuilder for OtherRecipientInfoBuilder { } /// Builds CMS `EnvelopedData` according to RFC 5652 § 6. -pub struct EnvelopedDataBuilder<'c, E> -where - E: BlockEncryptMut + KeySizeUser + BlockSizeUser, -{ +pub struct EnvelopedDataBuilder<'c> { originator_info: Option, recipient_infos: Vec>, unencrypted_content: &'c [u8], @@ -797,27 +804,22 @@ where // content_encryptor: E, content_encryption_algorithm: AlgorithmIdentifierOwned, unprotected_attributes: Option, - phantom: PhantomData, } -impl<'c, E> EnvelopedDataBuilder<'c, E> -where - E: BlockEncryptMut + KeySizeUser + BlockSizeUser, -{ +impl<'c> EnvelopedDataBuilder<'c> { /// Create a new builder for `EnvelopedData` pub fn new( originator_info: Option, unencrypted_content: &'c [u8], content_encryption_algorithm: AlgorithmIdentifierOwned, unprotected_attributes: Option, - ) -> Result> { + ) -> Result> { Ok(EnvelopedDataBuilder { originator_info, recipient_infos: Vec::new(), unencrypted_content, content_encryption_algorithm, unprotected_attributes, - phantom: Default::default(), }) } @@ -834,10 +836,10 @@ where /// Generate an `EnvelopedData` object according to RFC 5652 § 6. pub fn build(&mut self) -> Result { - // Generate encryption key + // Generate content encryption key // Encrypt content // Build recipient infos - // Make sure, key is securely destroyed + // Make sure, content encryption key is securely destroyed let (encrypted_content, mut content_encryption_key) = encrypt_data( &self.unencrypted_content, &self.content_encryption_algorithm, diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 3467e5dd4..e07a2c3ef 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -1,7 +1,11 @@ #![cfg(feature = "builder")] -use cms::builder::{create_signing_time_attribute, SignedDataBuilder, SignerInfoBuilder}; +use cms::builder::{ + create_signing_time_attribute, EnvelopedDataBuilder, KeyEncryptionInfo, + KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder, +}; use cms::cert::{CertificateChoices, IssuerAndSerialNumber}; +use cms::enveloped_data::RecipientIdentifier; use cms::signed_data::{EncapsulatedContentInfo, SignerIdentifier}; use der::asn1::{OctetString, SetOfVec, Utf8StringRef}; use der::{Any, DecodePem, Encode, Tag, Tagged}; @@ -9,6 +13,7 @@ use p256::{pkcs8::DecodePrivateKey, NistP256}; use pem_rfc7468::LineEnding; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; +use rsa::{rand_core, RsaPrivateKey, RsaPublicKey}; use sha2::Sha256; use spki::AlgorithmIdentifierOwned; use x509_cert::attr::{Attribute, AttributeTypeAndValue}; @@ -46,6 +51,23 @@ fn signer_identifier(id: i32) -> SignerIdentifier { }) } +fn recipient_identifier(id: i32) -> RecipientIdentifier { + let mut rdn_sequence = RdnSequence::default(); + let rdn = &[AttributeTypeAndValue { + oid: const_oid::db::rfc4519::CN, + value: Any::from(Utf8StringRef::new(&format!("test client {id}")).unwrap()), + }]; + let set_of_vector = SetOfVec::try_from(rdn.to_vec()).unwrap(); + rdn_sequence + .0 + .push(RelativeDistinguishedName::from(set_of_vector)); + RecipientIdentifier::IssuerAndSerialNumber(IssuerAndSerialNumber { + issuer: rdn_sequence, + serial_number: SerialNumber::new(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) + .expect("failed to create a serial number"), + }) +} + #[test] fn test_build_signed_data() { // Make some content @@ -125,6 +147,49 @@ fn test_build_signed_data() { // - enveloped data content // - additional signed attributes +#[test] +fn test_build_enveloped_data() { + let content_encryption_algorithm = AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5911::ID_AES_128_CBC, + parameters: None, + }; + let recipient_identifier = recipient_identifier(1); + let mut rng = rand_core::OsRng; + let bits = 2048; + let recipient_private_key = + RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); + let recipient_public_key = RsaPublicKey::from(&recipient_private_key); + let recipient_info_builder = KeyTransRecipientInfoBuilder::new( + recipient_identifier, + KeyEncryptionInfo::Rsa(recipient_public_key), + ) + .expect("Could not create a KeyTransRecipientInfoBuilder"); + + let mut builder = EnvelopedDataBuilder::new( + None, + "Arbitrary unencrypted content".as_bytes(), + content_encryption_algorithm, + None, + ) + .expect("Could not create an EnvelopedData builder."); + let enveloped_data = builder + .add_recipient_info(recipient_info_builder) + .expect("Could not add a recipient info") + .build() + .expect("Building EnvelopedData failed"); + let enveloped_data_der = enveloped_data + .to_der() + .expect("conversion of enveloped data to DER failed."); + println!( + "{}", + pem_rfc7468::encode_string("ENVELOPEDDATA", LineEnding::LF, &enveloped_data_der) + .expect("PEM encoding of enveloped data DER failed") + ); +} + +#[test] +fn build_pkcs7() {} + #[test] fn test_create_signing_attribute() { let attribute: Attribute = From d1b53413b02d306183a7cb4521a9ca324305b6d3 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 22 Jun 2023 16:38:16 +0200 Subject: [PATCH 03/22] Fixed content-encryption algorithm. --- cms/src/builder.rs | 129 +++++++++++++++++++++++++++++-------------- cms/tests/builder.rs | 11 +--- 2 files changed, 89 insertions(+), 51 deletions(-) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index a7a21edb5..8ca046291 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -28,6 +28,7 @@ use core::cmp::Ordering; use core::fmt; use der::asn1::{BitString, OctetStringRef, SetOfVec}; use der::oid::db::DB; +use der::Tag::OctetString; use der::{Any, AnyRef, DateTime, Decode, Encode, ErrorKind, Tag}; use digest::Digest; use rsa::Pkcs1v15Encrypt; @@ -788,6 +789,27 @@ impl RecipientInfoBuilder for OtherRecipientInfoBuilder { } } +/// Supported content encryption algorithms. +pub enum ContentEncryptionAlgorithm { + /// AES-128 CBC + Aes128Cbc, + /// AES-192 CBC + Aes192Cbc, + /// AES-256 CBC + Aes256Cbc, +} + +impl ContentEncryptionAlgorithm { + /// Return the OID of the algorithm. + pub fn oid(&self) -> ObjectIdentifier { + match self { + ContentEncryptionAlgorithm::Aes128Cbc => const_oid::db::rfc5911::ID_AES_128_CBC, + ContentEncryptionAlgorithm::Aes192Cbc => const_oid::db::rfc5911::ID_AES_192_CBC, + ContentEncryptionAlgorithm::Aes256Cbc => const_oid::db::rfc5911::ID_AES_256_CBC, + } + } +} + /// Builds CMS `EnvelopedData` according to RFC 5652 § 6. pub struct EnvelopedDataBuilder<'c> { originator_info: Option, @@ -802,7 +824,7 @@ pub struct EnvelopedDataBuilder<'c> { // analogy to `DynSignatureAlgorithmIdentifier`. // Going for (2) // content_encryptor: E, - content_encryption_algorithm: AlgorithmIdentifierOwned, + content_encryption_algorithm: ContentEncryptionAlgorithm, unprotected_attributes: Option, } @@ -811,7 +833,7 @@ impl<'c> EnvelopedDataBuilder<'c> { pub fn new( originator_info: Option, unencrypted_content: &'c [u8], - content_encryption_algorithm: AlgorithmIdentifierOwned, + content_encryption_algorithm: ContentEncryptionAlgorithm, unprotected_attributes: Option, ) -> Result> { Ok(EnvelopedDataBuilder { @@ -840,7 +862,7 @@ impl<'c> EnvelopedDataBuilder<'c> { // Encrypt content // Build recipient infos // Make sure, content encryption key is securely destroyed - let (encrypted_content, mut content_encryption_key) = encrypt_data( + let (encrypted_content, mut content_encryption_key, content_enc_alg) = encrypt_data( &self.unencrypted_content, &self.content_encryption_algorithm, None, @@ -849,7 +871,7 @@ impl<'c> EnvelopedDataBuilder<'c> { let encrypted_content_octetstring = der::asn1::OctetString::new(encrypted_content)?; let encrypted_content_info = EncryptedContentInfo { content_type: const_oid::db::rfc5911::ID_DATA, // TODO bk should this be configurable? - content_enc_alg: self.content_encryption_algorithm.clone(), + content_enc_alg, encrypted_content: Some(encrypted_content_octetstring), // TODO bk `None` (external content) should also be possible }; @@ -984,7 +1006,7 @@ fn get_hasher( /// Helps encrypting. #[macro_export] macro_rules! encrypt_block_mode { - ($data:expr, $block_mode:ident::$typ:ident<$alg:ident>, $key:expr, $rng:expr) => {{ + ($data:expr, $block_mode:ident::$typ:ident<$alg:ident>, $key:expr, $iv:expr, $rng:expr, $oid:expr) => {{ let (key, iv) = match $key { None => $block_mode::$typ::<$alg>::generate_key_iv($rng), Some(key) => { @@ -1003,6 +1025,10 @@ macro_rules! encrypt_block_mode { Ok(( encryptor.encrypt_padded_vec_mut::($data), key.to_vec(), + AlgorithmIdentifierOwned { + oid: $oid, + parameters: Some(Any::new(OctetString, iv.to_vec())?), + }, )) }}; } @@ -1011,51 +1037,70 @@ macro_rules! encrypt_block_mode { /// Get an encryptor for a given encryption algorithm. Currently, only AES-CBC is supported. /// If `key` is `Some`, it's length must fit the chosen encryption algorithm. Otherwise the /// conversion `Key::::from_slice(key)` panics. +/// Returns encrypted content, content-encryption key and the used algorithm identifier (including +/// the used algorithm parameters). /// -/// TODO bk add CFB, CTR, OFB and others? Not GCM, as AEAD is not necessary for CMS? +/// TODO bk which encryption algorithms shall also be supported? fn encrypt_data( data: &[u8], - encryption_algorithm_identifier: &AlgorithmIdentifierOwned, + encryption_algorithm_identifier: &ContentEncryptionAlgorithm, key: Option<&[u8]>, rng: &mut R, -) -> Result<(Vec, Vec)> +) -> Result<(Vec, Vec, AlgorithmIdentifierOwned)> where R: CryptoRng + RngCore, { - let encryption_algorithm_name = - DB.by_oid(&encryption_algorithm_identifier.oid) - .ok_or(Error::Builder(String::from( - "Encryption algorithm oid not found", - )))?; - match encryption_algorithm_name { - "id-aes128-CBC" => encrypt_block_mode!(data, cbc::Encryptor, key, rng), - "id-aes192-CBC" => encrypt_block_mode!(data, cbc::Encryptor, key, rng), - "id-aes256-CBC" => encrypt_block_mode!(data, cbc::Encryptor, key, rng), + match encryption_algorithm_identifier { + ContentEncryptionAlgorithm::Aes128Cbc => encrypt_block_mode!( + data, + cbc::Encryptor, + key, + iv, + rng, + encryption_algorithm_identifier.oid() + ), + ContentEncryptionAlgorithm::Aes192Cbc => encrypt_block_mode!( + data, + cbc::Encryptor, + key, + iv, + rng, + encryption_algorithm_identifier.oid() + ), + ContentEncryptionAlgorithm::Aes256Cbc => encrypt_block_mode!( + data, + cbc::Encryptor, + key, + iv, + rng, + encryption_algorithm_identifier.oid() + ), // TODO bk remove following test code - "test-for-debugging" => Ok({ - let (key, iv) = match key { - None => cbc::Encryptor::::generate_key_iv(rng), - Some(key) => { - if key.len() != ::key_size() { - return Err(Error::Builder(String::from( - "Invalid key size for chosen algorithm", - ))); - } - ( - Key::>::from_slice(key).to_owned(), - cbc::Encryptor::::generate_iv(rng), - ) - } - }; - let encryptor = cbc::Encryptor::::new(&key.into(), &iv.into()); - ( - encryptor.encrypt_padded_vec_mut::(data), - key.to_vec(), - ) - }), - _ => Err(Error::Builder(String::from( - "Unsupported encryption algorithm", - ))), + // _ => Ok({ + // let (key, iv) = match key { + // None => cbc::Encryptor::::generate_key_iv(rng), + // Some(key) => { + // if key.len() != ::key_size() { + // return Err(Error::Builder(String::from( + // "Invalid key size for chosen algorithm", + // ))); + // } + // ( + // Key::>::from_slice(key).to_owned(), + // cbc::Encryptor::::generate_iv(rng), + // ) + // } + // }; + // let encryptor = cbc::Encryptor::::new(&key.into(), &iv.into()); + // ( + // encryptor.encrypt_padded_vec_mut::(data), + // key.to_vec(), + // AlgorithmIdentifierOwned { + // oid: encryption_algorithm_identifier.oid(), + // parameters: Some(Any::new(OctetString, iv.to_vec())?), + // }, + // ) + // }), } } @@ -1078,7 +1123,7 @@ pub fn create_content_type_attribute(content_type: ObjectIdentifier) -> Result Result { let message_digest_der = OctetStringRef::new(message_digest)?; let message_digest_attribute_value = - AttributeValue::new(Tag::OctetString, message_digest_der.as_bytes())?; + AttributeValue::new(OctetString, message_digest_der.as_bytes())?; let mut values = SetOfVec::new(); values.insert(message_digest_attribute_value)?; let attribute = Attribute { diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index e07a2c3ef..564e54fc4 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -1,9 +1,6 @@ #![cfg(feature = "builder")] -use cms::builder::{ - create_signing_time_attribute, EnvelopedDataBuilder, KeyEncryptionInfo, - KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder, -}; +use cms::builder::{ContentEncryptionAlgorithm, create_signing_time_attribute, EnvelopedDataBuilder, KeyEncryptionInfo, KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder}; use cms::cert::{CertificateChoices, IssuerAndSerialNumber}; use cms::enveloped_data::RecipientIdentifier; use cms::signed_data::{EncapsulatedContentInfo, SignerIdentifier}; @@ -149,10 +146,6 @@ fn test_build_signed_data() { #[test] fn test_build_enveloped_data() { - let content_encryption_algorithm = AlgorithmIdentifierOwned { - oid: const_oid::db::rfc5911::ID_AES_128_CBC, - parameters: None, - }; let recipient_identifier = recipient_identifier(1); let mut rng = rand_core::OsRng; let bits = 2048; @@ -168,7 +161,7 @@ fn test_build_enveloped_data() { let mut builder = EnvelopedDataBuilder::new( None, "Arbitrary unencrypted content".as_bytes(), - content_encryption_algorithm, + ContentEncryptionAlgorithm::Aes128Cbc, None, ) .expect("Could not create an EnvelopedData builder."); From 9bbb93693faae412c8db36d504505b10631f2c1c Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 23 Jun 2023 16:58:05 +0200 Subject: [PATCH 04/22] Test and example for `EnvelopedData` (SCEP PKCSReq). --- cms/tests/builder.rs | 203 +++++++++++++++++- .../examples/sceptest_cert-selfsigned.pem | 27 +++ cms/tests/examples/sceptest_csr.der | Bin 0 -> 933 bytes cms/tests/examples/sceptest_key.pem | 28 +++ 4 files changed, 251 insertions(+), 7 deletions(-) create mode 100755 cms/tests/examples/sceptest_cert-selfsigned.pem create mode 100755 cms/tests/examples/sceptest_csr.der create mode 100755 cms/tests/examples/sceptest_key.pem diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 564e54fc4..e0a15aa00 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -1,22 +1,35 @@ #![cfg(feature = "builder")] -use cms::builder::{ContentEncryptionAlgorithm, create_signing_time_attribute, EnvelopedDataBuilder, KeyEncryptionInfo, KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder}; +use cms::builder::{ + create_signing_time_attribute, ContentEncryptionAlgorithm, EnvelopedDataBuilder, + KeyEncryptionInfo, KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder, +}; use cms::cert::{CertificateChoices, IssuerAndSerialNumber}; use cms::enveloped_data::RecipientIdentifier; use cms::signed_data::{EncapsulatedContentInfo, SignerIdentifier}; -use der::asn1::{OctetString, SetOfVec, Utf8StringRef}; -use der::{Any, DecodePem, Encode, Tag, Tagged}; +use const_oid::ObjectIdentifier; +use der::asn1::{Int, OctetString, PrintableString, SetOfVec, Utf8StringRef}; +use der::{Any, AnyRef, DecodePem, Encode, Tag, Tagged}; use p256::{pkcs8::DecodePrivateKey, NistP256}; use pem_rfc7468::LineEnding; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; use rsa::{rand_core, RsaPrivateKey, RsaPublicKey}; use sha2::Sha256; +use cms::content_info::ContentInfo; use spki::AlgorithmIdentifierOwned; -use x509_cert::attr::{Attribute, AttributeTypeAndValue}; +use x509_cert::attr::{Attribute, AttributeTypeAndValue, AttributeValue}; use x509_cert::name::{RdnSequence, RelativeDistinguishedName}; use x509_cert::serial_number::SerialNumber; +// TODO bk replace this by const_oid definitions as soon as merged +const RFC8894_ID_MESSAGE_TYPE: ObjectIdentifier = + ObjectIdentifier::new_unwrap("2.16.840.1.113733.1.9.2"); +const RFC8894_ID_SENDER_NONCE: ObjectIdentifier = + ObjectIdentifier::new_unwrap("2.16.840.1.113733.1.9.5"); +const RFC8894_ID_TRANSACTION_ID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("2.16.840.1.113733.1.9.7"); + const RSA_2048_PRIV_DER_EXAMPLE: &[u8] = include_bytes!("examples/rsa2048-priv.der"); const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("examples/p256-priv.der"); @@ -124,7 +137,7 @@ fn test_build_signed_data() { .add_signer_info::, p256::ecdsa::DerSignature>( signer_info_builder_2, ) - .expect("error adding RSA signer info") + .expect("error adding P256 signer info") .build() .expect("building signed data failed"); let signed_data_pkcs7_der = signed_data_pkcs7 @@ -141,7 +154,6 @@ fn test_build_signed_data() { // - external message // - PKCS #7 message: // - different encapsulated content ASN.1 encoding -// - enveloped data content // - additional signed attributes #[test] @@ -181,7 +193,184 @@ fn test_build_enveloped_data() { } #[test] -fn build_pkcs7() {} +fn build_pkcs7_scep_pkcsreq() { + // This test demonstrates how to build a PKCS7 message for the SCEP PKCSReq pkiMessage + // according to RFC 8894. + // We use the key transport mechanism in this example, which means, we have the recipient + // public (RSA) key. + // Prerequisites are + // - the recipients public RSA key, + // - an RSA key pair of the sender and + // - a CSR (PKCS #10) signed with the sender's key + // A CMS `SignedData` message is roughly structured as follows: + // ContentInfo + // SignedData + // version + // digestAlgorithms* + // encapContentInfo + // ContentInfo + // EnvelopedData + // version + // [originatorInfo] + // recipientInfos* + // e.g. KeyTransRecipientInfo + // version + // rid + // keyEncryptionAlgorithm + // encryptedKey + // encryptedContentInfo + // contentType + // contentEncryptionAlgorithm + // [encryptedContent] + // [unprotectedAttrs*] + // [certificates*] + // [crls*] + // signerInfos + // version + // sid + // digestAlgorithm + // [signedAttrs*] + // signatureAlgorithm + // signature + // [unsignedAttrs*] + // Reduced to the nested structures: + // ContentInfo + // SignedData + // encapContentInfo + // ContentInfo + // EnvelopedData + // encryptedContentInfo + // 4 builders are involved in the procedure: + // - `SignedDataBuilder` + // - `SignerInfoBuilder` + // - `EnvelopedDataBuilder` + // - `RecipientInfoBuilder` (trait) + // - `KeyTransRecipientInfoBuilder` (implementation used here) + // The procedure can be broken down to 4 steps: + // - Wrap CSR in `EnvelopedData`. + // - Add recipient information to `Enveloped data`. + // - Wrap enveloped data in `SignedData` + // - Sign with sender's RSA key. + + // Create recipient info + let recipient_identifier = recipient_identifier(1); + let recipient_private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); + let recipient_public_key = RsaPublicKey::from(&recipient_private_key); + + let recipient_info_builder = KeyTransRecipientInfoBuilder::new( + recipient_identifier, + KeyEncryptionInfo::Rsa(recipient_public_key), + ) + .unwrap(); + + // Build `EnvelopedData` + let csr_der = include_bytes!("examples/sceptest_csr.der"); // The CSR to be signed + let mut enveloped_data_builder = EnvelopedDataBuilder::new( + None, + csr_der, // data to be encrypted... + ContentEncryptionAlgorithm::Aes128Cbc, // ... with this algorithm + None, + ) + .unwrap(); + + // Add recipient info. Multiple recipients are possible, but not used here. + let enveloped_data = enveloped_data_builder + .add_recipient_info(recipient_info_builder) + .unwrap() + .build() + .unwrap(); + + let enveloped_data_der = enveloped_data.to_der().unwrap(); + let content = AnyRef::try_from(enveloped_data_der.as_slice()).unwrap(); + let content_info = ContentInfo { + content_type: const_oid::db::rfc5911::ID_ENVELOPED_DATA, + content: Any::from(content), + }; + + // Encapsulate the `EnvelopedData` + let content_info_der = content_info.to_der().unwrap(); + let content = EncapsulatedContentInfo { + econtent_type: const_oid::db::rfc5911::ID_DATA, + econtent: Some(Any::new(Tag::OctetString, content_info_der).unwrap()), + }; + + // Create a signer info. Multiple signers are possible, but not used here. + let signer = { + let sender_rsa_key_pem = include_str!("examples/sceptest_key.pem"); + let sender_rsa_key = RsaPrivateKey::from_pkcs8_pem(sender_rsa_key_pem).unwrap(); + SigningKey::::new(sender_rsa_key) + }; + let digest_algorithm = AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5912::ID_SHA_256, + parameters: None, + }; + let mut signer_info_builder = SignerInfoBuilder::new( + &signer, + signer_identifier(1), + digest_algorithm.clone(), + &content, + None, + ) + .unwrap(); + + // For a SCEP pkiMessage, we need to add signed the following attributes: + // - messageType + // - senderNonce + // - transactionID + let mut message_type_value: SetOfVec = Default::default(); + let pkcsreq = 19_i8; // Numerical value of PKCSReq messageType + // TODO bk: is the correct way to create an `Int` from an `i8`? + let pkcsreq_bytes = pkcsreq.to_be_bytes(); + let pkcsreq_as_int = Int::new(&pkcsreq_bytes).unwrap(); + message_type_value.insert(Any::new(Tag::Integer, pkcsreq_as_int.as_bytes()).unwrap()).unwrap(); + let message_type = Attribute { + oid: RFC8894_ID_MESSAGE_TYPE, + values: message_type_value, + }; + let mut sender_nonce_value: SetOfVec = Default::default(); + let nonce = OctetString::new(*&[42; 32]).unwrap(); + sender_nonce_value.insert(Any::new(Tag::OctetString, nonce.as_bytes()).unwrap()).unwrap(); + let sender_nonce = Attribute { + oid: RFC8894_ID_SENDER_NONCE, + values: sender_nonce_value, + }; + let mut transaction_id_value: SetOfVec = Default::default(); + let id = PrintableString::try_from(String::from("Test Transaction ID")).unwrap(); + transaction_id_value.insert(Any::from(&id)).unwrap(); + let transaction_id = Attribute { + oid: RFC8894_ID_TRANSACTION_ID, + values: transaction_id_value, + }; + + signer_info_builder.add_signed_attribute(message_type).unwrap(); + signer_info_builder.add_signed_attribute(sender_nonce).unwrap(); + signer_info_builder.add_signed_attribute(transaction_id).unwrap(); + + let certificate_buf = include_bytes!("examples/sceptest_cert-selfsigned.pem"); + let certificate = x509_cert::Certificate::from_pem(certificate_buf).unwrap(); + + let mut builder = SignedDataBuilder::new(&content); + + let signed_data_pkcs7 = builder + .add_digest_algorithm(digest_algorithm) + .unwrap() + .add_certificate(CertificateChoices::Certificate(certificate)) + .unwrap() + .add_signer_info::, rsa::pkcs1v15::Signature>(signer_info_builder) + .unwrap() + .build() + .unwrap(); + let signed_data_pkcs7_der = signed_data_pkcs7.to_der().unwrap(); + println!( + "{}", + pem_rfc7468::encode_string("PKCS7", LineEnding::LF, &signed_data_pkcs7_der).unwrap() + ); + + // TODO bk + // Check signature + // Decode Message including decrypted enveloped content + // Check CSR +} #[test] fn test_create_signing_attribute() { diff --git a/cms/tests/examples/sceptest_cert-selfsigned.pem b/cms/tests/examples/sceptest_cert-selfsigned.pem new file mode 100755 index 000000000..584f02286 --- /dev/null +++ b/cms/tests/examples/sceptest_cert-selfsigned.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEfjCCA2agAwIBAgIUA8L9xXyJY/x08+LY54vPBfGUGygwDQYJKoZIhvcNAQEL +BQAwgcgxFDASBgNVBAMMC0NvbW1vbiBOYW1lMQswCQYDVQQGEwJERTEfMB0GA1UE +CAwWVGVzdCBTdGF0ZSBvciBQcm92aW5jZTEWMBQGA1UEBwwNVGVzdCBMb2NhbGl0 +eTEfMB0GA1UECgwWVGVzdCBPcmdhbml6YXRpb24gTmFtZTEmMCQGA1UECwwdVGVz +dCBPcmdhbml6YXRpb25hbCBVbml0IE5hbWUxITAfBgkqhkiG9w0BCQEWEnRlc3RA +ZW1haWwuYWRkcmVzczAeFw0yMzA2MjMxMzUxMTBaFw0yNDA2MjIxMzUxMTBaMIHI +MRQwEgYDVQQDDAtDb21tb24gTmFtZTELMAkGA1UEBhMCREUxHzAdBgNVBAgMFlRl +c3QgU3RhdGUgb3IgUHJvdmluY2UxFjAUBgNVBAcMDVRlc3QgTG9jYWxpdHkxHzAd +BgNVBAoMFlRlc3QgT3JnYW5pemF0aW9uIE5hbWUxJjAkBgNVBAsMHVRlc3QgT3Jn +YW5pemF0aW9uYWwgVW5pdCBOYW1lMSEwHwYJKoZIhvcNAQkBFhJ0ZXN0QGVtYWls +LmFkZHJlc3MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdnH2Fsby+ +YBJGIRUgK3iMkbTyoJkJV4KjlMQTZlRm5YnO3buP5Ewa3I07vypsSR+GucAwiSnD +yg+kqkSS1AmrhQyBGWhHdt49yFwEYQXFSofe8sewcPbPv5YnueTzuFvs86r9LBS5 +2CVFJ9xsgBE9VANCBFxiePYs1nLvSztlSyqMDyl1+XstdXx47XxQjiJwzN5z5dVD +47WrTHI1YDQ8+ptMz1tqbTfmQs1f0XZ//X4tkkmpVKMt2xxIqa5v45JTrfH/UHyR +oK5kmGjtdJn15ELooDRBK87XKuVOElAbvWYAVR1eSfnTMRayslt0riJwzlhUkPWR +x3+1gEGbO3otAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr +BgEFBQcDATAdBgNVHQ4EFgQUTxTNwOCSbXT5SlCu9SVPkJe95jcwFgYDVR0RBA8w +DYILZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAJFEScTz6RrcYxYehF6T +ig47n47nR+zCUevaxX6jS8vGQTf5Y0Aw037qoMIycWsqYooDGIHrB2frkON1EfJx +vJQQFeNF5XCSF6hVdZrcCEGfqZ9OKV34Z+Xq341iZeDDJij/IT153EtjouQGHNEK +8kl1idl42b1RiSXpP6R+n8Dsf0gGts4rihdj3b54udtaN0GE9rIyr5JaXlrflXxj +KhHQl2MiTsrDOqjpUU5t2LO/aoz88BqzhPJyAg5W1X5WrUYRKNMK/hXnOSLKX+6g +whtkXkuN3A3b/YbceprupuriF5elwe7NHwe8HlDdh3ps6sBrXGH2rg+UcaxGjiQt +6PQ= +-----END CERTIFICATE----- diff --git a/cms/tests/examples/sceptest_csr.der b/cms/tests/examples/sceptest_csr.der new file mode 100755 index 0000000000000000000000000000000000000000..5289dc26aeb9a867be83aa9ff95988414c7236fc GIT binary patch literal 933 zcmXqLVqR#_#MH^e$Y9WT!cfFOh>bavg_(!jIX^cyKTp9gF*nta+kg`!$|lU@;%X>w zAPeGf@Q8(^7MCalmn4>?D&!X_1Qg|$W#%QP8j2Z+fE2Lv@PZZiTQWR_IIG;%>T z`WK}q=4DnTmSn=LQ8Q2hY2@aS1#3iBkeH(onwMDuagL&aJR7H0n@8JsUPewvF`*Km zI~-DT6Ek!45>rx&Qj3cXni!P~c;Si|8Ce;an;7{SfZ|+CO^l2TbLZ5yZrrmkLC8%} zR6)C^^MFC8=HXNPOIEo| zy281-m8VfM!@caD?THwcMAoBT?e{(%-%#-F{Qha`JD+^s5&h=#s=qoSJ8!7Es^7_J z5VQ?pc4CQ1s`#dJt?0eCb*i^k55H#V&uZP$nu@nI0ewmZXYLh0z3TjU>uR4O(*zTn zU$cGAM`z`lKXW=8f3d9oZ=LQW&y^vIb#KdftX!A>cvA4%kN*Q|CN5Z)G9%+{$;_`$ zoL($2anwF{UF)fzP=NH_G=@;wIM1J#4aGKXiY{5FRB$dLWWv{p$LqH?IL@}N(q&?1 zWMEv-ILSa69!Q+*hGIM-jta>ci8(o`dFiPN1&PJQ<@rS^2H6Nje1>rbF$R3Vc#`F3 zWc<&8}K%9r&c897UZPrCFkcNM;kXd+87xk^*8?9ne#Ec`^uf>1sZab z9Zw#s+?o<_mL=Nw<(H00&Pg2RpOpT8O`Nc0s-e$G|Jzfo+^?=>W?HwRlP5@Q%9omF zm%j;!xR^5E)1A3eMdJGoO_p6PZIxT@{nC+$JG#SqvIFh9{Mu~GoXy_qD>#Ok_&YNHC8-+G!F|Cr(-?(Mdkplo+k!aum literal 0 HcmV?d00001 diff --git a/cms/tests/examples/sceptest_key.pem b/cms/tests/examples/sceptest_key.pem new file mode 100755 index 000000000..69ea3681b --- /dev/null +++ b/cms/tests/examples/sceptest_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdnH2Fsby+YBJG +IRUgK3iMkbTyoJkJV4KjlMQTZlRm5YnO3buP5Ewa3I07vypsSR+GucAwiSnDyg+k +qkSS1AmrhQyBGWhHdt49yFwEYQXFSofe8sewcPbPv5YnueTzuFvs86r9LBS52CVF +J9xsgBE9VANCBFxiePYs1nLvSztlSyqMDyl1+XstdXx47XxQjiJwzN5z5dVD47Wr +THI1YDQ8+ptMz1tqbTfmQs1f0XZ//X4tkkmpVKMt2xxIqa5v45JTrfH/UHyRoK5k +mGjtdJn15ELooDRBK87XKuVOElAbvWYAVR1eSfnTMRayslt0riJwzlhUkPWRx3+1 +gEGbO3otAgMBAAECggEADvZTrjT0y4fYREMQu9iUnZeZe20Gii3+D2RohsTwn0IM +JpDDJQJRvT0RxUm9D8GTVhldJt5mkhklCgdF8kBh2KANU1YjoaElsOzL23iQcS3F +n0Mh9NyMeaVg7k2F8CMgyupI4bblEs1zoFCL6trviAhpNMOwN4LvL8t95ryYG2H0 +j4tawzKh2Mqsi1kp0+wTHasQ1YDewto0+m4mpM2/ztpjrzhBPN9U6gsLn8LpIPH7 +dwb5bUuE5rm7NpG2eCiQ40sicJsth/asjzBYjagDYSkrLAXDvWvvvfbTsKCiaLEj +t6iqETwCGR4bFBBkbXkkCzmTH9F00wOogEzQxseZWwKBgQC3Jlud0lBJpUQgfO/P +JlpIJYF4NyQ+4FrT8CUBwTgyHLZb167KU7LtCY63q++EjgiKU73VTHegZNIiMi9z +HOZjwgWEprzhxoYGudq9psyxvjNdp7FIBLgzzHPLWsRdKkrJzVIlVegV5PXtZC77 +7TRCGcno8OpN4LG1IiicTkmdvwKBgQDcTZzm0f9a06gePcxKwbuTnPHM4JO89J9g +O9vJSxhopCL6fVayvgf3pWxbicQ9tDCO/2ZyvzVYxhZqBH++ckfClTIY9n8Q+/Ab +e4xnxfF/h+LAtOkW8JFuUYcI2v5TqvW10mwiBy+dZfufEHF0JlLeH5tuvD0QebN9 +Lbca5YZ7EwKBgFK3qUMjPI22bYl6w9g8CyRwhAPma6FWNM9ps10Shi3j19ytEc1h +dfsmiOWdasTkXSkUXzVZnuG7B0jYf6Ou1sMRWuqpX79cqSWahReIoQRZ0dsnpKLR +Ntx2J4odiXhGZJa1+7bPEM3qpcO8rperbyG3ggCJ5lib9cbIEa1eklMDAoGAdwmp +Aj/uTtGXQd/6h1pvVK+1KBMhQTSc+Y7ej8H4CnLGQ7t+IU71VycXic7DLuQyaNIw +NUiENteyPM75h5qQk9+yFjL7Ld40O2Vi1J/sghCWwbH/UNnke0uqP2q1idgStJBi +xXBEljQI5kYoR659kHBbuFYWWNSp27Xb/riPFekCgYBq/3D7D5Ly5HH6tFADJaNj +WH1UlbiByoBunRKLE5O8CyxDyU+7QsFw0unPxhewY5maCu/x+a8PXwXW0aTfmBf+ +PBX+3BJ5p+5CCj6Po7kpM2tBR9cwZyQ9bE4IF3AjJeGauCD4g1d/BOatAZsK2hlW +jQWfShnP6KqzjFiIymmjBQ== +-----END PRIVATE KEY----- From d335529e7566d57a709d12e847df4b9e5a8405ba Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 23 Jun 2023 17:29:05 +0200 Subject: [PATCH 05/22] Fixed clippy and fmt --- Cargo.lock | 77 ++++++++++++++++++++++++++++++++++++++++++++ cms/src/builder.rs | 30 ++++++++--------- cms/tests/builder.rs | 27 +++++++++++----- 3 files changed, 109 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 218d46ab1..33ba33b89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "aes" version = "0.8.3" @@ -13,6 +23,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.0.2" @@ -256,6 +280,10 @@ dependencies = [ name = "cms" version = "0.2.1" dependencies = [ + "aes", + "aes-gcm", + "cbc", + "cipher", "const-oid 0.9.2", "der", "ecdsa", @@ -270,6 +298,7 @@ dependencies = [ "signature", "spki", "x509-cert", + "zeroize", ] [[package]] @@ -407,9 +436,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "der" version = "0.7.6" @@ -676,6 +715,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "glob" version = "0.3.1" @@ -948,6 +997,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "p256" version = "0.13.2" @@ -1072,6 +1127,18 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1719,6 +1786,16 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/cms/src/builder.rs b/cms/src/builder.rs index 8ca046291..32e05f497 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -863,7 +863,7 @@ impl<'c> EnvelopedDataBuilder<'c> { // Build recipient infos // Make sure, content encryption key is securely destroyed let (encrypted_content, mut content_encryption_key, content_enc_alg) = encrypt_data( - &self.unencrypted_content, + self.unencrypted_content, &self.content_encryption_algorithm, None, &mut OsRng, @@ -878,7 +878,7 @@ impl<'c> EnvelopedDataBuilder<'c> { let recipient_infos_vec = self .recipient_infos .iter() - .map(|ri| Ok(ri.build(&content_encryption_key)?)) + .map(|ri| ri.build(&content_encryption_key)) .collect::>>()?; content_encryption_key.zeroize(); let recip_infos = RecipientInfos::try_from(recipient_infos_vec).unwrap(); @@ -964,22 +964,18 @@ impl<'c> EnvelopedDataBuilder<'c> { if originator_info_present && (other_certificates_present || other_crls_present) { CmsVersion::V4 + } else if (originator_info_present && v2_certificates_present) + || pwri_recipient_info_present + || ori_recipient_info_present + { + CmsVersion::V3 + } else if !originator_info_present + && !unprotected_attributes_present + && all_recipient_infos_are_v0 + { + CmsVersion::V0 } else { - if (originator_info_present && v2_certificates_present) - || pwri_recipient_info_present - || ori_recipient_info_present - { - CmsVersion::V3 - } else { - if !originator_info_present - && !unprotected_attributes_present - && all_recipient_infos_are_v0 - { - CmsVersion::V0 - } else { - CmsVersion::V2 - } - } + CmsVersion::V2 } } } diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index e0a15aa00..75273434c 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -5,6 +5,7 @@ use cms::builder::{ KeyEncryptionInfo, KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder, }; use cms::cert::{CertificateChoices, IssuerAndSerialNumber}; +use cms::content_info::ContentInfo; use cms::enveloped_data::RecipientIdentifier; use cms::signed_data::{EncapsulatedContentInfo, SignerIdentifier}; use const_oid::ObjectIdentifier; @@ -16,7 +17,6 @@ use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; use rsa::{rand_core, RsaPrivateKey, RsaPublicKey}; use sha2::Sha256; -use cms::content_info::ContentInfo; use spki::AlgorithmIdentifierOwned; use x509_cert::attr::{Attribute, AttributeTypeAndValue, AttributeValue}; use x509_cert::name::{RdnSequence, RelativeDistinguishedName}; @@ -254,7 +254,8 @@ fn build_pkcs7_scep_pkcsreq() { // Create recipient info let recipient_identifier = recipient_identifier(1); - let recipient_private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); + let recipient_private_key = + rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); let recipient_public_key = RsaPublicKey::from(&recipient_private_key); let recipient_info_builder = KeyTransRecipientInfoBuilder::new( @@ -319,17 +320,21 @@ fn build_pkcs7_scep_pkcsreq() { // - transactionID let mut message_type_value: SetOfVec = Default::default(); let pkcsreq = 19_i8; // Numerical value of PKCSReq messageType - // TODO bk: is the correct way to create an `Int` from an `i8`? + // TODO bk: is the correct way to create an `Int` from an `i8`? let pkcsreq_bytes = pkcsreq.to_be_bytes(); let pkcsreq_as_int = Int::new(&pkcsreq_bytes).unwrap(); - message_type_value.insert(Any::new(Tag::Integer, pkcsreq_as_int.as_bytes()).unwrap()).unwrap(); + message_type_value + .insert(Any::new(Tag::Integer, pkcsreq_as_int.as_bytes()).unwrap()) + .unwrap(); let message_type = Attribute { oid: RFC8894_ID_MESSAGE_TYPE, values: message_type_value, }; let mut sender_nonce_value: SetOfVec = Default::default(); let nonce = OctetString::new(*&[42; 32]).unwrap(); - sender_nonce_value.insert(Any::new(Tag::OctetString, nonce.as_bytes()).unwrap()).unwrap(); + sender_nonce_value + .insert(Any::new(Tag::OctetString, nonce.as_bytes()).unwrap()) + .unwrap(); let sender_nonce = Attribute { oid: RFC8894_ID_SENDER_NONCE, values: sender_nonce_value, @@ -342,9 +347,15 @@ fn build_pkcs7_scep_pkcsreq() { values: transaction_id_value, }; - signer_info_builder.add_signed_attribute(message_type).unwrap(); - signer_info_builder.add_signed_attribute(sender_nonce).unwrap(); - signer_info_builder.add_signed_attribute(transaction_id).unwrap(); + signer_info_builder + .add_signed_attribute(message_type) + .unwrap(); + signer_info_builder + .add_signed_attribute(sender_nonce) + .unwrap(); + signer_info_builder + .add_signed_attribute(transaction_id) + .unwrap(); let certificate_buf = include_bytes!("examples/sceptest_cert-selfsigned.pem"); let certificate = x509_cert::Certificate::from_pem(certificate_buf).unwrap(); From a602f0441305c168a59894d56899e9daadefbc6d Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 23 Jun 2023 23:52:15 +0200 Subject: [PATCH 06/22] Fixed minimal-versions test --- cms/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index abe4255f0..6538ea8d4 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -42,7 +42,7 @@ p256 = "0.13.0" [features] alloc = ["der/alloc"] std = ["der/std", "spki/std"] -builder = ["aes", "aes-gcm", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder"] +builder = ["aes", "aes-gcm", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder", "zeroize"] pem = ["alloc", "der/pem"] [package.metadata.docs.rs] From b43362fe5a9d548a97cfe528e1dce7e1d91dda3a Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 30 Jun 2023 09:23:55 +0200 Subject: [PATCH 07/22] Custom rng for `EnvelopedData` --- Cargo.lock | 73 +------------------------------------ cms/Cargo.toml | 21 +++++------ cms/src/builder.rs | 85 ++++++++++++++++++-------------------------- cms/tests/builder.rs | 19 +++++++--- 4 files changed, 61 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33ba33b89..90d5e5e61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - [[package]] name = "aes" version = "0.8.3" @@ -23,20 +13,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "aho-corasick" version = "1.0.2" @@ -281,7 +257,6 @@ name = "cms" version = "0.2.1" dependencies = [ "aes", - "aes-gcm", "cbc", "cipher", "const-oid 0.9.2", @@ -291,6 +266,7 @@ dependencies = [ "p256", "pem-rfc7468", "pkcs5", + "rand", "rsa", "sha1", "sha2", @@ -440,15 +416,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - [[package]] name = "der" version = "0.7.6" @@ -715,16 +682,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "glob" version = "0.3.1" @@ -997,12 +954,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "p256" version = "0.13.2" @@ -1127,18 +1078,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1786,16 +1725,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "version_check" version = "0.9.4" diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 6538ea8d4..260acf3d6 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -20,29 +20,30 @@ x509-cert = { version = "0.2.3", default-features = false, features = ["pem"] } const-oid = { version = "0.9", features = ["db"] } # TODO: path = "../const-oid" # optional dependencies -aes = { version = "0.8.2", optional = true} -aes-gcm = { version = "0.10.2", optional = true} -cbc = { version = "0.1.2", optional = true} -cipher = { version = "0.4.4", features = ["alloc", "block-padding"], optional = true} +aes = { version = "0.8.2", optional = true } +cbc = { version = "0.1.2", optional = true } +cipher = { version = "0.4.4", features = ["alloc", "block-padding", "rand_core"], optional = true } rsa = { version = "0.9.2", optional = true } -sha1 = { version = "0.10", optional = true} -sha2 = { version = "0.10", optional = true} -sha3 = { version = "0.10", optional = true} -signature = { version = "2.1.0", features = ["digest", "alloc"], optional = true} -zeroize = { version = "1.6.0", optional = true} +sha1 = { version = "0.10", optional = true } +sha2 = { version = "0.10", optional = true } +sha3 = { version = "0.10", optional = true } +signature = { version = "2.1.0", features = ["digest", "alloc"], optional = true } +zeroize = { version = "1.6.0", optional = true } [dev-dependencies] hex-literal = "0.4" pem-rfc7468 = "0.7.0" pkcs5 = { version = "0.7" } +rand = { version = "0.8.5" } rsa = { version = "0.9.2", features = ["sha2"] } ecdsa = { version = "0.16.7", features = ["digest", "pem"] } p256 = "0.13.0" [features] alloc = ["der/alloc"] +getrandom = ["rand/getrandom"] std = ["der/std", "spki/std"] -builder = ["aes", "aes-gcm", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder", "zeroize"] +builder = ["aes", "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 32e05f497..b55c94e1c 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -20,7 +20,6 @@ use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use cipher::block_padding::Pkcs7; -use cipher::crypto_common::rand_core::{CryptoRng, OsRng, RngCore}; use cipher::BlockEncryptMut; use cipher::{Key, KeyIvInit, KeySizeUser}; use const_oid::ObjectIdentifier; @@ -41,6 +40,7 @@ use spki::{ }; use std::time::SystemTime; use std::vec; +use cipher::rand_core::{RngCore, CryptoRng, CryptoRngCore}; // TODO bk remove redundancy use x509_cert::attr::{Attribute, AttributeValue, Attributes}; use x509_cert::builder::Builder; use zeroize::Zeroize; @@ -530,7 +530,7 @@ pub trait RecipientInfoBuilder { /// Encrypt the `content_encryption_key` using a method, that is specific for the implementing /// builder type. Finally return a `RecipientInfo`. - fn build(&self, content_encryption_key: &[u8]) -> Result; + fn build(&mut self, content_encryption_key: &[u8]) -> Result; } /// `RecipientInfoBuilder` must be implemented for these 5 recipient info types @@ -557,27 +557,40 @@ pub enum KeyEncryptionInfo { /// Builds a `KeyTransRecipientInfo` according to RFC 5652 § 6. /// This type uses the recipient's public key to encrypt the content-encryption key. -pub struct KeyTransRecipientInfoBuilder { +pub struct KeyTransRecipientInfoBuilder<'a, R> +where + R: CryptoRngCore +{ /// Identifies the recipient pub rid: RecipientIdentifier, /// Info for key encryption pub key_encryption_info: KeyEncryptionInfo, + /// Rng + rng: &'a mut R, } -impl KeyTransRecipientInfoBuilder { +impl<'a, R> KeyTransRecipientInfoBuilder<'a, R> + where + R: CryptoRngCore +{ /// Creates a `KeyTransRecipientInfoBuilder` pub fn new( rid: RecipientIdentifier, key_encryption_info: KeyEncryptionInfo, - ) -> Result { + rng: &'a mut R, + ) -> Result> { Ok(KeyTransRecipientInfoBuilder { rid, key_encryption_info, + rng, }) } } -impl RecipientInfoBuilder for KeyTransRecipientInfoBuilder { +impl<'a, R> RecipientInfoBuilder for KeyTransRecipientInfoBuilder<'a, R> + where + R: CryptoRngCore +{ fn recipient_info_type(&self) -> RecipientInfoType { RecipientInfoType::Ktri } @@ -591,13 +604,13 @@ impl RecipientInfoBuilder for KeyTransRecipientInfoBuilder { /// Build a `KeyTransRecipientInfo`. See RFC 5652 § 6.2.1 /// `content_encryption_key` will be encrypted with the recipient's public key. - fn build(&self, content_encryption_key: &[u8]) -> Result { + fn build(&mut self, content_encryption_key: &[u8]) -> Result { // Encrypt key let (encrypted_key, key_enc_alg) = match &self.key_encryption_info { // RSA encryption KeyEncryptionInfo::Rsa(recipient_public_key) => ( recipient_public_key - .encrypt(&mut OsRng, Pkcs1v15Encrypt, content_encryption_key) + .encrypt(self.rng, Pkcs1v15Encrypt, content_encryption_key) .map_err(|_| Error::Builder(String::from("Could not encrypt key")))?, AlgorithmIdentifierOwned { oid: const_oid::db::rfc5912::RSA_ENCRYPTION, @@ -656,7 +669,7 @@ impl RecipientInfoBuilder for KeyAgreeRecipientInfoBuilder { } /// Build a `KeyAgreeRecipientInfoBuilder`. See RFC 5652 § 6.2.1 - fn build(&self, _content_encryption_key: &[u8]) -> Result { + fn build(&mut self, _content_encryption_key: &[u8]) -> Result { Err(Error::Builder(String::from( "Building KeyAgreeRecipientInfo is not implemented, yet.", ))) @@ -699,7 +712,7 @@ impl RecipientInfoBuilder for KekRecipientInfoBuilder { } /// Build a `KekRecipientInfoBuilder`. See RFC 5652 § 6.2.1 - fn build(&self, _content_encryption_key: &[u8]) -> Result { + fn build(&mut self, _content_encryption_key: &[u8]) -> Result { Err(Error::Builder(String::from( "Building KekRecipientInfo is not implemented, yet.", ))) @@ -743,7 +756,7 @@ impl RecipientInfoBuilder for PasswordRecipientInfoBuilder { } /// Build a `PasswordRecipientInfoBuilder`. See RFC 5652 § 6.2.1 - fn build(&self, _content_encryption_key: &[u8]) -> Result { + fn build(&mut self, _content_encryption_key: &[u8]) -> Result { Err(Error::Builder(String::from( "Building PasswordRecipientInfo is not implemented, yet.", ))) @@ -778,13 +791,11 @@ impl RecipientInfoBuilder for OtherRecipientInfoBuilder { /// Returns the `CMSVersion` for this `RecipientInfo` fn recipient_info_version(&self) -> CmsVersion { - // TODO bk panic!("Ori has no CMSVersion") } /// Build a `OtherRecipientInfoBuilder`. See RFC 5652 § 6.2.1 - fn build(&self, _content_encryption_key: &[u8]) -> Result { - // TODO bk + fn build(&mut self, _content_encryption_key: &[u8]) -> Result { panic!("Ori has no common build method.") } } @@ -813,7 +824,7 @@ impl ContentEncryptionAlgorithm { /// Builds CMS `EnvelopedData` according to RFC 5652 § 6. pub struct EnvelopedDataBuilder<'c> { originator_info: Option, - recipient_infos: Vec>, + recipient_infos: Vec>, unencrypted_content: &'c [u8], // TODO bk Not good to offer both, `content_encryptor` and `content_encryption_algorithm`. // We should @@ -849,15 +860,16 @@ impl<'c> EnvelopedDataBuilder<'c> { /// RFC 5652 § 6.2, when `EnvelopedData` is built. pub fn add_recipient_info( &mut self, - recipient_info_builder: impl RecipientInfoBuilder + 'static, + recipient_info_builder: impl RecipientInfoBuilder + 'c, ) -> Result<&mut Self> { self.recipient_infos.push(Box::new(recipient_info_builder)); Ok(self) } - /// Generate an `EnvelopedData` object according to RFC 5652 § 6. - pub fn build(&mut self) -> Result { + /// Generate an `EnvelopedData` object according to RFC 5652 § 6 using a provided + /// random number generator. + pub fn build_with_rng(&mut self, rng: &mut impl CryptoRngCore) -> Result { // Generate content encryption key // Encrypt content // Build recipient infos @@ -866,7 +878,7 @@ impl<'c> EnvelopedDataBuilder<'c> { self.unencrypted_content, &self.content_encryption_algorithm, None, - &mut OsRng, + rng, )?; let encrypted_content_octetstring = der::asn1::OctetString::new(encrypted_content)?; let encrypted_content_info = EncryptedContentInfo { @@ -877,7 +889,7 @@ impl<'c> EnvelopedDataBuilder<'c> { let recipient_infos_vec = self .recipient_infos - .iter() + .iter_mut() .map(|ri| ri.build(&content_encryption_key)) .collect::>>()?; content_encryption_key.zeroize(); @@ -1029,14 +1041,11 @@ macro_rules! encrypt_block_mode { }}; } -// TODO bk this must be refactored so we can select the encryption algorithm -/// Get an encryptor for a given encryption algorithm. Currently, only AES-CBC is supported. -/// If `key` is `Some`, it's length must fit the chosen encryption algorithm. Otherwise the -/// conversion `Key::::from_slice(key)` panics. +/// Symmetrically encrypt data. /// Returns encrypted content, content-encryption key and the used algorithm identifier (including /// the used algorithm parameters). /// -/// TODO bk which encryption algorithms shall also be supported? +/// TODO Which encryption algorithms shall also be supported? fn encrypt_data( data: &[u8], encryption_algorithm_identifier: &ContentEncryptionAlgorithm, @@ -1071,32 +1080,6 @@ where rng, encryption_algorithm_identifier.oid() ), - // TODO bk remove following test code - // _ => Ok({ - // let (key, iv) = match key { - // None => cbc::Encryptor::::generate_key_iv(rng), - // Some(key) => { - // if key.len() != ::key_size() { - // return Err(Error::Builder(String::from( - // "Invalid key size for chosen algorithm", - // ))); - // } - // ( - // Key::>::from_slice(key).to_owned(), - // cbc::Encryptor::::generate_iv(rng), - // ) - // } - // }; - // let encryptor = cbc::Encryptor::::new(&key.into(), &iv.into()); - // ( - // encryptor.encrypt_padded_vec_mut::(data), - // key.to_vec(), - // AlgorithmIdentifierOwned { - // oid: encryption_algorithm_identifier.oid(), - // parameters: Some(Any::new(OctetString, iv.to_vec())?), - // }, - // ) - // }), } } diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 75273434c..ac11cc57d 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -12,10 +12,11 @@ use const_oid::ObjectIdentifier; use der::asn1::{Int, OctetString, PrintableString, SetOfVec, Utf8StringRef}; use der::{Any, AnyRef, DecodePem, Encode, Tag, Tagged}; use p256::{pkcs8::DecodePrivateKey, NistP256}; +use rand::rngs::OsRng; use pem_rfc7468::LineEnding; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; -use rsa::{rand_core, RsaPrivateKey, RsaPublicKey}; +use rsa::{RsaPrivateKey, RsaPublicKey}; use sha2::Sha256; use spki::AlgorithmIdentifierOwned; use x509_cert::attr::{Attribute, AttributeTypeAndValue, AttributeValue}; @@ -157,19 +158,23 @@ fn test_build_signed_data() { // - additional signed attributes #[test] +#[cfg(feature ="getrandom" )] fn test_build_enveloped_data() { let recipient_identifier = recipient_identifier(1); - let mut rng = rand_core::OsRng; + let mut rng = OsRng; let bits = 2048; let recipient_private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); let recipient_public_key = RsaPublicKey::from(&recipient_private_key); + let recipient_info_builder = KeyTransRecipientInfoBuilder::new( recipient_identifier, KeyEncryptionInfo::Rsa(recipient_public_key), + &mut rng ) .expect("Could not create a KeyTransRecipientInfoBuilder"); + let mut rng = OsRng; let mut builder = EnvelopedDataBuilder::new( None, "Arbitrary unencrypted content".as_bytes(), @@ -180,7 +185,7 @@ fn test_build_enveloped_data() { let enveloped_data = builder .add_recipient_info(recipient_info_builder) .expect("Could not add a recipient info") - .build() + .build_with_rng(&mut rng) .expect("Building EnvelopedData failed"); let enveloped_data_der = enveloped_data .to_der() @@ -258,9 +263,13 @@ fn build_pkcs7_scep_pkcsreq() { rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); let recipient_public_key = RsaPublicKey::from(&recipient_private_key); + // Generate a random number generator + let mut rng = rand::thread_rng(); + let recipient_info_builder = KeyTransRecipientInfoBuilder::new( recipient_identifier, KeyEncryptionInfo::Rsa(recipient_public_key), + &mut rng, ) .unwrap(); @@ -274,11 +283,13 @@ fn build_pkcs7_scep_pkcsreq() { ) .unwrap(); + let mut rng = rand::thread_rng(); + // Add recipient info. Multiple recipients are possible, but not used here. let enveloped_data = enveloped_data_builder .add_recipient_info(recipient_info_builder) .unwrap() - .build() + .build_with_rng(&mut rng) .unwrap(); let enveloped_data_der = enveloped_data.to_der().unwrap(); From e0eaf414799bce567089f1641feecb0f09f49f89 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 30 Jun 2023 10:45:40 +0200 Subject: [PATCH 08/22] Reverted feature "getrandom" --- cms/Cargo.toml | 4 ++-- cms/tests/builder.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 260acf3d6..9e4ab952d 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -14,7 +14,7 @@ edition = "2021" rust-version = "1.65" [dependencies] -der = { version = "0.7.6", features = ["alloc", "derive", "oid", "pem"] } +der = { version = "0.7.7", features = ["alloc", "derive", "oid", "pem"] } spki = { version = "0.7" } x509-cert = { version = "0.2.3", default-features = false, features = ["pem"] } const-oid = { version = "0.9", features = ["db"] } # TODO: path = "../const-oid" @@ -31,6 +31,7 @@ signature = { version = "2.1.0", features = ["digest", "alloc"], optional = true zeroize = { version = "1.6.0", optional = true } [dev-dependencies] +getrandom = "0.2" hex-literal = "0.4" pem-rfc7468 = "0.7.0" pkcs5 = { version = "0.7" } @@ -41,7 +42,6 @@ p256 = "0.13.0" [features] alloc = ["der/alloc"] -getrandom = ["rand/getrandom"] std = ["der/std", "spki/std"] builder = ["aes", "cbc", "cipher", "rsa", "sha1", "sha2", "sha3", "signature", "std", "spki/alloc", "x509-cert/builder", "zeroize"] pem = ["alloc", "der/pem"] diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index ac11cc57d..850d0e951 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -158,7 +158,6 @@ fn test_build_signed_data() { // - additional signed attributes #[test] -#[cfg(feature ="getrandom" )] fn test_build_enveloped_data() { let recipient_identifier = recipient_identifier(1); let mut rng = OsRng; From 27ff500d3482026f86c404e483b957657d9962c4 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 30 Jun 2023 10:48:02 +0200 Subject: [PATCH 09/22] rustfmt --- cms/src/builder.rs | 12 ++++++------ cms/tests/builder.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index b55c94e1c..db4154ac0 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -20,6 +20,7 @@ use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use cipher::block_padding::Pkcs7; +use cipher::rand_core::{CryptoRng, CryptoRngCore, RngCore}; // TODO bk remove redundancy use cipher::BlockEncryptMut; use cipher::{Key, KeyIvInit, KeySizeUser}; use const_oid::ObjectIdentifier; @@ -40,7 +41,6 @@ use spki::{ }; use std::time::SystemTime; use std::vec; -use cipher::rand_core::{RngCore, CryptoRng, CryptoRngCore}; // TODO bk remove redundancy use x509_cert::attr::{Attribute, AttributeValue, Attributes}; use x509_cert::builder::Builder; use zeroize::Zeroize; @@ -559,7 +559,7 @@ pub enum KeyEncryptionInfo { /// This type uses the recipient's public key to encrypt the content-encryption key. pub struct KeyTransRecipientInfoBuilder<'a, R> where - R: CryptoRngCore + R: CryptoRngCore, { /// Identifies the recipient pub rid: RecipientIdentifier, @@ -570,8 +570,8 @@ where } impl<'a, R> KeyTransRecipientInfoBuilder<'a, R> - where - R: CryptoRngCore +where + R: CryptoRngCore, { /// Creates a `KeyTransRecipientInfoBuilder` pub fn new( @@ -588,8 +588,8 @@ impl<'a, R> KeyTransRecipientInfoBuilder<'a, R> } impl<'a, R> RecipientInfoBuilder for KeyTransRecipientInfoBuilder<'a, R> - where - R: CryptoRngCore +where + R: CryptoRngCore, { fn recipient_info_type(&self) -> RecipientInfoType { RecipientInfoType::Ktri diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 850d0e951..bf27323bc 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -12,8 +12,8 @@ use const_oid::ObjectIdentifier; use der::asn1::{Int, OctetString, PrintableString, SetOfVec, Utf8StringRef}; use der::{Any, AnyRef, DecodePem, Encode, Tag, Tagged}; use p256::{pkcs8::DecodePrivateKey, NistP256}; -use rand::rngs::OsRng; use pem_rfc7468::LineEnding; +use rand::rngs::OsRng; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; use rsa::{RsaPrivateKey, RsaPublicKey}; @@ -169,7 +169,7 @@ fn test_build_enveloped_data() { let recipient_info_builder = KeyTransRecipientInfoBuilder::new( recipient_identifier, KeyEncryptionInfo::Rsa(recipient_public_key), - &mut rng + &mut rng, ) .expect("Could not create a KeyTransRecipientInfoBuilder"); From 4bd4ef6701017358a00195fcd21d1be7596c137e Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 30 Jun 2023 17:25:07 +0200 Subject: [PATCH 10/22] cms scep test checks. --- Cargo.lock | 23 ++++---- cms/tests/builder.rs | 123 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1ee9a277..c4432448d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,7 +244,7 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" name = "cmpv2" version = "0.2.0" dependencies = [ - "const-oid 0.9.2", + "const-oid 0.9.3", "crmf", "der", "hex-literal", @@ -259,9 +259,10 @@ dependencies = [ "aes", "cbc", "cipher", - "const-oid 0.9.2", + "const-oid 0.9.3", "der", "ecdsa", + "getrandom", "hex-literal", "p256", "pem-rfc7468", @@ -279,9 +280,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" dependencies = [ "arbitrary", ] @@ -344,7 +345,7 @@ name = "crmf" version = "0.2.0" dependencies = [ "cms", - "const-oid 0.9.2", + "const-oid 0.9.3", "der", "spki", "x509-cert", @@ -421,7 +422,7 @@ name = "der" version = "0.7.7" dependencies = [ "arbitrary", - "const-oid 0.9.2", + "const-oid 0.9.3", "der_derive", "flagset", "hex-literal", @@ -468,7 +469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid 0.9.2", + "const-oid 0.9.3", "crypto-common", "subtle", ] @@ -1005,7 +1006,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" name = "pkcs1" version = "0.7.5" dependencies = [ - "const-oid 0.9.2", + "const-oid 0.9.3", "der", "hex-literal", "pkcs8", @@ -1277,7 +1278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" dependencies = [ "byteorder", - "const-oid 0.9.2", + "const-oid 0.9.3", "digest", "num-bigint-dig", "num-integer", @@ -1937,7 +1938,7 @@ name = "x509-cert" version = "0.2.3" dependencies = [ "arbitrary", - "const-oid 0.9.2", + "const-oid 0.9.3", "der", "ecdsa", "hex-literal", @@ -1966,7 +1967,7 @@ dependencies = [ name = "x509-ocsp" version = "0.2.0-pre" dependencies = [ - "const-oid 0.9.2", + "const-oid 0.9.3", "der", "hex-literal", "spki", diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index bf27323bc..de271b05d 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -1,29 +1,34 @@ #![cfg(feature = "builder")] +use aes::Aes128; +use cipher::block_padding::Pkcs7; +use cipher::{BlockDecryptMut, KeyIvInit}; use cms::builder::{ create_signing_time_attribute, ContentEncryptionAlgorithm, EnvelopedDataBuilder, KeyEncryptionInfo, KeyTransRecipientInfoBuilder, SignedDataBuilder, SignerInfoBuilder, }; use cms::cert::{CertificateChoices, IssuerAndSerialNumber}; use cms::content_info::ContentInfo; -use cms::enveloped_data::RecipientIdentifier; -use cms::signed_data::{EncapsulatedContentInfo, SignerIdentifier}; +use cms::enveloped_data::RecipientInfo::Ktri; +use cms::enveloped_data::{EnvelopedData, RecipientIdentifier, RecipientInfo}; +use cms::signed_data::{EncapsulatedContentInfo, SignedData, SignerIdentifier}; use const_oid::ObjectIdentifier; use der::asn1::{Int, OctetString, PrintableString, SetOfVec, Utf8StringRef}; -use der::{Any, AnyRef, DecodePem, Encode, Tag, Tagged}; +use der::{Any, AnyRef, Decode, DecodePem, Encode, Tag, Tagged}; use p256::{pkcs8::DecodePrivateKey, NistP256}; use pem_rfc7468::LineEnding; use rand::rngs::OsRng; use rsa::pkcs1::DecodeRsaPrivateKey; -use rsa::pkcs1v15::SigningKey; -use rsa::{RsaPrivateKey, RsaPublicKey}; +use rsa::pkcs1v15::{SigningKey, VerifyingKey}; +use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; use sha2::Sha256; +use signature::Verifier; use spki::AlgorithmIdentifierOwned; use x509_cert::attr::{Attribute, AttributeTypeAndValue, AttributeValue}; use x509_cert::name::{RdnSequence, RelativeDistinguishedName}; use x509_cert::serial_number::SerialNumber; -// TODO bk replace this by const_oid definitions as soon as merged +// TODO bk replace this by const_oid definitions as soon as released const RFC8894_ID_MESSAGE_TYPE: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.113733.1.9.2"); const RFC8894_ID_SENDER_NONCE: ObjectIdentifier = @@ -256,17 +261,19 @@ fn build_pkcs7_scep_pkcsreq() { // - Wrap enveloped data in `SignedData` // - Sign with sender's RSA key. + // Generate a random number generator + let mut rng = rand::thread_rng(); + // Create recipient info - let recipient_identifier = recipient_identifier(1); + let recipient_identifier = recipient_identifier(42); let recipient_private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); let recipient_public_key = RsaPublicKey::from(&recipient_private_key); - // Generate a random number generator - let mut rng = rand::thread_rng(); - + //---------------------------------------------------------------------------------------------- + // Create enveloped data let recipient_info_builder = KeyTransRecipientInfoBuilder::new( - recipient_identifier, + recipient_identifier.clone(), KeyEncryptionInfo::Rsa(recipient_public_key), &mut rng, ) @@ -298,6 +305,9 @@ fn build_pkcs7_scep_pkcsreq() { content: Any::from(content), }; + //---------------------------------------------------------------------------------------------- + // Create signed data + // Encapsulate the `EnvelopedData` let content_info_der = content_info.to_der().unwrap(); let content = EncapsulatedContentInfo { @@ -387,10 +397,95 @@ fn build_pkcs7_scep_pkcsreq() { pem_rfc7468::encode_string("PKCS7", LineEnding::LF, &signed_data_pkcs7_der).unwrap() ); - // TODO bk - // Check signature + //---------------------------------------------------------------------------------------------- + // Verify + // Decode Message including decrypted enveloped content - // Check CSR + // Check signature + // Decrypt content-encryption key + // Decrypt content + let ci = ContentInfo::from_der(signed_data_pkcs7_der.as_slice()).unwrap(); + assert_eq!(ci.content_type, const_oid::db::rfc5911::ID_SIGNED_DATA); + + // Decode CMS message (by converting `Any` to `SignedData`) + let signed_data_der = ci.content.to_der().unwrap(); + let signed_data = SignedData::from_der(signed_data_der.as_slice()).unwrap(); + + // Check signatures (only one in this test) + for signer_info in signed_data.signer_infos.0.iter() { + let signature = + rsa::pkcs1v15::Signature::try_from(signer_info.signature.as_bytes()).unwrap(); + let signed_attributes_der = signer_info.signed_attrs.clone().unwrap().to_der().unwrap(); + let verifier = { + let verifier_rsa_key_pem = include_str!("examples/sceptest_key.pem"); + let verifier_rsa_key = RsaPrivateKey::from_pkcs8_pem(verifier_rsa_key_pem).unwrap(); + VerifyingKey::::new(RsaPublicKey::from(verifier_rsa_key)) + }; + assert!(verifier + .verify(signed_attributes_der.as_slice(), &signature) + .is_ok()); + } + + // Decode contained enveloped data + let encap_content_info = signed_data.encap_content_info; + assert_eq!( + encap_content_info.econtent_type, + const_oid::db::rfc5911::ID_DATA + ); + let econtent = encap_content_info + .econtent + .expect("this cms must contain content"); + // let octet_string = OctetString::from_der(econtent.value()).unwrap(); + // let ci = ContentInfo::from_der(octet_string.as_bytes()).unwrap(); + let ci = ContentInfo::from_der(econtent.value()).unwrap(); + assert_eq!(ci.content_type, const_oid::db::rfc5911::ID_ENVELOPED_DATA); + let enveloped_data_der = ci.content.to_der().unwrap(); + let enveloped_data = EnvelopedData::from_der(enveloped_data_der.as_slice()).unwrap(); + let my_recipient_info: &RecipientInfo = enveloped_data + .recip_infos + .0 + .iter() + .find(|&recipient_info| match recipient_info { + Ktri(ri) => ri.rid == recipient_identifier, + _ => false, + }) + .unwrap(); + let key_trans_recipient_info = if let Ktri(recipient_info) = my_recipient_info { + recipient_info // this must succeed + } else { + panic!(); + }; + let encrypted_key = &key_trans_recipient_info.enc_key; + + // Decrypt the content-encryption key + let content_encryption_key = recipient_private_key + .decrypt(Pkcs1v15Encrypt, encrypted_key.as_bytes()) + .unwrap(); + + // Decrypt the CSR + let encryption_info = enveloped_data.encrypted_content; + assert_eq!( + encryption_info.content_enc_alg.oid, + const_oid::db::rfc5911::ID_AES_128_CBC + ); + let iv_octet_string = OctetString::from_der( + encryption_info + .content_enc_alg + .parameters + .unwrap() + .to_der() + .unwrap() + .as_slice(), + ) + .unwrap(); + let iv = iv_octet_string.as_bytes(); + let encrypted_content_octet_string = encryption_info.encrypted_content.unwrap(); + let encrypted_content = encrypted_content_octet_string.as_bytes(); + let csr_der_decrypted = + cbc::Decryptor::::new(content_encryption_key.as_slice().into(), iv.into()) + .decrypt_padded_vec_mut::(encrypted_content) + .unwrap(); + assert_eq!(csr_der_decrypted.as_slice(), csr_der) } #[test] From 1b0f6f8bbc53baf1789c4430fa54c12338ea8e4e Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 6 Jul 2023 17:43:03 +0200 Subject: [PATCH 11/22] Cleaning cms builder --- cms/src/builder.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index db4154ac0..fa3733952 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -112,7 +112,6 @@ pub struct SignerInfoBuilder<'s, S> { impl<'s, S> SignerInfoBuilder<'s, S> where S: Keypair + DynSignatureAlgorithmIdentifier, - S::VerifyingKey: EncodePublicKey, { /// Create a new `SignerInfoBuilder`. This is used for adding `SignerInfo`s to `SignedData` /// structures. @@ -1012,9 +1011,8 @@ fn get_hasher( } /// Helps encrypting. -#[macro_export] macro_rules! encrypt_block_mode { - ($data:expr, $block_mode:ident::$typ:ident<$alg:ident>, $key:expr, $iv:expr, $rng:expr, $oid:expr) => {{ + ($data:expr, $block_mode:ident::$typ:ident<$alg:ident>, $key:expr, $rng:expr, $oid:expr) => {{ let (key, iv) = match $key { None => $block_mode::$typ::<$alg>::generate_key_iv($rng), Some(key) => { @@ -1060,7 +1058,6 @@ where data, cbc::Encryptor, key, - iv, rng, encryption_algorithm_identifier.oid() ), @@ -1068,7 +1065,6 @@ where data, cbc::Encryptor, key, - iv, rng, encryption_algorithm_identifier.oid() ), @@ -1076,7 +1072,6 @@ where data, cbc::Encryptor, key, - iv, rng, encryption_algorithm_identifier.oid() ), From 29ac6373068510ad679b3ed1fef1e8bc48eaffcf Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 6 Jul 2023 17:44:06 +0200 Subject: [PATCH 12/22] Fixed encoding of `SubjectKeyIdentifier` --- cms/src/signed_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/src/signed_data.rs b/cms/src/signed_data.rs index bd5e3e74c..04ff9eb35 100644 --- a/cms/src/signed_data.rs +++ b/cms/src/signed_data.rs @@ -169,7 +169,7 @@ pub type SignedAttributes = Attributes; pub enum SignerIdentifier { IssuerAndSerialNumber(IssuerAndSerialNumber), - #[asn1(context_specific = "0", tag_mode = "EXPLICIT")] + #[asn1(context_specific = "0", tag_mode = "IMPLICIT")] SubjectKeyIdentifier(SubjectKeyIdentifier), } From 57c0829b86281af81ac0c95bcede57635b211d5a Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 6 Jul 2023 17:44:38 +0200 Subject: [PATCH 13/22] Fixed message_type in builder test --- cms/tests/builder.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index de271b05d..77f6f44c2 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -13,7 +13,7 @@ use cms::enveloped_data::RecipientInfo::Ktri; use cms::enveloped_data::{EnvelopedData, RecipientIdentifier, RecipientInfo}; use cms::signed_data::{EncapsulatedContentInfo, SignedData, SignerIdentifier}; use const_oid::ObjectIdentifier; -use der::asn1::{Int, OctetString, PrintableString, SetOfVec, Utf8StringRef}; +use der::asn1::{OctetString, PrintableString, SetOfVec, Utf8StringRef}; use der::{Any, AnyRef, Decode, DecodePem, Encode, Tag, Tagged}; use p256::{pkcs8::DecodePrivateKey, NistP256}; use pem_rfc7468::LineEnding; @@ -339,13 +339,8 @@ fn build_pkcs7_scep_pkcsreq() { // - senderNonce // - transactionID let mut message_type_value: SetOfVec = Default::default(); - let pkcsreq = 19_i8; // Numerical value of PKCSReq messageType - // TODO bk: is the correct way to create an `Int` from an `i8`? - let pkcsreq_bytes = pkcsreq.to_be_bytes(); - let pkcsreq_as_int = Int::new(&pkcsreq_bytes).unwrap(); - message_type_value - .insert(Any::new(Tag::Integer, pkcsreq_as_int.as_bytes()).unwrap()) - .unwrap(); + let message_type = PrintableString::try_from("19".to_string()).unwrap(); + message_type_value.insert(Any::from(&message_type)).unwrap(); let message_type = Attribute { oid: RFC8894_ID_MESSAGE_TYPE, values: message_type_value, From 46e02dbffd9982c9c0fcb39534563fae211c2257 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 7 Jul 2023 13:26:51 +0200 Subject: [PATCH 14/22] Harmonized test function naming --- cms/tests/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 77f6f44c2..58fe3fd0b 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -202,7 +202,7 @@ fn test_build_enveloped_data() { } #[test] -fn build_pkcs7_scep_pkcsreq() { +fn test_build_pkcs7_scep_pkcsreq() { // This test demonstrates how to build a PKCS7 message for the SCEP PKCSReq pkiMessage // according to RFC 8894. // We use the key transport mechanism in this example, which means, we have the recipient From 1c3af42a8ee2f754cce3e1d5740c370284923c58 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 7 Jul 2023 13:27:17 +0200 Subject: [PATCH 15/22] Removed unneeded trait bound --- cms/src/builder.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index fa3733952..c60dbc74f 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -35,10 +35,7 @@ use rsa::Pkcs1v15Encrypt; use sha2::digest; use signature::digest::DynDigest; use signature::{Keypair, Signer}; -use spki::{ - AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, EncodePublicKey, - SignatureBitStringEncoding, -}; +use spki::{AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, SignatureBitStringEncoding}; use std::time::SystemTime; use std::vec; use x509_cert::attr::{Attribute, AttributeValue, Attributes}; @@ -173,7 +170,6 @@ where impl<'s, S> Builder for SignerInfoBuilder<'s, S> where S: Keypair + DynSignatureAlgorithmIdentifier, - S::VerifyingKey: EncodePublicKey, { type Signer = S; type Output = SignerInfo; @@ -397,7 +393,6 @@ impl<'s> SignedDataBuilder<'s> { ) -> Result<&mut Self> where S: Keypair + DynSignatureAlgorithmIdentifier, - S::VerifyingKey: EncodePublicKey, S: Signer, Signature: SignatureBitStringEncoding, { From f941000ecea3c2472820b1e6e14a1f4b28c29edf Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 14 Jul 2023 14:28:24 +0200 Subject: [PATCH 16/22] Fixed file permissions --- cms/tests/examples/sceptest_cert-selfsigned.pem | 0 cms/tests/examples/sceptest_csr.der | Bin cms/tests/examples/sceptest_key.pem | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 cms/tests/examples/sceptest_cert-selfsigned.pem mode change 100755 => 100644 cms/tests/examples/sceptest_csr.der mode change 100755 => 100644 cms/tests/examples/sceptest_key.pem diff --git a/cms/tests/examples/sceptest_cert-selfsigned.pem b/cms/tests/examples/sceptest_cert-selfsigned.pem old mode 100755 new mode 100644 diff --git a/cms/tests/examples/sceptest_csr.der b/cms/tests/examples/sceptest_csr.der old mode 100755 new mode 100644 diff --git a/cms/tests/examples/sceptest_key.pem b/cms/tests/examples/sceptest_key.pem old mode 100755 new mode 100644 From 104fa7ece87e1e750d753014cd1687f2daa668a7 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Fri, 14 Jul 2023 14:56:27 +0200 Subject: [PATCH 17/22] Updated Cargo.lock --- Cargo.lock | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e807d3271..d40c21ba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,7 +249,7 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" name = "cmpv2" version = "0.2.0" dependencies = [ - "const-oid 0.9.3", + "const-oid 0.9.4", "crmf", "der", "hex-literal", @@ -264,7 +264,7 @@ dependencies = [ "aes", "cbc", "cipher", - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "ecdsa", "getrandom", @@ -285,9 +285,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" dependencies = [ "arbitrary", ] @@ -350,7 +350,7 @@ name = "crmf" version = "0.2.0" dependencies = [ "cms", - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "spki", "x509-cert", @@ -427,7 +427,7 @@ name = "der" version = "0.7.7" dependencies = [ "arbitrary", - "const-oid 0.9.3", + "const-oid 0.9.4", "der_derive", "flagset", "hex-literal", @@ -474,7 +474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid 0.9.3", + "const-oid 0.9.4", "crypto-common", "subtle", ] @@ -521,9 +521,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" @@ -801,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.3", + "rustix 0.38.4", "windows-sys", ] @@ -1007,7 +1007,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" name = "pkcs1" version = "0.7.5" dependencies = [ - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "hex-literal", "pkcs8", @@ -1248,18 +1248,18 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.3", + "regex-syntax 0.7.4", ] [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.3", + "regex-syntax 0.7.4", ] [[package]] @@ -1270,9 +1270,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rfc6979" @@ -1291,7 +1291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" dependencies = [ "byteorder", - "const-oid 0.9.3", + "const-oid 0.9.4", "digest", "num-bigint-dig", "num-integer", @@ -1358,9 +1358,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ "bitflags 2.3.3", "errno", @@ -1475,9 +1475,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" dependencies = [ "itoa", "ryu", @@ -1714,9 +1714,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "5f8751d9c1b03c6500c387e96f81f815a4f8e72d142d2d4a9ffa6fedd51ddee7" dependencies = [ "indexmap", "serde", @@ -1952,9 +1952,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] @@ -1964,7 +1964,7 @@ name = "x509-cert" version = "0.2.3" dependencies = [ "arbitrary", - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "ecdsa", "hex-literal", @@ -1993,7 +1993,7 @@ dependencies = [ name = "x509-ocsp" version = "0.2.0-pre" dependencies = [ - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "hex-literal", "spki", From ce240ef1cefff850726706b2a739aa1d0cce72fb Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Mon, 17 Jul 2023 16:32:24 +0200 Subject: [PATCH 18/22] Added cloning and other derives for ContentEncryptionAlgorithm --- cms/src/builder.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index 61848c3ae..fd078bc50 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -515,6 +515,7 @@ pub trait RecipientInfoBuilder { /// `RecipientInfoBuilder` must be implemented for these 5 recipient info types /// as defined in RFC 5652 § 6: +#[derive(Clone, Debug, Eq, PartialEq)] pub enum RecipientInfoType { /// KeyTransRecipientInfo Ktri, @@ -529,6 +530,7 @@ pub enum RecipientInfoType { } /// Contains information required to encrypt the content encryption key with a specific method +#[derive(Clone, Debug, Eq, PartialEq)] pub enum KeyEncryptionInfo { /// Encrypt key with RSA Rsa(rsa::RsaPublicKey), @@ -781,6 +783,7 @@ impl RecipientInfoBuilder for OtherRecipientInfoBuilder { } /// Supported content encryption algorithms. +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ContentEncryptionAlgorithm { /// AES-128 CBC Aes128Cbc, From 5c16a4c17e4a3d916913a6ae73f024e088962807 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Tue, 1 Aug 2023 15:09:34 +0200 Subject: [PATCH 19/22] Support degenerate certificates-only SignedData --- Cargo.lock | 29 +++++++++++++-------- cms/Cargo.toml | 2 +- cms/src/builder.rs | 64 ++++++++++++++++++++++++---------------------- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdb9733f2..68a92a6ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,7 +255,7 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" name = "cmpv2" version = "0.2.0" dependencies = [ - "const-oid 0.9.3", + "const-oid 0.9.4", "crmf", "der", "hex-literal", @@ -267,13 +267,18 @@ dependencies = [ name = "cms" version = "0.2.2" dependencies = [ - "const-oid 0.9.3", + "aes", + "cbc", + "cipher", + "const-oid 0.9.4", "der", "ecdsa", + "getrandom", "hex-literal", "p256", "pem-rfc7468", "pkcs5", + "rand", "rsa", "sha1", "sha2", @@ -281,13 +286,14 @@ dependencies = [ "signature", "spki", "x509-cert", + "zeroize", ] [[package]] name = "const-oid" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" dependencies = [ "arbitrary", ] @@ -350,7 +356,7 @@ name = "crmf" version = "0.2.0" dependencies = [ "cms", - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "spki", "x509-cert", @@ -418,6 +424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] @@ -427,7 +434,7 @@ version = "0.7.7" dependencies = [ "arbitrary", "bytes", - "const-oid 0.9.3", + "const-oid 0.9.4", "der_derive", "flagset", "hex-literal", @@ -474,7 +481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid 0.9.3", + "const-oid 0.9.4", "crypto-common", "subtle", ] @@ -1007,7 +1014,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" name = "pkcs1" version = "0.7.5" dependencies = [ - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "hex-literal", "pkcs8", @@ -1298,7 +1305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" dependencies = [ "byteorder", - "const-oid 0.9.3", + "const-oid 0.9.4", "digest", "num-bigint-dig", "num-integer", @@ -1980,7 +1987,7 @@ name = "x509-cert" version = "0.2.4" dependencies = [ "arbitrary", - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "ecdsa", "hex-literal", @@ -2009,7 +2016,7 @@ dependencies = [ name = "x509-ocsp" version = "0.2.0-pre" dependencies = [ - "const-oid 0.9.3", + "const-oid 0.9.4", "der", "hex-literal", "spki", diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 1df94539b..dcea6aa64 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.65" der = { version = "0.7.7", features = ["alloc", "derive", "oid", "pem"] } spki = { version = "0.7" } x509-cert = { version = "0.2.3", default-features = false, features = ["pem"] } -const-oid = { version = "0.9", features = ["db"] } # TODO: path = "../const-oid" +const-oid = { version = "0.9.4", features = ["db"] } # TODO: path = "../const-oid" # optional dependencies aes = { version = "0.8.2", optional = true } diff --git a/cms/src/builder.rs b/cms/src/builder.rs index fd078bc50..101d1f903 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -190,12 +190,13 @@ where // Encapsulated content must be empty, if external digest is given. return Err(der::Error::from(ErrorKind::Failed)); } - external_content_digest.to_vec() + Some(external_content_digest.to_vec()) } None => match &self.encapsulated_content_info.econtent { None => { - // Content missing, cannot sign - return Err(der::Error::from(ErrorKind::Failed)); + // This case is allowed. E.g. for degenerate certificates-only messages. + // See RFC 5652 § 5.2 or RFC 8894 § 3.4. + None } Some(content) => { let mut hasher = get_hasher(&self.digest_algorithm).ok_or_else(|| { @@ -207,48 +208,51 @@ where // or the length octets. let content_value = content.value(); hasher.update(content_value); - hasher.finalize_reset().to_vec() + Some(hasher.finalize_reset().to_vec()) } }, }; - // This implementation uses signed attributes. + // This implementation uses signed attributes to store the message digest. if self.signed_attributes.is_none() { self.signed_attributes = Some(vec![]); } - // Add digest attribute to (to be) signed attributes let signed_attributes = self .signed_attributes .as_mut() .expect("Signed attributes must be present."); - signed_attributes.push( - create_message_digest_attribute(&message_digest) - .map_err(|_| der::Error::from(ErrorKind::Failed))?, - ); - - // The content-type attribute type specifies the content type of the - // ContentInfo within signed-data or authenticated-data. The content- - // type attribute type MUST be present whenever signed attributes are - // present in signed-data or authenticated attributes present in - // authenticated-data. The content-type attribute value MUST match the - // encapContentInfo eContentType value in the signed-data or - // authenticated-data. - let econtent_type = self.encapsulated_content_info.econtent_type; - let signed_attributes_content_type = signed_attributes - .iter() - .find(|attr| attr.oid.cmp(&const_oid::db::rfc5911::ID_CONTENT_TYPE) == Ordering::Equal); - if let Some(signed_attributes_content_type) = signed_attributes_content_type { - // Check against `eContentType` - if signed_attributes_content_type.oid != econtent_type { - // Mismatch between content types: encapsulated content info <-> signed attributes. - return Err(der::Error::from(ErrorKind::Failed)); - } - } else { + + if let Some(message_digest) = message_digest { + // Add digest attribute to (to be) signed attributes signed_attributes.push( - create_content_type_attribute(econtent_type) + create_message_digest_attribute(&message_digest) .map_err(|_| der::Error::from(ErrorKind::Failed))?, ); + + // The content-type attribute type specifies the content type of the + // ContentInfo within signed-data or authenticated-data. The content- + // type attribute type MUST be present whenever signed attributes are + // present in signed-data or authenticated attributes present in + // authenticated-data. The content-type attribute value MUST match the + // encapContentInfo eContentType value in the signed-data or + // authenticated-data. + let econtent_type = self.encapsulated_content_info.econtent_type; + let signed_attributes_content_type = signed_attributes + .iter() + .find(|attr| attr.oid.cmp(&const_oid::db::rfc5911::ID_CONTENT_TYPE) == Ordering::Equal); + if let Some(signed_attributes_content_type) = signed_attributes_content_type { + // Check against `eContentType` + if signed_attributes_content_type.oid != econtent_type { + // Mismatch between content types: encapsulated content info <-> signed attributes. + return Err(der::Error::from(ErrorKind::Failed)); + } + } else { + signed_attributes.push( + create_content_type_attribute(econtent_type) + .map_err(|_| der::Error::from(ErrorKind::Failed))?, + ); + } } // Now use `signer` to sign the DER encoded signed attributes From 9a4c830f0960707d0faf4e5f68e8ae678af62b9a Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 3 Aug 2023 15:21:49 +0200 Subject: [PATCH 20/22] Degenerate certificates-only test --- cms/tests/builder.rs | 105 ++++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 35 deletions(-) diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 58fe3fd0b..9593ef1b7 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -211,44 +211,43 @@ fn test_build_pkcs7_scep_pkcsreq() { // - the recipients public RSA key, // - an RSA key pair of the sender and // - a CSR (PKCS #10) signed with the sender's key + // // A CMS `SignedData` message is roughly structured as follows: - // ContentInfo - // SignedData - // version - // digestAlgorithms* - // encapContentInfo - // ContentInfo - // EnvelopedData - // version - // [originatorInfo] - // recipientInfos* - // e.g. KeyTransRecipientInfo - // version - // rid - // keyEncryptionAlgorithm - // encryptedKey - // encryptedContentInfo - // contentType - // contentEncryptionAlgorithm - // [encryptedContent] - // [unprotectedAttrs*] + // cms_message: ContentInfo ::= SEQUENCE + // contentType: ContentType = id-signed-data + // content: ANY == SignedData + // version: CMSVersion + // digestAlgorithms*: DigestAlgorithmIdentifiers + // encapContentInfo: EncapsulatedContentInfo ::= SEQUENCE + // eContentType: ContentType = id-data + // eContent: OCTET STRING + // value_of_econtent_without_tag_and_length_bytes: ContentInfo + // contentType: ContentType = id-enveloped-data + // content: ANY == EnvelopeData ::= SEQUENCE + // version: CMSVersion + // [originatorInfo]: OriginatorInfo + // recipientInfos: RecipientInfos ::= SET OF RecipientInfo + // e.g. KeyTransRecipientInfo ::= SEQUENCE + // version: CMSVersion + // rid: RecipientIdentifier + // keyEncryptionAlgorithm: KeyEncryptionAlgorithmIdentifier + // encryptedKey: EncryptedKey + // encryptedContentInfo: EncryptedContentInfo ::= SEQUENCE + // contentType: ContentType + // contentEncryptionAlgorithm: ContentEncryptionAlgorithmIdentifier + // [encryptedContent]: EncryptedContent == OCTET STRING + // [unprotectedAttrs*] // [certificates*] // [crls*] - // signerInfos - // version - // sid - // digestAlgorithm - // [signedAttrs*] - // signatureAlgorithm - // signature - // [unsignedAttrs*] - // Reduced to the nested structures: - // ContentInfo - // SignedData - // encapContentInfo - // ContentInfo - // EnvelopedData - // encryptedContentInfo + // signerInfos*: SET OF SignerInfo + // version: CMSVersion + // sid: SignerIdentifier + // digestAlgorithm: DigestAlgorithmIdentifier + // [signedAttrs*]: SignedAttributes + // signatureAlgorithm: SignatureAlgorithmIdentifier + // signature: SignatureValue + // [unsignedAttrs*]: UnsignedAttributes + // // 4 builders are involved in the procedure: // - `SignedDataBuilder` // - `SignerInfoBuilder` @@ -483,6 +482,42 @@ fn test_build_pkcs7_scep_pkcsreq() { assert_eq!(csr_der_decrypted.as_slice(), csr_der) } +#[test] +fn test_degenerate_certificates_only_cms() { + let cert_buf = include_bytes!("examples/ValidCertificatePathTest1EE.pem"); + let cert = x509_cert::Certificate::from_pem(cert_buf).unwrap(); + let certs = vec![cert]; + + let encapsulated_content_info = EncapsulatedContentInfo { + econtent_type: const_oid::db::rfc5911::ID_DATA, + econtent: None, + }; + let mut signed_data_builder = SignedDataBuilder::new(&encapsulated_content_info); + + for cert in certs { + signed_data_builder.add_certificate(CertificateChoices::Certificate(cert.clone())).unwrap(); + } + + let degenerate_certificates_only_cms = signed_data_builder.build().unwrap(); + + // Extract certificates from `degenerate_certificates_only_cms` + let signed_data = SignedData::from_der( + degenerate_certificates_only_cms + .content + .to_der() + .unwrap() + .as_slice(), + ) + .unwrap(); + let certs = signed_data.certificates.unwrap(); + let CertificateChoices::Certificate(extracted_cert) = certs.0.get(0).unwrap() else { + panic!("Invalid certificate choice encountered"); + }; + + let original_cert = x509_cert::Certificate::from_pem(cert_buf).unwrap(); + assert_eq!(original_cert.signature, extracted_cert.signature) +} + #[test] fn test_create_signing_attribute() { let attribute: Attribute = From 64c7b82ed9d699096d900702d3e2f2a629fb97f9 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 3 Aug 2023 15:41:55 +0200 Subject: [PATCH 21/22] Fixed a TODO --- cms/src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index 101d1f903..cf21256c1 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -20,7 +20,7 @@ use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; use cipher::block_padding::Pkcs7; -use cipher::rand_core::{CryptoRng, CryptoRngCore, RngCore}; // TODO bk remove redundancy +use cipher::rand_core::{CryptoRng, CryptoRngCore, RngCore}; use cipher::BlockEncryptMut; use cipher::{Key, KeyIvInit, KeySizeUser}; use const_oid::ObjectIdentifier; From 9eb70cc1c636624f34c1db5ea3861b79f0928d93 Mon Sep 17 00:00:00 2001 From: Bernd Krietenstein Date: Thu, 3 Aug 2023 16:13:46 +0200 Subject: [PATCH 22/22] Rustfmt --- cms/src/builder.rs | 6 +++--- cms/tests/builder.rs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cms/src/builder.rs b/cms/src/builder.rs index cf21256c1..906ed6bf7 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -238,9 +238,9 @@ where // encapContentInfo eContentType value in the signed-data or // authenticated-data. let econtent_type = self.encapsulated_content_info.econtent_type; - let signed_attributes_content_type = signed_attributes - .iter() - .find(|attr| attr.oid.cmp(&const_oid::db::rfc5911::ID_CONTENT_TYPE) == Ordering::Equal); + let signed_attributes_content_type = signed_attributes.iter().find(|attr| { + attr.oid.cmp(&const_oid::db::rfc5911::ID_CONTENT_TYPE) == Ordering::Equal + }); if let Some(signed_attributes_content_type) = signed_attributes_content_type { // Check against `eContentType` if signed_attributes_content_type.oid != econtent_type { diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index 9593ef1b7..8efab51ec 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -495,7 +495,9 @@ fn test_degenerate_certificates_only_cms() { let mut signed_data_builder = SignedDataBuilder::new(&encapsulated_content_info); for cert in certs { - signed_data_builder.add_certificate(CertificateChoices::Certificate(cert.clone())).unwrap(); + signed_data_builder + .add_certificate(CertificateChoices::Certificate(cert.clone())) + .unwrap(); } let degenerate_certificates_only_cms = signed_data_builder.build().unwrap();