From c1eeacb49064d6ed9fe5760f22d34ea94c471814 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Fri, 28 Aug 2026 20:17:02 +0000 Subject: [PATCH 01/20] add pluggable crypto provider --- .github/workflows/ci.yml | 12 + Cargo.lock | 12 +- README.md | 20 + rcgen/Cargo.toml | 7 +- rcgen/examples/rsa-irc-openssl.rs | 4 +- rcgen/src/certificate.rs | 175 +++++++- rcgen/src/crl.rs | 75 +++- rcgen/src/crypto/aws_lc_rs.rs | 446 +++++++++++++++++++ rcgen/src/crypto/mod.rs | 195 +++++++++ rcgen/src/crypto/ring.rs | 281 ++++++++++++ rcgen/src/csr.rs | 77 +++- rcgen/src/error.rs | 14 +- rcgen/src/key_pair.rs | 705 +++++++++--------------------- rcgen/src/lib.rs | 101 ++++- rcgen/src/oid.rs | 2 - rcgen/src/ring_like.rs | 50 --- rcgen/src/sign_algo.rs | 84 +--- rcgen/tests/custom_provider.rs | 186 ++++++++ 18 files changed, 1756 insertions(+), 690 deletions(-) create mode 100644 rcgen/src/crypto/aws_lc_rs.rs create mode 100644 rcgen/src/crypto/mod.rs create mode 100644 rcgen/src/crypto/ring.rs delete mode 100644 rcgen/src/ring_like.rs create mode 100644 rcgen/tests/custom_provider.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1354ceb..a9691e37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,12 @@ jobs: - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets + - run: cargo clippy -p rcgen --no-default-features --features crypto,pem,x509-parser --all-targets + - name: Ensure custom-provider builds have no built-in crypto dependencies + run: | + if cargo tree -p rcgen --no-default-features --features crypto --edges normal,build | grep -E 'ring v|aws-lc'; then + exit 1 + fi - run: cargo clippy --no-default-features --features ring --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs_unstable,pem,x509-parser --all-targets @@ -73,6 +79,10 @@ jobs: run: cargo doc --features ring,pem,x509-parser --document-private-items env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} + - name: cargo doc (custom provider) + run: cargo doc -p rcgen --no-default-features --features crypto,pem,x509-parser + env: + RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (aws_lc_rs_unstable) run: cargo doc --features aws_lc_rs_unstable,pem,x509-parser --document-private-items env: @@ -165,6 +175,8 @@ jobs: run: cargo test --features x509-parser - name: Run the tests with aws_lc_rs backend enabled run: cargo test --no-default-features --features aws_lc_rs,pem + - name: Run the tests with a custom provider and no built-in backend + run: cargo test -p rcgen --no-default-features --features crypto,pem,x509-parser # rustls-cert-gen require either aws_lc_rs or ring feature - name: Run the tests with no features enabled run: cargo test -p rcgen --no-default-features diff --git a/Cargo.lock b/Cargo.lock index 2faca654..f31d1cb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,6 @@ checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", - "untrusted 0.7.1", "zeroize", ] @@ -874,7 +873,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -939,7 +938,7 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -1117,12 +1116,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -1293,7 +1286,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ "asn1-rs", - "aws-lc-rs", "data-encoding", "der-parser", "lazy_static", diff --git a/README.md b/README.md index df3830f1..b0bd6c70 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,26 @@ println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); ``` +## Pluggable cryptography providers + +Key generation, private-key loading, hashing, and CSR signature verification are selected through +`rcgen::crypto::CryptoProvider`. The `ring` and `aws_lc_rs` features provide built-in providers, +but neither backend is required. + +To use a completely separate cryptography implementation, disable default features and enable the +backend-neutral `crypto` feature: + +```toml +[dependencies] +rcgen = { version = "0.14", default-features = false, features = ["crypto", "pem"] } +``` + +Implement `KeyPairProvider`, `DigestProvider`, and `SignatureVerificationProvider`, assemble them +into a `CryptoProvider`, then either call `CryptoProvider::install_default()` once near process +startup or use the explicit `*_with_provider` APIs. In this configuration, neither `ring` nor +`aws-lc-rs` is present in rcgen's dependency graph. The `custom-provider` feature can additionally +disable automatic built-in selection if another dependency enables a built-in backend feature. + ## Trying it out with openssl You can do this: diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 68ad52d7..c0a5bd45 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -12,11 +12,12 @@ keywords.workspace = true [features] default = ["crypto", "pem", "ring"] -aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys", "x509-parser?/verify-aws"] -aws_lc_rs_unstable = ["aws_lc_rs"] # For backwards compatibility only +aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] +aws_lc_rs_unstable = ["aws_lc_rs", "aws-lc-rs/unstable"] # For backwards compatibility only +custom-provider = ["crypto"] fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] crypto = [] -ring = ["crypto", "dep:ring", "x509-parser?/verify"] +ring = ["crypto", "dep:ring"] [dependencies] aws-lc-rs = { workspace = true, optional = true } diff --git a/rcgen/examples/rsa-irc-openssl.rs b/rcgen/examples/rsa-irc-openssl.rs index aacb1791..d227abb0 100644 --- a/rcgen/examples/rsa-irc-openssl.rs +++ b/rcgen/examples/rsa-irc-openssl.rs @@ -18,8 +18,8 @@ fn main() -> Result<(), Box> { let pem_serialized = cert.pem(); let pem = pem::parse(&pem_serialized)?; let der_serialized = pem.contents(); - let hash = ring::digest::digest(&ring::digest::SHA512, der_serialized); - let hash_hex = hash.as_ref().iter().fold(String::new(), |mut output, b| { + let hash = openssl::sha::sha512(der_serialized); + let hash_hex = hash.iter().fold(String::new(), |mut output, b| { let _ = write!(output, "{b:02x}"); output }); diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 6c518018..ff22e114 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -9,10 +9,10 @@ use yasna::models::ObjectIdentifier; use yasna::{DERWriter, DERWriterSeq, Tag}; use crate::crl::CrlDistributionPoint; +#[cfg(feature = "crypto")] +use crate::crypto::{CryptoProvider, HashAlgorithm}; use crate::csr::CertificateSigningRequest; use crate::key_pair::{serialize_public_key_der, sign_der, PublicKeyData}; -#[cfg(feature = "crypto")] -use crate::ring_like::digest; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; use crate::{ @@ -147,6 +147,19 @@ impl CertificateParams { }) } + /// Generate a certificate using an explicit cryptography provider. + #[cfg(feature = "crypto")] + pub fn signed_by_with_provider( + &self, + public_key: &(impl PublicKeyData + ?Sized), + issuer: &Issuer<'_, impl SigningKey>, + provider: &CryptoProvider, + ) -> Result { + Ok(Certificate { + der: self.serialize_der_with_signer_with_provider(public_key, issuer, provider)?, + }) + } + /// Generates a new self-signed certificate from the given parameters. /// /// The returned [`Certificate`] may be serialized using [`Certificate::der`] and @@ -158,14 +171,55 @@ impl CertificateParams { }) } + /// Generate a self-signed certificate using an explicit cryptography provider. + #[cfg(feature = "crypto")] + pub fn self_signed_with_provider( + &self, + signing_key: &(impl SigningKey + ?Sized), + provider: &CryptoProvider, + ) -> Result { + let issuer = Issuer::from_params(self, signing_key); + Ok(Certificate { + der: self.serialize_der_with_signer_with_provider(signing_key, &issuer, provider)?, + }) + } + /// Calculates a subject key identifier for the certificate subject's public key. /// This key identifier is used in the SubjectKeyIdentifier X.509v3 extension. pub fn key_identifier(&self, key: &impl PublicKeyData) -> Vec { + #[cfg(feature = "crypto")] + { + let provider = CryptoProvider::get_default_or_install_from_crate_features() + .expect("a cryptography provider is required to derive a key identifier"); + self.key_identifier_with_provider(key, provider) + .expect("the cryptography provider failed to derive a key identifier") + } + #[cfg(not(feature = "crypto"))] self.key_identifier_method .derive(key.subject_public_key_info()) } - #[cfg(all(test, feature = "x509-parser"))] + /// Calculate a subject key identifier using an explicit cryptography provider. + #[cfg(feature = "crypto")] + pub fn key_identifier_with_provider( + &self, + key: &(impl PublicKeyData + ?Sized), + provider: &CryptoProvider, + ) -> Result, Error> { + self.key_identifier_method + .derive(provider, key.subject_public_key_info()) + } + + #[cfg(all( + test, + feature = "x509-parser", + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) + ))] pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) .map_err(|_| Error::CouldNotParseCertificate)?; @@ -249,21 +303,23 @@ impl CertificateParams { } /// Write a certificate's BasicConstraints as defined in RFC 5280. - fn write_ca_extensions(&self, writer: &mut DERWriterSeq, pub_key_spki: Option<&[u8]>) { + fn write_ca_extensions( + &self, + writer: &mut DERWriterSeq, + subject_key_identifier: Option<&[u8]>, + ) { let is_ca = match &self.is_ca { IsCa::Ca(bc) => Some(bc), IsCa::ExplicitNoCa => None, IsCa::NoCa => return, }; - if let Some(pub_key_spki) = pub_key_spki { + if let Some(subject_key_identifier) = subject_key_identifier { write_x509_extension( writer.next(), oid::SUBJECT_KEY_IDENTIFIER, false, - |writer| { - writer.write_bytes(&self.key_identifier_method.derive(pub_key_spki)); - }, + |writer| writer.write_bytes(subject_key_identifier), ); } @@ -432,10 +488,35 @@ impl CertificateParams { }) } - pub(crate) fn serialize_der_with_signer( + pub(crate) fn serialize_der_with_signer( &self, pub_key: &K, issuer: &Issuer<'_, impl SigningKey>, + ) -> Result, Error> { + #[cfg(feature = "crypto")] + { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + self.serialize_der_with_signer_with_provider(pub_key, issuer, provider) + } + #[cfg(not(feature = "crypto"))] + self.serialize_der_with_signer_inner(pub_key, issuer) + } + + #[cfg(feature = "crypto")] + pub(crate) fn serialize_der_with_signer_with_provider( + &self, + pub_key: &K, + issuer: &Issuer<'_, impl SigningKey>, + provider: &CryptoProvider, + ) -> Result, Error> { + self.serialize_der_with_signer_inner(pub_key, issuer, provider) + } + + fn serialize_der_with_signer_inner( + &self, + pub_key: &K, + issuer: &Issuer<'_, impl SigningKey>, + #[cfg(feature = "crypto")] provider: &CryptoProvider, ) -> Result, Error> { // An empty distribution point would be encoded as an empty fullName, // violating GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName @@ -460,9 +541,9 @@ impl CertificateParams { } else { #[cfg(feature = "crypto")] { - let hash = digest::digest(&digest::SHA256, pub_key.der_bytes()); + let hash = provider.digest(HashAlgorithm::Sha256, pub_key.der_bytes())?; // RFC 5280 specifies at most 20 bytes for a serial number - let mut sl = hash.as_ref()[0..20].to_vec(); + let mut sl = hash[0..20].to_vec(); sl[0] &= 0x7f; // MSB must be 0 to ensure encoding bignum in 20 bytes writer.next().write_bigint_bytes(&sl, true); } @@ -505,6 +586,11 @@ impl CertificateParams { } writer.next().write_tagged(Tag::context(3), |writer| { + #[cfg(feature = "crypto")] + return writer.write_sequence(|writer| { + self.write_extensions(writer, &pub_key_spki, issuer, provider) + }); + #[cfg(not(feature = "crypto"))] writer.write_sequence(|writer| self.write_extensions(writer, &pub_key_spki, issuer)) })?; @@ -519,6 +605,7 @@ impl CertificateParams { writer: &mut DERWriterSeq, pub_key_spki: &[u8], issuer: &Issuer<'_, impl SigningKey>, + #[cfg(feature = "crypto")] provider: &CryptoProvider, ) -> Result<(), Error> { if self.use_authority_key_identifier_extension { write_x509_authority_key_identifier( @@ -528,7 +615,7 @@ impl CertificateParams { #[cfg(feature = "crypto")] _ => issuer .key_identifier_method - .derive(issuer.signing_key.subject_public_key_info()), + .derive(provider, issuer.signing_key.subject_public_key_info())?, }, ); } @@ -576,7 +663,15 @@ impl CertificateParams { ); } - self.write_ca_extensions(writer, Some(pub_key_spki)); + let write_subject_key_identifier = !matches!(self.is_ca, IsCa::NoCa); + #[cfg(feature = "crypto")] + let subject_key_identifier = write_subject_key_identifier + .then(|| self.key_identifier_method.derive(provider, pub_key_spki)) + .transpose()?; + #[cfg(not(feature = "crypto"))] + let subject_key_identifier = + write_subject_key_identifier.then(|| self.key_identifier_method.derive(pub_key_spki)); + self.write_ca_extensions(writer, subject_key_identifier.as_deref()); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { @@ -768,7 +863,16 @@ pub enum ExtendedKeyUsagePurpose { } impl ExtendedKeyUsagePurpose { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 .extended_key_usage() @@ -833,7 +937,16 @@ pub struct NameConstraints { } impl NameConstraints { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) + ))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, ) -> Result, Error> { @@ -886,7 +999,16 @@ pub enum GeneralSubtree { } impl GeneralSubtree { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) + ))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], ) -> Result, Error> { @@ -1058,7 +1180,16 @@ pub enum IsCa { } impl IsCa { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 .basic_constraints() @@ -1107,7 +1238,15 @@ pub enum BasicConstraints { Constrained(u8), } -#[cfg(test)] +#[cfg(all( + test, + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) +))] mod tests { #[cfg(feature = "x509-parser")] use std::net::Ipv4Addr; diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index 3addf637..abe82acb 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -4,6 +4,8 @@ use pki_types::CertificateRevocationListDer; use time::OffsetDateTime; use yasna::{DERWriter, Tag}; +#[cfg(feature = "crypto")] +use crate::crypto::CryptoProvider; use crate::key_pair::sign_der; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; @@ -17,7 +19,7 @@ use crate::{ /// /// ## Example /// -/// ``` +/// ```no_run /// extern crate rcgen; /// use rcgen::*; /// @@ -188,6 +190,28 @@ impl CertificateRevocationListParams { &self, issuer: &Issuer<'_, impl SigningKey>, ) -> Result { + self.validate(issuer)?; + + Ok(CertificateRevocationList { + der: self.serialize_der(issuer)?.into(), + }) + } + + /// Serialize and sign this CRL using an explicit cryptography provider. + #[cfg(feature = "crypto")] + pub fn signed_by_with_provider( + &self, + issuer: &Issuer<'_, impl SigningKey>, + provider: &CryptoProvider, + ) -> Result { + self.validate(issuer)?; + + Ok(CertificateRevocationList { + der: self.serialize_der_with_provider(issuer, provider)?.into(), + }) + } + + fn validate(&self, issuer: &Issuer<'_, impl SigningKey>) -> Result<(), Error> { if self.next_update.le(&self.this_update) { return Err(Error::InvalidCrlNextUpdate); } @@ -206,13 +230,42 @@ impl CertificateRevocationListParams { { return Err(Error::EmptyCrlDistributionPointUris); } - - Ok(CertificateRevocationList { - der: self.serialize_der(issuer)?.into(), - }) + Ok(()) } fn serialize_der(&self, issuer: &Issuer<'_, impl SigningKey>) -> Result, Error> { + #[cfg(feature = "crypto")] + { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + self.serialize_der_with_provider(issuer, provider) + } + #[cfg(not(feature = "crypto"))] + self.serialize_der_inner(issuer) + } + + #[cfg(feature = "crypto")] + fn serialize_der_with_provider( + &self, + issuer: &Issuer<'_, impl SigningKey>, + provider: &CryptoProvider, + ) -> Result, Error> { + self.serialize_der_inner(issuer, provider) + } + + fn serialize_der_inner( + &self, + issuer: &Issuer<'_, impl SigningKey>, + #[cfg(feature = "crypto")] provider: &CryptoProvider, + ) -> Result, Error> { + #[cfg(feature = "crypto")] + let key_identifier = self + .key_identifier_method + .derive(provider, issuer.signing_key.subject_public_key_info())?; + #[cfg(not(feature = "crypto"))] + let key_identifier = self + .key_identifier_method + .derive(issuer.signing_key.subject_public_key_info()); + sign_der(&issuer.signing_key, |writer| { // Write CRL version. // RFC 5280 §5.1.2.1: @@ -273,11 +326,7 @@ impl CertificateRevocationListParams { writer.next().write_tagged(Tag::context(0), |writer| { writer.write_sequence(|writer| { // Write authority key identifier. - write_x509_authority_key_identifier( - writer.next(), - self.key_identifier_method - .derive(issuer.signing_key.subject_public_key_info()), - ); + write_x509_authority_key_identifier(writer.next(), key_identifier.clone()); // Write CRL number. write_x509_extension(writer.next(), oid::CRL_NUMBER, false, |writer| { @@ -422,7 +471,11 @@ impl RevokedCertParams { } } -#[cfg(all(test, feature = "crypto"))] +#[cfg(all( + test, + feature = "crypto", + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +))] mod tests { use x509_parser::num_bigint::BigUint; use x509_parser::{oid_registry, parse_x509_crl}; diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs new file mode 100644 index 00000000..b779eb26 --- /dev/null +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -0,0 +1,446 @@ +//! The built-in AWS-LC cryptography provider. + +use ::aws_lc_rs::digest; +use ::aws_lc_rs::encoding::AsDer; +use ::aws_lc_rs::rand::SystemRandom; +use ::aws_lc_rs::rsa::KeySize; +use ::aws_lc_rs::signature::{ + self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as _, RsaEncoding, RsaKeyPair, + VerificationAlgorithm, +}; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +use ::aws_lc_rs::unstable::signature::{ + PqdsaKeyPair, PqdsaSigningAlgorithm, ML_DSA_44, ML_DSA_44_SIGNING, ML_DSA_65, + ML_DSA_65_SIGNING, ML_DSA_87, ML_DSA_87_SIGNING, +}; +use pki_types::PrivateKeyDer; + +use super::{ + CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, +}; +use crate::{ + Error, KeyPair, PublicKeyData, RsaKeySize, SignatureAlgorithm, SigningKey, + PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ECDSA_P521_SHA256, PKCS_ECDSA_P521_SHA384, + PKCS_ECDSA_P521_SHA512, PKCS_ED25519, PKCS_RSA_SHA256, PKCS_RSA_SHA384, PKCS_RSA_SHA512, +}; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +use crate::{PKCS_ML_DSA_44, PKCS_ML_DSA_65, PKCS_ML_DSA_87}; + +/// Return rcgen's built-in AWS-LC provider. +pub fn default_provider() -> CryptoProvider { + CryptoProvider { + key_pair_provider: &AwsLcKeyPairProvider, + digest_provider: &AwsLcDigestProvider, + signature_verification_provider: &AwsLcSignatureVerificationProvider, + } +} + +#[derive(Debug)] +struct AwsLcDigestProvider; + +impl DigestProvider for AwsLcDigestProvider { + fn digest( + &self, + algorithm: HashAlgorithm, + input: &[u8], + output: &mut [u8], + ) -> Result<(), Error> { + let algorithm = match algorithm { + HashAlgorithm::Sha256 => &digest::SHA256, + HashAlgorithm::Sha384 => &digest::SHA384, + HashAlgorithm::Sha512 => &digest::SHA512, + }; + output.copy_from_slice(digest::digest(algorithm, input).as_ref()); + Ok(()) + } +} + +#[derive(Debug)] +struct AwsLcKeyPairProvider; + +impl AwsLcKeyPairProvider { + fn ecdsa_from_key( + algorithm: &'static signature::EcdsaSigningAlgorithm, + key_der: &[u8], + ) -> Result { + EcdsaKeyPair::from_private_key_der(algorithm, key_der) + .map_err(|e| Error::RingKeyRejected(e.to_string())) + } + + fn rsa_from_key(key_der: &[u8], is_pkcs8: bool) -> Result { + if is_pkcs8 { + RsaKeyPair::from_pkcs8(key_der) + } else { + RsaKeyPair::from_der(key_der) + } + .map_err(|e| Error::RingKeyRejected(e.to_string())) + } + + fn load_with_algorithm( + &self, + key_der: &[u8], + is_pkcs8: bool, + algorithm: &'static SignatureAlgorithm, + ) -> Result { + let kind = if algorithm == &PKCS_ED25519 { + AwsLcKeyKind::Ed( + Ed25519KeyPair::from_pkcs8_maybe_unchecked(key_der) + .map_err(|e| Error::RingKeyRejected(e.to_string()))?, + ) + } else if algorithm == &PKCS_ECDSA_P256_SHA256 { + AwsLcKeyKind::Ec(Self::ecdsa_from_key( + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + key_der, + )?) + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + AwsLcKeyKind::Ec(Self::ecdsa_from_key( + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + key_der, + )?) + } else if algorithm == &PKCS_ECDSA_P521_SHA256 { + AwsLcKeyKind::Ec(Self::ecdsa_from_key( + &signature::ECDSA_P521_SHA256_ASN1_SIGNING, + key_der, + )?) + } else if algorithm == &PKCS_ECDSA_P521_SHA384 { + AwsLcKeyKind::Ec(Self::ecdsa_from_key( + &signature::ECDSA_P521_SHA384_ASN1_SIGNING, + key_der, + )?) + } else if algorithm == &PKCS_ECDSA_P521_SHA512 { + AwsLcKeyKind::Ec(Self::ecdsa_from_key( + &signature::ECDSA_P521_SHA512_ASN1_SIGNING, + key_der, + )?) + } else if algorithm == &PKCS_RSA_SHA256 { + AwsLcKeyKind::Rsa( + Self::rsa_from_key(key_der, is_pkcs8)?, + &signature::RSA_PKCS1_SHA256, + ) + } else if algorithm == &PKCS_RSA_SHA384 { + AwsLcKeyKind::Rsa( + Self::rsa_from_key(key_der, is_pkcs8)?, + &signature::RSA_PKCS1_SHA384, + ) + } else if algorithm == &PKCS_RSA_SHA512 { + AwsLcKeyKind::Rsa( + Self::rsa_from_key(key_der, is_pkcs8)?, + &signature::RSA_PKCS1_SHA512, + ) + } else { + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + { + let signing_algorithm = if algorithm == &PKCS_ML_DSA_44 { + Some(&ML_DSA_44_SIGNING) + } else if algorithm == &PKCS_ML_DSA_65 { + Some(&ML_DSA_65_SIGNING) + } else if algorithm == &PKCS_ML_DSA_87 { + Some(&ML_DSA_87_SIGNING) + } else { + None + }; + if let Some(signing_algorithm) = signing_algorithm { + if !is_pkcs8 { + return Err(Error::CouldNotParseKeyPair); + } + return Ok(AwsLcSigningKey { + kind: AwsLcKeyKind::Pq( + PqdsaKeyPair::from_pkcs8(signing_algorithm, key_der) + .map_err(|e| Error::RingKeyRejected(e.to_string()))?, + ), + algorithm, + }); + } + } + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + Ok(AwsLcSigningKey { kind, algorithm }) + } + + fn detect(&self, key_der: &[u8], is_pkcs8: bool) -> Result { + for algorithm in [ + &PKCS_ED25519, + &PKCS_ECDSA_P256_SHA256, + &PKCS_ECDSA_P384_SHA384, + &PKCS_ECDSA_P521_SHA512, + &PKCS_RSA_SHA256, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + &PKCS_ML_DSA_44, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + &PKCS_ML_DSA_65, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + &PKCS_ML_DSA_87, + ] { + if let Ok(key) = self.load_with_algorithm(key_der, is_pkcs8, algorithm) { + return Ok(key); + } + } + Err(Error::CouldNotParseKeyPair) + } + + fn generate_ecdsa( + &self, + algorithm: &'static SignatureAlgorithm, + signing_algorithm: &'static signature::EcdsaSigningAlgorithm, + ) -> Result { + let document = EcdsaKeyPair::generate_pkcs8(signing_algorithm, &SystemRandom::new()) + .map_err(|_| Error::RingUnspecified)?; + let serialized_der = document.as_ref().to_vec(); + let signing_key = self.load_with_algorithm(&serialized_der, true, algorithm)?; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } + + fn generate_rsa_inner( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: KeySize, + ) -> Result { + if algorithm != &PKCS_RSA_SHA256 + && algorithm != &PKCS_RSA_SHA384 + && algorithm != &PKCS_RSA_SHA512 + { + return Err(Error::KeyGenerationUnavailable); + } + let key = RsaKeyPair::generate(key_size).map_err(|_| Error::RingUnspecified)?; + let serialized_der = key + .as_der() + .map_err(|_| Error::RingUnspecified)? + .as_ref() + .to_vec(); + let signing_key = self.load_with_algorithm(&serialized_der, true, algorithm)?; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } + + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + fn generate_pqdsa( + &self, + algorithm: &'static SignatureAlgorithm, + signing_algorithm: &'static PqdsaSigningAlgorithm, + ) -> Result { + let key = PqdsaKeyPair::generate(signing_algorithm).map_err(|_| Error::RingUnspecified)?; + let serialized_der = key + .to_pkcs8() + .map_err(|_| Error::RingUnspecified)? + .as_ref() + .to_vec(); + Ok(KeyPair::from_signing_key( + Box::new(AwsLcSigningKey { + kind: AwsLcKeyKind::Pq(key), + algorithm, + }), + serialized_der, + )) + } +} + +impl KeyPairProvider for AwsLcKeyPairProvider { + fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result { + if algorithm == &PKCS_ECDSA_P256_SHA256 { + self.generate_ecdsa(algorithm, &signature::ECDSA_P256_SHA256_ASN1_SIGNING) + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + self.generate_ecdsa(algorithm, &signature::ECDSA_P384_SHA384_ASN1_SIGNING) + } else if algorithm == &PKCS_ECDSA_P521_SHA256 { + self.generate_ecdsa(algorithm, &signature::ECDSA_P521_SHA256_ASN1_SIGNING) + } else if algorithm == &PKCS_ECDSA_P521_SHA384 { + self.generate_ecdsa(algorithm, &signature::ECDSA_P521_SHA384_ASN1_SIGNING) + } else if algorithm == &PKCS_ECDSA_P521_SHA512 { + self.generate_ecdsa(algorithm, &signature::ECDSA_P521_SHA512_ASN1_SIGNING) + } else if algorithm == &PKCS_ED25519 { + let document = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()) + .map_err(|_| Error::RingUnspecified)?; + let serialized_der = document.as_ref().to_vec(); + let signing_key = self.load_with_algorithm(&serialized_der, true, algorithm)?; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } else if algorithm == &PKCS_RSA_SHA256 + || algorithm == &PKCS_RSA_SHA384 + || algorithm == &PKCS_RSA_SHA512 + { + self.generate_rsa_inner(algorithm, KeySize::Rsa2048) + } else { + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + { + if algorithm == &PKCS_ML_DSA_44 { + return self.generate_pqdsa(algorithm, &ML_DSA_44_SIGNING); + } + if algorithm == &PKCS_ML_DSA_65 { + return self.generate_pqdsa(algorithm, &ML_DSA_65_SIGNING); + } + if algorithm == &PKCS_ML_DSA_87 { + return self.generate_pqdsa(algorithm, &ML_DSA_87_SIGNING); + } + } + Err(Error::UnsupportedSignatureAlgorithm) + } + } + + fn generate_rsa( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: RsaKeySize, + ) -> Result { + let key_size = match key_size { + RsaKeySize::_2048 => KeySize::Rsa2048, + RsaKeySize::_3072 => KeySize::Rsa3072, + RsaKeySize::_4096 => KeySize::Rsa4096, + }; + self.generate_rsa_inner(algorithm, key_size) + } + + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result { + let is_pkcs8 = matches!(key_der, PrivateKeyDer::Pkcs8(_)); + let serialized_der = key_der.secret_der().to_vec(); + let signing_key = match algorithm { + Some(algorithm) => self.load_with_algorithm(&serialized_der, is_pkcs8, algorithm)?, + None => self.detect(&serialized_der, is_pkcs8)?, + }; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } +} + +enum AwsLcKeyKind { + Ec(EcdsaKeyPair), + Ed(Ed25519KeyPair), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + Pq(PqdsaKeyPair), + Rsa(RsaKeyPair, &'static dyn RsaEncoding), +} + +struct AwsLcSigningKey { + kind: AwsLcKeyKind, + algorithm: &'static SignatureAlgorithm, +} + +impl PublicKeyData for AwsLcSigningKey { + fn der_bytes(&self) -> &[u8] { + match &self.kind { + AwsLcKeyKind::Ec(key) => key.public_key().as_ref(), + AwsLcKeyKind::Ed(key) => key.public_key().as_ref(), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + AwsLcKeyKind::Pq(key) => key.public_key().as_ref(), + AwsLcKeyKind::Rsa(key, _) => key.public_key().as_ref(), + } + } + + fn algorithm(&self) -> &'static SignatureAlgorithm { + self.algorithm + } +} + +impl SigningKey for AwsLcSigningKey { + fn sign(&self, message: &[u8]) -> Result, Error> { + match &self.kind { + AwsLcKeyKind::Ec(key) => key + .sign(&SystemRandom::new(), message) + .map(|signature| signature.as_ref().to_vec()) + .map_err(|_| Error::RingUnspecified), + AwsLcKeyKind::Ed(key) => Ok(key.sign(message).as_ref().to_vec()), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + AwsLcKeyKind::Pq(key) => { + let mut signature = vec![0; key.algorithm().signature_len()]; + key.sign(message, &mut signature) + .map_err(|_| Error::RingUnspecified)?; + Ok(signature) + }, + AwsLcKeyKind::Rsa(key, encoding) => { + let mut signature = vec![0; key.public_modulus_len()]; + key.sign(*encoding, &SystemRandom::new(), message, &mut signature) + .map_err(|_| Error::RingUnspecified)?; + Ok(signature) + }, + } + } +} + +#[derive(Debug)] +struct AwsLcSignatureVerificationProvider; + +impl SignatureVerificationProvider for AwsLcSignatureVerificationProvider { + fn verify( + &self, + algorithm: &'static SignatureAlgorithm, + public_key: &[u8], + message: &[u8], + signature_bytes: &[u8], + ) -> Result<(), Error> { + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + { + let pqdsa_algorithm = if algorithm == &PKCS_ML_DSA_44 { + Some(&ML_DSA_44) + } else if algorithm == &PKCS_ML_DSA_65 { + Some(&ML_DSA_65) + } else if algorithm == &PKCS_ML_DSA_87 { + Some(&ML_DSA_87) + } else { + None + }; + if let Some(pqdsa_algorithm) = pqdsa_algorithm { + return pqdsa_algorithm + .verify_sig(public_key, message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed); + } + } + + let verification_algorithm: &'static dyn VerificationAlgorithm = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + &signature::ECDSA_P256_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + &signature::ECDSA_P384_SHA384_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA256 { + &signature::ECDSA_P521_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA384 { + &signature::ECDSA_P521_SHA384_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA512 { + &signature::ECDSA_P521_SHA512_ASN1 + } else if algorithm == &PKCS_ED25519 { + &signature::ED25519 + } else if algorithm == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if algorithm == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if algorithm == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + signature::UnparsedPublicKey::new(verification_algorithm, public_key) + .verify(message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sha256_known_answer() { + assert_eq!( + default_provider() + .digest(HashAlgorithm::Sha256, b"abc") + .unwrap(), + [ + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, + 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad, + ] + ); + } +} diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs new file mode 100644 index 00000000..70141dda --- /dev/null +++ b/rcgen/src/crypto/mod.rs @@ -0,0 +1,195 @@ +//! Pluggable cryptography providers. +//! +//! rcgen keeps certificate encoding independent from cryptographic implementations. A +//! [`CryptoProvider`] supplies the operations rcgen performs itself: key generation and +//! loading, hashing, and signature verification when parsing certificate signing requests. +//! +//! Applications using a custom provider should disable rcgen's default features, enable +//! `crypto`, and install their provider before using convenience APIs such as +//! [`KeyPair::generate`](crate::KeyPair::generate): +//! +//! ```ignore +//! custom_provider().install_default() +//! .expect("a crypto provider was already installed"); +//! ``` +//! +//! A provider can also be passed explicitly to APIs whose names end in `with_provider`. +//! Explicit selection is useful for libraries and does not access the process-wide default. + +use std::fmt::Debug; +use std::sync::{Arc, OnceLock}; + +use pki_types::PrivateKeyDer; + +use crate::{Error, KeyPair, RsaKeySize, SignatureAlgorithm}; + +/// A hash algorithm required by rcgen. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum HashAlgorithm { + /// SHA-256. + Sha256, + /// SHA-384. + Sha384, + /// SHA-512. + Sha512, +} + +impl HashAlgorithm { + /// Return the digest output length in bytes. + pub const fn output_len(self) -> usize { + match self { + Self::Sha256 => 32, + Self::Sha384 => 48, + Self::Sha512 => 64, + } + } +} + +/// Hash operations supplied by a [`CryptoProvider`]. +pub trait DigestProvider: Debug + Send + Sync { + /// Hash `input` with `algorithm`, writing the digest to `output`. + /// + /// `output` is always exactly [`HashAlgorithm::output_len`] bytes long. + fn digest( + &self, + algorithm: HashAlgorithm, + input: &[u8], + output: &mut [u8], + ) -> Result<(), Error>; +} + +/// Key generation and private-key loading supplied by a [`CryptoProvider`]. +pub trait KeyPairProvider: Debug + Send + Sync { + /// Generate an exportable key pair for `algorithm`. + fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result; + + /// Generate an exportable RSA key pair of `key_size` for `algorithm`. + /// + /// Providers that do not support selectable RSA key sizes may retain the default + /// implementation. + fn generate_rsa( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: RsaKeySize, + ) -> Result { + let _ = (algorithm, key_size); + Err(Error::KeyGenerationUnavailable) + } + + /// Decode and validate an exportable private key. + /// + /// If `algorithm` is `Some`, the key must be loaded for exactly that signature algorithm. + /// If it is `None`, the provider detects a supported algorithm from the key. + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result; +} + +/// Signature verification supplied by a [`CryptoProvider`]. +/// +/// rcgen uses this operation to verify the self-signature on a parsed PKCS#10 certificate +/// signing request. Public keys are provided as the contents of the SubjectPublicKeyInfo +/// `subjectPublicKey` BIT STRING, matching [`PublicKeyData::der_bytes`](crate::PublicKeyData::der_bytes). +pub trait SignatureVerificationProvider: Debug + Send + Sync { + /// Verify `signature` over `message` using `public_key` and `algorithm`. + fn verify( + &self, + algorithm: &'static SignatureAlgorithm, + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), Error>; +} + +/// Controls the cryptography used by rcgen. +/// +/// The component fields can come from one backend or be composed from several backends. rcgen +/// provides built-in providers through `crypto::ring::default_provider()` and +/// `crypto::aws_lc_rs::default_provider()` when their corresponding crate features are enabled. A custom +/// provider does not require either dependency. +/// +/// # Process-wide default +/// +/// [`install_default`](Self::install_default) sets a provider once for convenience APIs. If no +/// provider has been installed, rcgen automatically installs a built-in provider selected by +/// crate features. As in earlier rcgen releases, AWS-LC takes precedence if both built-in backend +/// features are enabled. With no backend or the `custom-provider` feature, applications must +/// install a provider explicitly. +#[derive(Clone, Debug)] +pub struct CryptoProvider { + /// Provider for generating and loading private key pairs. + pub key_pair_provider: &'static dyn KeyPairProvider, + /// Provider for SHA-256, SHA-384, and SHA-512 hashing. + pub digest_provider: &'static dyn DigestProvider, + /// Provider for signature verification. + pub signature_verification_provider: &'static dyn SignatureVerificationProvider, +} + +impl CryptoProvider { + /// Set this provider as the default for the current process. + /// + /// This can succeed at most once during a process execution. Call it before invoking any + /// convenience API that uses the process-wide provider. + pub fn install_default(self) -> Result<(), Arc> { + PROCESS_DEFAULT_PROVIDER.set(Arc::new(self)) + } + + /// Return the process-wide default provider, if one has been installed. + pub fn get_default() -> Option<&'static Arc> { + PROCESS_DEFAULT_PROVIDER.get() + } + + /// Compute a digest using this provider. + pub fn digest(&self, algorithm: HashAlgorithm, input: &[u8]) -> Result, Error> { + let mut output = vec![0; algorithm.output_len()]; + self.digest_provider.digest(algorithm, input, &mut output)?; + Ok(output) + } + + pub(crate) fn get_default_or_install_from_crate_features() -> Result<&'static Arc, Error> + { + if let Some(provider) = Self::get_default() { + return Ok(provider); + } + + let provider = Self::from_crate_features().ok_or(Error::CryptoProviderNotInstalled)?; + // Another thread may install a provider first. In that case its choice wins. + let _ = provider.install_default(); + Self::get_default().ok_or(Error::CryptoProviderNotInstalled) + } + + fn from_crate_features() -> Option { + #[cfg(all( + feature = "ring", + not(any(feature = "aws_lc_rs", feature = "fips")), + not(feature = "custom-provider") + ))] + { + return Some(ring::default_provider()); + } + + #[cfg(all( + any(feature = "aws_lc_rs", feature = "fips"), + not(feature = "custom-provider") + ))] + { + return Some(aws_lc_rs::default_provider()); + } + + #[allow(unreachable_code)] + None + } +} + +static PROCESS_DEFAULT_PROVIDER: OnceLock> = OnceLock::new(); + +/// `ring`-based cryptography provider. +#[cfg(feature = "ring")] +pub mod ring; + +/// AWS-LC-based cryptography provider. +#[cfg(any(feature = "aws_lc_rs", feature = "fips"))] +pub mod aws_lc_rs; diff --git a/rcgen/src/crypto/ring.rs b/rcgen/src/crypto/ring.rs new file mode 100644 index 00000000..2bc645ba --- /dev/null +++ b/rcgen/src/crypto/ring.rs @@ -0,0 +1,281 @@ +//! The built-in `ring` cryptography provider. + +use ::ring::digest; +use ::ring::rand::SystemRandom; +use ::ring::signature::{ + self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as _, RsaEncoding, RsaKeyPair, + VerificationAlgorithm, +}; +use pki_types::PrivateKeyDer; + +use super::{ + CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, +}; +use crate::{ + Error, KeyPair, PublicKeyData, RsaKeySize, SignatureAlgorithm, SigningKey, + PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ED25519, PKCS_RSA_SHA256, PKCS_RSA_SHA384, + PKCS_RSA_SHA512, +}; + +/// Return rcgen's built-in `ring` provider. +pub fn default_provider() -> CryptoProvider { + CryptoProvider { + key_pair_provider: &RingKeyPairProvider, + digest_provider: &RingDigestProvider, + signature_verification_provider: &RingSignatureVerificationProvider, + } +} + +#[derive(Debug)] +struct RingDigestProvider; + +impl DigestProvider for RingDigestProvider { + fn digest( + &self, + algorithm: HashAlgorithm, + input: &[u8], + output: &mut [u8], + ) -> Result<(), Error> { + let algorithm = match algorithm { + HashAlgorithm::Sha256 => &digest::SHA256, + HashAlgorithm::Sha384 => &digest::SHA384, + HashAlgorithm::Sha512 => &digest::SHA512, + }; + output.copy_from_slice(digest::digest(algorithm, input).as_ref()); + Ok(()) + } +} + +#[derive(Debug)] +struct RingKeyPairProvider; + +impl RingKeyPairProvider { + fn ecdsa_from_pkcs8( + algorithm: &'static signature::EcdsaSigningAlgorithm, + pkcs8: &[u8], + ) -> Result { + EcdsaKeyPair::from_pkcs8(algorithm, pkcs8, &SystemRandom::new()) + .map_err(|e| Error::RingKeyRejected(e.to_string())) + } + + fn load_with_algorithm( + &self, + pkcs8: &[u8], + algorithm: &'static SignatureAlgorithm, + ) -> Result { + let kind = if algorithm == &PKCS_ED25519 { + RingKeyKind::Ed( + Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8) + .map_err(|e| Error::RingKeyRejected(e.to_string()))?, + ) + } else if algorithm == &PKCS_ECDSA_P256_SHA256 { + RingKeyKind::Ec(Self::ecdsa_from_pkcs8( + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + pkcs8, + )?) + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + RingKeyKind::Ec(Self::ecdsa_from_pkcs8( + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + pkcs8, + )?) + } else if algorithm == &PKCS_RSA_SHA256 { + RingKeyKind::Rsa( + RsaKeyPair::from_pkcs8(pkcs8).map_err(|e| Error::RingKeyRejected(e.to_string()))?, + &signature::RSA_PKCS1_SHA256, + ) + } else if algorithm == &PKCS_RSA_SHA384 { + RingKeyKind::Rsa( + RsaKeyPair::from_pkcs8(pkcs8).map_err(|e| Error::RingKeyRejected(e.to_string()))?, + &signature::RSA_PKCS1_SHA384, + ) + } else if algorithm == &PKCS_RSA_SHA512 { + RingKeyKind::Rsa( + RsaKeyPair::from_pkcs8(pkcs8).map_err(|e| Error::RingKeyRejected(e.to_string()))?, + &signature::RSA_PKCS1_SHA512, + ) + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + Ok(RingSigningKey { kind, algorithm }) + } + + fn detect(&self, pkcs8: &[u8]) -> Result { + for algorithm in [ + &PKCS_ED25519, + &PKCS_ECDSA_P256_SHA256, + &PKCS_ECDSA_P384_SHA384, + &PKCS_RSA_SHA256, + ] { + if let Ok(key) = self.load_with_algorithm(pkcs8, algorithm) { + return Ok(key); + } + } + Err(Error::CouldNotParseKeyPair) + } +} + +impl KeyPairProvider for RingKeyPairProvider { + fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result { + let rng = SystemRandom::new(); + let (signing_key, serialized_der) = if algorithm == &PKCS_ECDSA_P256_SHA256 { + let document = + EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, &rng) + .map_err(|_| Error::RingUnspecified)?; + ( + self.load_with_algorithm(document.as_ref(), algorithm)?, + document.as_ref().to_vec(), + ) + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + let document = + EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, &rng) + .map_err(|_| Error::RingUnspecified)?; + ( + self.load_with_algorithm(document.as_ref(), algorithm)?, + document.as_ref().to_vec(), + ) + } else if algorithm == &PKCS_ED25519 { + let document = + Ed25519KeyPair::generate_pkcs8(&rng).map_err(|_| Error::RingUnspecified)?; + ( + self.load_with_algorithm(document.as_ref(), algorithm)?, + document.as_ref().to_vec(), + ) + } else if algorithm == &PKCS_RSA_SHA256 + || algorithm == &PKCS_RSA_SHA384 + || algorithm == &PKCS_RSA_SHA512 + { + return Err(Error::KeyGenerationUnavailable); + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } + + fn generate_rsa( + &self, + _algorithm: &'static SignatureAlgorithm, + _key_size: RsaKeySize, + ) -> Result { + Err(Error::KeyGenerationUnavailable) + } + + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result { + let PrivateKeyDer::Pkcs8(pkcs8) = key_der else { + return Err(Error::CouldNotParseKeyPair); + }; + let serialized_der = pkcs8.secret_pkcs8_der().to_vec(); + let signing_key = match algorithm { + Some(algorithm) => self.load_with_algorithm(&serialized_der, algorithm)?, + None => self.detect(&serialized_der)?, + }; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } +} + +enum RingKeyKind { + Ec(EcdsaKeyPair), + Ed(Ed25519KeyPair), + Rsa(RsaKeyPair, &'static dyn RsaEncoding), +} + +struct RingSigningKey { + kind: RingKeyKind, + algorithm: &'static SignatureAlgorithm, +} + +impl PublicKeyData for RingSigningKey { + fn der_bytes(&self) -> &[u8] { + match &self.kind { + RingKeyKind::Ec(key) => key.public_key().as_ref(), + RingKeyKind::Ed(key) => key.public_key().as_ref(), + RingKeyKind::Rsa(key, _) => key.public_key().as_ref(), + } + } + + fn algorithm(&self) -> &'static SignatureAlgorithm { + self.algorithm + } +} + +impl SigningKey for RingSigningKey { + fn sign(&self, message: &[u8]) -> Result, Error> { + match &self.kind { + RingKeyKind::Ec(key) => key + .sign(&SystemRandom::new(), message) + .map(|signature| signature.as_ref().to_vec()) + .map_err(|_| Error::RingUnspecified), + RingKeyKind::Ed(key) => Ok(key.sign(message).as_ref().to_vec()), + RingKeyKind::Rsa(key, encoding) => { + let mut signature = vec![0; key.public().modulus_len()]; + key.sign(*encoding, &SystemRandom::new(), message, &mut signature) + .map_err(|_| Error::RingUnspecified)?; + Ok(signature) + }, + } + } +} + +#[derive(Debug)] +struct RingSignatureVerificationProvider; + +impl SignatureVerificationProvider for RingSignatureVerificationProvider { + fn verify( + &self, + algorithm: &'static SignatureAlgorithm, + public_key: &[u8], + message: &[u8], + signature_bytes: &[u8], + ) -> Result<(), Error> { + let verification_algorithm: &'static dyn VerificationAlgorithm = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + &signature::ECDSA_P256_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + &signature::ECDSA_P384_SHA384_ASN1 + } else if algorithm == &PKCS_ED25519 { + &signature::ED25519 + } else if algorithm == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if algorithm == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if algorithm == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + signature::UnparsedPublicKey::new(verification_algorithm, public_key) + .verify(message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sha256_known_answer() { + assert_eq!( + default_provider() + .digest(HashAlgorithm::Sha256, b"abc") + .unwrap(), + [ + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, + 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad, + ] + ); + } +} diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index 28bd5c34..192315ba 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -4,6 +4,8 @@ use std::hash::Hash; use pem::Pem; use pki_types::CertificateSigningRequestDer; +#[cfg(feature = "crypto")] +use crate::crypto::CryptoProvider; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; use crate::{ @@ -83,10 +85,17 @@ impl CertificateSigningRequestParams { /// Parse and verify a certificate signing request from the ASCII PEM format /// /// See [`from_der`](Self::from_der) for more details. - #[cfg(all(feature = "pem", feature = "x509-parser"))] + #[cfg(all(feature = "pem", feature = "x509-parser", feature = "crypto"))] pub fn from_pem(pem_str: &str) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_pem_with_provider(pem_str, provider) + } + + /// Parse and verify a certificate signing request from PEM using `provider`. + #[cfg(all(feature = "pem", feature = "x509-parser", feature = "crypto"))] + pub fn from_pem_with_provider(pem_str: &str, provider: &CryptoProvider) -> Result { let csr = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificationRequest)?; - Self::from_der(&csr.contents().into()) + Self::from_der_with_provider(&csr.contents().into(), provider) } /// Parse and verify a certificate signing request from DER-encoded bytes @@ -106,24 +115,58 @@ impl CertificateSigningRequestParams { /// into [`CertificateSigningRequestDer`] using the [`Into`] trait. /// /// [`PemObject`]: pki_types::pem::PemObject - #[cfg(feature = "x509-parser")] + #[cfg(all(feature = "x509-parser", feature = "crypto"))] pub fn from_der(csr: &CertificateSigningRequestDer<'_>) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_der_with_provider(csr, provider) + } + + /// Parse and verify a certificate signing request from DER using `provider`. + #[cfg(all(feature = "x509-parser", feature = "crypto"))] + pub fn from_der_with_provider( + csr: &CertificateSigningRequestDer<'_>, + provider: &CryptoProvider, + ) -> Result { use x509_parser::prelude::FromDer; + use x509_parser::x509::AlgorithmIdentifier; let csr = x509_parser::certification_request::X509CertificationRequest::from_der(csr) .map_err(|_| Error::CouldNotParseCertificationRequest)? .1; - csr.verify_signature() - .map_err(|_| Error::InvalidCertificationRequestSignature)?; let alg_oid = csr .signature_algorithm .algorithm .iter() .ok_or(Error::CouldNotParseCertificationRequest)? .collect::>(); - let alg = SignatureAlgorithm::from_oid(&alg_oid)?; let info = &csr.certification_request_info; + let alg = SignatureAlgorithm::iter() + .find(|alg| { + if !alg.matches_signature_oid(&alg_oid) { + return false; + } + let bytes = yasna::construct_der(|writer| alg.write_oids_sign_alg(writer)); + let Ok((rest, public_key_algorithm)) = AlgorithmIdentifier::from_der(&bytes) else { + return false; + }; + rest.is_empty() && public_key_algorithm == info.subject_pki.algorithm + }) + .ok_or(Error::UnsupportedSignatureAlgorithm)?; + + provider + .signature_verification_provider + .verify( + alg, + info.subject_pki.subject_public_key.data.as_ref(), + info.raw, + csr.signature_value.data.as_ref(), + ) + .map_err(|error| match error { + Error::UnsupportedSignatureAlgorithm => error, + _ => Error::InvalidCertificationRequestSignature, + })?; + let mut params = CertificateParams { distinguished_name: DistinguishedName::from_name(&info.subject)?, ..CertificateParams::default() @@ -210,9 +253,29 @@ impl CertificateSigningRequestParams { .serialize_der_with_signer(&self.public_key, issuer)?, }) } + + /// Generate a certificate using an explicit cryptography provider. + #[cfg(feature = "crypto")] + pub fn signed_by_with_provider( + &self, + issuer: &Issuer, + provider: &CryptoProvider, + ) -> Result { + Ok(Certificate { + der: self.params.serialize_der_with_signer_with_provider( + &self.public_key, + issuer, + provider, + )?, + }) + } } -#[cfg(all(test, feature = "x509-parser"))] +#[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +))] mod tests { use x509_parser::certification_request::X509CertificationRequest; use x509_parser::prelude::{FromDer, ParsedExtension}; diff --git a/rcgen/src/error.rs b/rcgen/src/error.rs index 9ba0b30e..d113c24a 100644 --- a/rcgen/src/error.rs +++ b/rcgen/src/error.rs @@ -10,6 +10,11 @@ pub enum Error { CouldNotParseCertificationRequest, /// The given key pair couldn't be parsed CouldNotParseKeyPair, + /// No process-wide cryptography provider has been installed and crate features do not select + /// exactly one built-in provider. + CryptoProviderNotInstalled, + /// A cryptography provider failed an operation. + CryptoProviderError(String), /// The CSR signature is invalid #[cfg(feature = "x509-parser")] InvalidCertificationRequestSignature, @@ -28,6 +33,8 @@ pub enum Error { UnsupportedExtension, /// The requested signature algorithm is not supported UnsupportedSignatureAlgorithm, + /// A signature failed cryptographic verification. + SignatureVerificationFailed, /// Unspecified `ring` error RingUnspecified, /// The `ring` library rejected the key upon loading @@ -66,6 +73,10 @@ impl fmt::Display for Error { request" )?, CouldNotParseKeyPair => write!(f, "Could not parse key pair")?, + CryptoProviderNotInstalled => { + write!(f, "No process-wide cryptography provider is installed")? + }, + CryptoProviderError(e) => write!(f, "Cryptography provider error: {e}")?, #[cfg(feature = "x509-parser")] InvalidCertificationRequestSignature => write!(f, "Invalid CSR signature")?, #[cfg(feature = "x509-parser")] @@ -84,6 +95,7 @@ impl fmt::Display for Error { "The requested signature algorithm \ is not supported" )?, + SignatureVerificationFailed => write!(f, "Signature verification failed")?, #[cfg(feature = "x509-parser")] UnsupportedExtension => write!(f, "Unsupported extension requested in CSR")?, RingUnspecified => write!(f, "Unspecified ring error")?, @@ -147,7 +159,7 @@ impl fmt::Display for InvalidAsn1String { /// /// We use this trait to avoid leaking external error types into the public API /// through a `From for Error` implementation. -#[cfg(any(feature = "crypto", feature = "pem"))] +#[cfg(feature = "pem")] pub(crate) trait ExternalError: Sized { fn _err(self) -> Result; } diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 84df1678..d3e7f57b 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -1,69 +1,31 @@ #[cfg(feature = "crypto")] use std::fmt; -#[cfg(feature = "aws_lc_rs")] -use aws_lc_rs::signature::PqdsaKeyPair; #[cfg(feature = "pem")] use pem::Pem; #[cfg(feature = "crypto")] use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; use yasna::{DERWriter, DERWriterSeq}; -#[cfg(any(feature = "crypto", feature = "pem"))] +#[cfg(feature = "crypto")] +use crate::crypto::CryptoProvider; +#[cfg(feature = "pem")] use crate::error::ExternalError; -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] -use crate::ring_like::ecdsa_from_private_key_der; -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] -use crate::ring_like::rsa::KeySize; #[cfg(feature = "crypto")] -use crate::ring_like::{ - error as ring_error, - rand::SystemRandom, - signature::{ - self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as RingKeyPair, RsaEncoding, RsaKeyPair, - }, - {ecdsa_from_pkcs8, rsa_key_pair_public_modulus_len}, -}; +use crate::sign_algo::algo::*; use crate::sign_algo::SignatureAlgorithm; -#[cfg(feature = "crypto")] -use crate::sign_algo::{algo::*, SignAlgo}; use crate::Error; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; -/// A key pair variant -#[allow(clippy::large_enum_variant)] -#[cfg(feature = "crypto")] -pub(crate) enum KeyPairKind { - /// A Ecdsa key pair - Ec(EcdsaKeyPair), - /// A Ed25519 key pair - Ed(Ed25519KeyPair), - /// A Pqdsa key pair - #[cfg(feature = "aws_lc_rs")] - Pq(PqdsaKeyPair), - /// A RSA key pair - Rsa(RsaKeyPair, &'static dyn RsaEncoding), -} - -#[cfg(feature = "crypto")] -impl fmt::Debug for KeyPairKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Ec(key_pair) => write!(f, "{key_pair:?}"), - Self::Ed(key_pair) => write!(f, "{key_pair:?}"), - #[cfg(feature = "aws_lc_rs")] - Self::Pq(key_pair) => write!(f, "{key_pair:?}"), - Self::Rsa(key_pair, _) => write!(f, "{key_pair:?}"), - } - } -} - -/// A key pair used to sign certificates and CSRs +/// A key pair used to sign certificates and CSRs. +/// +/// `KeyPair` is independent of a concrete cryptography library. Its implementation is created by +/// the selected [`CryptoProvider`], while this type retains the stable rcgen API and exportable +/// private-key bytes. #[cfg(feature = "crypto")] pub struct KeyPair { - pub(crate) kind: KeyPairKind, - pub(crate) alg: &'static SignatureAlgorithm, + pub(crate) signing_key: Box, pub(crate) serialized_der: Vec, } @@ -71,8 +33,7 @@ pub struct KeyPair { impl fmt::Debug for KeyPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("KeyPair") - .field("kind", &self.kind) - .field("alg", &self.alg) + .field("alg", &self.algorithm()) .field("serialized_der", &"[secret key elided]") .finish() } @@ -80,347 +41,198 @@ impl fmt::Debug for KeyPair { #[cfg(feature = "crypto")] impl KeyPair { - /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair - #[cfg(feature = "crypto")] + /// Construct a key pair from a provider-specific signing key and its PKCS#8 DER encoding. + /// + /// This constructor is intended for implementations of + /// [`KeyPairProvider`](crate::crypto::KeyPairProvider). `serialized_der` must encode the same + /// private key exposed by `signing_key` and must remain exportable to callers. + pub fn from_signing_key( + signing_key: Box, + serialized_der: Vec, + ) -> Self { + Self { + signing_key, + serialized_der, + } + } + + /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair. pub fn generate() -> Result { Self::generate_for(&PKCS_ECDSA_P256_SHA256) } - /// Generate a new random key pair for the specified signature algorithm + /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair with `provider`. + pub fn generate_with_provider(provider: &CryptoProvider) -> Result { + Self::generate_for_with_provider(&PKCS_ECDSA_P256_SHA256, provider) + } + + /// Generate a new random key pair for the specified signature algorithm. /// - /// If you're not sure which algorithm to use, [`PKCS_ECDSA_P256_SHA256`] is a good choice. - /// If passed an RSA signature algorithm, it depends on the backend whether we return - /// a generated key or an error for key generation being unavailable. - /// Currently, only `aws-lc-rs` supports RSA key generation. - #[cfg(feature = "crypto")] + /// If no process-wide provider has been installed, a built-in provider is selected only when + /// a built-in backend feature is enabled. AWS-LC takes precedence if both are enabled. pub fn generate_for(alg: &'static SignatureAlgorithm) -> Result { - let rng = &SystemRandom::new(); - - match alg.sign_alg { - SignAlgo::EcDsa(sign_alg) => { - let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, rng)._err()?; - let key_pair_serialized = key_pair_doc.as_ref().to_vec(); - - let key_pair = ecdsa_from_pkcs8(sign_alg, key_pair_doc.as_ref(), rng).unwrap(); - Ok(KeyPair { - kind: KeyPairKind::Ec(key_pair), - alg, - serialized_der: key_pair_serialized, - }) - }, - SignAlgo::EdDsa(_sign_alg) => { - let key_pair_doc = Ed25519KeyPair::generate_pkcs8(rng)._err()?; - let key_pair_serialized = key_pair_doc.as_ref().to_vec(); - - let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); - Ok(KeyPair { - kind: KeyPairKind::Ed(key_pair), - alg, - serialized_der: key_pair_serialized, - }) - }, - #[cfg(feature = "aws_lc_rs")] - SignAlgo::PqDsa(sign_alg) => { - let key_pair = PqdsaKeyPair::generate(sign_alg)._err()?; - let key_pair_serialized = key_pair.to_pkcs8v1()._err()?.as_ref().to_vec(); - - Ok(KeyPair { - kind: KeyPairKind::Pq(key_pair), - alg, - serialized_der: key_pair_serialized, - }) - }, - #[cfg(feature = "aws_lc_rs")] - SignAlgo::Rsa(sign_alg) => Self::generate_rsa_inner(alg, sign_alg, KeySize::Rsa2048), - // Ring doesn't have RSA key generation yet: - // https://github.com/briansmith/ring/issues/219 - // https://github.com/briansmith/ring/pull/733 - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] - SignAlgo::Rsa(_sign_alg) => Err(Error::KeyGenerationUnavailable), - } + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::generate_for_with_provider(alg, provider) } - /// Generates a new random RSA key pair for the specified key size - /// - /// If passed a signature algorithm that is not RSA, it will return - /// [`Error::KeyGenerationUnavailable`]. - #[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] + /// Generate a new random key pair using `provider`. + pub fn generate_for_with_provider( + alg: &'static SignatureAlgorithm, + provider: &CryptoProvider, + ) -> Result { + provider.key_pair_provider.generate(alg) + } + + /// Generate a new random RSA key pair for the specified key size. pub fn generate_rsa_for( alg: &'static SignatureAlgorithm, key_size: RsaKeySize, ) -> Result { - match alg.sign_alg { - SignAlgo::Rsa(sign_alg) => { - let key_size = match key_size { - RsaKeySize::_2048 => KeySize::Rsa2048, - RsaKeySize::_3072 => KeySize::Rsa3072, - RsaKeySize::_4096 => KeySize::Rsa4096, - }; - Self::generate_rsa_inner(alg, sign_alg, key_size) - }, - _ => Err(Error::KeyGenerationUnavailable), - } + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::generate_rsa_for_with_provider(alg, key_size, provider) } - #[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] - fn generate_rsa_inner( + /// Generate a new random RSA key pair of `key_size` using `provider`. + pub fn generate_rsa_for_with_provider( alg: &'static SignatureAlgorithm, - sign_alg: &'static dyn RsaEncoding, - key_size: KeySize, + key_size: RsaKeySize, + provider: &CryptoProvider, ) -> Result { - use aws_lc_rs::encoding::AsDer; - let key_pair = RsaKeyPair::generate(key_size)._err()?; - let key_pair_serialized = key_pair.as_der()._err()?.as_ref().to_vec(); - - Ok(KeyPair { - kind: KeyPairKind::Rsa(key_pair, sign_alg), - alg, - serialized_der: key_pair_serialized, - }) + provider.key_pair_provider.generate_rsa(alg, key_size) } - /// Returns the key pair's signature algorithm + /// Returns the key pair's signature algorithm. pub fn algorithm(&self) -> &'static SignatureAlgorithm { - self.alg + self.signing_key.algorithm() } - /// Parses the key pair from the ASCII PEM format - /// - /// If `aws_lc_rs` feature is used, then the key must be a DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958, SEC1/RFC 5915, or PKCS#1/RFC 3447; - /// Appears as "PRIVATE KEY", "RSA PRIVATE KEY", or "EC PRIVATE KEY" in PEM files. - /// - /// Otherwise if the `ring` feature is used, then the key must be a DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958; - /// Appears as "PRIVATE KEY" in PEM files. - #[cfg(all(feature = "pem", feature = "crypto"))] + /// Parse a key pair from ASCII PEM using the process-wide provider. + #[cfg(feature = "pem")] pub fn from_pem(pem_str: &str) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_pem_with_provider(pem_str, provider) + } + + /// Parse a key pair from ASCII PEM using `provider`. + #[cfg(feature = "pem")] + pub fn from_pem_with_provider(pem_str: &str, provider: &CryptoProvider) -> Result { let private_key = pem::parse(pem_str)._err()?; - Self::try_from(private_key.contents()) + let private_key = PrivateKeyDer::try_from(private_key.into_contents()) + .map_err(|_| Error::CouldNotParseKeyPair)?; + Self::from_der_with_provider(&private_key, provider) } - /// Obtains the key pair from a DER formatted key - /// using the specified [`SignatureAlgorithm`] - /// - /// The key must be a DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958; - /// - /// Appears as "PRIVATE KEY" in PEM files - /// Same as [from_pkcs8_pem_and_sign_algo](Self::from_pkcs8_pem_and_sign_algo). - #[cfg(all(feature = "pem", feature = "crypto"))] + /// Parse a PKCS#8 PEM key for a specified signature algorithm. + #[cfg(feature = "pem")] pub fn from_pkcs8_pem_and_sign_algo( pem_str: &str, alg: &'static SignatureAlgorithm, + ) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_pkcs8_pem_and_sign_algo_with_provider(pem_str, alg, provider) + } + + /// Parse a PKCS#8 PEM key for `alg` using `provider`. + #[cfg(feature = "pem")] + pub fn from_pkcs8_pem_and_sign_algo_with_provider( + pem_str: &str, + alg: &'static SignatureAlgorithm, + provider: &CryptoProvider, ) -> Result { let private_key = pem::parse(pem_str)._err()?; - let private_key_der: &[_] = private_key.contents(); - Self::from_pkcs8_der_and_sign_algo(&PrivatePkcs8KeyDer::from(private_key_der), alg) + let private_key = PrivatePkcs8KeyDer::from(private_key.into_contents()); + Self::from_pkcs8_der_and_sign_algo_with_provider(&private_key, alg, provider) } - /// Obtains the key pair from a DER formatted key using the specified [`SignatureAlgorithm`] - /// - /// If you have a [`PrivatePkcs8KeyDer`], you can usually rely on the [`TryFrom`] implementation - /// to obtain a [`KeyPair`] -- it will determine the correct [`SignatureAlgorithm`] for you. - /// However, sometimes multiple signature algorithms fit for the same DER key. In those instances, - /// you can use this function to precisely specify the `SignatureAlgorithm`. - /// - /// [`rustls_pemfile::private_key()`] is often used to obtain a [`PrivateKeyDer`] from PEM - /// input. If the obtained [`PrivateKeyDer`] is a `Pkcs8` variant, you can use its contents - /// as input for this function. Alternatively, if you already have a byte slice containing DER, - /// it can trivially be converted into [`PrivatePkcs8KeyDer`] using the [`Into`] trait. - /// - /// [`rustls_pemfile::private_key()`]: https://docs.rs/rustls-pemfile/latest/rustls_pemfile/fn.private_key.html - /// [`PrivateKeyDer`]: https://docs.rs/rustls-pki-types/latest/rustls_pki_types/enum.PrivateKeyDer.html - #[cfg(feature = "crypto")] + /// Parse a PKCS#8 DER key for a specified signature algorithm. pub fn from_pkcs8_der_and_sign_algo( pkcs8: &PrivatePkcs8KeyDer<'_>, alg: &'static SignatureAlgorithm, ) -> Result { - let rng = &SystemRandom::new(); - let serialized_der = pkcs8.secret_pkcs8_der().to_vec(); - - let kind = if alg == &PKCS_ED25519 { - KeyPairKind::Ed(Ed25519KeyPair::from_pkcs8_maybe_unchecked(&serialized_der)._err()?) - } else if alg == &PKCS_ECDSA_P256_SHA256 { - KeyPairKind::Ec(ecdsa_from_pkcs8( - &signature::ECDSA_P256_SHA256_ASN1_SIGNING, - &serialized_der, - rng, - )?) - } else if alg == &PKCS_ECDSA_P384_SHA384 { - KeyPairKind::Ec(ecdsa_from_pkcs8( - &signature::ECDSA_P384_SHA384_ASN1_SIGNING, - &serialized_der, - rng, - )?) - } else if alg == &PKCS_RSA_SHA256 { - let rsakp = RsaKeyPair::from_pkcs8(&serialized_der)._err()?; - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256) - } else if alg == &PKCS_RSA_SHA384 { - let rsakp = RsaKeyPair::from_pkcs8(&serialized_der)._err()?; - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA384) - } else if alg == &PKCS_RSA_SHA512 { - let rsakp = RsaKeyPair::from_pkcs8(&serialized_der)._err()?; - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA512) - } else { - #[cfg(feature = "aws_lc_rs")] - if alg == &PKCS_ECDSA_P521_SHA256 { - KeyPairKind::Ec(ecdsa_from_pkcs8( - &signature::ECDSA_P521_SHA256_ASN1_SIGNING, - &serialized_der, - rng, - )?) - } else if alg == &PKCS_ECDSA_P521_SHA384 { - KeyPairKind::Ec(ecdsa_from_pkcs8( - &signature::ECDSA_P521_SHA384_ASN1_SIGNING, - &serialized_der, - rng, - )?) - } else if alg == &PKCS_ECDSA_P521_SHA512 { - KeyPairKind::Ec(ecdsa_from_pkcs8( - &signature::ECDSA_P521_SHA512_ASN1_SIGNING, - &serialized_der, - rng, - )?) - } else { - panic!("Unknown SignatureAlgorithm specified!"); - } - - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] - panic!("Unknown SignatureAlgorithm specified!"); - }; - - Ok(KeyPair { - kind, - alg, - serialized_der, - }) + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_pkcs8_der_and_sign_algo_with_provider(pkcs8, alg, provider) } - /// Obtains the key pair from a PEM formatted key - /// using the specified [`SignatureAlgorithm`] - /// - /// If `aws_lc_rs` feature is used, then the key must be a DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958, SEC1/RFC 5915, or PKCS#1/RFC 3447; - /// Appears as "PRIVATE KEY", "RSA PRIVATE KEY", or "EC PRIVATE KEY" in PEM files. - /// - /// Otherwise if the `ring` feature is used, then the key must be a DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958; - /// Appears as "PRIVATE KEY" in PEM files. - /// - /// Same as [from_pem_and_sign_algo](Self::from_pem_and_sign_algo). - #[cfg(all(feature = "pem", feature = "crypto"))] + /// Parse a PKCS#8 DER key for `alg` using `provider`. + pub fn from_pkcs8_der_and_sign_algo_with_provider( + pkcs8: &PrivatePkcs8KeyDer<'_>, + alg: &'static SignatureAlgorithm, + provider: &CryptoProvider, + ) -> Result { + provider + .key_pair_provider + .load_private_key(PrivateKeyDer::Pkcs8(pkcs8.clone_key()), Some(alg)) + } + + /// Parse a PEM key for a specified signature algorithm. + #[cfg(feature = "pem")] pub fn from_pem_and_sign_algo( pem_str: &str, alg: &'static SignatureAlgorithm, + ) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_pem_and_sign_algo_with_provider(pem_str, alg, provider) + } + + /// Parse a PEM key for `alg` using `provider`. + #[cfg(feature = "pem")] + pub fn from_pem_and_sign_algo_with_provider( + pem_str: &str, + alg: &'static SignatureAlgorithm, + provider: &CryptoProvider, ) -> Result { let private_key = pem::parse(pem_str)._err()?; - let private_key: &[_] = private_key.contents(); - Self::from_der_and_sign_algo( - &PrivateKeyDer::try_from(private_key).map_err(|_| Error::CouldNotParseKeyPair)?, - alg, - ) + let private_key = PrivateKeyDer::try_from(private_key.into_contents()) + .map_err(|_| Error::CouldNotParseKeyPair)?; + Self::from_der_and_sign_algo_with_provider(&private_key, alg, provider) } - /// Obtains the key pair from a DER formatted key - /// using the specified [`SignatureAlgorithm`] - /// - /// Note that using the `ring` feature, this function only support [`PrivateKeyDer::Pkcs8`] variant. - /// Consider using the `aws_lc_rs` features to support [`PrivateKeyDer`] fully. - /// - /// If you have a [`PrivateKeyDer`], you can usually rely on the [`TryFrom`] implementation - /// to obtain a [`KeyPair`] -- it will determine the correct [`SignatureAlgorithm`] for you. - /// However, sometimes multiple signature algorithms fit for the same DER key. In those instances, - /// you can use this function to precisely specify the `SignatureAlgorithm`. - /// - /// You can use [`rustls_pemfile::private_key`] to get the `key` input. If - /// you have already a byte slice, just calling `try_into()` will convert it to a [`PrivateKeyDer`]. - /// - /// [`rustls_pemfile::private_key`]: https://docs.rs/rustls-pemfile/latest/rustls_pemfile/fn.private_key.html - #[cfg(feature = "crypto")] + /// Parse a DER key for a specified signature algorithm. pub fn from_der_and_sign_algo( key: &PrivateKeyDer<'_>, alg: &'static SignatureAlgorithm, ) -> Result { - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] - { - if let PrivateKeyDer::Pkcs8(key) = key { - Self::from_pkcs8_der_and_sign_algo(key, alg) - } else { - Err(Error::CouldNotParseKeyPair) - } - } - #[cfg(feature = "aws_lc_rs")] - { - let is_pkcs8 = matches!(key, PrivateKeyDer::Pkcs8(_)); - - let rsa_key_pair_from = if is_pkcs8 { - RsaKeyPair::from_pkcs8 - } else { - RsaKeyPair::from_der - }; - - let serialized_der = key.secret_der().to_vec(); - - let kind = if alg == &PKCS_ED25519 { - KeyPairKind::Ed(Ed25519KeyPair::from_pkcs8_maybe_unchecked(&serialized_der)._err()?) - } else if alg == &PKCS_ECDSA_P256_SHA256 { - KeyPairKind::Ec(ecdsa_from_private_key_der( - &signature::ECDSA_P256_SHA256_ASN1_SIGNING, - &serialized_der, - )?) - } else if alg == &PKCS_ECDSA_P384_SHA384 { - KeyPairKind::Ec(ecdsa_from_private_key_der( - &signature::ECDSA_P384_SHA384_ASN1_SIGNING, - &serialized_der, - )?) - } else if alg == &PKCS_ECDSA_P521_SHA512 { - KeyPairKind::Ec(ecdsa_from_private_key_der( - &signature::ECDSA_P521_SHA512_ASN1_SIGNING, - &serialized_der, - )?) - } else if alg == &PKCS_RSA_SHA256 { - let rsakp = rsa_key_pair_from(&serialized_der)._err()?; - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256) - } else if alg == &PKCS_RSA_SHA384 { - let rsakp = rsa_key_pair_from(&serialized_der)._err()?; - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA384) - } else if alg == &PKCS_RSA_SHA512 { - let rsakp = rsa_key_pair_from(&serialized_der)._err()?; - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA512) - } else { - panic!("Unknown SignatureAlgorithm specified!"); - }; - - Ok(KeyPair { - kind, - alg, - serialized_der, - }) - } + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_der_and_sign_algo_with_provider(key, alg, provider) } - /// Get the raw public key of this key pair - /// - /// The key is in raw format, as how [`KeyPair::public_key()`][public_key] - /// would output, and how [`UnparsedPublicKey::verify()`][verify] - /// would accept. - /// - /// [public_key]: crate::ring_like::signature::KeyPair::public_key() - /// [verify]: crate::ring_like::signature::UnparsedPublicKey::verify() + /// Parse a DER key for `alg` using `provider`. + pub fn from_der_and_sign_algo_with_provider( + key: &PrivateKeyDer<'_>, + alg: &'static SignatureAlgorithm, + provider: &CryptoProvider, + ) -> Result { + provider + .key_pair_provider + .load_private_key(key.clone_key(), Some(alg)) + } + + /// Parse a DER key and let `provider` detect its signature algorithm. + pub fn from_der_with_provider( + key: &PrivateKeyDer<'_>, + provider: &CryptoProvider, + ) -> Result { + provider + .key_pair_provider + .load_private_key(key.clone_key(), None) + } + + /// Get the raw public key of this key pair. pub fn public_key_raw(&self) -> &[u8] { self.der_bytes() } - /// Check if this key pair can be used with the given signature algorithm + /// Check if this key pair can be used with the given signature algorithm. pub fn is_compatible(&self, signature_algorithm: &SignatureAlgorithm) -> bool { - self.alg == signature_algorithm + self.algorithm() == signature_algorithm } - /// Returns (possibly multiple) compatible [`SignatureAlgorithm`]'s - /// that the key can be used with + /// Return the compatible [`SignatureAlgorithm`] for this key pair. pub fn compatible_algs(&self) -> impl Iterator { - std::iter::once(self.alg) + std::iter::once(self.algorithm()) } - /// Return the key pair's public key in PEM format - /// - /// The returned string can be interpreted with `openssl pkey --inform PEM -pubout -pubin -text` + /// Return the key pair's public key in PEM format. #[cfg(feature = "pem")] pub fn public_key_pem(&self) -> String { let contents = self.subject_public_key_info(); @@ -428,22 +240,20 @@ impl KeyPair { pem::encode_config(&p, ENCODE_CONFIG) } - /// Serializes the key pair (including the private key) in PKCS#8 format in DER + /// Serialize the key pair, including its private key, as PKCS#8 DER. pub fn serialize_der(&self) -> Vec { self.serialized_der.clone() } - /// Returns a reference to the serialized key pair (including the private key) - /// in PKCS#8 format in DER + /// Borrow the serialized key pair, including its private key, as PKCS#8 DER. pub fn serialized_der(&self) -> &[u8] { &self.serialized_der } - /// Serializes the key pair (including the private key) in PKCS#8 format in PEM + /// Serialize the key pair, including its private key, as PKCS#8 PEM. #[cfg(feature = "pem")] pub fn serialize_pem(&self) -> String { - let contents = self.serialize_der(); - let p = Pem::new("PRIVATE KEY", contents); + let p = Pem::new("PRIVATE KEY", self.serialize_der()); pem::encode_config(&p, ENCODE_CONFIG) } } @@ -451,44 +261,18 @@ impl KeyPair { #[cfg(feature = "crypto")] impl SigningKey for KeyPair { fn sign(&self, msg: &[u8]) -> Result, Error> { - Ok(match &self.kind { - KeyPairKind::Ec(kp) => { - let system_random = SystemRandom::new(); - let signature = kp.sign(&system_random, msg)._err()?; - signature.as_ref().to_owned() - }, - KeyPairKind::Ed(kp) => kp.sign(msg).as_ref().to_owned(), - #[cfg(feature = "aws_lc_rs")] - KeyPairKind::Pq(kp) => { - let mut signature = vec![0; kp.algorithm().signature_len()]; - kp.sign(msg, &mut signature)._err()?; - signature - }, - KeyPairKind::Rsa(kp, padding_alg) => { - let system_random = SystemRandom::new(); - let mut signature = vec![0; rsa_key_pair_public_modulus_len(kp)]; - kp.sign(*padding_alg, &system_random, msg, &mut signature) - ._err()?; - signature - }, - }) + self.signing_key.sign(msg) } } #[cfg(feature = "crypto")] impl PublicKeyData for KeyPair { fn der_bytes(&self) -> &[u8] { - match &self.kind { - KeyPairKind::Ec(kp) => kp.public_key().as_ref(), - KeyPairKind::Ed(kp) => kp.public_key().as_ref(), - #[cfg(feature = "aws_lc_rs")] - KeyPairKind::Pq(kp) => kp.public_key().as_ref(), - KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(), - } + self.signing_key.der_bytes() } fn algorithm(&self) -> &'static SignatureAlgorithm { - self.alg + self.signing_key.algorithm() } } @@ -496,10 +280,10 @@ impl PublicKeyData for KeyPair { impl TryFrom<&[u8]> for KeyPair { type Error = Error; - fn try_from(key: &[u8]) -> Result { - let key = &PrivateKeyDer::try_from(key).map_err(|_| Error::CouldNotParseKeyPair)?; - - key.try_into() + fn try_from(key: &[u8]) -> Result { + let key = PrivateKeyDer::try_from(key).map_err(|_| Error::CouldNotParseKeyPair)?; + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_der_with_provider(&key, provider) } } @@ -507,10 +291,10 @@ impl TryFrom<&[u8]> for KeyPair { impl TryFrom> for KeyPair { type Error = Error; - fn try_from(key: Vec) -> Result { - let key = &PrivateKeyDer::try_from(key).map_err(|_| Error::CouldNotParseKeyPair)?; - - key.try_into() + fn try_from(key: Vec) -> Result { + let key = PrivateKeyDer::try_from(key).map_err(|_| Error::CouldNotParseKeyPair)?; + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_der_with_provider(&key, provider) } } @@ -518,8 +302,11 @@ impl TryFrom> for KeyPair { impl TryFrom<&PrivatePkcs8KeyDer<'_>> for KeyPair { type Error = Error; - fn try_from(key: &PrivatePkcs8KeyDer) -> Result { - key.secret_pkcs8_der().try_into() + fn try_from(key: &PrivatePkcs8KeyDer<'_>) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + provider + .key_pair_provider + .load_private_key(PrivateKeyDer::Pkcs8(key.clone_key()), None) } } @@ -527,77 +314,9 @@ impl TryFrom<&PrivatePkcs8KeyDer<'_>> for KeyPair { impl TryFrom<&PrivateKeyDer<'_>> for KeyPair { type Error = Error; - fn try_from(key: &PrivateKeyDer) -> Result { - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] - let (kind, alg) = { - let PrivateKeyDer::Pkcs8(pkcs8) = key else { - return Err(Error::CouldNotParseKeyPair); - }; - let pkcs8 = pkcs8.secret_pkcs8_der(); - let rng = SystemRandom::new(); - let (kind, alg) = if let Ok(edkp) = Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8) { - (KeyPairKind::Ed(edkp), &PKCS_ED25519) - } else if let Ok(eckp) = - ecdsa_from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8, &rng) - { - (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P256_SHA256) - } else if let Ok(eckp) = - ecdsa_from_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, pkcs8, &rng) - { - (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P384_SHA384) - } else if let Ok(rsakp) = RsaKeyPair::from_pkcs8(pkcs8) { - ( - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256), - &PKCS_RSA_SHA256, - ) - } else { - return Err(Error::CouldNotParseKeyPair); - }; - - (kind, alg) - }; - #[cfg(feature = "aws_lc_rs")] - let (kind, alg) = { - let is_pkcs8 = matches!(key, PrivateKeyDer::Pkcs8(_)); - - let key = key.secret_der(); - - let rsa_key_pair_from = if is_pkcs8 { - RsaKeyPair::from_pkcs8 - } else { - RsaKeyPair::from_der - }; - - let (kind, alg) = if let Ok(edkp) = Ed25519KeyPair::from_pkcs8_maybe_unchecked(key) { - (KeyPairKind::Ed(edkp), &PKCS_ED25519) - } else if let Ok(eckp) = - ecdsa_from_private_key_der(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, key) - { - (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P256_SHA256) - } else if let Ok(eckp) = - ecdsa_from_private_key_der(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, key) - { - (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P384_SHA384) - } else if let Ok(eckp) = - ecdsa_from_private_key_der(&signature::ECDSA_P521_SHA512_ASN1_SIGNING, key) - { - (KeyPairKind::Ec(eckp), &PKCS_ECDSA_P521_SHA512) - } else if let Ok(rsakp) = rsa_key_pair_from(key) { - ( - KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256), - &PKCS_RSA_SHA256, - ) - } else { - return Err(Error::CouldNotParseKeyPair); - }; - (kind, alg) - }; - - Ok(KeyPair { - kind, - alg, - serialized_der: key.secret_der().into(), - }) + fn try_from(key: &PrivateKeyDer<'_>) -> Result { + let provider = CryptoProvider::get_default_or_install_from_crate_features()?; + Self::from_der_with_provider(key, provider) } } @@ -615,21 +334,21 @@ impl From for PrivateKeyDer<'static> { } } -/// The key size used for RSA key generation -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] +/// The key size used for RSA key generation. +#[cfg(feature = "crypto")] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RsaKeySize { - /// 2048 bits + /// 2048 bits. _2048, - /// 3072 bits + /// 3072 bits. _3072, - /// 4096 bits + /// 4096 bits. _4096, } pub(crate) fn sign_der( - key: &impl SigningKey, + key: &(impl SigningKey + ?Sized), f: impl FnOnce(&mut DERWriterSeq<'_>) -> Result<(), Error>, ) -> Result, Error> { yasna::try_construct_der(|writer| { @@ -637,13 +356,10 @@ pub(crate) fn sign_der( let data = yasna::try_construct_der(|writer| writer.write_sequence(f))?; writer.next().write_der(&data); - // Write signatureAlgorithm key.algorithm().write_alg_ident(writer.next()); - // Write signature let sig = key.sign(&data)?; - let writer = writer.next(); - writer.write_bitvec_bytes(&sig, sig.len() * 8); + writer.next().write_bitvec_bytes(&sig, sig.len() * 8); Ok(()) }) @@ -656,24 +372,16 @@ impl SigningKey for &S { } } -/// A key that can be used to sign messages -pub trait SigningKey: PublicKeyData { - /// Signs `msg` using the selected algorithm - fn sign(&self, msg: &[u8]) -> Result, Error>; -} - -#[cfg(feature = "crypto")] -impl ExternalError for Result { - fn _err(self) -> Result { - self.map_err(|e| Error::RingKeyRejected(e.to_string())) +impl SigningKey for Box { + fn sign(&self, msg: &[u8]) -> Result, Error> { + (**self).sign(msg) } } -#[cfg(feature = "crypto")] -impl ExternalError for Result { - fn _err(self) -> Result { - self.map_err(|_| Error::RingUnspecified) - } +/// A key that can be used to sign messages. +pub trait SigningKey: PublicKeyData { + /// Sign `msg` using the selected algorithm. + fn sign(&self, msg: &[u8]) -> Result, Error>; } #[cfg(feature = "pem")] @@ -683,7 +391,7 @@ impl ExternalError for Result { } } -/// A public key +/// A public key. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SubjectPublicKeyInfo { pub(crate) alg: &'static SignatureAlgorithm, @@ -691,13 +399,13 @@ pub struct SubjectPublicKeyInfo { } impl SubjectPublicKeyInfo { - /// Create a `SubjectPublicKey` value from a PEM-encoded SubjectPublicKeyInfo string + /// Create a `SubjectPublicKeyInfo` value from PEM. #[cfg(all(feature = "x509-parser", feature = "pem"))] pub fn from_pem(pem_str: &str) -> Result { Self::from_der(&pem::parse(pem_str)._err()?.into_contents()) } - /// Create a `SubjectPublicKey` value from DER-encoded SubjectPublicKeyInfo bytes + /// Create a `SubjectPublicKeyInfo` value from DER. #[cfg(feature = "x509-parser")] pub fn from_der(spki_der: &[u8]) -> Result { use x509_parser::prelude::FromDer; @@ -713,16 +421,11 @@ impl SubjectPublicKeyInfo { let alg = SignatureAlgorithm::iter() .find(|alg| { - let bytes = yasna::construct_der(|writer| { - alg.write_oids_sign_alg(writer); - }); + let bytes = yasna::construct_der(|writer| alg.write_oids_sign_alg(writer)); let Ok((rest, aid)) = AlgorithmIdentifier::from_der(&bytes) else { return false; }; - if !rest.is_empty() { - return false; - } - aid == spki.algorithm + rest.is_empty() && aid == spki.algorithm }) .ok_or(Error::UnsupportedSignatureAlgorithm)?; @@ -753,20 +456,27 @@ impl PublicKeyData for &K { } } -/// The public key data of a key pair +impl PublicKeyData for Box { + fn der_bytes(&self) -> &[u8] { + (**self).der_bytes() + } + + fn algorithm(&self) -> &'static SignatureAlgorithm { + (**self).algorithm() + } +} + +/// The public key data of a key pair. pub trait PublicKeyData { - /// The public key data in DER format - /// - /// The key is formatted according to the X.509 SubjectPublicKeyInfo struct. - /// See [RFC 5280 section 4.1](https://tools.ietf.org/html/rfc5280#section-4.1). + /// Return the public key as a DER-encoded SubjectPublicKeyInfo structure. fn subject_public_key_info(&self) -> Vec { yasna::construct_der(|writer| serialize_public_key_der(self, writer)) } - /// The public key in DER format + /// Return the contents of the SubjectPublicKeyInfo `subjectPublicKey` BIT STRING. fn der_bytes(&self) -> &[u8]; - /// The algorithm used by the key pair + /// Return the algorithm used by the key pair. fn algorithm(&self) -> &'static SignatureAlgorithm; } @@ -778,11 +488,13 @@ pub(crate) fn serialize_public_key_der(key: &(impl PublicKeyData + ?Sized), writ }) } -#[cfg(all(test, feature = "crypto"))] +#[cfg(all( + test, + feature = "crypto", + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +))] mod test { use super::*; - use crate::ring_like::rand::SystemRandom; - use crate::ring_like::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; #[cfg(all(feature = "x509-parser", feature = "pem"))] #[test] @@ -791,9 +503,9 @@ mod test { &PKCS_ED25519, &PKCS_ECDSA_P256_SHA256, &PKCS_ECDSA_P384_SHA384, - #[cfg(feature = "aws_lc_rs")] + #[cfg(all(any(feature = "aws_lc_rs", feature = "fips"), not(feature = "ring")))] &PKCS_ECDSA_P521_SHA512, - #[cfg(feature = "aws_lc_rs")] + #[cfg(all(any(feature = "aws_lc_rs", feature = "fips"), not(feature = "ring")))] &PKCS_RSA_SHA256, ] { let kp = KeyPair::generate_for(alg).expect("keygen"); @@ -810,11 +522,8 @@ mod test { #[test] fn test_algorithm() { - let rng = SystemRandom::new(); - let pkcs8 = EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &rng).unwrap(); - let der = pkcs8.as_ref().to_vec(); - - let key_pair = KeyPair::try_from(der).unwrap(); + let original = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap(); + let key_pair = KeyPair::try_from(original.serialize_der()).unwrap(); assert_eq!(key_pair.algorithm(), &PKCS_ECDSA_P256_SHA256); } } diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 83816182..9f88bb0f 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -13,7 +13,7 @@ a key pair to call [`CertificateParams::signed_by()`] or [`CertificateParams::se doc = r##" ## Example -``` +```no_run use rcgen::{generate_simple_self_signed, CertifiedKey}; # fn main () { // Generate a certificate that's valid for "localhost" and "hello.world.example" @@ -49,18 +49,20 @@ pub use crl::{ CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams, }; +#[cfg(feature = "crypto")] +pub use crypto::{ + CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, +}; pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, PublicKey}; pub use error::{Error, InvalidAsn1String}; #[cfg(feature = "crypto")] pub use key_pair::KeyPair; -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] +#[cfg(feature = "crypto")] pub use key_pair::RsaKeySize; pub use key_pair::{PublicKeyData, SigningKey, SubjectPublicKeyInfo}; #[cfg(feature = "pem")] use pem::Pem; use pki_types::CertificateDer; -#[cfg(feature = "crypto")] -use ring_like::digest; pub use sign_algo::algo::*; pub use sign_algo::SignatureAlgorithm; use time::{OffsetDateTime, Time}; @@ -72,11 +74,12 @@ use crate::string::{BmpString, Ia5String, PrintableString, TeletexString, Univer mod certificate; mod crl; +#[cfg(feature = "crypto")] +pub mod crypto; mod csr; mod error; mod key_pair; mod oid; -mod ring_like; mod sign_algo; pub mod string; @@ -109,7 +112,7 @@ and key pair as output. doc = r##" ## Example -``` +```no_run use rcgen::{generate_simple_self_signed, CertifiedKey}; # fn main () { // Generate a certificate that's valid for "localhost" and "hello.world.example" @@ -133,6 +136,18 @@ pub fn generate_simple_self_signed( Ok(CertifiedKey { cert, signing_key }) } +/// Generate a simple self-signed certificate using an explicit cryptography provider. +#[cfg(feature = "crypto")] +pub fn generate_simple_self_signed_with_provider( + subject_alt_names: impl Into>, + provider: &CryptoProvider, +) -> Result, Error> { + let signing_key = KeyPair::generate_with_provider(provider)?; + let cert = CertificateParams::new(subject_alt_names)? + .self_signed_with_provider(&signing_key, provider)?; + Ok(CertifiedKey { cert, signing_key }) +} + /// An [`Issuer`] wrapper that also contains the issuer's [`Certificate`]. #[derive(Debug)] pub struct CertifiedIssuer<'a, S> { @@ -149,6 +164,19 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { }) } + /// Create a new issuer with a self-signed certificate using `provider`. + #[cfg(feature = "crypto")] + pub fn self_signed_with_provider( + params: CertificateParams, + signing_key: S, + provider: &CryptoProvider, + ) -> Result { + Ok(Self { + certificate: params.self_signed_with_provider(&signing_key, provider)?, + issuer: Issuer::new(params, signing_key), + }) + } + /// Create a new issuer from the given parameters and key, signed by the given `issuer`. pub fn signed_by( params: CertificateParams, @@ -161,6 +189,20 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { }) } + /// Create a new issuer signed by `issuer` using `provider`. + #[cfg(feature = "crypto")] + pub fn signed_by_with_provider( + params: CertificateParams, + signing_key: S, + issuer: &Issuer<'_, impl SigningKey>, + provider: &CryptoProvider, + ) -> Result { + Ok(Self { + certificate: params.signed_by_with_provider(&signing_key, issuer, provider)?, + issuer: Issuer::new(params, signing_key), + }) + } + /// Get the certificate in PEM encoded format. #[cfg(feature = "pem")] pub fn pem(&self) -> String { @@ -315,7 +357,16 @@ pub enum SanType { } impl SanType { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any( + not(feature = "crypto"), + feature = "ring", + feature = "aws_lc_rs", + feature = "fips" + ) + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 .subject_alternative_name() @@ -717,24 +768,26 @@ impl KeyIdMethod { /// /// This key identifier is used in the SubjectKeyIdentifier and AuthorityKeyIdentifier /// X.509v3 extensions. - #[allow(unused_variables)] - pub(crate) fn derive(&self, subject_public_key_info: impl AsRef<[u8]>) -> Vec { - #[cfg_attr(not(feature = "crypto"), expect(clippy::let_unit_value))] - let digest_method = match &self { - #[cfg(feature = "crypto")] - Self::Sha256 => &digest::SHA256, - #[cfg(feature = "crypto")] - Self::Sha384 => &digest::SHA384, - #[cfg(feature = "crypto")] - Self::Sha512 => &digest::SHA512, - Self::PreSpecified(b) => { - return b.to_vec(); - }, + #[cfg(feature = "crypto")] + pub(crate) fn derive( + &self, + provider: &CryptoProvider, + subject_public_key_info: impl AsRef<[u8]>, + ) -> Result, Error> { + let algorithm = match self { + Self::Sha256 => HashAlgorithm::Sha256, + Self::Sha384 => HashAlgorithm::Sha384, + Self::Sha512 => HashAlgorithm::Sha512, + Self::PreSpecified(value) => return Ok(value.clone()), }; - #[cfg(feature = "crypto")] - { - let digest = digest::digest(digest_method, subject_public_key_info.as_ref()); - digest.as_ref()[0..20].to_vec() + let digest = provider.digest(algorithm, subject_public_key_info.as_ref())?; + Ok(digest[..20].to_vec()) + } + + #[cfg(not(feature = "crypto"))] + pub(crate) fn derive(&self, _subject_public_key_info: impl AsRef<[u8]>) -> Vec { + match self { + Self::PreSpecified(value) => value.clone(), } } } diff --git a/rcgen/src/oid.rs b/rcgen/src/oid.rs index d61e858f..cae8b0f3 100644 --- a/rcgen/src/oid.rs +++ b/rcgen/src/oid.rs @@ -21,8 +21,6 @@ pub(crate) const EC_SECP_256_R1: &[u64] = &[1, 2, 840, 10045, 3, 1, 7]; /// secp384r1 in [RFC 5480](https://datatracker.ietf.org/doc/html/rfc5480#appendix-A) pub(crate) const EC_SECP_384_R1: &[u64] = &[1, 3, 132, 0, 34]; /// secp521r1 in [RFC 5480](https://datatracker.ietf.org/doc/html/rfc5480#appendix-A) -/// Currently this is only supported with the `aws_lc_rs` feature -#[cfg(feature = "aws_lc_rs")] pub(crate) const EC_SECP_521_R1: &[u64] = &[1, 3, 132, 0, 35]; #[cfg(feature = "aws_lc_rs")] diff --git a/rcgen/src/ring_like.rs b/rcgen/src/ring_like.rs deleted file mode 100644 index d1eef384..00000000 --- a/rcgen/src/ring_like.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] -pub(crate) use aws_lc_rs::*; -#[cfg(all(feature = "crypto", feature = "ring", not(feature = "aws_lc_rs")))] -pub(crate) use ring::*; - -#[cfg(feature = "crypto")] -use crate::error::ExternalError; -#[cfg(feature = "crypto")] -use crate::Error; - -#[cfg(feature = "crypto")] -pub(crate) fn ecdsa_from_pkcs8( - alg: &'static signature::EcdsaSigningAlgorithm, - pkcs8: &[u8], - _rng: &dyn rand::SecureRandom, -) -> Result { - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] - { - signature::EcdsaKeyPair::from_pkcs8(alg, pkcs8, _rng)._err() - } - - #[cfg(feature = "aws_lc_rs")] - { - signature::EcdsaKeyPair::from_pkcs8(alg, pkcs8)._err() - } -} - -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] -pub(crate) fn ecdsa_from_private_key_der( - alg: &'static signature::EcdsaSigningAlgorithm, - key: &[u8], -) -> Result { - signature::EcdsaKeyPair::from_private_key_der(alg, key)._err() -} - -#[cfg(feature = "crypto")] -pub(crate) fn rsa_key_pair_public_modulus_len(kp: &signature::RsaKeyPair) -> usize { - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] - { - kp.public().modulus_len() - } - - #[cfg(feature = "aws_lc_rs")] - { - kp.public_modulus_len() - } -} - -#[cfg(all(feature = "crypto", not(any(feature = "ring", feature = "aws_lc_rs"))))] -compile_error!("At least one of the 'ring' or 'aws_lc_rs' features must be activated when the 'crypto' feature is enabled"); diff --git a/rcgen/src/sign_algo.rs b/rcgen/src/sign_algo.rs index a7fe1c7c..bfd7d288 100644 --- a/rcgen/src/sign_algo.rs +++ b/rcgen/src/sign_algo.rs @@ -1,27 +1,11 @@ use std::fmt; use std::hash::{Hash, Hasher}; -#[cfg(feature = "aws_lc_rs")] -use aws_lc_rs::signature::{ - PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, -}; use yasna::models::ObjectIdentifier; use yasna::DERWriter; -#[cfg(feature = "crypto")] -use crate::ring_like::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters, RsaEncoding}; use crate::Error; -#[cfg(feature = "crypto")] -#[derive(Clone, Copy, Debug)] -pub(crate) enum SignAlgo { - EcDsa(&'static EcdsaSigningAlgorithm), - EdDsa(&'static EdDSAParameters), - #[cfg(feature = "aws_lc_rs")] - PqDsa(&'static PqdsaSigningAlgorithm), - Rsa(&'static dyn RsaEncoding), -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) enum SignatureAlgorithmParams { /// Omit the parameters @@ -34,8 +18,6 @@ pub(crate) enum SignatureAlgorithmParams { #[derive(Clone)] pub struct SignatureAlgorithm { oids_sign_alg: &'static [&'static [u64]], - #[cfg(feature = "crypto")] - pub(crate) sign_alg: SignAlgo, oid_components: &'static [u64], params: SignatureAlgorithmParams, } @@ -55,20 +37,13 @@ impl fmt::Debug for SignatureAlgorithm { write!(f, "PKCS_ECDSA_P384_SHA384") } else if self == &PKCS_ED25519 { write!(f, "PKCS_ED25519") + } else if self == &PKCS_ECDSA_P521_SHA256 { + write!(f, "PKCS_ECDSA_P521_SHA256") + } else if self == &PKCS_ECDSA_P521_SHA384 { + write!(f, "PKCS_ECDSA_P521_SHA384") + } else if self == &PKCS_ECDSA_P521_SHA512 { + write!(f, "PKCS_ECDSA_P521_SHA512") } else { - #[cfg(feature = "aws_lc_rs")] - if self == &PKCS_ECDSA_P521_SHA256 { - return write!(f, "PKCS_ECDSA_P521_SHA256"); - } - #[cfg(feature = "aws_lc_rs")] - if self == &PKCS_ECDSA_P521_SHA384 { - return write!(f, "PKCS_ECDSA_P521_SHA384"); - } - #[cfg(feature = "aws_lc_rs")] - if self == &PKCS_ECDSA_P521_SHA512 { - return write!(f, "PKCS_ECDSA_P521_SHA512"); - } - write!(f, "Unknown") } } @@ -98,13 +73,16 @@ impl SignatureAlgorithm { &PKCS_RSA_SHA512, &PKCS_ECDSA_P256_SHA256, &PKCS_ECDSA_P384_SHA384, - #[cfg(feature = "aws_lc_rs")] &PKCS_ECDSA_P521_SHA256, - #[cfg(feature = "aws_lc_rs")] &PKCS_ECDSA_P521_SHA384, - #[cfg(feature = "aws_lc_rs")] &PKCS_ECDSA_P521_SHA512, &PKCS_ED25519, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + &PKCS_ML_DSA_44, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + &PKCS_ML_DSA_65, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + &PKCS_ML_DSA_87, ]; ALGORITHMS.iter() } @@ -118,6 +96,11 @@ impl SignatureAlgorithm { } Err(Error::UnsupportedSignatureAlgorithm) } + + #[cfg(feature = "x509-parser")] + pub(crate) fn matches_signature_oid(&self, oid: &[u64]) -> bool { + self.oid_components == oid + } } /// The list of supported signature algorithms @@ -128,8 +111,6 @@ pub(crate) mod algo { /// RSA signing with PKCS#1 1.5 padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) pub static PKCS_RSA_SHA256: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[RSA_ENCRYPTION], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::Rsa(&signature::RSA_PKCS1_SHA256), // sha256WithRSAEncryption in RFC 4055 oid_components: &[1, 2, 840, 113549, 1, 1, 11], params: SignatureAlgorithmParams::Null, @@ -138,8 +119,6 @@ pub(crate) mod algo { /// RSA signing with PKCS#1 1.5 padding and SHA-384 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) pub static PKCS_RSA_SHA384: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[RSA_ENCRYPTION], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::Rsa(&signature::RSA_PKCS1_SHA384), // sha384WithRSAEncryption in RFC 4055 oid_components: &[1, 2, 840, 113549, 1, 1, 12], params: SignatureAlgorithmParams::Null, @@ -148,8 +127,6 @@ pub(crate) mod algo { /// RSA signing with PKCS#1 1.5 padding and SHA-512 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055) pub static PKCS_RSA_SHA512: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[RSA_ENCRYPTION], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::Rsa(&signature::RSA_PKCS1_SHA512), // sha512WithRSAEncryption in RFC 4055 oid_components: &[1, 2, 840, 113549, 1, 1, 13], params: SignatureAlgorithmParams::Null, @@ -158,8 +135,6 @@ pub(crate) mod algo { /// ECDSA signing using the P-256 curves and SHA-256 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) pub static PKCS_ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_256_R1], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P256_SHA256_ASN1_SIGNING), // ecdsa-with-SHA256 in RFC 5758 oid_components: &[1, 2, 840, 10045, 4, 3, 2], params: SignatureAlgorithmParams::None, @@ -168,8 +143,6 @@ pub(crate) mod algo { /// ECDSA signing using the P-384 curves and SHA-384 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) pub static PKCS_ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_384_R1], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P384_SHA384_ASN1_SIGNING), // ecdsa-with-SHA384 in RFC 5758 oid_components: &[1, 2, 840, 10045, 4, 3, 3], params: SignatureAlgorithmParams::None, @@ -179,12 +152,9 @@ pub(crate) mod algo { /// /// Note that this algorithm is not widely supported, and is not supported in TLS 1.3. /// - /// Only supported with the `aws_lc_rs` backend. - #[cfg(feature = "aws_lc_rs")] + /// This algorithm is not supported by every [`CryptoProvider`](crate::crypto::CryptoProvider). pub static PKCS_ECDSA_P521_SHA256: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P521_SHA256_ASN1_SIGNING), // ecdsa-with-SHA256 in RFC 5758 oid_components: &[1, 2, 840, 10045, 4, 3, 2], params: SignatureAlgorithmParams::None, @@ -194,12 +164,9 @@ pub(crate) mod algo { /// /// Note that this algorithm is not widely supported, and is not supported in TLS 1.3. /// - /// Only supported with the `aws_lc_rs` backend. - #[cfg(feature = "aws_lc_rs")] + /// This algorithm is not supported by every [`CryptoProvider`](crate::crypto::CryptoProvider). pub static PKCS_ECDSA_P521_SHA384: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P521_SHA384_ASN1_SIGNING), // ecdsa-with-SHA384 in RFC 5758 oid_components: &[1, 2, 840, 10045, 4, 3, 3], params: SignatureAlgorithmParams::None, @@ -207,12 +174,9 @@ pub(crate) mod algo { /// ECDSA signing using the P-521 curves and SHA-512 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) /// - /// Only supported with the `aws_lc_rs` backend. - #[cfg(feature = "aws_lc_rs")] + /// This algorithm is not supported by every [`CryptoProvider`](crate::crypto::CryptoProvider). pub static PKCS_ECDSA_P521_SHA512: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P521_SHA512_ASN1_SIGNING), // ecdsa-with-SHA512 in RFC 5758 oid_components: &[1, 2, 840, 10045, 4, 3, 4], params: SignatureAlgorithmParams::None, @@ -222,8 +186,6 @@ pub(crate) mod algo { pub static PKCS_ED25519: SignatureAlgorithm = SignatureAlgorithm { // id-Ed25519 in RFC 8410 oids_sign_alg: &[&[1, 3, 101, 112]], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::EdDsa(&signature::ED25519), // id-Ed25519 in RFC 8410 oid_components: &[1, 3, 101, 112], params: SignatureAlgorithmParams::None, @@ -233,8 +195,6 @@ pub(crate) mod algo { #[cfg(feature = "aws_lc_rs")] pub static PKCS_ML_DSA_44: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[ML_DSA_44], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::PqDsa(&ML_DSA_44_SIGNING), oid_components: ML_DSA_44, params: SignatureAlgorithmParams::None, }; @@ -243,8 +203,6 @@ pub(crate) mod algo { #[cfg(feature = "aws_lc_rs")] pub static PKCS_ML_DSA_65: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[ML_DSA_65], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::PqDsa(&ML_DSA_65_SIGNING), oid_components: ML_DSA_65, params: SignatureAlgorithmParams::None, }; @@ -253,8 +211,6 @@ pub(crate) mod algo { #[cfg(feature = "aws_lc_rs")] pub static PKCS_ML_DSA_87: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[ML_DSA_87], - #[cfg(feature = "crypto")] - sign_alg: SignAlgo::PqDsa(&ML_DSA_87_SIGNING), oid_components: ML_DSA_87, params: SignatureAlgorithmParams::None, }; diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs new file mode 100644 index 00000000..5601b759 --- /dev/null +++ b/rcgen/tests/custom_provider.rs @@ -0,0 +1,186 @@ +#![cfg(feature = "crypto")] + +use std::sync::atomic::{AtomicUsize, Ordering}; + +use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; +use rcgen::crypto::{ + CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, +}; +use rcgen::{ + BasicConstraints, CertificateParams, CertificateRevocationListParams, Error, IsCa, Issuer, + KeyIdMethod, KeyPair, PublicKeyData, RsaKeySize, SerialNumber, SignatureAlgorithm, SigningKey, + PKCS_ED25519, +}; + +static GENERATIONS: AtomicUsize = AtomicUsize::new(0); +static LOADS: AtomicUsize = AtomicUsize::new(0); +static DIGESTS: AtomicUsize = AtomicUsize::new(0); +static VERIFICATIONS: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug)] +struct TestBackend; + +static TEST_BACKEND: TestBackend = TestBackend; + +fn provider() -> CryptoProvider { + CryptoProvider { + key_pair_provider: &TEST_BACKEND, + digest_provider: &TEST_BACKEND, + signature_verification_provider: &TEST_BACKEND, + } +} + +impl DigestProvider for TestBackend { + fn digest( + &self, + algorithm: HashAlgorithm, + input: &[u8], + output: &mut [u8], + ) -> Result<(), Error> { + assert_eq!(output.len(), algorithm.output_len()); + assert!(!input.is_empty()); + DIGESTS.fetch_add(1, Ordering::Relaxed); + output.fill(0x42); + Ok(()) + } +} + +impl KeyPairProvider for TestBackend { + fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result { + GENERATIONS.fetch_add(1, Ordering::Relaxed); + Ok(test_key_pair(algorithm, vec![0x30, 0x00])) + } + + fn generate_rsa( + &self, + _algorithm: &'static SignatureAlgorithm, + _key_size: RsaKeySize, + ) -> Result { + Err(Error::KeyGenerationUnavailable) + } + + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result { + LOADS.fetch_add(1, Ordering::Relaxed); + Ok(test_key_pair( + algorithm.unwrap_or(&PKCS_ED25519), + key_der.secret_der().to_vec(), + )) + } +} + +impl SignatureVerificationProvider for TestBackend { + fn verify( + &self, + algorithm: &'static SignatureAlgorithm, + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), Error> { + assert_eq!(algorithm, &PKCS_ED25519); + assert_eq!(public_key, [7; 32]); + assert!(!message.is_empty()); + assert_eq!(signature, [9; 64]); + VERIFICATIONS.fetch_add(1, Ordering::Relaxed); + Ok(()) + } +} + +struct TestSigningKey { + algorithm: &'static SignatureAlgorithm, + public_key: [u8; 32], +} + +impl PublicKeyData for TestSigningKey { + fn der_bytes(&self) -> &[u8] { + &self.public_key + } + + fn algorithm(&self) -> &'static SignatureAlgorithm { + self.algorithm + } +} + +impl SigningKey for TestSigningKey { + fn sign(&self, _msg: &[u8]) -> Result, Error> { + Ok(vec![9; 64]) + } +} + +fn test_key_pair(algorithm: &'static SignatureAlgorithm, serialized_der: Vec) -> KeyPair { + KeyPair::from_signing_key( + Box::new(TestSigningKey { + algorithm, + public_key: [7; 32], + }), + serialized_der, + ) +} + +#[test] +fn explicit_provider_covers_all_rcgen_crypto() { + assert!(CryptoProvider::get_default().is_none()); + #[cfg(not(any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] + assert_eq!( + KeyPair::generate().unwrap_err(), + Error::CryptoProviderNotInstalled + ); + let custom_provider = provider(); + let key = KeyPair::generate_for_with_provider(&PKCS_ED25519, &custom_provider).unwrap(); + assert_eq!(GENERATIONS.load(Ordering::Relaxed), 1); + + let mut params = CertificateParams::default(); + params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + let certificate = params + .self_signed_with_provider(&key, &custom_provider) + .unwrap(); + assert!(!certificate.der().is_empty()); + assert!(DIGESTS.load(Ordering::Relaxed) >= 2); // default serial and subject key ID + + let issuer = Issuer::new(params, key); + let crl = CertificateRevocationListParams { + this_update: rcgen::date_time_ymd(2025, 1, 1), + next_update: rcgen::date_time_ymd(2026, 1, 1), + crl_number: SerialNumber::from(1u64), + issuing_distribution_point: None, + revoked_certs: Vec::new(), + key_identifier_method: KeyIdMethod::Sha384, + } + .signed_by_with_provider(&issuer, &custom_provider) + .unwrap(); + assert!(!crl.der().is_empty()); + + let fake_der = PrivatePkcs8KeyDer::from(vec![0x30, 0x00]); + let loaded = KeyPair::from_pkcs8_der_and_sign_algo_with_provider( + &fake_der, + &PKCS_ED25519, + &custom_provider, + ) + .unwrap(); + assert_eq!(loaded.algorithm(), &PKCS_ED25519); + assert_eq!(LOADS.load(Ordering::Relaxed), 1); + + #[cfg(feature = "x509-parser")] + { + let request = CertificateParams::default() + .serialize_request(&loaded) + .unwrap(); + let parsed = rcgen::CertificateSigningRequestParams::from_der_with_provider( + request.der(), + &custom_provider, + ) + .unwrap(); + assert_eq!(parsed.public_key.algorithm(), &PKCS_ED25519); + assert_eq!(VERIFICATIONS.load(Ordering::Relaxed), 1); + } + + assert!(CryptoProvider::get_default().is_none()); + provider().install_default().unwrap(); + let generated = KeyPair::generate_for(&PKCS_ED25519).unwrap(); + assert_eq!(generated.algorithm(), &PKCS_ED25519); + assert_eq!(GENERATIONS.load(Ordering::Relaxed), 2); + assert!(provider().install_default().is_err()); +} From 3c970a4f3c1b96bad86a9b29136369c0ccd5e6cc Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 1 Sep 2026 16:34:57 +0000 Subject: [PATCH 02/20] restore docs --- rcgen/src/crl.rs | 17 +++- rcgen/src/error.rs | 2 +- rcgen/src/key_pair.rs | 183 +++++++++++++++++++++++++++++++---------- rcgen/src/lib.rs | 22 ++++- rcgen/src/oid.rs | 1 + rcgen/src/sign_algo.rs | 6 +- 6 files changed, 179 insertions(+), 52 deletions(-) diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index abe82acb..be5112db 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -19,7 +19,7 @@ use crate::{ /// /// ## Example /// -/// ```no_run +/// ``` /// extern crate rcgen; /// use rcgen::*; /// @@ -34,6 +34,13 @@ use crate::{ /// fn der_bytes(&self) -> &[u8] { &self.public_key } /// fn algorithm(&self) -> &'static SignatureAlgorithm { &PKCS_ED25519 } /// } +/// # #[cfg(any( +/// # not(feature = "crypto"), +/// # all( +/// # not(feature = "custom-provider"), +/// # any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +/// # ) +/// # ))] /// # fn main () { /// // Generate a CRL issuer. /// let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]).unwrap(); @@ -66,6 +73,14 @@ use crate::{ /// key_identifier_method: KeyIdMethod::PreSpecified(vec![]), /// }.signed_by(&issuer).unwrap(); ///# } +/// # #[cfg(not(any( +/// # not(feature = "crypto"), +/// # all( +/// # not(feature = "custom-provider"), +/// # any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +/// # ) +/// # )))] +/// # fn main() {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateRevocationList { der: CertificateRevocationListDer<'static>, diff --git a/rcgen/src/error.rs b/rcgen/src/error.rs index d113c24a..f964cdb9 100644 --- a/rcgen/src/error.rs +++ b/rcgen/src/error.rs @@ -11,7 +11,7 @@ pub enum Error { /// The given key pair couldn't be parsed CouldNotParseKeyPair, /// No process-wide cryptography provider has been installed and crate features do not select - /// exactly one built-in provider. + /// a built-in provider. CryptoProviderNotInstalled, /// A cryptography provider failed an operation. CryptoProviderError(String), diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index d3e7f57b..56f8ffb6 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -18,11 +18,9 @@ use crate::Error; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; -/// A key pair used to sign certificates and CSRs. +/// A key pair used to sign certificates and CSRs /// -/// `KeyPair` is independent of a concrete cryptography library. Its implementation is created by -/// the selected [`CryptoProvider`], while this type retains the stable rcgen API and exportable -/// private-key bytes. +/// The cryptographic implementation is supplied by the selected [`CryptoProvider`]. #[cfg(feature = "crypto")] pub struct KeyPair { pub(crate) signing_key: Box, @@ -56,26 +54,29 @@ impl KeyPair { } } - /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair. + /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair pub fn generate() -> Result { Self::generate_for(&PKCS_ECDSA_P256_SHA256) } - /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair with `provider`. + /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair using `provider` pub fn generate_with_provider(provider: &CryptoProvider) -> Result { Self::generate_for_with_provider(&PKCS_ECDSA_P256_SHA256, provider) } - /// Generate a new random key pair for the specified signature algorithm. + /// Generate a new random key pair for the specified signature algorithm /// - /// If no process-wide provider has been installed, a built-in provider is selected only when - /// a built-in backend feature is enabled. AWS-LC takes precedence if both are enabled. + /// If you're not sure which algorithm to use, [`PKCS_ECDSA_P256_SHA256`] is a good choice. + /// If passed an RSA signature algorithm, it depends on the provider whether we return + /// a generated key or an error for key generation being unavailable. + /// Currently, the built-in `aws-lc-rs` provider supports RSA key generation while the + /// built-in `ring` provider does not. pub fn generate_for(alg: &'static SignatureAlgorithm) -> Result { let provider = CryptoProvider::get_default_or_install_from_crate_features()?; Self::generate_for_with_provider(alg, provider) } - /// Generate a new random key pair using `provider`. + /// Generate a new random key pair for the specified signature algorithm using `provider` pub fn generate_for_with_provider( alg: &'static SignatureAlgorithm, provider: &CryptoProvider, @@ -83,7 +84,12 @@ impl KeyPair { provider.key_pair_provider.generate(alg) } - /// Generate a new random RSA key pair for the specified key size. + /// Generates a new random RSA key pair for the specified key size + /// + /// If passed a signature algorithm that is not RSA, it will return + /// [`Error::KeyGenerationUnavailable`]. + /// + /// It depends on the selected provider whether RSA key generation is available. pub fn generate_rsa_for( alg: &'static SignatureAlgorithm, key_size: RsaKeySize, @@ -92,7 +98,10 @@ impl KeyPair { Self::generate_rsa_for_with_provider(alg, key_size, provider) } - /// Generate a new random RSA key pair of `key_size` using `provider`. + /// Generates a new random RSA key pair for the specified key size using `provider` + /// + /// If passed a signature algorithm that is not RSA, it will return + /// [`Error::KeyGenerationUnavailable`]. pub fn generate_rsa_for_with_provider( alg: &'static SignatureAlgorithm, key_size: RsaKeySize, @@ -101,19 +110,30 @@ impl KeyPair { provider.key_pair_provider.generate_rsa(alg, key_size) } - /// Returns the key pair's signature algorithm. + /// Returns the key pair's signature algorithm pub fn algorithm(&self) -> &'static SignatureAlgorithm { self.signing_key.algorithm() } - /// Parse a key pair from ASCII PEM using the process-wide provider. + /// Parses the key pair from the ASCII PEM format + /// + /// The accepted private-key encodings depend on the selected provider. + /// + /// If the built-in `aws_lc_rs` provider is used, then the key must be a DER-encoded plaintext + /// private key as specified in PKCS #8/RFC 5958, SEC1/RFC 5915, or PKCS#1/RFC 3447; + /// these appear as "PRIVATE KEY", "RSA PRIVATE KEY", or "EC PRIVATE KEY" in PEM files. + /// + /// If the built-in `ring` provider is used, then the key must be a DER-encoded plaintext + /// private key as specified in PKCS #8/RFC 5958; this appears as "PRIVATE KEY" in PEM files. #[cfg(feature = "pem")] pub fn from_pem(pem_str: &str) -> Result { let provider = CryptoProvider::get_default_or_install_from_crate_features()?; Self::from_pem_with_provider(pem_str, provider) } - /// Parse a key pair from ASCII PEM using `provider`. + /// Parses the key pair from the ASCII PEM format using `provider` + /// + /// See [`from_pem`](Self::from_pem) for details about supported key encodings. #[cfg(feature = "pem")] pub fn from_pem_with_provider(pem_str: &str, provider: &CryptoProvider) -> Result { let private_key = pem::parse(pem_str)._err()?; @@ -122,7 +142,13 @@ impl KeyPair { Self::from_der_with_provider(&private_key, provider) } - /// Parse a PKCS#8 PEM key for a specified signature algorithm. + /// Obtains the key pair from a PEM formatted key + /// using the specified [`SignatureAlgorithm`] + /// + /// The key must be a DER-encoded plaintext private key as specified in PKCS #8/RFC 5958; + /// it appears as "PRIVATE KEY" in PEM files. + /// + /// Same as [`from_pem_and_sign_algo`](Self::from_pem_and_sign_algo), but only accepts PKCS#8. #[cfg(feature = "pem")] pub fn from_pkcs8_pem_and_sign_algo( pem_str: &str, @@ -132,7 +158,8 @@ impl KeyPair { Self::from_pkcs8_pem_and_sign_algo_with_provider(pem_str, alg, provider) } - /// Parse a PKCS#8 PEM key for `alg` using `provider`. + /// Obtains the key pair from a PKCS#8 PEM formatted key using `provider` + /// and the specified [`SignatureAlgorithm`] #[cfg(feature = "pem")] pub fn from_pkcs8_pem_and_sign_algo_with_provider( pem_str: &str, @@ -144,7 +171,20 @@ impl KeyPair { Self::from_pkcs8_der_and_sign_algo_with_provider(&private_key, alg, provider) } - /// Parse a PKCS#8 DER key for a specified signature algorithm. + /// Obtains the key pair from a DER formatted key using the specified [`SignatureAlgorithm`] + /// + /// If you have a [`PrivatePkcs8KeyDer`], you can usually rely on the [`TryFrom`] implementation + /// to obtain a [`KeyPair`] -- it will determine the correct [`SignatureAlgorithm`] for you. + /// However, sometimes multiple signature algorithms fit for the same DER key. In those instances, + /// you can use this function to precisely specify the `SignatureAlgorithm`. + /// + /// [`rustls_pemfile::private_key()`] is often used to obtain a [`PrivateKeyDer`] from PEM + /// input. If the obtained [`PrivateKeyDer`] is a `Pkcs8` variant, you can use its contents + /// as input for this function. Alternatively, if you already have a byte slice containing DER, + /// it can trivially be converted into [`PrivatePkcs8KeyDer`] using the [`Into`] trait. + /// + /// [`rustls_pemfile::private_key()`]: https://docs.rs/rustls-pemfile/latest/rustls_pemfile/fn.private_key.html + /// [`PrivateKeyDer`]: https://docs.rs/rustls-pki-types/latest/rustls_pki_types/enum.PrivateKeyDer.html pub fn from_pkcs8_der_and_sign_algo( pkcs8: &PrivatePkcs8KeyDer<'_>, alg: &'static SignatureAlgorithm, @@ -153,7 +193,8 @@ impl KeyPair { Self::from_pkcs8_der_and_sign_algo_with_provider(pkcs8, alg, provider) } - /// Parse a PKCS#8 DER key for `alg` using `provider`. + /// Obtains the key pair from a PKCS#8 DER formatted key using `provider` + /// and the specified [`SignatureAlgorithm`] pub fn from_pkcs8_der_and_sign_algo_with_provider( pkcs8: &PrivatePkcs8KeyDer<'_>, alg: &'static SignatureAlgorithm, @@ -164,7 +205,19 @@ impl KeyPair { .load_private_key(PrivateKeyDer::Pkcs8(pkcs8.clone_key()), Some(alg)) } - /// Parse a PEM key for a specified signature algorithm. + /// Obtains the key pair from a PEM formatted key + /// using the specified [`SignatureAlgorithm`] + /// + /// The accepted private-key encodings depend on the selected provider. + /// + /// If the built-in `aws_lc_rs` provider is used, then the key must be a DER-encoded plaintext + /// private key as specified in PKCS #8/RFC 5958, SEC1/RFC 5915, or PKCS#1/RFC 3447; + /// these appear as "PRIVATE KEY", "RSA PRIVATE KEY", or "EC PRIVATE KEY" in PEM files. + /// + /// If the built-in `ring` provider is used, then the key must be a DER-encoded plaintext + /// private key as specified in PKCS #8/RFC 5958; this appears as "PRIVATE KEY" in PEM files. + /// + /// Same as [`from_pkcs8_pem_and_sign_algo`](Self::from_pkcs8_pem_and_sign_algo) for PKCS#8 keys. #[cfg(feature = "pem")] pub fn from_pem_and_sign_algo( pem_str: &str, @@ -174,7 +227,11 @@ impl KeyPair { Self::from_pem_and_sign_algo_with_provider(pem_str, alg, provider) } - /// Parse a PEM key for `alg` using `provider`. + /// Obtains the key pair from a PEM formatted key using `provider` + /// and the specified [`SignatureAlgorithm`] + /// + /// See [`from_pem_and_sign_algo`](Self::from_pem_and_sign_algo) for details about supported + /// key encodings. #[cfg(feature = "pem")] pub fn from_pem_and_sign_algo_with_provider( pem_str: &str, @@ -187,7 +244,22 @@ impl KeyPair { Self::from_der_and_sign_algo_with_provider(&private_key, alg, provider) } - /// Parse a DER key for a specified signature algorithm. + /// Obtains the key pair from a DER formatted key + /// using the specified [`SignatureAlgorithm`] + /// + /// The accepted [`PrivateKeyDer`] variants depend on the selected provider. The built-in + /// `ring` provider only supports [`PrivateKeyDer::Pkcs8`], while the built-in `aws_lc_rs` + /// provider supports PKCS#8, PKCS#1, and SEC1 keys. + /// + /// If you have a [`PrivateKeyDer`], you can usually rely on the [`TryFrom`] implementation + /// to obtain a [`KeyPair`] -- it will determine the correct [`SignatureAlgorithm`] for you. + /// However, sometimes multiple signature algorithms fit for the same DER key. In those instances, + /// you can use this function to precisely specify the `SignatureAlgorithm`. + /// + /// You can use [`rustls_pemfile::private_key`] to get the `key` input. If + /// you already have a byte slice, just calling `try_into()` will convert it to a [`PrivateKeyDer`]. + /// + /// [`rustls_pemfile::private_key`]: https://docs.rs/rustls-pemfile/latest/rustls_pemfile/fn.private_key.html pub fn from_der_and_sign_algo( key: &PrivateKeyDer<'_>, alg: &'static SignatureAlgorithm, @@ -196,7 +268,11 @@ impl KeyPair { Self::from_der_and_sign_algo_with_provider(key, alg, provider) } - /// Parse a DER key for `alg` using `provider`. + /// Obtains the key pair from a DER formatted key using `provider` + /// and the specified [`SignatureAlgorithm`] + /// + /// See [`from_der_and_sign_algo`](Self::from_der_and_sign_algo) for details about supported + /// key encodings. pub fn from_der_and_sign_algo_with_provider( key: &PrivateKeyDer<'_>, alg: &'static SignatureAlgorithm, @@ -207,7 +283,9 @@ impl KeyPair { .load_private_key(key.clone_key(), Some(alg)) } - /// Parse a DER key and let `provider` detect its signature algorithm. + /// Obtains the key pair from a DER formatted key using `provider` + /// + /// The provider determines the correct [`SignatureAlgorithm`] for the key. pub fn from_der_with_provider( key: &PrivateKeyDer<'_>, provider: &CryptoProvider, @@ -217,22 +295,30 @@ impl KeyPair { .load_private_key(key.clone_key(), None) } - /// Get the raw public key of this key pair. + /// Get the raw public key of this key pair + /// + /// The returned bytes are the contents of the X.509 SubjectPublicKeyInfo + /// `subjectPublicKey` BIT STRING, matching [`PublicKeyData::der_bytes`]. This is also the + /// public-key format passed to + /// [`SignatureVerificationProvider::verify`](crate::crypto::SignatureVerificationProvider::verify). pub fn public_key_raw(&self) -> &[u8] { self.der_bytes() } - /// Check if this key pair can be used with the given signature algorithm. + /// Check if this key pair can be used with the given signature algorithm pub fn is_compatible(&self, signature_algorithm: &SignatureAlgorithm) -> bool { self.algorithm() == signature_algorithm } - /// Return the compatible [`SignatureAlgorithm`] for this key pair. + /// Returns (possibly multiple) compatible [`SignatureAlgorithm`]'s + /// that the key can be used with pub fn compatible_algs(&self) -> impl Iterator { std::iter::once(self.algorithm()) } - /// Return the key pair's public key in PEM format. + /// Return the key pair's public key in PEM format + /// + /// The returned string can be interpreted with `openssl pkey --inform PEM -pubout -pubin -text` #[cfg(feature = "pem")] pub fn public_key_pem(&self) -> String { let contents = self.subject_public_key_info(); @@ -240,17 +326,18 @@ impl KeyPair { pem::encode_config(&p, ENCODE_CONFIG) } - /// Serialize the key pair, including its private key, as PKCS#8 DER. + /// Serializes the key pair (including the private key) in PKCS#8 format in DER pub fn serialize_der(&self) -> Vec { self.serialized_der.clone() } - /// Borrow the serialized key pair, including its private key, as PKCS#8 DER. + /// Returns a reference to the serialized key pair (including the private key) + /// in PKCS#8 format in DER pub fn serialized_der(&self) -> &[u8] { &self.serialized_der } - /// Serialize the key pair, including its private key, as PKCS#8 PEM. + /// Serializes the key pair (including the private key) in PKCS#8 format in PEM #[cfg(feature = "pem")] pub fn serialize_pem(&self) -> String { let p = Pem::new("PRIVATE KEY", self.serialize_der()); @@ -334,16 +421,16 @@ impl From for PrivateKeyDer<'static> { } } -/// The key size used for RSA key generation. +/// The key size used for RSA key generation #[cfg(feature = "crypto")] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RsaKeySize { - /// 2048 bits. + /// 2048 bits _2048, - /// 3072 bits. + /// 3072 bits _3072, - /// 4096 bits. + /// 4096 bits _4096, } @@ -356,10 +443,13 @@ pub(crate) fn sign_der( let data = yasna::try_construct_der(|writer| writer.write_sequence(f))?; writer.next().write_der(&data); + // Write signatureAlgorithm key.algorithm().write_alg_ident(writer.next()); + // Write signature let sig = key.sign(&data)?; - writer.next().write_bitvec_bytes(&sig, sig.len() * 8); + let writer = writer.next(); + writer.write_bitvec_bytes(&sig, sig.len() * 8); Ok(()) }) @@ -378,9 +468,9 @@ impl SigningKey for Box { } } -/// A key that can be used to sign messages. +/// A key that can be used to sign messages pub trait SigningKey: PublicKeyData { - /// Sign `msg` using the selected algorithm. + /// Signs `msg` using the selected algorithm fn sign(&self, msg: &[u8]) -> Result, Error>; } @@ -391,7 +481,7 @@ impl ExternalError for Result { } } -/// A public key. +/// A public key #[derive(Clone, Debug, Eq, PartialEq)] pub struct SubjectPublicKeyInfo { pub(crate) alg: &'static SignatureAlgorithm, @@ -399,13 +489,13 @@ pub struct SubjectPublicKeyInfo { } impl SubjectPublicKeyInfo { - /// Create a `SubjectPublicKeyInfo` value from PEM. + /// Create a `SubjectPublicKey` value from a PEM-encoded SubjectPublicKeyInfo string #[cfg(all(feature = "x509-parser", feature = "pem"))] pub fn from_pem(pem_str: &str) -> Result { Self::from_der(&pem::parse(pem_str)._err()?.into_contents()) } - /// Create a `SubjectPublicKeyInfo` value from DER. + /// Create a `SubjectPublicKey` value from DER-encoded SubjectPublicKeyInfo bytes #[cfg(feature = "x509-parser")] pub fn from_der(spki_der: &[u8]) -> Result { use x509_parser::prelude::FromDer; @@ -466,17 +556,20 @@ impl PublicKeyData for Box { } } -/// The public key data of a key pair. +/// The public key data of a key pair pub trait PublicKeyData { - /// Return the public key as a DER-encoded SubjectPublicKeyInfo structure. + /// The public key data in DER format + /// + /// The key is formatted according to the X.509 SubjectPublicKeyInfo struct. + /// See [RFC 5280 section 4.1](https://tools.ietf.org/html/rfc5280#section-4.1). fn subject_public_key_info(&self) -> Vec { yasna::construct_der(|writer| serialize_public_key_der(self, writer)) } - /// Return the contents of the SubjectPublicKeyInfo `subjectPublicKey` BIT STRING. + /// The public key in DER format fn der_bytes(&self) -> &[u8]; - /// Return the algorithm used by the key pair. + /// The algorithm used by the key pair fn algorithm(&self) -> &'static SignatureAlgorithm; } diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 9f88bb0f..8a8e1ead 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -13,8 +13,12 @@ a key pair to call [`CertificateParams::signed_by()`] or [`CertificateParams::se doc = r##" ## Example -```no_run +``` use rcgen::{generate_simple_self_signed, CertifiedKey}; +# #[cfg(all( +# not(feature = "custom-provider"), +# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# ))] # fn main () { // Generate a certificate that's valid for "localhost" and "hello.world.example" let subject_alt_names = vec!["hello.world.example".to_string(), @@ -24,6 +28,11 @@ let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); # } +# #[cfg(not(all( +# not(feature = "custom-provider"), +# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# )))] +# fn main() {} ```"## )] #![forbid(unsafe_code)] @@ -112,8 +121,12 @@ and key pair as output. doc = r##" ## Example -```no_run +``` use rcgen::{generate_simple_self_signed, CertifiedKey}; +# #[cfg(all( +# not(feature = "custom-provider"), +# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# ))] # fn main () { // Generate a certificate that's valid for "localhost" and "hello.world.example" let subject_alt_names = vec!["hello.world.example".to_string(), @@ -125,6 +138,11 @@ let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); # } +# #[cfg(not(all( +# not(feature = "custom-provider"), +# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# )))] +# fn main() {} ``` "## )] diff --git a/rcgen/src/oid.rs b/rcgen/src/oid.rs index cae8b0f3..7851a77e 100644 --- a/rcgen/src/oid.rs +++ b/rcgen/src/oid.rs @@ -21,6 +21,7 @@ pub(crate) const EC_SECP_256_R1: &[u64] = &[1, 2, 840, 10045, 3, 1, 7]; /// secp384r1 in [RFC 5480](https://datatracker.ietf.org/doc/html/rfc5480#appendix-A) pub(crate) const EC_SECP_384_R1: &[u64] = &[1, 3, 132, 0, 34]; /// secp521r1 in [RFC 5480](https://datatracker.ietf.org/doc/html/rfc5480#appendix-A) +/// Currently this is supported by the built-in `aws_lc_rs` provider, but not `ring` pub(crate) const EC_SECP_521_R1: &[u64] = &[1, 3, 132, 0, 35]; #[cfg(feature = "aws_lc_rs")] diff --git a/rcgen/src/sign_algo.rs b/rcgen/src/sign_algo.rs index bfd7d288..81be78a8 100644 --- a/rcgen/src/sign_algo.rs +++ b/rcgen/src/sign_algo.rs @@ -152,7 +152,7 @@ pub(crate) mod algo { /// /// Note that this algorithm is not widely supported, and is not supported in TLS 1.3. /// - /// This algorithm is not supported by every [`CryptoProvider`](crate::crypto::CryptoProvider). + /// Only supported by the built-in `aws_lc_rs` provider, or a custom provider that implements it. pub static PKCS_ECDSA_P521_SHA256: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1], // ecdsa-with-SHA256 in RFC 5758 @@ -164,7 +164,7 @@ pub(crate) mod algo { /// /// Note that this algorithm is not widely supported, and is not supported in TLS 1.3. /// - /// This algorithm is not supported by every [`CryptoProvider`](crate::crypto::CryptoProvider). + /// Only supported by the built-in `aws_lc_rs` provider, or a custom provider that implements it. pub static PKCS_ECDSA_P521_SHA384: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1], // ecdsa-with-SHA384 in RFC 5758 @@ -174,7 +174,7 @@ pub(crate) mod algo { /// ECDSA signing using the P-521 curves and SHA-512 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2) /// - /// This algorithm is not supported by every [`CryptoProvider`](crate::crypto::CryptoProvider). + /// Only supported by the built-in `aws_lc_rs` provider, or a custom provider that implements it. pub static PKCS_ECDSA_P521_SHA512: SignatureAlgorithm = SignatureAlgorithm { oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1], // ecdsa-with-SHA512 in RFC 5758 From ec2e9ce4f9e2c41548338c979730b26aadb9febc Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 1 Sep 2026 18:00:07 +0000 Subject: [PATCH 03/20] add back stable ML-DSA for AWS-LC --- rcgen/Cargo.toml | 2 +- rcgen/src/crypto/aws_lc_rs.rs | 74 ++++++++++++++++++++++++++++------- rcgen/src/sign_algo.rs | 6 +-- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index c0a5bd45..391fed13 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -13,7 +13,7 @@ keywords.workspace = true [features] default = ["crypto", "pem", "ring"] aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] -aws_lc_rs_unstable = ["aws_lc_rs", "aws-lc-rs/unstable"] # For backwards compatibility only +aws_lc_rs_unstable = ["aws_lc_rs"] # For backwards compatibility only custom-provider = ["crypto"] fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] crypto = [] diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index b779eb26..44223c36 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -8,8 +8,8 @@ use ::aws_lc_rs::signature::{ self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as _, RsaEncoding, RsaKeyPair, VerificationAlgorithm, }; -#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] -use ::aws_lc_rs::unstable::signature::{ +#[cfg(feature = "aws_lc_rs")] +use ::aws_lc_rs::signature::{ PqdsaKeyPair, PqdsaSigningAlgorithm, ML_DSA_44, ML_DSA_44_SIGNING, ML_DSA_65, ML_DSA_65_SIGNING, ML_DSA_87, ML_DSA_87_SIGNING, }; @@ -23,7 +23,7 @@ use crate::{ PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ECDSA_P521_SHA256, PKCS_ECDSA_P521_SHA384, PKCS_ECDSA_P521_SHA512, PKCS_ED25519, PKCS_RSA_SHA256, PKCS_RSA_SHA384, PKCS_RSA_SHA512, }; -#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +#[cfg(feature = "aws_lc_rs")] use crate::{PKCS_ML_DSA_44, PKCS_ML_DSA_65, PKCS_ML_DSA_87}; /// Return rcgen's built-in AWS-LC provider. @@ -128,7 +128,7 @@ impl AwsLcKeyPairProvider { &signature::RSA_PKCS1_SHA512, ) } else { - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] { let signing_algorithm = if algorithm == &PKCS_ML_DSA_44 { Some(&ML_DSA_44_SIGNING) @@ -165,11 +165,11 @@ impl AwsLcKeyPairProvider { &PKCS_ECDSA_P384_SHA384, &PKCS_ECDSA_P521_SHA512, &PKCS_RSA_SHA256, - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] &PKCS_ML_DSA_44, - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] &PKCS_ML_DSA_65, - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] &PKCS_ML_DSA_87, ] { if let Ok(key) = self.load_with_algorithm(key_der, is_pkcs8, algorithm) { @@ -218,7 +218,7 @@ impl AwsLcKeyPairProvider { )) } - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] fn generate_pqdsa( &self, algorithm: &'static SignatureAlgorithm, @@ -226,7 +226,7 @@ impl AwsLcKeyPairProvider { ) -> Result { let key = PqdsaKeyPair::generate(signing_algorithm).map_err(|_| Error::RingUnspecified)?; let serialized_der = key - .to_pkcs8() + .to_pkcs8v1() .map_err(|_| Error::RingUnspecified)? .as_ref() .to_vec(); @@ -267,7 +267,7 @@ impl KeyPairProvider for AwsLcKeyPairProvider { { self.generate_rsa_inner(algorithm, KeySize::Rsa2048) } else { - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] { if algorithm == &PKCS_ML_DSA_44 { return self.generate_pqdsa(algorithm, &ML_DSA_44_SIGNING); @@ -317,7 +317,7 @@ impl KeyPairProvider for AwsLcKeyPairProvider { enum AwsLcKeyKind { Ec(EcdsaKeyPair), Ed(Ed25519KeyPair), - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] Pq(PqdsaKeyPair), Rsa(RsaKeyPair, &'static dyn RsaEncoding), } @@ -332,7 +332,7 @@ impl PublicKeyData for AwsLcSigningKey { match &self.kind { AwsLcKeyKind::Ec(key) => key.public_key().as_ref(), AwsLcKeyKind::Ed(key) => key.public_key().as_ref(), - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] AwsLcKeyKind::Pq(key) => key.public_key().as_ref(), AwsLcKeyKind::Rsa(key, _) => key.public_key().as_ref(), } @@ -351,7 +351,7 @@ impl SigningKey for AwsLcSigningKey { .map(|signature| signature.as_ref().to_vec()) .map_err(|_| Error::RingUnspecified), AwsLcKeyKind::Ed(key) => Ok(key.sign(message).as_ref().to_vec()), - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] AwsLcKeyKind::Pq(key) => { let mut signature = vec![0; key.algorithm().signature_len()]; key.sign(message, &mut signature) @@ -379,7 +379,7 @@ impl SignatureVerificationProvider for AwsLcSignatureVerificationProvider { message: &[u8], signature_bytes: &[u8], ) -> Result<(), Error> { - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] { let pqdsa_algorithm = if algorithm == &PKCS_ML_DSA_44 { Some(&ML_DSA_44) @@ -428,6 +428,9 @@ impl SignatureVerificationProvider for AwsLcSignatureVerificationProvider { #[cfg(test)] mod tests { + #[cfg(feature = "aws_lc_rs")] + use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; + use super::*; #[test] @@ -443,4 +446,47 @@ mod tests { ] ); } + + #[cfg(feature = "aws_lc_rs")] + #[test] + fn ml_dsa_round_trip() { + let provider = default_provider(); + for algorithm in [&PKCS_ML_DSA_44, &PKCS_ML_DSA_65, &PKCS_ML_DSA_87] { + let generated = KeyPair::generate_for_with_provider(algorithm, &provider).unwrap(); + let private_key = PrivatePkcs8KeyDer::from(generated.serialize_der()); + + let loaded = KeyPair::from_pkcs8_der_and_sign_algo_with_provider( + &private_key, + algorithm, + &provider, + ) + .unwrap(); + assert_eq!(loaded.algorithm(), algorithm); + + let detected = + KeyPair::from_der_with_provider(&PrivateKeyDer::Pkcs8(private_key), &provider) + .unwrap(); + assert_eq!(detected.algorithm(), algorithm); + + let message = b"stable ML-DSA provider"; + let signature = loaded.sign(message).unwrap(); + provider + .signature_verification_provider + .verify(algorithm, loaded.der_bytes(), message, &signature) + .unwrap(); + + #[cfg(feature = "x509-parser")] + { + let request = crate::CertificateParams::default() + .serialize_request(&loaded) + .unwrap(); + let parsed = crate::CertificateSigningRequestParams::from_der_with_provider( + request.der(), + &provider, + ) + .unwrap(); + assert_eq!(parsed.public_key.algorithm(), algorithm); + } + } + } } diff --git a/rcgen/src/sign_algo.rs b/rcgen/src/sign_algo.rs index 81be78a8..e877c533 100644 --- a/rcgen/src/sign_algo.rs +++ b/rcgen/src/sign_algo.rs @@ -77,11 +77,11 @@ impl SignatureAlgorithm { &PKCS_ECDSA_P521_SHA384, &PKCS_ECDSA_P521_SHA512, &PKCS_ED25519, - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] &PKCS_ML_DSA_44, - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] &PKCS_ML_DSA_65, - #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + #[cfg(feature = "aws_lc_rs")] &PKCS_ML_DSA_87, ]; ALGORITHMS.iter() From f7a4a199aabaaad6ed9a728d329b92d148b05207 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 1 Sep 2026 18:50:14 +0000 Subject: [PATCH 04/20] clean up readme --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b0bd6c70..f5212321 100644 --- a/README.md +++ b/README.md @@ -17,25 +17,21 @@ println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); ``` -## Pluggable cryptography providers +## Cryptography providers -Key generation, private-key loading, hashing, and CSR signature verification are selected through -`rcgen::crypto::CryptoProvider`. The `ring` and `aws_lc_rs` features provide built-in providers, -but neither backend is required. +Rcgen uses Ring by default. AWS-LC is available through the `aws_lc_rs` feature. +AWS-LC FIPS mode requires both the `aws_lc_rs` and `fips` features. -To use a completely separate cryptography implementation, disable default features and enable the -backend-neutral `crypto` feature: +To use a custom cryptography provider without enabling either built-in backend: ```toml [dependencies] -rcgen = { version = "0.14", default-features = false, features = ["crypto", "pem"] } +rcgen = { version = "0.14", default-features = false, features = ["custom-provider", "pem"] } ``` Implement `KeyPairProvider`, `DigestProvider`, and `SignatureVerificationProvider`, assemble them into a `CryptoProvider`, then either call `CryptoProvider::install_default()` once near process -startup or use the explicit `*_with_provider` APIs. In this configuration, neither `ring` nor -`aws-lc-rs` is present in rcgen's dependency graph. The `custom-provider` feature can additionally -disable automatic built-in selection if another dependency enables a built-in backend feature. +startup or use the explicit `*_with_provider` APIs. The `pem` feature is optional. ## Trying it out with openssl From 045718e3c990dad027fd521528c86007020a85af Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 1 Sep 2026 18:50:56 +0000 Subject: [PATCH 05/20] fips doesn't enable aws-lc --- .github/workflows/ci.yml | 14 ++++++++---- rcgen/Cargo.toml | 2 +- rcgen/src/certificate.rs | 42 +++++----------------------------- rcgen/src/crl.rs | 10 +++----- rcgen/src/crypto/mod.rs | 9 +++----- rcgen/src/csr.rs | 2 +- rcgen/src/key_pair.rs | 10 +++----- rcgen/src/lib.rs | 18 +++++++-------- rcgen/tests/custom_provider.rs | 2 +- rustls-cert-gen/Cargo.toml | 2 +- verify-tests/Cargo.toml | 2 +- 11 files changed, 38 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9691e37..c2a0e443 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: uses: dtolnay/rust-toolchain@stable with: components: clippy - # `fips` and `aws_lc_rs_unstable` cannot be used together, so avoid `--all-features` + # `custom-provider` disables automatic built-in provider selection, so avoid `--all-features` - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets @@ -54,11 +54,17 @@ jobs: if cargo tree -p rcgen --no-default-features --features crypto --edges normal,build | grep -E 'ring v|aws-lc'; then exit 1 fi + - name: Ensure FIPS requires an explicit provider + run: | + if output=$(cargo check -p rcgen --no-default-features --features fips 2>&1); then + exit 1 + fi + grep -Fq "the 'fips' feature currently requires the 'aws_lc_rs' feature" <<<"$output" - run: cargo clippy --no-default-features --features ring --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs_unstable,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs --all-targets - - run: cargo clippy --no-default-features --features fips,pem,x509-parser --all-targets + - run: cargo clippy --no-default-features --features aws_lc_rs,fips,pem,x509-parser --all-targets rustdoc: name: Documentation @@ -88,7 +94,7 @@ jobs: env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (fips) - run: cargo doc --no-default-features --features fips --document-private-items + run: cargo doc --no-default-features --features aws_lc_rs,fips --document-private-items env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} @@ -137,7 +143,7 @@ jobs: toolchain: 1.88.0 - run: cargo check --locked --lib --features ring,pem,x509-parser - run: cargo check --locked --lib --features aws_lc_rs_unstable - - run: cargo check --locked --lib --features fips + - run: cargo check --locked --lib --features aws_lc_rs,fips build-windows: runs-on: windows-latest diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 391fed13..ecbe59a9 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -15,7 +15,7 @@ default = ["crypto", "pem", "ring"] aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] aws_lc_rs_unstable = ["aws_lc_rs"] # For backwards compatibility only custom-provider = ["crypto"] -fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] +fips = ["crypto", "aws-lc-rs?/fips"] crypto = [] ring = ["crypto", "dep:ring"] diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index ff22e114..bdc78358 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -213,12 +213,7 @@ impl CertificateParams { #[cfg(all( test, feature = "x509-parser", - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) @@ -866,12 +861,7 @@ impl ExtendedKeyUsagePurpose { #[cfg(all( test, feature = "x509-parser", - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 @@ -940,12 +930,7 @@ impl NameConstraints { #[cfg(all( test, feature = "x509-parser", - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, @@ -1002,12 +987,7 @@ impl GeneralSubtree { #[cfg(all( test, feature = "x509-parser", - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], @@ -1183,12 +1163,7 @@ impl IsCa { #[cfg(all( test, feature = "x509-parser", - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 @@ -1240,12 +1215,7 @@ pub enum BasicConstraints { #[cfg(all( test, - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] mod tests { #[cfg(feature = "x509-parser")] diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index be5112db..6c66f3e4 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -38,7 +38,7 @@ use crate::{ /// # not(feature = "crypto"), /// # all( /// # not(feature = "custom-provider"), -/// # any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +/// # any(feature = "ring", feature = "aws_lc_rs") /// # ) /// # ))] /// # fn main () { @@ -77,7 +77,7 @@ use crate::{ /// # not(feature = "crypto"), /// # all( /// # not(feature = "custom-provider"), -/// # any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +/// # any(feature = "ring", feature = "aws_lc_rs") /// # ) /// # )))] /// # fn main() {} @@ -486,11 +486,7 @@ impl RevokedCertParams { } } -#[cfg(all( - test, - feature = "crypto", - any(feature = "ring", feature = "aws_lc_rs", feature = "fips") -))] +#[cfg(all(test, feature = "crypto", any(feature = "ring", feature = "aws_lc_rs")))] mod tests { use x509_parser::num_bigint::BigUint; use x509_parser::{oid_registry, parse_x509_crl}; diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index 70141dda..d9f8518a 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -164,17 +164,14 @@ impl CryptoProvider { fn from_crate_features() -> Option { #[cfg(all( feature = "ring", - not(any(feature = "aws_lc_rs", feature = "fips")), + not(feature = "aws_lc_rs"), not(feature = "custom-provider") ))] { return Some(ring::default_provider()); } - #[cfg(all( - any(feature = "aws_lc_rs", feature = "fips"), - not(feature = "custom-provider") - ))] + #[cfg(all(feature = "aws_lc_rs", not(feature = "custom-provider")))] { return Some(aws_lc_rs::default_provider()); } @@ -191,5 +188,5 @@ static PROCESS_DEFAULT_PROVIDER: OnceLock> = OnceLock::new() pub mod ring; /// AWS-LC-based cryptography provider. -#[cfg(any(feature = "aws_lc_rs", feature = "fips"))] +#[cfg(feature = "aws_lc_rs")] pub mod aws_lc_rs; diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index 192315ba..f4203cc5 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -274,7 +274,7 @@ impl CertificateSigningRequestParams { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs", feature = "fips") + any(feature = "ring", feature = "aws_lc_rs") ))] mod tests { use x509_parser::certification_request::X509CertificationRequest; diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 56f8ffb6..e5f92cd6 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -581,11 +581,7 @@ pub(crate) fn serialize_public_key_der(key: &(impl PublicKeyData + ?Sized), writ }) } -#[cfg(all( - test, - feature = "crypto", - any(feature = "ring", feature = "aws_lc_rs", feature = "fips") -))] +#[cfg(all(test, feature = "crypto", any(feature = "ring", feature = "aws_lc_rs")))] mod test { use super::*; @@ -596,9 +592,9 @@ mod test { &PKCS_ED25519, &PKCS_ECDSA_P256_SHA256, &PKCS_ECDSA_P384_SHA384, - #[cfg(all(any(feature = "aws_lc_rs", feature = "fips"), not(feature = "ring")))] + #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))] &PKCS_ECDSA_P521_SHA512, - #[cfg(all(any(feature = "aws_lc_rs", feature = "fips"), not(feature = "ring")))] + #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))] &PKCS_RSA_SHA256, ] { let kp = KeyPair::generate_for(alg).expect("keygen"); diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 8a8e1ead..4add20ed 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -17,7 +17,7 @@ a key pair to call [`CertificateParams::signed_by()`] or [`CertificateParams::se use rcgen::{generate_simple_self_signed, CertifiedKey}; # #[cfg(all( # not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# any(feature = "ring", feature = "aws_lc_rs") # ))] # fn main () { // Generate a certificate that's valid for "localhost" and "hello.world.example" @@ -30,7 +30,7 @@ println!("{}", signing_key.serialize_pem()); # } # #[cfg(not(all( # not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# any(feature = "ring", feature = "aws_lc_rs") # )))] # fn main() {} ```"## @@ -41,6 +41,9 @@ println!("{}", signing_key.serialize_pem()); #![cfg_attr(rcgen_docsrs, feature(doc_cfg))] #![warn(unreachable_pub)] +#[cfg(all(feature = "fips", not(feature = "aws_lc_rs")))] +compile_error!("the 'fips' feature currently requires the 'aws_lc_rs' feature"); + use std::borrow::Cow; use std::collections::HashMap; use std::fmt; @@ -125,7 +128,7 @@ and key pair as output. use rcgen::{generate_simple_self_signed, CertifiedKey}; # #[cfg(all( # not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# any(feature = "ring", feature = "aws_lc_rs") # ))] # fn main () { // Generate a certificate that's valid for "localhost" and "hello.world.example" @@ -140,7 +143,7 @@ println!("{}", signing_key.serialize_pem()); # } # #[cfg(not(all( # not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs", feature = "fips") +# any(feature = "ring", feature = "aws_lc_rs") # )))] # fn main() {} ``` @@ -378,12 +381,7 @@ impl SanType { #[cfg(all( test, feature = "x509-parser", - any( - not(feature = "crypto"), - feature = "ring", - feature = "aws_lc_rs", - feature = "fips" - ) + any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs index 5601b759..6c88a619 100644 --- a/rcgen/tests/custom_provider.rs +++ b/rcgen/tests/custom_provider.rs @@ -123,7 +123,7 @@ fn test_key_pair(algorithm: &'static SignatureAlgorithm, serialized_der: Vec #[test] fn explicit_provider_covers_all_rcgen_crypto() { assert!(CryptoProvider::get_default().is_none()); - #[cfg(not(any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] + #[cfg(not(any(feature = "ring", feature = "aws_lc_rs")))] assert_eq!( KeyPair::generate().unwrap_err(), Error::CryptoProviderNotInstalled diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index 28f7c300..e31dcb43 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -14,7 +14,7 @@ keywords.workspace = true default = ["ring"] aws_lc_rs = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/aws-lc-sys"] aws_lc_rs_unstable = ["rcgen/aws_lc_rs_unstable"] -fips = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/fips"] +fips = ["aws_lc_rs", "rcgen/fips"] ring = ["dep:ring", "rcgen/ring"] [dependencies] diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml index 8e6b1bcf..b855cbd7 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -7,7 +7,7 @@ publish = false [features] default = [] aws_lc_rs = ["rcgen/aws_lc_rs", "rustls-webpki/aws-lc-rs", "dep:aws-lc-rs"] -fips = ["rcgen/fips"] +fips = ["aws_lc_rs", "rcgen/fips"] pem = ["dep:pem", "rcgen/pem"] ring = ["rcgen/ring"] x509-parser = ["dep:x509-parser", "rcgen/x509-parser"] From 87670568e87fb466acfbb23fc23b0b2effcf8546 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Wed, 2 Sep 2026 16:35:10 +0000 Subject: [PATCH 06/20] fix formatting issues --- rcgen/src/crypto/aws_lc_rs.rs | 12 ++++----- rcgen/src/crypto/mod.rs | 46 +++++++++++++++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index 44223c36..75cfb65c 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -1,15 +1,15 @@ //! The built-in AWS-LC cryptography provider. -use ::aws_lc_rs::digest; -use ::aws_lc_rs::encoding::AsDer; -use ::aws_lc_rs::rand::SystemRandom; -use ::aws_lc_rs::rsa::KeySize; -use ::aws_lc_rs::signature::{ +use aws_lc_rs::digest; +use aws_lc_rs::encoding::AsDer; +use aws_lc_rs::rand::SystemRandom; +use aws_lc_rs::rsa::KeySize; +use aws_lc_rs::signature::{ self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as _, RsaEncoding, RsaKeyPair, VerificationAlgorithm, }; #[cfg(feature = "aws_lc_rs")] -use ::aws_lc_rs::signature::{ +use aws_lc_rs::signature::{ PqdsaKeyPair, PqdsaSigningAlgorithm, ML_DSA_44, ML_DSA_44_SIGNING, ML_DSA_65, ML_DSA_65_SIGNING, ML_DSA_87, ML_DSA_87_SIGNING, }; diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index d9f8518a..184b4be1 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -23,29 +23,6 @@ use pki_types::PrivateKeyDer; use crate::{Error, KeyPair, RsaKeySize, SignatureAlgorithm}; -/// A hash algorithm required by rcgen. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum HashAlgorithm { - /// SHA-256. - Sha256, - /// SHA-384. - Sha384, - /// SHA-512. - Sha512, -} - -impl HashAlgorithm { - /// Return the digest output length in bytes. - pub const fn output_len(self) -> usize { - match self { - Self::Sha256 => 32, - Self::Sha384 => 48, - Self::Sha512 => 64, - } - } -} - /// Hash operations supplied by a [`CryptoProvider`]. pub trait DigestProvider: Debug + Send + Sync { /// Hash `input` with `algorithm`, writing the digest to `output`. @@ -190,3 +167,26 @@ pub mod ring; /// AWS-LC-based cryptography provider. #[cfg(feature = "aws_lc_rs")] pub mod aws_lc_rs; + +/// A hash algorithm required by rcgen. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum HashAlgorithm { + /// SHA-256. + Sha256, + /// SHA-384. + Sha384, + /// SHA-512. + Sha512, +} + +impl HashAlgorithm { + /// Return the digest output length in bytes. + pub const fn output_len(self) -> usize { + match self { + Self::Sha256 => 32, + Self::Sha384 => 48, + Self::Sha512 => 64, + } + } +} From f33a3b800fc560c5d30f9ac1582d956890667f5a Mon Sep 17 00:00:00 2001 From: jgreeer Date: Wed, 2 Sep 2026 17:57:30 +0000 Subject: [PATCH 07/20] combine generate_rsa into generate --- rcgen/src/crypto/aws_lc_rs.rs | 38 +++++++++++++++++----------------- rcgen/src/crypto/mod.rs | 15 ++++---------- rcgen/src/crypto/ring.rs | 17 +++++++-------- rcgen/src/key_pair.rs | 4 ++-- rcgen/tests/custom_provider.rs | 32 +++++++++++++++++++--------- 5 files changed, 55 insertions(+), 51 deletions(-) diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index 75cfb65c..e317376d 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -241,7 +241,18 @@ impl AwsLcKeyPairProvider { } impl KeyPairProvider for AwsLcKeyPairProvider { - fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result { + fn generate( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: Option, + ) -> Result { + let is_rsa = algorithm == &PKCS_RSA_SHA256 + || algorithm == &PKCS_RSA_SHA384 + || algorithm == &PKCS_RSA_SHA512; + if key_size.is_some() && !is_rsa { + return Err(Error::KeyGenerationUnavailable); + } + if algorithm == &PKCS_ECDSA_P256_SHA256 { self.generate_ecdsa(algorithm, &signature::ECDSA_P256_SHA256_ASN1_SIGNING) } else if algorithm == &PKCS_ECDSA_P384_SHA384 { @@ -261,11 +272,13 @@ impl KeyPairProvider for AwsLcKeyPairProvider { Box::new(signing_key), serialized_der, )) - } else if algorithm == &PKCS_RSA_SHA256 - || algorithm == &PKCS_RSA_SHA384 - || algorithm == &PKCS_RSA_SHA512 - { - self.generate_rsa_inner(algorithm, KeySize::Rsa2048) + } else if is_rsa { + let key_size = match key_size.unwrap_or(RsaKeySize::_2048) { + RsaKeySize::_2048 => KeySize::Rsa2048, + RsaKeySize::_3072 => KeySize::Rsa3072, + RsaKeySize::_4096 => KeySize::Rsa4096, + }; + self.generate_rsa_inner(algorithm, key_size) } else { #[cfg(feature = "aws_lc_rs")] { @@ -283,19 +296,6 @@ impl KeyPairProvider for AwsLcKeyPairProvider { } } - fn generate_rsa( - &self, - algorithm: &'static SignatureAlgorithm, - key_size: RsaKeySize, - ) -> Result { - let key_size = match key_size { - RsaKeySize::_2048 => KeySize::Rsa2048, - RsaKeySize::_3072 => KeySize::Rsa3072, - RsaKeySize::_4096 => KeySize::Rsa4096, - }; - self.generate_rsa_inner(algorithm, key_size) - } - fn load_private_key( &self, key_der: PrivateKeyDer<'static>, diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index 184b4be1..bba1b8f3 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -39,20 +39,13 @@ pub trait DigestProvider: Debug + Send + Sync { /// Key generation and private-key loading supplied by a [`CryptoProvider`]. pub trait KeyPairProvider: Debug + Send + Sync { /// Generate an exportable key pair for `algorithm`. - fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result; - - /// Generate an exportable RSA key pair of `key_size` for `algorithm`. /// - /// Providers that do not support selectable RSA key sizes may retain the default - /// implementation. - fn generate_rsa( + /// `key_size` selects an explicit RSA key size. It must be `None` for non-RSA algorithms. + fn generate( &self, algorithm: &'static SignatureAlgorithm, - key_size: RsaKeySize, - ) -> Result { - let _ = (algorithm, key_size); - Err(Error::KeyGenerationUnavailable) - } + key_size: Option, + ) -> Result; /// Decode and validate an exportable private key. /// diff --git a/rcgen/src/crypto/ring.rs b/rcgen/src/crypto/ring.rs index 2bc645ba..67c22887 100644 --- a/rcgen/src/crypto/ring.rs +++ b/rcgen/src/crypto/ring.rs @@ -116,7 +116,14 @@ impl RingKeyPairProvider { } impl KeyPairProvider for RingKeyPairProvider { - fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result { + fn generate( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: Option, + ) -> Result { + if key_size.is_some() { + return Err(Error::KeyGenerationUnavailable); + } let rng = SystemRandom::new(); let (signing_key, serialized_der) = if algorithm == &PKCS_ECDSA_P256_SHA256 { let document = @@ -156,14 +163,6 @@ impl KeyPairProvider for RingKeyPairProvider { )) } - fn generate_rsa( - &self, - _algorithm: &'static SignatureAlgorithm, - _key_size: RsaKeySize, - ) -> Result { - Err(Error::KeyGenerationUnavailable) - } - fn load_private_key( &self, key_der: PrivateKeyDer<'static>, diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index e5f92cd6..d6e7f484 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -81,7 +81,7 @@ impl KeyPair { alg: &'static SignatureAlgorithm, provider: &CryptoProvider, ) -> Result { - provider.key_pair_provider.generate(alg) + provider.key_pair_provider.generate(alg, None) } /// Generates a new random RSA key pair for the specified key size @@ -107,7 +107,7 @@ impl KeyPair { key_size: RsaKeySize, provider: &CryptoProvider, ) -> Result { - provider.key_pair_provider.generate_rsa(alg, key_size) + provider.key_pair_provider.generate(alg, Some(key_size)) } /// Returns the key pair's signature algorithm diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs index 6c88a619..5e75a0ec 100644 --- a/rcgen/tests/custom_provider.rs +++ b/rcgen/tests/custom_provider.rs @@ -9,10 +9,11 @@ use rcgen::crypto::{ use rcgen::{ BasicConstraints, CertificateParams, CertificateRevocationListParams, Error, IsCa, Issuer, KeyIdMethod, KeyPair, PublicKeyData, RsaKeySize, SerialNumber, SignatureAlgorithm, SigningKey, - PKCS_ED25519, + PKCS_ED25519, PKCS_RSA_SHA256, }; static GENERATIONS: AtomicUsize = AtomicUsize::new(0); +static RSA_GENERATIONS: AtomicUsize = AtomicUsize::new(0); static LOADS: AtomicUsize = AtomicUsize::new(0); static DIGESTS: AtomicUsize = AtomicUsize::new(0); static VERIFICATIONS: AtomicUsize = AtomicUsize::new(0); @@ -46,17 +47,18 @@ impl DigestProvider for TestBackend { } impl KeyPairProvider for TestBackend { - fn generate(&self, algorithm: &'static SignatureAlgorithm) -> Result { - GENERATIONS.fetch_add(1, Ordering::Relaxed); - Ok(test_key_pair(algorithm, vec![0x30, 0x00])) - } - - fn generate_rsa( + fn generate( &self, - _algorithm: &'static SignatureAlgorithm, - _key_size: RsaKeySize, + algorithm: &'static SignatureAlgorithm, + key_size: Option, ) -> Result { - Err(Error::KeyGenerationUnavailable) + if let Some(key_size) = key_size { + assert_eq!(key_size, RsaKeySize::_3072); + RSA_GENERATIONS.fetch_add(1, Ordering::Relaxed); + return Err(Error::KeyGenerationUnavailable); + } + GENERATIONS.fetch_add(1, Ordering::Relaxed); + Ok(test_key_pair(algorithm, vec![0x30, 0x00])) } fn load_private_key( @@ -131,6 +133,16 @@ fn explicit_provider_covers_all_rcgen_crypto() { let custom_provider = provider(); let key = KeyPair::generate_for_with_provider(&PKCS_ED25519, &custom_provider).unwrap(); assert_eq!(GENERATIONS.load(Ordering::Relaxed), 1); + assert_eq!( + KeyPair::generate_rsa_for_with_provider( + &PKCS_RSA_SHA256, + RsaKeySize::_3072, + &custom_provider, + ) + .unwrap_err(), + Error::KeyGenerationUnavailable + ); + assert_eq!(RSA_GENERATIONS.load(Ordering::Relaxed), 1); let mut params = CertificateParams::default(); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); From c08876a76e1f39ad16b72e5449d4ba40cc05bb49 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Wed, 2 Sep 2026 20:41:58 +0000 Subject: [PATCH 08/20] no default/global + cryptoProvider trait + use rustls hash setup --- .github/workflows/ci.yml | 7 +- README.md | 21 +- rcgen/Cargo.toml | 11 +- rcgen/examples/rsa-irc-openssl.rs | 5 +- rcgen/examples/sign-leaf-with-ca.rs | 21 +- rcgen/examples/sign-leaf-with-pem-files.rs | 7 +- rcgen/examples/simple.rs | 5 +- rcgen/src/certificate.rs | 221 ++++++++++----------- rcgen/src/crl.rs | 84 +++----- rcgen/src/crypto/aws_lc_rs.rs | 185 +++++++---------- rcgen/src/crypto/mod.rs | 152 ++++---------- rcgen/src/crypto/ring.rs | 114 +++++------ rcgen/src/csr.rs | 51 ++--- rcgen/src/error.rs | 6 - rcgen/src/key_pair.rs | 203 ++++--------------- rcgen/src/lib.rs | 107 ++++------ rcgen/tests/custom_provider.rs | 72 ++----- rustls-cert-gen/src/cert.rs | 30 ++- verify-tests/Cargo.toml | 4 +- verify-tests/src/lib.rs | 21 +- verify-tests/tests/botan.rs | 58 +++--- verify-tests/tests/generic.rs | 49 +++-- verify-tests/tests/openssl.rs | 82 ++++---- verify-tests/tests/webpki.rs | 92 +++++---- 24 files changed, 635 insertions(+), 973 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2a0e443..2018cff9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,12 +44,11 @@ jobs: uses: dtolnay/rust-toolchain@stable with: components: clippy - # `custom-provider` disables automatic built-in provider selection, so avoid `--all-features` - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets - run: cargo clippy -p rcgen --no-default-features --features crypto,pem,x509-parser --all-targets - - name: Ensure custom-provider builds have no built-in crypto dependencies + - name: Ensure backend-free builds have no built-in crypto dependencies run: | if cargo tree -p rcgen --no-default-features --features crypto --edges normal,build | grep -E 'ring v|aws-lc'; then exit 1 @@ -85,7 +84,7 @@ jobs: run: cargo doc --features ring,pem,x509-parser --document-private-items env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - - name: cargo doc (custom provider) + - name: cargo doc (provider API without a built-in backend) run: cargo doc -p rcgen --no-default-features --features crypto,pem,x509-parser env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} @@ -181,7 +180,7 @@ jobs: run: cargo test --features x509-parser - name: Run the tests with aws_lc_rs backend enabled run: cargo test --no-default-features --features aws_lc_rs,pem - - name: Run the tests with a custom provider and no built-in backend + - name: Run the tests with no built-in backend run: cargo test -p rcgen --no-default-features --features crypto,pem,x509-parser # rustls-cert-gen require either aws_lc_rs or ring feature - name: Run the tests with no features enabled diff --git a/README.md b/README.md index f5212321..86090093 100644 --- a/README.md +++ b/README.md @@ -6,32 +6,35 @@ Simple Rust library to generate X.509 certificates. -```Rust +```rust use rcgen::{generate_simple_self_signed, CertifiedKey}; +let provider = rcgen::crypto::ring::default_provider(); // Generate a certificate that's valid for "localhost" and "hello.world.example" let subject_alt_names = vec!["hello.world.example".to_string(), "localhost".to_string()]; -let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt_names).unwrap(); +let CertifiedKey { cert, signing_key } = + generate_simple_self_signed(subject_alt_names, provider).unwrap(); println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); ``` ## Cryptography providers -Rcgen uses Ring by default. AWS-LC is available through the `aws_lc_rs` feature. -AWS-LC FIPS mode requires both the `aws_lc_rs` and `fips` features. +Rcgen does not select a cryptography provider. Ring and AWS-LC are available through the `ring` +and `aws_lc_rs` features, respectively. AWS-LC FIPS mode requires both the `aws_lc_rs` and +`fips` features. -To use a custom cryptography provider without enabling either built-in backend: +Enable a built-in provider explicitly: ```toml [dependencies] -rcgen = { version = "0.14", default-features = false, features = ["custom-provider", "pem"] } +rcgen = { version = "0.14", features = ["ring"] } ``` -Implement `KeyPairProvider`, `DigestProvider`, and `SignatureVerificationProvider`, assemble them -into a `CryptoProvider`, then either call `CryptoProvider::install_default()` once near process -startup or use the explicit `*_with_provider` APIs. The `pem` feature is optional. +Applications pass the selected provider explicitly to APIs that perform cryptographic work. A +custom provider can instead implement the `CryptoProvider` trait without enabling either built-in +backend. The `pem` feature is optional. ## Trying it out with openssl diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index ecbe59a9..607cc873 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -11,10 +11,9 @@ rust-version.workspace = true keywords.workspace = true [features] -default = ["crypto", "pem", "ring"] +default = ["crypto", "pem"] aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] aws_lc_rs_unstable = ["aws_lc_rs"] # For backwards compatibility only -custom-provider = ["crypto"] fips = ["crypto", "aws-lc-rs?/fips"] crypto = [] ring = ["crypto", "dep:ring"] @@ -34,19 +33,19 @@ openssl = { workspace = true } [[example]] name = "rsa-irc-openssl" -required-features = ["pem"] +required-features = ["pem", "ring"] [[example]] name = "sign-leaf-with-ca" -required-features = ["pem", "x509-parser"] +required-features = ["pem", "ring", "x509-parser"] [[example]] name = "sign-leaf-with-pem-files" -required-features = ["pem", "x509-parser"] +required-features = ["pem", "ring", "x509-parser"] [[example]] name = "simple" -required-features = ["crypto", "pem"] +required-features = ["pem", "ring"] [package.metadata.docs.rs] features = ["aws_lc_rs", "aws_lc_rs_unstable", "crypto", "ring", "x509-parser"] diff --git a/rcgen/examples/rsa-irc-openssl.rs b/rcgen/examples/rsa-irc-openssl.rs index d227abb0..e72c8b67 100644 --- a/rcgen/examples/rsa-irc-openssl.rs +++ b/rcgen/examples/rsa-irc-openssl.rs @@ -5,6 +5,7 @@ fn main() -> Result<(), Box> { use rcgen::{date_time_ymd, CertificateParams, DistinguishedName}; + let provider = rcgen::crypto::ring::default_provider(); let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(2021, 5, 19); params.not_after = date_time_ymd(4096, 1, 1); @@ -12,9 +13,9 @@ fn main() -> Result<(), Box> { let pkey: openssl::pkey::PKey<_> = openssl::rsa::Rsa::generate(2048)?.try_into()?; let key_pair_pem = String::from_utf8(pkey.private_key_to_pem_pkcs8()?)?; - let key_pair = rcgen::KeyPair::from_pem(&key_pair_pem)?; + let key_pair = rcgen::KeyPair::from_pem(&key_pair_pem, provider)?; - let cert = params.self_signed(&key_pair)?; + let cert = params.self_signed(&key_pair, provider)?; let pem_serialized = cert.pem(); let pem = pem::parse(&pem_serialized)?; let der_serialized = pem.contents(); diff --git a/rcgen/examples/sign-leaf-with-ca.rs b/rcgen/examples/sign-leaf-with-ca.rs index bfa08eeb..ca3f2e4f 100644 --- a/rcgen/examples/sign-leaf-with-ca.rs +++ b/rcgen/examples/sign-leaf-with-ca.rs @@ -1,14 +1,15 @@ use rcgen::DnValue::PrintableString; use rcgen::{ - BasicConstraints, Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, IsCa, - Issuer, KeyPair, KeyUsagePurpose, + BasicConstraints, Certificate, CertificateParams, CryptoProvider, DnType, + ExtendedKeyUsagePurpose, IsCa, Issuer, KeyPair, KeyUsagePurpose, }; use time::{Duration, OffsetDateTime}; /// Example demonstrating signing end-entity certificate with ca fn main() { - let (ca, issuer) = new_ca(); - let end_entity = new_end_entity(&issuer); + let provider = rcgen::crypto::ring::default_provider(); + let (ca, issuer) = new_ca(provider); + let end_entity = new_end_entity(&issuer, provider); let end_entity_pem = end_entity.pem(); println!("directly signed end-entity certificate: {end_entity_pem}"); @@ -17,7 +18,7 @@ fn main() { println!("ca certificate: {ca_cert_pem}"); } -fn new_ca() -> (Certificate, Issuer<'static, KeyPair>) { +fn new_ca(provider: &dyn CryptoProvider) -> (Certificate, Issuer<'static, KeyPair>) { let mut params = CertificateParams::new(Vec::default()).expect("empty subject alt name can't produce error"); let (yesterday, tomorrow) = validity_period(); @@ -36,12 +37,12 @@ fn new_ca() -> (Certificate, Issuer<'static, KeyPair>) { params.not_before = yesterday; params.not_after = tomorrow; - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(provider).unwrap(); + let cert = params.self_signed(&key_pair, provider).unwrap(); (cert, Issuer::new(params, key_pair)) } -fn new_end_entity(issuer: &Issuer<'static, KeyPair>) -> Certificate { +fn new_end_entity(issuer: &Issuer<'static, KeyPair>, provider: &dyn CryptoProvider) -> Certificate { let name = "entity.other.host"; let mut params = CertificateParams::new(vec![name.into()]).expect("we know the name is valid"); let (yesterday, tomorrow) = validity_period(); @@ -54,8 +55,8 @@ fn new_end_entity(issuer: &Issuer<'static, KeyPair>) -> Certificate { params.not_before = yesterday; params.not_after = tomorrow; - let key_pair = KeyPair::generate().unwrap(); - params.signed_by(&key_pair, issuer).unwrap() + let key_pair = KeyPair::generate(provider).unwrap(); + params.signed_by(&key_pair, issuer, provider).unwrap() } fn validity_period() -> (OffsetDateTime, OffsetDateTime) { diff --git a/rcgen/examples/sign-leaf-with-pem-files.rs b/rcgen/examples/sign-leaf-with-pem-files.rs index 33ee4436..2b83eab6 100644 --- a/rcgen/examples/sign-leaf-with-pem-files.rs +++ b/rcgen/examples/sign-leaf-with-pem-files.rs @@ -15,6 +15,7 @@ use rcgen::{CertificateParams, DnType, ExtendedKeyUsagePurpose, Issuer, KeyPair, use time::{Duration, OffsetDateTime}; fn main() -> Result<(), Box> { + let provider = rcgen::crypto::ring::default_provider(); let mut args = std::env::args().skip(1); let signer_keys_file = PathBuf::from( @@ -36,7 +37,7 @@ fn main() -> Result<(), Box> { let keys_pem = fs::read_to_string(&signer_keys_file)?; let cert_pem = fs::read_to_string(&signer_cert_file)?; - let key_pair = KeyPair::from_pem(&keys_pem)?; + let key_pair = KeyPair::from_pem(&keys_pem, provider)?; let signer = Issuer::from_ca_cert_pem(&cert_pem, key_pair)?; // Create a new signed server certificate @@ -66,8 +67,8 @@ fn main() -> Result<(), Box> { params.not_before = yesterday; params.not_after = tomorrow; - let output_keys = KeyPair::generate()?; - let output_cert = params.signed_by(&output_keys, &signer)?; + let output_keys = KeyPair::generate(provider)?; + let output_cert = params.signed_by(&output_keys, &signer, provider)?; // Write new certificate fs::write(&output_keys_file, output_keys.serialize_pem())?; diff --git a/rcgen/examples/simple.rs b/rcgen/examples/simple.rs index 08558382..43a13726 100644 --- a/rcgen/examples/simple.rs +++ b/rcgen/examples/simple.rs @@ -3,6 +3,7 @@ use std::fs; use rcgen::{date_time_ymd, CertificateParams, DistinguishedName, DnType, KeyPair, SanType}; fn main() -> Result<(), Box> { + let provider = rcgen::crypto::ring::default_provider(); let mut params: CertificateParams = Default::default(); params.not_before = date_time_ymd(1975, 1, 1); params.not_after = date_time_ymd(4096, 1, 1); @@ -18,8 +19,8 @@ fn main() -> Result<(), Box> { SanType::DnsName("localhost".try_into()?), ]; - let key_pair = KeyPair::generate()?; - let cert = params.self_signed(&key_pair)?; + let key_pair = KeyPair::generate(provider)?; + let cert = params.self_signed(&key_pair, provider)?; let pem_serialized = cert.pem(); let pem = pem::parse(&pem_serialized)?; diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index bdc78358..56f1d366 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -138,25 +138,18 @@ impl CertificateParams { /// The returned [`Certificate`] may be serialized using [`Certificate::der`] and /// [`Certificate::pem`]. pub fn signed_by( - &self, - public_key: &impl PublicKeyData, - issuer: &Issuer<'_, impl SigningKey>, - ) -> Result { - Ok(Certificate { - der: self.serialize_der_with_signer(public_key, issuer)?, - }) - } - - /// Generate a certificate using an explicit cryptography provider. - #[cfg(feature = "crypto")] - pub fn signed_by_with_provider( &self, public_key: &(impl PublicKeyData + ?Sized), issuer: &Issuer<'_, impl SigningKey>, - provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result { Ok(Certificate { - der: self.serialize_der_with_signer_with_provider(public_key, issuer, provider)?, + der: self.serialize_der_with_signer( + public_key, + issuer, + #[cfg(feature = "crypto")] + provider, + )?, }) } @@ -164,52 +157,38 @@ impl CertificateParams { /// /// The returned [`Certificate`] may be serialized using [`Certificate::der`] and /// [`Certificate::pem`]. - pub fn self_signed(&self, signing_key: &impl SigningKey) -> Result { - let issuer = Issuer::from_params(self, signing_key); - Ok(Certificate { - der: self.serialize_der_with_signer(signing_key, &issuer)?, - }) - } - - /// Generate a self-signed certificate using an explicit cryptography provider. - #[cfg(feature = "crypto")] - pub fn self_signed_with_provider( + pub fn self_signed( &self, signing_key: &(impl SigningKey + ?Sized), - provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result { let issuer = Issuer::from_params(self, signing_key); Ok(Certificate { - der: self.serialize_der_with_signer_with_provider(signing_key, &issuer, provider)?, + der: self.serialize_der_with_signer( + signing_key, + &issuer, + #[cfg(feature = "crypto")] + provider, + )?, }) } /// Calculates a subject key identifier for the certificate subject's public key. /// This key identifier is used in the SubjectKeyIdentifier X.509v3 extension. - pub fn key_identifier(&self, key: &impl PublicKeyData) -> Vec { + pub fn key_identifier( + &self, + key: &(impl PublicKeyData + ?Sized), + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + ) -> Vec { #[cfg(feature = "crypto")] - { - let provider = CryptoProvider::get_default_or_install_from_crate_features() - .expect("a cryptography provider is required to derive a key identifier"); - self.key_identifier_with_provider(key, provider) - .expect("the cryptography provider failed to derive a key identifier") - } + return self + .key_identifier_method + .derive(provider, key.subject_public_key_info()); #[cfg(not(feature = "crypto"))] self.key_identifier_method .derive(key.subject_public_key_info()) } - /// Calculate a subject key identifier using an explicit cryptography provider. - #[cfg(feature = "crypto")] - pub fn key_identifier_with_provider( - &self, - key: &(impl PublicKeyData + ?Sized), - provider: &CryptoProvider, - ) -> Result, Error> { - self.key_identifier_method - .derive(provider, key.subject_public_key_info()) - } - #[cfg(all( test, feature = "x509-parser", @@ -247,7 +226,12 @@ impl CertificateParams { self.write_key_usage(writer.next()); self.write_subject_alt_names(writer.next()); self.write_extended_key_usage(writer.next()); - self.write_ca_extensions(writer, None); + self.write_ca_extensions( + writer, + None, + #[cfg(feature = "crypto")] + None, + ); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { writer.write_der(ext.content()) @@ -301,7 +285,8 @@ impl CertificateParams { fn write_ca_extensions( &self, writer: &mut DERWriterSeq, - subject_key_identifier: Option<&[u8]>, + pub_key_spki: Option<&[u8]>, + #[cfg(feature = "crypto")] provider: Option<&dyn CryptoProvider>, ) { let is_ca = match &self.is_ca { IsCa::Ca(bc) => Some(bc), @@ -309,12 +294,19 @@ impl CertificateParams { IsCa::NoCa => return, }; - if let Some(subject_key_identifier) = subject_key_identifier { + if let Some(pub_key_spki) = pub_key_spki { + #[cfg(feature = "crypto")] + let subject_key_identifier = self.key_identifier_method.derive( + provider.expect("a provider is required with public key data"), + pub_key_spki, + ); + #[cfg(not(feature = "crypto"))] + let subject_key_identifier = self.key_identifier_method.derive(pub_key_spki); write_x509_extension( writer.next(), oid::SUBJECT_KEY_IDENTIFIER, false, - |writer| writer.write_bytes(subject_key_identifier), + |writer| writer.write_bytes(&subject_key_identifier), ); } @@ -487,31 +479,7 @@ impl CertificateParams { &self, pub_key: &K, issuer: &Issuer<'_, impl SigningKey>, - ) -> Result, Error> { - #[cfg(feature = "crypto")] - { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - self.serialize_der_with_signer_with_provider(pub_key, issuer, provider) - } - #[cfg(not(feature = "crypto"))] - self.serialize_der_with_signer_inner(pub_key, issuer) - } - - #[cfg(feature = "crypto")] - pub(crate) fn serialize_der_with_signer_with_provider( - &self, - pub_key: &K, - issuer: &Issuer<'_, impl SigningKey>, - provider: &CryptoProvider, - ) -> Result, Error> { - self.serialize_der_with_signer_inner(pub_key, issuer, provider) - } - - fn serialize_der_with_signer_inner( - &self, - pub_key: &K, - issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result, Error> { // An empty distribution point would be encoded as an empty fullName, // violating GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName @@ -536,9 +504,9 @@ impl CertificateParams { } else { #[cfg(feature = "crypto")] { - let hash = provider.digest(HashAlgorithm::Sha256, pub_key.der_bytes())?; + let hash = provider.hash(HashAlgorithm::Sha256, pub_key.der_bytes()); // RFC 5280 specifies at most 20 bytes for a serial number - let mut sl = hash[0..20].to_vec(); + let mut sl = hash.as_ref()[0..20].to_vec(); sl[0] &= 0x7f; // MSB must be 0 to ensure encoding bignum in 20 bytes writer.next().write_bigint_bytes(&sl, true); } @@ -600,7 +568,7 @@ impl CertificateParams { writer: &mut DERWriterSeq, pub_key_spki: &[u8], issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result<(), Error> { if self.use_authority_key_identifier_extension { write_x509_authority_key_identifier( @@ -610,7 +578,7 @@ impl CertificateParams { #[cfg(feature = "crypto")] _ => issuer .key_identifier_method - .derive(provider, issuer.signing_key.subject_public_key_info())?, + .derive(provider, issuer.signing_key.subject_public_key_info()), }, ); } @@ -658,15 +626,12 @@ impl CertificateParams { ); } - let write_subject_key_identifier = !matches!(self.is_ca, IsCa::NoCa); - #[cfg(feature = "crypto")] - let subject_key_identifier = write_subject_key_identifier - .then(|| self.key_identifier_method.derive(provider, pub_key_spki)) - .transpose()?; - #[cfg(not(feature = "crypto"))] - let subject_key_identifier = - write_subject_key_identifier.then(|| self.key_identifier_method.derive(pub_key_spki)); - self.write_ca_extensions(writer, subject_key_identifier.as_deref()); + self.write_ca_extensions( + writer, + Some(pub_key_spki), + #[cfg(feature = "crypto")] + Some(provider), + ); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { @@ -1249,8 +1214,10 @@ mod tests { }; // Make the cert - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // Parse it let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); @@ -1287,8 +1254,10 @@ mod tests { }; // Make the cert - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // Parse it let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); @@ -1323,9 +1292,11 @@ mod tests { // A distribution point with no URIs would be encoded as an empty // fullName, violating GeneralNames ::= SEQUENCE SIZE (1..MAX) OF // GeneralName (RFC 5280 §4.2.1.13), so it must be rejected. - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); assert_eq!( - params.self_signed(&key_pair).unwrap_err(), + params + .self_signed(&key_pair, crate::test_provider()) + .unwrap_err(), Error::EmptyCrlDistributionPointUris ); } @@ -1343,8 +1314,10 @@ mod tests { ..CertificateParams::default() }; - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); assert!(cert.key_usage().unwrap().is_some()); @@ -1362,8 +1335,10 @@ mod tests { ..CertificateParams::default() }; - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); assert!(cert.iter_extensions().any(|ext| matches!( @@ -1384,8 +1359,10 @@ mod tests { }; // Make the cert - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // Parse it let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); @@ -1419,8 +1396,10 @@ mod tests { }; // Make the cert - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // Parse it let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); @@ -1447,8 +1426,10 @@ mod tests { }; // Make the cert - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // Parse it let (_rem, cert) = x509_parser::parse_x509_certificate(cert.der()).unwrap(); @@ -1468,16 +1449,20 @@ mod tests { #[test] #[cfg(windows)] fn test_windows_line_endings() { - let key_pair = KeyPair::generate().unwrap(); - let cert = CertificateParams::default().self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = CertificateParams::default() + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); assert!(cert.pem().contains("\r\n")); } #[test] #[cfg(not(windows))] fn test_not_windows_line_endings() { - let key_pair = KeyPair::generate().unwrap(); - let cert = CertificateParams::default().self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = CertificateParams::default() + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); assert!(!cert.pem().contains('\r')); } } @@ -1489,8 +1474,10 @@ mod tests { let mut params = CertificateParams::default(); let other_name = SanType::OtherName((vec![1, 2, 3, 4], "Foo".into())); params.subject_alt_names.push(other_name.clone()); - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // We should be able to parse the certificate with x509-parser. assert!(x509_parser::parse_x509_certificate(cert.der()).is_ok()); @@ -1519,8 +1506,10 @@ mod tests { email_address_dn_type.clone(), email_address_dn_value.clone(), ); - let key_pair = KeyPair::generate().unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); + let cert = params + .self_signed(&key_pair, crate::test_provider()) + .unwrap(); // We should be able to parse the certificate with x509-parser. assert!(x509_parser::parse_x509_certificate(cert.der()).is_ok()); @@ -1544,7 +1533,7 @@ mod tests { let ip_san = SanType::IpAddress(IpAddr::V4(ip)); let mut params = CertificateParams::new(vec!["crabs".to_owned()]).unwrap(); - let ca_key = KeyPair::generate().unwrap(); + let ca_key = KeyPair::generate(crate::test_provider()).unwrap(); // Add the SAN we want to test the parsing for params.subject_alt_names.push(ip_san.clone()); @@ -1553,7 +1542,7 @@ mod tests { params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); // Serialize our cert that has our chosen san, so we can testing parsing/deserializing it. - let cert = params.self_signed(&ca_key).unwrap(); + let cert = params.self_signed(&ca_key, crate::test_provider()).unwrap(); let actual = CertificateParams::from_ca_cert_der(cert.der()).unwrap(); assert!(actual.subject_alt_names.contains(&ip_san)); @@ -1649,7 +1638,7 @@ JiY98T5oN1X0C/qAXxJfSvklbru9fipwGt3dho5Tm6Ee3cYf+plnk4WZhSnqyef4 PITGdT9dgN88nHPCle0B1+OY+OZ5 -----END PRIVATE KEY-----"#; - let ca_kp = KeyPair::from_pem(ca_key).unwrap(); + let ca_kp = KeyPair::from_pem(ca_key, crate::test_provider()).unwrap(); let ca = Issuer::from_ca_cert_pem(ca_cert, ca_kp).unwrap(); let ca_ski = vec![ 0x97, 0xD4, 0x76, 0xA1, 0x9B, 0x1A, 0x71, 0x35, 0x2A, 0xC7, 0xF4, 0xA1, 0x84, 0x12, @@ -1676,12 +1665,14 @@ PITGdT9dgN88nHPCle0B1+OY+OZ5 .unwrap() ); - let ee_key = KeyPair::generate().unwrap(); + let ee_key = KeyPair::generate(crate::test_provider()).unwrap(); let ee_params = CertificateParams { use_authority_key_identifier_extension: true, ..CertificateParams::default() }; - let ee_cert = ee_params.signed_by(&ee_key, &ca).unwrap(); + let ee_cert = ee_params + .signed_by(&ee_key, &ca, crate::test_provider()) + .unwrap(); let (_, x509_ee) = x509_parser::parse_x509_certificate(ee_cert.der()).unwrap(); assert_eq!( diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index 6c66f3e4..d439b198 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -34,13 +34,7 @@ use crate::{ /// fn der_bytes(&self) -> &[u8] { &self.public_key } /// fn algorithm(&self) -> &'static SignatureAlgorithm { &PKCS_ED25519 } /// } -/// # #[cfg(any( -/// # not(feature = "crypto"), -/// # all( -/// # not(feature = "custom-provider"), -/// # any(feature = "ring", feature = "aws_lc_rs") -/// # ) -/// # ))] +/// # #[cfg(any(not(feature = "crypto"), feature = "ring"))] /// # fn main () { /// // Generate a CRL issuer. /// let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]).unwrap(); @@ -48,7 +42,9 @@ use crate::{ /// issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); /// issuer_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign]; /// #[cfg(feature = "crypto")] -/// let key_pair = KeyPair::generate().unwrap(); +/// let provider = rcgen::crypto::ring::default_provider(); +/// #[cfg(feature = "crypto")] +/// let key_pair = KeyPair::generate(provider).unwrap(); /// #[cfg(not(feature = "crypto"))] /// let key_pair = MyKeyPair { public_key: vec![] }; /// let issuer = Issuer::new(issuer_params, key_pair); @@ -71,15 +67,13 @@ use crate::{ /// key_identifier_method: KeyIdMethod::Sha256, /// #[cfg(not(feature = "crypto"))] /// key_identifier_method: KeyIdMethod::PreSpecified(vec![]), -/// }.signed_by(&issuer).unwrap(); +/// }.signed_by( +/// &issuer, +/// #[cfg(feature = "crypto")] +/// provider, +/// ).unwrap(); ///# } -/// # #[cfg(not(any( -/// # not(feature = "crypto"), -/// # all( -/// # not(feature = "custom-provider"), -/// # any(feature = "ring", feature = "aws_lc_rs") -/// # ) -/// # )))] +/// # #[cfg(all(feature = "crypto", not(feature = "ring")))] /// # fn main() {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateRevocationList { @@ -204,25 +198,18 @@ impl CertificateRevocationListParams { pub fn signed_by( &self, issuer: &Issuer<'_, impl SigningKey>, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result { self.validate(issuer)?; Ok(CertificateRevocationList { - der: self.serialize_der(issuer)?.into(), - }) - } - - /// Serialize and sign this CRL using an explicit cryptography provider. - #[cfg(feature = "crypto")] - pub fn signed_by_with_provider( - &self, - issuer: &Issuer<'_, impl SigningKey>, - provider: &CryptoProvider, - ) -> Result { - self.validate(issuer)?; - - Ok(CertificateRevocationList { - der: self.serialize_der_with_provider(issuer, provider)?.into(), + der: self + .serialize_der( + issuer, + #[cfg(feature = "crypto")] + provider, + )? + .into(), }) } @@ -248,34 +235,15 @@ impl CertificateRevocationListParams { Ok(()) } - fn serialize_der(&self, issuer: &Issuer<'_, impl SigningKey>) -> Result, Error> { - #[cfg(feature = "crypto")] - { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - self.serialize_der_with_provider(issuer, provider) - } - #[cfg(not(feature = "crypto"))] - self.serialize_der_inner(issuer) - } - - #[cfg(feature = "crypto")] - fn serialize_der_with_provider( - &self, - issuer: &Issuer<'_, impl SigningKey>, - provider: &CryptoProvider, - ) -> Result, Error> { - self.serialize_der_inner(issuer, provider) - } - - fn serialize_der_inner( + fn serialize_der( &self, issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result, Error> { #[cfg(feature = "crypto")] let key_identifier = self .key_identifier_method - .derive(provider, issuer.signing_key.subject_public_key_info())?; + .derive(provider, issuer.signing_key.subject_public_key_info()); #[cfg(not(feature = "crypto"))] let key_identifier = self .key_identifier_method @@ -512,7 +480,8 @@ mod tests { // fullName, violating GeneralNames ::= SEQUENCE SIZE (1..MAX) OF // GeneralName (RFC 5280 §4.2.1.13), so it must be rejected. assert_eq!( - crl.signed_by(&test_issuer()).unwrap_err(), + crl.signed_by(&test_issuer(), crate::test_provider()) + .unwrap_err(), Error::EmptyCrlDistributionPointUris ); } @@ -569,7 +538,7 @@ mod tests { revoked_certs: vec![revoked_cert], key_identifier_method: KeyIdMethod::Sha256, } - .signed_by(&test_issuer()) + .signed_by(&test_issuer(), crate::test_provider()) .unwrap() } @@ -583,6 +552,9 @@ mod tests { KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign, ]; - Issuer::new(issuer_params, KeyPair::generate().unwrap()) + Issuer::new( + issuer_params, + KeyPair::generate(crate::test_provider()).unwrap(), + ) } } diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index e317376d..1e45ba47 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -15,9 +15,7 @@ use aws_lc_rs::signature::{ }; use pki_types::PrivateKeyDer; -use super::{ - CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, -}; +use super::{CryptoProvider, HashAlgorithm, HashOutput}; use crate::{ Error, KeyPair, PublicKeyData, RsaKeySize, SignatureAlgorithm, SigningKey, PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ECDSA_P521_SHA256, PKCS_ECDSA_P521_SHA384, @@ -27,38 +25,14 @@ use crate::{ use crate::{PKCS_ML_DSA_44, PKCS_ML_DSA_65, PKCS_ML_DSA_87}; /// Return rcgen's built-in AWS-LC provider. -pub fn default_provider() -> CryptoProvider { - CryptoProvider { - key_pair_provider: &AwsLcKeyPairProvider, - digest_provider: &AwsLcDigestProvider, - signature_verification_provider: &AwsLcSignatureVerificationProvider, - } +pub fn default_provider() -> &'static dyn CryptoProvider { + &AwsLcProvider } #[derive(Debug)] -struct AwsLcDigestProvider; +struct AwsLcProvider; -impl DigestProvider for AwsLcDigestProvider { - fn digest( - &self, - algorithm: HashAlgorithm, - input: &[u8], - output: &mut [u8], - ) -> Result<(), Error> { - let algorithm = match algorithm { - HashAlgorithm::Sha256 => &digest::SHA256, - HashAlgorithm::Sha384 => &digest::SHA384, - HashAlgorithm::Sha512 => &digest::SHA512, - }; - output.copy_from_slice(digest::digest(algorithm, input).as_ref()); - Ok(()) - } -} - -#[derive(Debug)] -struct AwsLcKeyPairProvider; - -impl AwsLcKeyPairProvider { +impl AwsLcProvider { fn ecdsa_from_key( algorithm: &'static signature::EcdsaSigningAlgorithm, key_der: &[u8], @@ -240,7 +214,16 @@ impl AwsLcKeyPairProvider { } } -impl KeyPairProvider for AwsLcKeyPairProvider { +impl CryptoProvider for AwsLcProvider { + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { + let algorithm = match algorithm { + HashAlgorithm::Sha256 => &digest::SHA256, + HashAlgorithm::Sha384 => &digest::SHA384, + HashAlgorithm::Sha512 => &digest::SHA512, + }; + HashOutput::new(digest::digest(algorithm, input).as_ref()) + } + fn generate( &self, algorithm: &'static SignatureAlgorithm, @@ -312,6 +295,59 @@ impl KeyPairProvider for AwsLcKeyPairProvider { serialized_der, )) } + + fn verify( + &self, + algorithm: &'static SignatureAlgorithm, + public_key: &[u8], + message: &[u8], + signature_bytes: &[u8], + ) -> Result<(), Error> { + #[cfg(feature = "aws_lc_rs")] + { + let pqdsa_algorithm = if algorithm == &PKCS_ML_DSA_44 { + Some(&ML_DSA_44) + } else if algorithm == &PKCS_ML_DSA_65 { + Some(&ML_DSA_65) + } else if algorithm == &PKCS_ML_DSA_87 { + Some(&ML_DSA_87) + } else { + None + }; + if let Some(pqdsa_algorithm) = pqdsa_algorithm { + return pqdsa_algorithm + .verify_sig(public_key, message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed); + } + } + + let verification_algorithm: &'static dyn VerificationAlgorithm = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + &signature::ECDSA_P256_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + &signature::ECDSA_P384_SHA384_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA256 { + &signature::ECDSA_P521_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA384 { + &signature::ECDSA_P521_SHA384_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA512 { + &signature::ECDSA_P521_SHA512_ASN1 + } else if algorithm == &PKCS_ED25519 { + &signature::ED25519 + } else if algorithm == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if algorithm == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if algorithm == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + signature::UnparsedPublicKey::new(verification_algorithm, public_key) + .verify(message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed) + } } enum AwsLcKeyKind { @@ -368,64 +404,6 @@ impl SigningKey for AwsLcSigningKey { } } -#[derive(Debug)] -struct AwsLcSignatureVerificationProvider; - -impl SignatureVerificationProvider for AwsLcSignatureVerificationProvider { - fn verify( - &self, - algorithm: &'static SignatureAlgorithm, - public_key: &[u8], - message: &[u8], - signature_bytes: &[u8], - ) -> Result<(), Error> { - #[cfg(feature = "aws_lc_rs")] - { - let pqdsa_algorithm = if algorithm == &PKCS_ML_DSA_44 { - Some(&ML_DSA_44) - } else if algorithm == &PKCS_ML_DSA_65 { - Some(&ML_DSA_65) - } else if algorithm == &PKCS_ML_DSA_87 { - Some(&ML_DSA_87) - } else { - None - }; - if let Some(pqdsa_algorithm) = pqdsa_algorithm { - return pqdsa_algorithm - .verify_sig(public_key, message, signature_bytes) - .map_err(|_| Error::SignatureVerificationFailed); - } - } - - let verification_algorithm: &'static dyn VerificationAlgorithm = - if algorithm == &PKCS_ECDSA_P256_SHA256 { - &signature::ECDSA_P256_SHA256_ASN1 - } else if algorithm == &PKCS_ECDSA_P384_SHA384 { - &signature::ECDSA_P384_SHA384_ASN1 - } else if algorithm == &PKCS_ECDSA_P521_SHA256 { - &signature::ECDSA_P521_SHA256_ASN1 - } else if algorithm == &PKCS_ECDSA_P521_SHA384 { - &signature::ECDSA_P521_SHA384_ASN1 - } else if algorithm == &PKCS_ECDSA_P521_SHA512 { - &signature::ECDSA_P521_SHA512_ASN1 - } else if algorithm == &PKCS_ED25519 { - &signature::ED25519 - } else if algorithm == &PKCS_RSA_SHA256 { - &signature::RSA_PKCS1_2048_8192_SHA256 - } else if algorithm == &PKCS_RSA_SHA384 { - &signature::RSA_PKCS1_2048_8192_SHA384 - } else if algorithm == &PKCS_RSA_SHA512 { - &signature::RSA_PKCS1_2048_8192_SHA512 - } else { - return Err(Error::UnsupportedSignatureAlgorithm); - }; - - signature::UnparsedPublicKey::new(verification_algorithm, public_key) - .verify(message, signature_bytes) - .map_err(|_| Error::SignatureVerificationFailed) - } -} - #[cfg(test)] mod tests { #[cfg(feature = "aws_lc_rs")] @@ -437,8 +415,8 @@ mod tests { fn sha256_known_answer() { assert_eq!( default_provider() - .digest(HashAlgorithm::Sha256, b"abc") - .unwrap(), + .hash(HashAlgorithm::Sha256, b"abc") + .as_ref(), [ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, @@ -452,26 +430,19 @@ mod tests { fn ml_dsa_round_trip() { let provider = default_provider(); for algorithm in [&PKCS_ML_DSA_44, &PKCS_ML_DSA_65, &PKCS_ML_DSA_87] { - let generated = KeyPair::generate_for_with_provider(algorithm, &provider).unwrap(); + let generated = KeyPair::generate_for(algorithm, provider).unwrap(); let private_key = PrivatePkcs8KeyDer::from(generated.serialize_der()); - let loaded = KeyPair::from_pkcs8_der_and_sign_algo_with_provider( - &private_key, - algorithm, - &provider, - ) - .unwrap(); + let loaded = + KeyPair::from_pkcs8_der_and_sign_algo(&private_key, algorithm, provider).unwrap(); assert_eq!(loaded.algorithm(), algorithm); - let detected = - KeyPair::from_der_with_provider(&PrivateKeyDer::Pkcs8(private_key), &provider) - .unwrap(); + let detected = KeyPair::from_der(&PrivateKeyDer::Pkcs8(private_key), provider).unwrap(); assert_eq!(detected.algorithm(), algorithm); let message = b"stable ML-DSA provider"; let signature = loaded.sign(message).unwrap(); provider - .signature_verification_provider .verify(algorithm, loaded.der_bytes(), message, &signature) .unwrap(); @@ -480,11 +451,9 @@ mod tests { let request = crate::CertificateParams::default() .serialize_request(&loaded) .unwrap(); - let parsed = crate::CertificateSigningRequestParams::from_der_with_provider( - request.der(), - &provider, - ) - .unwrap(); + let parsed = + crate::CertificateSigningRequestParams::from_der(request.der(), provider) + .unwrap(); assert_eq!(parsed.public_key.algorithm(), algorithm); } } diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index bba1b8f3..b8f052d9 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -4,40 +4,17 @@ //! [`CryptoProvider`] supplies the operations rcgen performs itself: key generation and //! loading, hashing, and signature verification when parsing certificate signing requests. //! -//! Applications using a custom provider should disable rcgen's default features, enable -//! `crypto`, and install their provider before using convenience APIs such as -//! [`KeyPair::generate`](crate::KeyPair::generate): -//! -//! ```ignore -//! custom_provider().install_default() -//! .expect("a crypto provider was already installed"); -//! ``` -//! -//! A provider can also be passed explicitly to APIs whose names end in `with_provider`. -//! Explicit selection is useful for libraries and does not access the process-wide default. - -use std::fmt::Debug; -use std::sync::{Arc, OnceLock}; +//! Applications select a provider explicitly for each API that performs cryptographic work. use pki_types::PrivateKeyDer; use crate::{Error, KeyPair, RsaKeySize, SignatureAlgorithm}; -/// Hash operations supplied by a [`CryptoProvider`]. -pub trait DigestProvider: Debug + Send + Sync { - /// Hash `input` with `algorithm`, writing the digest to `output`. - /// - /// `output` is always exactly [`HashAlgorithm::output_len`] bytes long. - fn digest( - &self, - algorithm: HashAlgorithm, - input: &[u8], - output: &mut [u8], - ) -> Result<(), Error>; -} +/// Cryptographic operations used by rcgen. +pub trait CryptoProvider: std::fmt::Debug + Send + Sync { + /// Hash `input` with `algorithm`. + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput; -/// Key generation and private-key loading supplied by a [`CryptoProvider`]. -pub trait KeyPairProvider: Debug + Send + Sync { /// Generate an exportable key pair for `algorithm`. /// /// `key_size` selects an explicit RSA key size. It must be `None` for non-RSA algorithms. @@ -56,15 +33,12 @@ pub trait KeyPairProvider: Debug + Send + Sync { key_der: PrivateKeyDer<'static>, algorithm: Option<&'static SignatureAlgorithm>, ) -> Result; -} -/// Signature verification supplied by a [`CryptoProvider`]. -/// -/// rcgen uses this operation to verify the self-signature on a parsed PKCS#10 certificate -/// signing request. Public keys are provided as the contents of the SubjectPublicKeyInfo -/// `subjectPublicKey` BIT STRING, matching [`PublicKeyData::der_bytes`](crate::PublicKeyData::der_bytes). -pub trait SignatureVerificationProvider: Debug + Send + Sync { /// Verify `signature` over `message` using `public_key` and `algorithm`. + /// + /// rcgen uses this operation to verify the self-signature on a parsed PKCS#10 certificate + /// signing request. `public_key` contains the SubjectPublicKeyInfo `subjectPublicKey` BIT + /// STRING contents, matching [`PublicKeyData::der_bytes`](crate::PublicKeyData::der_bytes). fn verify( &self, algorithm: &'static SignatureAlgorithm, @@ -74,85 +48,6 @@ pub trait SignatureVerificationProvider: Debug + Send + Sync { ) -> Result<(), Error>; } -/// Controls the cryptography used by rcgen. -/// -/// The component fields can come from one backend or be composed from several backends. rcgen -/// provides built-in providers through `crypto::ring::default_provider()` and -/// `crypto::aws_lc_rs::default_provider()` when their corresponding crate features are enabled. A custom -/// provider does not require either dependency. -/// -/// # Process-wide default -/// -/// [`install_default`](Self::install_default) sets a provider once for convenience APIs. If no -/// provider has been installed, rcgen automatically installs a built-in provider selected by -/// crate features. As in earlier rcgen releases, AWS-LC takes precedence if both built-in backend -/// features are enabled. With no backend or the `custom-provider` feature, applications must -/// install a provider explicitly. -#[derive(Clone, Debug)] -pub struct CryptoProvider { - /// Provider for generating and loading private key pairs. - pub key_pair_provider: &'static dyn KeyPairProvider, - /// Provider for SHA-256, SHA-384, and SHA-512 hashing. - pub digest_provider: &'static dyn DigestProvider, - /// Provider for signature verification. - pub signature_verification_provider: &'static dyn SignatureVerificationProvider, -} - -impl CryptoProvider { - /// Set this provider as the default for the current process. - /// - /// This can succeed at most once during a process execution. Call it before invoking any - /// convenience API that uses the process-wide provider. - pub fn install_default(self) -> Result<(), Arc> { - PROCESS_DEFAULT_PROVIDER.set(Arc::new(self)) - } - - /// Return the process-wide default provider, if one has been installed. - pub fn get_default() -> Option<&'static Arc> { - PROCESS_DEFAULT_PROVIDER.get() - } - - /// Compute a digest using this provider. - pub fn digest(&self, algorithm: HashAlgorithm, input: &[u8]) -> Result, Error> { - let mut output = vec![0; algorithm.output_len()]; - self.digest_provider.digest(algorithm, input, &mut output)?; - Ok(output) - } - - pub(crate) fn get_default_or_install_from_crate_features() -> Result<&'static Arc, Error> - { - if let Some(provider) = Self::get_default() { - return Ok(provider); - } - - let provider = Self::from_crate_features().ok_or(Error::CryptoProviderNotInstalled)?; - // Another thread may install a provider first. In that case its choice wins. - let _ = provider.install_default(); - Self::get_default().ok_or(Error::CryptoProviderNotInstalled) - } - - fn from_crate_features() -> Option { - #[cfg(all( - feature = "ring", - not(feature = "aws_lc_rs"), - not(feature = "custom-provider") - ))] - { - return Some(ring::default_provider()); - } - - #[cfg(all(feature = "aws_lc_rs", not(feature = "custom-provider")))] - { - return Some(aws_lc_rs::default_provider()); - } - - #[allow(unreachable_code)] - None - } -} - -static PROCESS_DEFAULT_PROVIDER: OnceLock> = OnceLock::new(); - /// `ring`-based cryptography provider. #[cfg(feature = "ring")] pub mod ring; @@ -183,3 +78,32 @@ impl HashAlgorithm { } } } + +/// The output of a cryptographic hash function. +#[derive(Clone)] +pub struct HashOutput { + buf: [u8; Self::MAX_LEN], + used: usize, +} + +impl HashOutput { + /// Construct a hash output from at most [`Self::MAX_LEN`] bytes. + pub fn new(bytes: &[u8]) -> Self { + assert!(bytes.len() <= Self::MAX_LEN); + let mut output = Self { + buf: [0; Self::MAX_LEN], + used: bytes.len(), + }; + output.buf[..bytes.len()].copy_from_slice(bytes); + output + } + + /// Maximum supported hash output size, sufficient for SHA-512. + pub const MAX_LEN: usize = 64; +} + +impl AsRef<[u8]> for HashOutput { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.used] + } +} diff --git a/rcgen/src/crypto/ring.rs b/rcgen/src/crypto/ring.rs index 67c22887..3643009b 100644 --- a/rcgen/src/crypto/ring.rs +++ b/rcgen/src/crypto/ring.rs @@ -8,9 +8,7 @@ use ::ring::signature::{ }; use pki_types::PrivateKeyDer; -use super::{ - CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, -}; +use super::{CryptoProvider, HashAlgorithm, HashOutput}; use crate::{ Error, KeyPair, PublicKeyData, RsaKeySize, SignatureAlgorithm, SigningKey, PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ED25519, PKCS_RSA_SHA256, PKCS_RSA_SHA384, @@ -18,38 +16,14 @@ use crate::{ }; /// Return rcgen's built-in `ring` provider. -pub fn default_provider() -> CryptoProvider { - CryptoProvider { - key_pair_provider: &RingKeyPairProvider, - digest_provider: &RingDigestProvider, - signature_verification_provider: &RingSignatureVerificationProvider, - } +pub fn default_provider() -> &'static dyn CryptoProvider { + &RingProvider } #[derive(Debug)] -struct RingDigestProvider; +struct RingProvider; -impl DigestProvider for RingDigestProvider { - fn digest( - &self, - algorithm: HashAlgorithm, - input: &[u8], - output: &mut [u8], - ) -> Result<(), Error> { - let algorithm = match algorithm { - HashAlgorithm::Sha256 => &digest::SHA256, - HashAlgorithm::Sha384 => &digest::SHA384, - HashAlgorithm::Sha512 => &digest::SHA512, - }; - output.copy_from_slice(digest::digest(algorithm, input).as_ref()); - Ok(()) - } -} - -#[derive(Debug)] -struct RingKeyPairProvider; - -impl RingKeyPairProvider { +impl RingProvider { fn ecdsa_from_pkcs8( algorithm: &'static signature::EcdsaSigningAlgorithm, pkcs8: &[u8], @@ -115,7 +89,16 @@ impl RingKeyPairProvider { } } -impl KeyPairProvider for RingKeyPairProvider { +impl CryptoProvider for RingProvider { + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { + let algorithm = match algorithm { + HashAlgorithm::Sha256 => &digest::SHA256, + HashAlgorithm::Sha384 => &digest::SHA384, + HashAlgorithm::Sha512 => &digest::SHA512, + }; + HashOutput::new(digest::digest(algorithm, input).as_ref()) + } + fn generate( &self, algorithm: &'static SignatureAlgorithm, @@ -181,6 +164,35 @@ impl KeyPairProvider for RingKeyPairProvider { serialized_der, )) } + + fn verify( + &self, + algorithm: &'static SignatureAlgorithm, + public_key: &[u8], + message: &[u8], + signature_bytes: &[u8], + ) -> Result<(), Error> { + let verification_algorithm: &'static dyn VerificationAlgorithm = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + &signature::ECDSA_P256_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + &signature::ECDSA_P384_SHA384_ASN1 + } else if algorithm == &PKCS_ED25519 { + &signature::ED25519 + } else if algorithm == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if algorithm == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if algorithm == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + signature::UnparsedPublicKey::new(verification_algorithm, public_key) + .verify(message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed) + } } enum RingKeyKind { @@ -226,40 +238,6 @@ impl SigningKey for RingSigningKey { } } -#[derive(Debug)] -struct RingSignatureVerificationProvider; - -impl SignatureVerificationProvider for RingSignatureVerificationProvider { - fn verify( - &self, - algorithm: &'static SignatureAlgorithm, - public_key: &[u8], - message: &[u8], - signature_bytes: &[u8], - ) -> Result<(), Error> { - let verification_algorithm: &'static dyn VerificationAlgorithm = - if algorithm == &PKCS_ECDSA_P256_SHA256 { - &signature::ECDSA_P256_SHA256_ASN1 - } else if algorithm == &PKCS_ECDSA_P384_SHA384 { - &signature::ECDSA_P384_SHA384_ASN1 - } else if algorithm == &PKCS_ED25519 { - &signature::ED25519 - } else if algorithm == &PKCS_RSA_SHA256 { - &signature::RSA_PKCS1_2048_8192_SHA256 - } else if algorithm == &PKCS_RSA_SHA384 { - &signature::RSA_PKCS1_2048_8192_SHA384 - } else if algorithm == &PKCS_RSA_SHA512 { - &signature::RSA_PKCS1_2048_8192_SHA512 - } else { - return Err(Error::UnsupportedSignatureAlgorithm); - }; - - signature::UnparsedPublicKey::new(verification_algorithm, public_key) - .verify(message, signature_bytes) - .map_err(|_| Error::SignatureVerificationFailed) - } -} - #[cfg(test)] mod tests { use super::*; @@ -268,8 +246,8 @@ mod tests { fn sha256_known_answer() { assert_eq!( default_provider() - .digest(HashAlgorithm::Sha256, b"abc") - .unwrap(), + .hash(HashAlgorithm::Sha256, b"abc") + .as_ref(), [ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index f4203cc5..0c0da745 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -86,16 +86,9 @@ impl CertificateSigningRequestParams { /// /// See [`from_der`](Self::from_der) for more details. #[cfg(all(feature = "pem", feature = "x509-parser", feature = "crypto"))] - pub fn from_pem(pem_str: &str) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_pem_with_provider(pem_str, provider) - } - - /// Parse and verify a certificate signing request from PEM using `provider`. - #[cfg(all(feature = "pem", feature = "x509-parser", feature = "crypto"))] - pub fn from_pem_with_provider(pem_str: &str, provider: &CryptoProvider) -> Result { + pub fn from_pem(pem_str: &str, provider: &dyn CryptoProvider) -> Result { let csr = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificationRequest)?; - Self::from_der_with_provider(&csr.contents().into(), provider) + Self::from_der(&csr.contents().into(), provider) } /// Parse and verify a certificate signing request from DER-encoded bytes @@ -116,16 +109,9 @@ impl CertificateSigningRequestParams { /// /// [`PemObject`]: pki_types::pem::PemObject #[cfg(all(feature = "x509-parser", feature = "crypto"))] - pub fn from_der(csr: &CertificateSigningRequestDer<'_>) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_der_with_provider(csr, provider) - } - - /// Parse and verify a certificate signing request from DER using `provider`. - #[cfg(all(feature = "x509-parser", feature = "crypto"))] - pub fn from_der_with_provider( + pub fn from_der( csr: &CertificateSigningRequestDer<'_>, - provider: &CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { use x509_parser::prelude::FromDer; use x509_parser::x509::AlgorithmIdentifier; @@ -155,7 +141,6 @@ impl CertificateSigningRequestParams { .ok_or(Error::UnsupportedSignatureAlgorithm)?; provider - .signature_verification_provider .verify( alg, info.subject_pki.subject_public_key.data.as_ref(), @@ -246,25 +231,16 @@ impl CertificateSigningRequestParams { /// /// The returned [`Certificate`] may be serialized using [`Certificate::der`] and /// [`Certificate::pem`]. - pub fn signed_by(&self, issuer: &Issuer) -> Result { - Ok(Certificate { - der: self - .params - .serialize_der_with_signer(&self.public_key, issuer)?, - }) - } - - /// Generate a certificate using an explicit cryptography provider. - #[cfg(feature = "crypto")] - pub fn signed_by_with_provider( + pub fn signed_by( &self, issuer: &Issuer, - provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result { Ok(Certificate { - der: self.params.serialize_der_with_signer_with_provider( + der: self.params.serialize_der_with_signer( &self.public_key, issuer, + #[cfg(feature = "crypto")] provider, )?, }) @@ -289,7 +265,7 @@ mod tests { fn dont_write_sans_extension_if_no_sans_are_present() { let mut params = CertificateParams::default(); params.key_usages.push(KeyUsagePurpose::DigitalSignature); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); let csr = params.serialize_request(&key_pair).unwrap(); let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); assert!(!parsed_csr @@ -304,7 +280,7 @@ mod tests { params .extended_key_usages .push(ExtendedKeyUsagePurpose::ClientAuth); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); let csr = params.serialize_request(&key_pair).unwrap(); let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); let requested_extensions = parsed_csr @@ -325,7 +301,7 @@ mod tests { is_ca: IsCa::ExplicitNoCa, ..Default::default() }; - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); let csr = params.serialize_request(&key_pair).unwrap(); let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); let requested_extensions = parsed_csr @@ -348,9 +324,10 @@ mod tests { is_ca: IsCa::Ca(BasicConstraints::Constrained(10)), ..Default::default() }; - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(crate::test_provider()).unwrap(); let csr = params.serialize_request(&key_pair).unwrap(); - let csr_de = CertificateSigningRequestParams::from_der(csr.der()).unwrap(); + let csr_de = + CertificateSigningRequestParams::from_der(csr.der(), crate::test_provider()).unwrap(); assert_eq!(csr_de.params.is_ca, params.is_ca); } diff --git a/rcgen/src/error.rs b/rcgen/src/error.rs index f964cdb9..10a4db9e 100644 --- a/rcgen/src/error.rs +++ b/rcgen/src/error.rs @@ -10,9 +10,6 @@ pub enum Error { CouldNotParseCertificationRequest, /// The given key pair couldn't be parsed CouldNotParseKeyPair, - /// No process-wide cryptography provider has been installed and crate features do not select - /// a built-in provider. - CryptoProviderNotInstalled, /// A cryptography provider failed an operation. CryptoProviderError(String), /// The CSR signature is invalid @@ -73,9 +70,6 @@ impl fmt::Display for Error { request" )?, CouldNotParseKeyPair => write!(f, "Could not parse key pair")?, - CryptoProviderNotInstalled => { - write!(f, "No process-wide cryptography provider is installed")? - }, CryptoProviderError(e) => write!(f, "Cryptography provider error: {e}")?, #[cfg(feature = "x509-parser")] InvalidCertificationRequestSignature => write!(f, "Invalid CSR signature")?, diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index d6e7f484..88cf5d97 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -41,8 +41,8 @@ impl fmt::Debug for KeyPair { impl KeyPair { /// Construct a key pair from a provider-specific signing key and its PKCS#8 DER encoding. /// - /// This constructor is intended for implementations of - /// [`KeyPairProvider`](crate::crypto::KeyPairProvider). `serialized_der` must encode the same + /// This constructor is intended for implementations of [`CryptoProvider`]. `serialized_der` + /// must encode the same /// private key exposed by `signing_key` and must remain exportable to callers. pub fn from_signing_key( signing_key: Box, @@ -54,14 +54,9 @@ impl KeyPair { } } - /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair - pub fn generate() -> Result { - Self::generate_for(&PKCS_ECDSA_P256_SHA256) - } - - /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair using `provider` - pub fn generate_with_provider(provider: &CryptoProvider) -> Result { - Self::generate_for_with_provider(&PKCS_ECDSA_P256_SHA256, provider) + /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair using `provider`. + pub fn generate(provider: &dyn CryptoProvider) -> Result { + Self::generate_for(&PKCS_ECDSA_P256_SHA256, provider) } /// Generate a new random key pair for the specified signature algorithm @@ -71,17 +66,11 @@ impl KeyPair { /// a generated key or an error for key generation being unavailable. /// Currently, the built-in `aws-lc-rs` provider supports RSA key generation while the /// built-in `ring` provider does not. - pub fn generate_for(alg: &'static SignatureAlgorithm) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::generate_for_with_provider(alg, provider) - } - - /// Generate a new random key pair for the specified signature algorithm using `provider` - pub fn generate_for_with_provider( + pub fn generate_for( alg: &'static SignatureAlgorithm, - provider: &CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { - provider.key_pair_provider.generate(alg, None) + provider.generate(alg, None) } /// Generates a new random RSA key pair for the specified key size @@ -93,21 +82,9 @@ impl KeyPair { pub fn generate_rsa_for( alg: &'static SignatureAlgorithm, key_size: RsaKeySize, + provider: &dyn CryptoProvider, ) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::generate_rsa_for_with_provider(alg, key_size, provider) - } - - /// Generates a new random RSA key pair for the specified key size using `provider` - /// - /// If passed a signature algorithm that is not RSA, it will return - /// [`Error::KeyGenerationUnavailable`]. - pub fn generate_rsa_for_with_provider( - alg: &'static SignatureAlgorithm, - key_size: RsaKeySize, - provider: &CryptoProvider, - ) -> Result { - provider.key_pair_provider.generate(alg, Some(key_size)) + provider.generate(alg, Some(key_size)) } /// Returns the key pair's signature algorithm @@ -126,20 +103,11 @@ impl KeyPair { /// If the built-in `ring` provider is used, then the key must be a DER-encoded plaintext /// private key as specified in PKCS #8/RFC 5958; this appears as "PRIVATE KEY" in PEM files. #[cfg(feature = "pem")] - pub fn from_pem(pem_str: &str) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_pem_with_provider(pem_str, provider) - } - - /// Parses the key pair from the ASCII PEM format using `provider` - /// - /// See [`from_pem`](Self::from_pem) for details about supported key encodings. - #[cfg(feature = "pem")] - pub fn from_pem_with_provider(pem_str: &str, provider: &CryptoProvider) -> Result { + pub fn from_pem(pem_str: &str, provider: &dyn CryptoProvider) -> Result { let private_key = pem::parse(pem_str)._err()?; let private_key = PrivateKeyDer::try_from(private_key.into_contents()) .map_err(|_| Error::CouldNotParseKeyPair)?; - Self::from_der_with_provider(&private_key, provider) + Self::from_der(&private_key, provider) } /// Obtains the key pair from a PEM formatted key @@ -153,30 +121,18 @@ impl KeyPair { pub fn from_pkcs8_pem_and_sign_algo( pem_str: &str, alg: &'static SignatureAlgorithm, - ) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_pkcs8_pem_and_sign_algo_with_provider(pem_str, alg, provider) - } - - /// Obtains the key pair from a PKCS#8 PEM formatted key using `provider` - /// and the specified [`SignatureAlgorithm`] - #[cfg(feature = "pem")] - pub fn from_pkcs8_pem_and_sign_algo_with_provider( - pem_str: &str, - alg: &'static SignatureAlgorithm, - provider: &CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { let private_key = pem::parse(pem_str)._err()?; let private_key = PrivatePkcs8KeyDer::from(private_key.into_contents()); - Self::from_pkcs8_der_and_sign_algo_with_provider(&private_key, alg, provider) + Self::from_pkcs8_der_and_sign_algo(&private_key, alg, provider) } /// Obtains the key pair from a DER formatted key using the specified [`SignatureAlgorithm`] /// - /// If you have a [`PrivatePkcs8KeyDer`], you can usually rely on the [`TryFrom`] implementation - /// to obtain a [`KeyPair`] -- it will determine the correct [`SignatureAlgorithm`] for you. - /// However, sometimes multiple signature algorithms fit for the same DER key. In those instances, - /// you can use this function to precisely specify the `SignatureAlgorithm`. + /// Use [`from_der`](Self::from_der) when the provider should determine the appropriate + /// [`SignatureAlgorithm`]. Use this function when multiple signature algorithms fit the same + /// key and you need to select one precisely. /// /// [`rustls_pemfile::private_key()`] is often used to obtain a [`PrivateKeyDer`] from PEM /// input. If the obtained [`PrivateKeyDer`] is a `Pkcs8` variant, you can use its contents @@ -188,21 +144,9 @@ impl KeyPair { pub fn from_pkcs8_der_and_sign_algo( pkcs8: &PrivatePkcs8KeyDer<'_>, alg: &'static SignatureAlgorithm, + provider: &dyn CryptoProvider, ) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_pkcs8_der_and_sign_algo_with_provider(pkcs8, alg, provider) - } - - /// Obtains the key pair from a PKCS#8 DER formatted key using `provider` - /// and the specified [`SignatureAlgorithm`] - pub fn from_pkcs8_der_and_sign_algo_with_provider( - pkcs8: &PrivatePkcs8KeyDer<'_>, - alg: &'static SignatureAlgorithm, - provider: &CryptoProvider, - ) -> Result { - provider - .key_pair_provider - .load_private_key(PrivateKeyDer::Pkcs8(pkcs8.clone_key()), Some(alg)) + provider.load_private_key(PrivateKeyDer::Pkcs8(pkcs8.clone_key()), Some(alg)) } /// Obtains the key pair from a PEM formatted key @@ -222,26 +166,12 @@ impl KeyPair { pub fn from_pem_and_sign_algo( pem_str: &str, alg: &'static SignatureAlgorithm, - ) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_pem_and_sign_algo_with_provider(pem_str, alg, provider) - } - - /// Obtains the key pair from a PEM formatted key using `provider` - /// and the specified [`SignatureAlgorithm`] - /// - /// See [`from_pem_and_sign_algo`](Self::from_pem_and_sign_algo) for details about supported - /// key encodings. - #[cfg(feature = "pem")] - pub fn from_pem_and_sign_algo_with_provider( - pem_str: &str, - alg: &'static SignatureAlgorithm, - provider: &CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { let private_key = pem::parse(pem_str)._err()?; let private_key = PrivateKeyDer::try_from(private_key.into_contents()) .map_err(|_| Error::CouldNotParseKeyPair)?; - Self::from_der_and_sign_algo_with_provider(&private_key, alg, provider) + Self::from_der_and_sign_algo(&private_key, alg, provider) } /// Obtains the key pair from a DER formatted key @@ -251,10 +181,9 @@ impl KeyPair { /// `ring` provider only supports [`PrivateKeyDer::Pkcs8`], while the built-in `aws_lc_rs` /// provider supports PKCS#8, PKCS#1, and SEC1 keys. /// - /// If you have a [`PrivateKeyDer`], you can usually rely on the [`TryFrom`] implementation - /// to obtain a [`KeyPair`] -- it will determine the correct [`SignatureAlgorithm`] for you. - /// However, sometimes multiple signature algorithms fit for the same DER key. In those instances, - /// you can use this function to precisely specify the `SignatureAlgorithm`. + /// Use [`from_der`](Self::from_der) when the provider should determine the appropriate + /// [`SignatureAlgorithm`]. Use this function when multiple signature algorithms fit the same + /// key and you need to select one precisely. /// /// You can use [`rustls_pemfile::private_key`] to get the `key` input. If /// you already have a byte slice, just calling `try_into()` will convert it to a [`PrivateKeyDer`]. @@ -263,36 +192,16 @@ impl KeyPair { pub fn from_der_and_sign_algo( key: &PrivateKeyDer<'_>, alg: &'static SignatureAlgorithm, + provider: &dyn CryptoProvider, ) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_der_and_sign_algo_with_provider(key, alg, provider) - } - - /// Obtains the key pair from a DER formatted key using `provider` - /// and the specified [`SignatureAlgorithm`] - /// - /// See [`from_der_and_sign_algo`](Self::from_der_and_sign_algo) for details about supported - /// key encodings. - pub fn from_der_and_sign_algo_with_provider( - key: &PrivateKeyDer<'_>, - alg: &'static SignatureAlgorithm, - provider: &CryptoProvider, - ) -> Result { - provider - .key_pair_provider - .load_private_key(key.clone_key(), Some(alg)) + provider.load_private_key(key.clone_key(), Some(alg)) } - /// Obtains the key pair from a DER formatted key using `provider` + /// Obtains the key pair from a DER formatted key using `provider`. /// /// The provider determines the correct [`SignatureAlgorithm`] for the key. - pub fn from_der_with_provider( - key: &PrivateKeyDer<'_>, - provider: &CryptoProvider, - ) -> Result { - provider - .key_pair_provider - .load_private_key(key.clone_key(), None) + pub fn from_der(key: &PrivateKeyDer<'_>, provider: &dyn CryptoProvider) -> Result { + provider.load_private_key(key.clone_key(), None) } /// Get the raw public key of this key pair @@ -300,7 +209,7 @@ impl KeyPair { /// The returned bytes are the contents of the X.509 SubjectPublicKeyInfo /// `subjectPublicKey` BIT STRING, matching [`PublicKeyData::der_bytes`]. This is also the /// public-key format passed to - /// [`SignatureVerificationProvider::verify`](crate::crypto::SignatureVerificationProvider::verify). + /// [`CryptoProvider::verify`]. pub fn public_key_raw(&self) -> &[u8] { self.der_bytes() } @@ -363,50 +272,6 @@ impl PublicKeyData for KeyPair { } } -#[cfg(feature = "crypto")] -impl TryFrom<&[u8]> for KeyPair { - type Error = Error; - - fn try_from(key: &[u8]) -> Result { - let key = PrivateKeyDer::try_from(key).map_err(|_| Error::CouldNotParseKeyPair)?; - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_der_with_provider(&key, provider) - } -} - -#[cfg(feature = "crypto")] -impl TryFrom> for KeyPair { - type Error = Error; - - fn try_from(key: Vec) -> Result { - let key = PrivateKeyDer::try_from(key).map_err(|_| Error::CouldNotParseKeyPair)?; - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_der_with_provider(&key, provider) - } -} - -#[cfg(feature = "crypto")] -impl TryFrom<&PrivatePkcs8KeyDer<'_>> for KeyPair { - type Error = Error; - - fn try_from(key: &PrivatePkcs8KeyDer<'_>) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - provider - .key_pair_provider - .load_private_key(PrivateKeyDer::Pkcs8(key.clone_key()), None) - } -} - -#[cfg(feature = "crypto")] -impl TryFrom<&PrivateKeyDer<'_>> for KeyPair { - type Error = Error; - - fn try_from(key: &PrivateKeyDer<'_>) -> Result { - let provider = CryptoProvider::get_default_or_install_from_crate_features()?; - Self::from_der_with_provider(key, provider) - } -} - #[cfg(feature = "crypto")] impl From for PrivatePkcs8KeyDer<'static> { fn from(val: KeyPair) -> Self { @@ -597,7 +462,7 @@ mod test { #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))] &PKCS_RSA_SHA256, ] { - let kp = KeyPair::generate_for(alg).expect("keygen"); + let kp = KeyPair::generate_for(alg, crate::test_provider()).expect("keygen"); let pem = kp.public_key_pem(); let der = kp.subject_public_key_info(); @@ -611,8 +476,10 @@ mod test { #[test] fn test_algorithm() { - let original = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap(); - let key_pair = KeyPair::try_from(original.serialize_der()).unwrap(); + let original = + KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256, crate::test_provider()).unwrap(); + let key = PrivateKeyDer::try_from(original.serialize_der()).unwrap(); + let key_pair = KeyPair::from_der(&key, crate::test_provider()).unwrap(); assert_eq!(key_pair.algorithm(), &PKCS_ECDSA_P256_SHA256); } } diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 4add20ed..2cc45a01 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -15,23 +15,19 @@ a key pair to call [`CertificateParams::signed_by()`] or [`CertificateParams::se ``` use rcgen::{generate_simple_self_signed, CertifiedKey}; -# #[cfg(all( -# not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs") -# ))] +# #[cfg(feature = "ring")] # fn main () { +let provider = rcgen::crypto::ring::default_provider(); // Generate a certificate that's valid for "localhost" and "hello.world.example" let subject_alt_names = vec!["hello.world.example".to_string(), "localhost".to_string()]; -let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt_names).unwrap(); +let CertifiedKey { cert, signing_key } = + generate_simple_self_signed(subject_alt_names, provider).unwrap(); println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); # } -# #[cfg(not(all( -# not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs") -# )))] +# #[cfg(not(feature = "ring"))] # fn main() {} ```"## )] @@ -62,9 +58,7 @@ pub use crl::{ CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams, }; #[cfg(feature = "crypto")] -pub use crypto::{ - CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, -}; +pub use crypto::{CryptoProvider, HashAlgorithm, HashOutput}; pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, PublicKey}; pub use error::{Error, InvalidAsn1String}; #[cfg(feature = "crypto")] @@ -95,6 +89,14 @@ mod oid; mod sign_algo; pub mod string; +#[cfg(all(test, feature = "crypto", any(feature = "ring", feature = "aws_lc_rs")))] +pub(crate) fn test_provider() -> &'static dyn CryptoProvider { + #[cfg(feature = "aws_lc_rs")] + return crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] + return crypto::ring::default_provider(); +} + /// Type-alias for the old name of [`Error`]. #[deprecated( note = "Renamed to `Error`. We recommend to refer to it by fully-qualifying the crate: `rcgen::Error`." @@ -126,46 +128,31 @@ and key pair as output. ``` use rcgen::{generate_simple_self_signed, CertifiedKey}; -# #[cfg(all( -# not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs") -# ))] +# #[cfg(feature = "ring")] # fn main () { +let provider = rcgen::crypto::ring::default_provider(); // Generate a certificate that's valid for "localhost" and "hello.world.example" let subject_alt_names = vec!["hello.world.example".to_string(), "localhost".to_string()]; -let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt_names).unwrap(); +let CertifiedKey { cert, signing_key } = + generate_simple_self_signed(subject_alt_names, provider).unwrap(); // The certificate is now valid for localhost and the domain "hello.world.example" println!("{}", cert.pem()); println!("{}", signing_key.serialize_pem()); # } -# #[cfg(not(all( -# not(feature = "custom-provider"), -# any(feature = "ring", feature = "aws_lc_rs") -# )))] +# #[cfg(not(feature = "ring"))] # fn main() {} ``` "## )] pub fn generate_simple_self_signed( subject_alt_names: impl Into>, + provider: &dyn CryptoProvider, ) -> Result, Error> { - let signing_key = KeyPair::generate()?; - let cert = CertificateParams::new(subject_alt_names)?.self_signed(&signing_key)?; - Ok(CertifiedKey { cert, signing_key }) -} - -/// Generate a simple self-signed certificate using an explicit cryptography provider. -#[cfg(feature = "crypto")] -pub fn generate_simple_self_signed_with_provider( - subject_alt_names: impl Into>, - provider: &CryptoProvider, -) -> Result, Error> { - let signing_key = KeyPair::generate_with_provider(provider)?; - let cert = CertificateParams::new(subject_alt_names)? - .self_signed_with_provider(&signing_key, provider)?; + let signing_key = KeyPair::generate(provider)?; + let cert = CertificateParams::new(subject_alt_names)?.self_signed(&signing_key, provider)?; Ok(CertifiedKey { cert, signing_key }) } @@ -178,22 +165,17 @@ pub struct CertifiedIssuer<'a, S> { impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { /// Create a new issuer from the given parameters and key, with a self-signed certificate. - pub fn self_signed(params: CertificateParams, signing_key: S) -> Result { - Ok(Self { - certificate: params.self_signed(&signing_key)?, - issuer: Issuer::new(params, signing_key), - }) - } - - /// Create a new issuer with a self-signed certificate using `provider`. - #[cfg(feature = "crypto")] - pub fn self_signed_with_provider( + pub fn self_signed( params: CertificateParams, signing_key: S, - provider: &CryptoProvider, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result { + #[cfg(feature = "crypto")] + let certificate = params.self_signed(&signing_key, provider)?; + #[cfg(not(feature = "crypto"))] + let certificate = params.self_signed(&signing_key)?; Ok(Self { - certificate: params.self_signed_with_provider(&signing_key, provider)?, + certificate, issuer: Issuer::new(params, signing_key), }) } @@ -203,23 +185,14 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { params: CertificateParams, signing_key: S, issuer: &Issuer<'_, impl SigningKey>, + #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, ) -> Result { + #[cfg(feature = "crypto")] + let certificate = params.signed_by(&signing_key, issuer, provider)?; + #[cfg(not(feature = "crypto"))] + let certificate = params.signed_by(&signing_key, issuer)?; Ok(Self { - certificate: params.signed_by(&signing_key, issuer)?, - issuer: Issuer::new(params, signing_key), - }) - } - - /// Create a new issuer signed by `issuer` using `provider`. - #[cfg(feature = "crypto")] - pub fn signed_by_with_provider( - params: CertificateParams, - signing_key: S, - issuer: &Issuer<'_, impl SigningKey>, - provider: &CryptoProvider, - ) -> Result { - Ok(Self { - certificate: params.signed_by_with_provider(&signing_key, issuer, provider)?, + certificate, issuer: Issuer::new(params, signing_key), }) } @@ -787,17 +760,17 @@ impl KeyIdMethod { #[cfg(feature = "crypto")] pub(crate) fn derive( &self, - provider: &CryptoProvider, + provider: &dyn CryptoProvider, subject_public_key_info: impl AsRef<[u8]>, - ) -> Result, Error> { + ) -> Vec { let algorithm = match self { Self::Sha256 => HashAlgorithm::Sha256, Self::Sha384 => HashAlgorithm::Sha384, Self::Sha512 => HashAlgorithm::Sha512, - Self::PreSpecified(value) => return Ok(value.clone()), + Self::PreSpecified(value) => return value.clone(), }; - let digest = provider.digest(algorithm, subject_public_key_info.as_ref())?; - Ok(digest[..20].to_vec()) + let digest = provider.hash(algorithm, subject_public_key_info.as_ref()); + digest.as_ref()[..20].to_vec() } #[cfg(not(feature = "crypto"))] diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs index 5e75a0ec..319c402d 100644 --- a/rcgen/tests/custom_provider.rs +++ b/rcgen/tests/custom_provider.rs @@ -3,9 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; -use rcgen::crypto::{ - CryptoProvider, DigestProvider, HashAlgorithm, KeyPairProvider, SignatureVerificationProvider, -}; +use rcgen::crypto::{CryptoProvider, HashAlgorithm, HashOutput}; use rcgen::{ BasicConstraints, CertificateParams, CertificateRevocationListParams, Error, IsCa, Issuer, KeyIdMethod, KeyPair, PublicKeyData, RsaKeySize, SerialNumber, SignatureAlgorithm, SigningKey, @@ -23,30 +21,17 @@ struct TestBackend; static TEST_BACKEND: TestBackend = TestBackend; -fn provider() -> CryptoProvider { - CryptoProvider { - key_pair_provider: &TEST_BACKEND, - digest_provider: &TEST_BACKEND, - signature_verification_provider: &TEST_BACKEND, - } +fn provider() -> &'static dyn CryptoProvider { + &TEST_BACKEND } -impl DigestProvider for TestBackend { - fn digest( - &self, - algorithm: HashAlgorithm, - input: &[u8], - output: &mut [u8], - ) -> Result<(), Error> { - assert_eq!(output.len(), algorithm.output_len()); +impl CryptoProvider for TestBackend { + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { assert!(!input.is_empty()); DIGESTS.fetch_add(1, Ordering::Relaxed); - output.fill(0x42); - Ok(()) + HashOutput::new(&vec![0x42; algorithm.output_len()]) } -} -impl KeyPairProvider for TestBackend { fn generate( &self, algorithm: &'static SignatureAlgorithm, @@ -72,9 +57,7 @@ impl KeyPairProvider for TestBackend { key_der.secret_der().to_vec(), )) } -} -impl SignatureVerificationProvider for TestBackend { fn verify( &self, algorithm: &'static SignatureAlgorithm, @@ -124,31 +107,19 @@ fn test_key_pair(algorithm: &'static SignatureAlgorithm, serialized_der: Vec #[test] fn explicit_provider_covers_all_rcgen_crypto() { - assert!(CryptoProvider::get_default().is_none()); - #[cfg(not(any(feature = "ring", feature = "aws_lc_rs")))] - assert_eq!( - KeyPair::generate().unwrap_err(), - Error::CryptoProviderNotInstalled - ); let custom_provider = provider(); - let key = KeyPair::generate_for_with_provider(&PKCS_ED25519, &custom_provider).unwrap(); + let key = KeyPair::generate_for(&PKCS_ED25519, custom_provider).unwrap(); assert_eq!(GENERATIONS.load(Ordering::Relaxed), 1); assert_eq!( - KeyPair::generate_rsa_for_with_provider( - &PKCS_RSA_SHA256, - RsaKeySize::_3072, - &custom_provider, - ) - .unwrap_err(), + KeyPair::generate_rsa_for(&PKCS_RSA_SHA256, RsaKeySize::_3072, custom_provider,) + .unwrap_err(), Error::KeyGenerationUnavailable ); assert_eq!(RSA_GENERATIONS.load(Ordering::Relaxed), 1); let mut params = CertificateParams::default(); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let certificate = params - .self_signed_with_provider(&key, &custom_provider) - .unwrap(); + let certificate = params.self_signed(&key, custom_provider).unwrap(); assert!(!certificate.der().is_empty()); assert!(DIGESTS.load(Ordering::Relaxed) >= 2); // default serial and subject key ID @@ -161,17 +132,13 @@ fn explicit_provider_covers_all_rcgen_crypto() { revoked_certs: Vec::new(), key_identifier_method: KeyIdMethod::Sha384, } - .signed_by_with_provider(&issuer, &custom_provider) + .signed_by(&issuer, custom_provider) .unwrap(); assert!(!crl.der().is_empty()); let fake_der = PrivatePkcs8KeyDer::from(vec![0x30, 0x00]); - let loaded = KeyPair::from_pkcs8_der_and_sign_algo_with_provider( - &fake_der, - &PKCS_ED25519, - &custom_provider, - ) - .unwrap(); + let loaded = + KeyPair::from_pkcs8_der_and_sign_algo(&fake_der, &PKCS_ED25519, custom_provider).unwrap(); assert_eq!(loaded.algorithm(), &PKCS_ED25519); assert_eq!(LOADS.load(Ordering::Relaxed), 1); @@ -180,19 +147,14 @@ fn explicit_provider_covers_all_rcgen_crypto() { let request = CertificateParams::default() .serialize_request(&loaded) .unwrap(); - let parsed = rcgen::CertificateSigningRequestParams::from_der_with_provider( - request.der(), - &custom_provider, - ) - .unwrap(); + let parsed = + rcgen::CertificateSigningRequestParams::from_der(request.der(), custom_provider) + .unwrap(); assert_eq!(parsed.public_key.algorithm(), &PKCS_ED25519); assert_eq!(VERIFICATIONS.load(Ordering::Relaxed), 1); } - assert!(CryptoProvider::get_default().is_none()); - provider().install_default().unwrap(); - let generated = KeyPair::generate_for(&PKCS_ED25519).unwrap(); + let generated = KeyPair::generate_for(&PKCS_ED25519, custom_provider).unwrap(); assert_eq!(generated.algorithm(), &PKCS_ED25519); assert_eq!(GENERATIONS.load(Ordering::Relaxed), 2); - assert!(provider().install_default().is_err()); } diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 3b3625d2..4b8bb9ce 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -6,10 +6,18 @@ use std::{fmt, io}; use bpaf::Bpaf; use rcgen::DnValue::PrintableString; use rcgen::{ - BasicConstraints, Certificate, CertificateParams, CertifiedIssuer, DistinguishedName, DnType, - ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, SignatureAlgorithm, + BasicConstraints, Certificate, CertificateParams, CertifiedIssuer, CryptoProvider, + DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, + SignatureAlgorithm, }; +fn provider() -> &'static dyn CryptoProvider { + #[cfg(feature = "aws_lc_rs")] + return rcgen::crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] + return rcgen::crypto::ring::default_provider(); +} + /// Builder to configure TLS [CertificateParams] to be finalized /// into either a [Ca] or an [EndEntity]. #[derive(Clone, Debug, Default)] @@ -88,9 +96,9 @@ impl CaBuilder { } /// build `Ca` Certificate. pub fn build(self) -> Result { - let key_pair = KeyPair::generate_for(self.alg.into())?; + let key_pair = KeyPair::generate_for(self.alg.into(), provider())?; Ok(Ca { - issuer: CertifiedIssuer::self_signed(self.params, key_pair)?, + issuer: CertifiedIssuer::self_signed(self.params, key_pair, provider())?, }) } } @@ -181,8 +189,10 @@ impl EndEntityBuilder { } /// build `EndEntity` Certificate. pub fn build(self, issuer: &Ca) -> Result { - let key_pair = KeyPair::generate_for(self.alg.into())?; - let cert = self.params.signed_by(&key_pair, &issuer.issuer)?; + let key_pair = KeyPair::generate_for(self.alg.into(), provider())?; + let cert = self + .params + .signed_by(&key_pair, &issuer.issuer, provider())?; Ok(EndEntity { cert, key_pair }) } } @@ -475,14 +485,14 @@ mod tests { #[test] fn key_pair_algorithm_to_keypair() -> anyhow::Result<()> { - let keypair = KeyPair::generate_for(KeyPairAlgorithm::Ed25519.into())?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::Ed25519.into(), provider())?; assert_eq!(format!("{:?}", keypair.algorithm()), "PKCS_ED25519"); - let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP256.into())?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP256.into(), provider())?; assert_eq!( format!("{:?}", keypair.algorithm()), "PKCS_ECDSA_P256_SHA256" ); - let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP384.into())?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP384.into(), provider())?; assert_eq!( format!("{:?}", keypair.algorithm()), "PKCS_ECDSA_P384_SHA384" @@ -490,7 +500,7 @@ mod tests { #[cfg(feature = "aws_lc_rs")] { - let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP521.into())?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP521.into(), provider())?; assert_eq!( format!("{:?}", keypair.algorithm()), "PKCS_ECDSA_P521_SHA512" diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml index b855cbd7..b4a895cd 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -5,7 +5,7 @@ edition = { workspace = true } publish = false [features] -default = [] +default = ["ring"] aws_lc_rs = ["rcgen/aws_lc_rs", "rustls-webpki/aws-lc-rs", "dep:aws-lc-rs"] fips = ["aws_lc_rs", "rcgen/fips"] pem = ["dep:pem", "rcgen/pem"] @@ -15,7 +15,7 @@ x509-parser = ["dep:x509-parser", "rcgen/x509-parser"] [dependencies] aws-lc-rs = { workspace = true, optional = true } pem = { workspace = true, optional = true } -rcgen = { path = "../rcgen", features = ["pem", "x509-parser"] } +rcgen = { path = "../rcgen", default-features = false, features = ["crypto", "pem", "x509-parser"] } ring = { workspace = true } rustls-webpki = { workspace = true } time = { workspace = true } diff --git a/verify-tests/src/lib.rs b/verify-tests/src/lib.rs index 466f3105..e78d1494 100644 --- a/verify-tests/src/lib.rs +++ b/verify-tests/src/lib.rs @@ -1,7 +1,7 @@ use rcgen::{ BasicConstraints, Certificate, CertificateParams, CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, - DnType, IsCa, Issuer, KeyIdMethod, KeyPair, KeyUsagePurpose, RevocationReason, + CryptoProvider, DnType, IsCa, Issuer, KeyIdMethod, KeyPair, KeyUsagePurpose, RevocationReason, RevokedCertParams, SerialNumber, }; use time::{Duration, OffsetDateTime}; @@ -61,6 +61,13 @@ YPTHy8SWRA2sMII3ArhHJ8A= -----END PRIVATE KEY----- "#; +pub fn provider() -> &'static dyn CryptoProvider { + #[cfg(feature = "aws_lc_rs")] + return rcgen::crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] + return rcgen::crypto::ring::default_provider(); +} + pub fn default_params() -> (CertificateParams, KeyPair) { let mut params = CertificateParams::new(vec!["crabs.crabs".to_string(), "localhost".to_string()]).unwrap(); @@ -71,7 +78,7 @@ pub fn default_params() -> (CertificateParams, KeyPair) { .distinguished_name .push(DnType::CommonName, "Master CA"); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(provider()).unwrap(); (params, key_pair) } @@ -88,7 +95,7 @@ pub fn test_crl() -> ( KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign, ]; - let issuer_cert = issuer.self_signed(&key_pair).unwrap(); + let issuer_cert = issuer.self_signed(&key_pair, provider()).unwrap(); let ca = Issuer::new(issuer, key_pair); let now = OffsetDateTime::now_utc(); @@ -114,7 +121,7 @@ pub fn test_crl() -> ( key_identifier_method: KeyIdMethod::Sha256, }; - let crl = params.signed_by(&ca).unwrap(); + let crl = params.signed_by(&ca, provider()).unwrap(); (params, crl, issuer_cert) } @@ -133,5 +140,9 @@ pub fn cert_with_crl_dps() -> Vec { }, ]; - params.self_signed(&key_pair).unwrap().der().to_vec() + params + .self_signed(&key_pair, provider()) + .unwrap() + .der() + .to_vec() } diff --git a/verify-tests/tests/botan.rs b/verify-tests/tests/botan.rs index 76c48a60..4a04d0dc 100644 --- a/verify-tests/tests/botan.rs +++ b/verify-tests/tests/botan.rs @@ -48,7 +48,7 @@ fn check_cert_ca(cert_der: &[u8], _cert: &Certificate, ca_der: &[u8]) { #[test] fn test_botan() { let (params, key_pair) = default_params(); - let cert = params.self_signed(&key_pair).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -57,8 +57,8 @@ fn test_botan() { #[test] fn test_botan_256() { let (params, _) = default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -67,8 +67,8 @@ fn test_botan_256() { #[test] fn test_botan_384() { let (params, _) = default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -78,8 +78,8 @@ fn test_botan_384() { #[cfg(feature = "aws_lc_rs")] fn test_botan_521() { let (params, _) = default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -88,8 +88,8 @@ fn test_botan_521() { #[test] fn test_botan_25519() { let (params, _) = default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -98,8 +98,8 @@ fn test_botan_25519() { #[test] fn test_botan_25519_v1_given() { let (params, _) = default_params(); - let key_pair = KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -108,8 +108,8 @@ fn test_botan_25519_v1_given() { #[test] fn test_botan_25519_v2_given() { let (params, _) = default_params(); - let key_pair = KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -118,8 +118,8 @@ fn test_botan_25519_v2_given() { #[test] fn test_botan_rsa_given() { let (params, _) = default_params(); - let key_pair = KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert); @@ -129,7 +129,7 @@ fn test_botan_rsa_given() { fn test_botan_separate_ca() { let (mut ca_params, ca_key) = default_params(); ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); params @@ -141,9 +141,9 @@ fn test_botan_separate_ca() { // Botan has a sanity check that enforces a maximum expiration date params.not_after = rcgen::date_time_ymd(3016, 1, 1); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); - let cert = params.signed_by(&key_pair, &ca).unwrap(); + let cert = params.signed_by(&key_pair, &ca, util::provider()).unwrap(); check_cert_ca(cert.der(), &cert, ca_cert.der()); } @@ -152,7 +152,7 @@ fn test_botan_separate_ca() { fn test_botan_imported_ca() { let (mut params, ca_key) = default_params(); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = params.self_signed(&ca_key).unwrap(); + let ca_cert = params.self_signed(&ca_key, util::provider()).unwrap(); let ca_cert_der = ca_cert.der(); let ca = Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap(); @@ -166,8 +166,8 @@ fn test_botan_imported_ca() { // Botan has a sanity check that enforces a maximum expiration date params.not_after = rcgen::date_time_ymd(3016, 1, 1); - let key_pair = KeyPair::generate().unwrap(); - let cert = params.signed_by(&key_pair, &ca).unwrap(); + let key_pair = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&key_pair, &ca, util::provider()).unwrap(); check_cert_ca(cert.der(), &cert, ca_cert_der); } @@ -180,7 +180,9 @@ fn test_botan_imported_ca_with_printable_string() { DnValue::PrintableString("US".try_into().unwrap()), ); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = params.self_signed(&imported_ca_key).unwrap(); + let ca_cert = params + .self_signed(&imported_ca_key, util::provider()) + .unwrap(); let ca = Issuer::from_ca_cert_der(ca_cert.der(), imported_ca_key).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); @@ -192,8 +194,8 @@ fn test_botan_imported_ca_with_printable_string() { .push(DnType::CommonName, "Dev domain"); // Botan has a sanity check that enforces a maximum expiration date params.not_after = rcgen::date_time_ymd(3016, 1, 1); - let key_pair = KeyPair::generate().unwrap(); - let cert = params.signed_by(&key_pair, &ca).unwrap(); + let key_pair = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&key_pair, &ca, util::provider()).unwrap(); check_cert_ca(cert.der(), &cert, ca_cert.der()); } @@ -209,7 +211,7 @@ fn test_botan_crl_parse() { KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign, ]; - let issuer_key = KeyPair::generate_for(alg).unwrap(); + let issuer_key = KeyPair::generate_for(alg, util::provider()).unwrap(); let ca = Issuer::new(issuer, issuer_key); // Create an end entity cert issued by the issuer. @@ -218,8 +220,8 @@ fn test_botan_crl_parse() { ee.serial_number = Some(SerialNumber::from(99999)); // Botan has a sanity check that enforces a maximum expiration date ee.not_after = rcgen::date_time_ymd(3016, 1, 1); - let ee_key = KeyPair::generate_for(alg).unwrap(); - let ee_cert = ee.signed_by(&ee_key, &ca).unwrap(); + let ee_key = KeyPair::generate_for(alg, util::provider()).unwrap(); + let ee_cert = ee.signed_by(&ee_key, &ca, util::provider()).unwrap(); let botan_ee = botan::Certificate::load(ee_cert.der()).unwrap(); // Generate a CRL with the issuer that revokes the EE cert. @@ -238,7 +240,7 @@ fn test_botan_crl_parse() { key_identifier_method: rcgen::KeyIdMethod::Sha256, }; - let crl = crl.signed_by(&ca).unwrap(); + let crl = crl.signed_by(&ca, util::provider()).unwrap(); // We should be able to load the CRL in both serializations. botan::CRL::load(crl.pem().unwrap().as_ref()).unwrap(); diff --git a/verify-tests/tests/generic.rs b/verify-tests/tests/generic.rs index 0837d527..05449252 100644 --- a/verify-tests/tests/generic.rs +++ b/verify-tests/tests/generic.rs @@ -75,7 +75,7 @@ mod test_x509_custom_ext { // Ensure the custom exts. being omitted into a CSR doesn't require SAN ext being present. // See https://github.com/rustls/rcgen/issues/122 params.subject_alt_names = Vec::default(); - let test_cert = params.self_signed(&test_key).unwrap(); + let test_cert = params.self_signed(&test_key, util::provider()).unwrap(); let (_, x509_test_cert) = X509Certificate::from_der(test_cert.der()).unwrap(); // We should be able to find the extension by OID, with expected criticality and value. @@ -152,7 +152,7 @@ mod test_csr_custom_attributes { // Serialize a DER-encoded CSR let params = CertificateParams::default(); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(verify_tests::provider()).unwrap(); let csr = params .serialize_request_with_attributes(&key_pair, vec![challenge_password_attribute]) .unwrap(); @@ -179,9 +179,11 @@ mod test_csr_basic_constraints { /// This should deserialize fine to a ca constrained to 5 #[test] fn test_csr_basic_constraints_true_pathlen() { - let csr_params = - CertificateSigningRequestParams::from_pem(CSR_TEST_BASIC_CONSTRAINTS_CA_TRUE_5_PEM) - .unwrap(); + let csr_params = CertificateSigningRequestParams::from_pem( + CSR_TEST_BASIC_CONSTRAINTS_CA_TRUE_5_PEM, + verify_tests::provider(), + ) + .unwrap(); assert_eq!( csr_params.params.is_ca, @@ -218,8 +220,10 @@ p5evxprnXDk0qMh66vSZ3Q== /// This should be too large for a u8 and fail #[test] fn test_csr_basic_constraints_true_pathlen_too_large() { - let result = - CertificateSigningRequestParams::from_pem(CSR_TEST_BASIC_CONSTRAINTS_CA_TRUE_256_PEM); + let result = CertificateSigningRequestParams::from_pem( + CSR_TEST_BASIC_CONSTRAINTS_CA_TRUE_256_PEM, + verify_tests::provider(), + ); assert_eq!(result.unwrap_err(), Error::CouldNotParseCertificate); } @@ -253,8 +257,11 @@ RioOvAyCH6bFMvSJxZm7FYM= /// This should deserialize fine to a ca unconstrained #[test] fn test_csr_basic_constraints_true() { - let csr_params = - CertificateSigningRequestParams::from_pem(CSR_TEST_BASIC_CONSTRAINTS_CA_TRUE).unwrap(); + let csr_params = CertificateSigningRequestParams::from_pem( + CSR_TEST_BASIC_CONSTRAINTS_CA_TRUE, + verify_tests::provider(), + ) + .unwrap(); assert_eq!( csr_params.params.is_ca, @@ -291,8 +298,11 @@ lZLnFMmv1pkn052qtQ== /// This should deserialize fine to explicitly no ca #[test] fn test_csr_basic_constraints_false() { - let csr_params = - CertificateSigningRequestParams::from_pem(CSR_TEST_BASIC_CONSTRAINTS_CA_FALSE).unwrap(); + let csr_params = CertificateSigningRequestParams::from_pem( + CSR_TEST_BASIC_CONSTRAINTS_CA_FALSE, + verify_tests::provider(), + ) + .unwrap(); assert_eq!(csr_params.params.is_ca, IsCa::ExplicitNoCa); } @@ -486,7 +496,7 @@ mod test_csr_extension_request { fn dont_write_sans_extension_if_no_sans_are_present() { let mut params = CertificateParams::default(); params.key_usages.push(KeyUsagePurpose::DigitalSignature); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(verify_tests::provider()).unwrap(); let csr = params.serialize_request(&key_pair).unwrap(); let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); assert!(!parsed_csr @@ -501,7 +511,7 @@ mod test_csr_extension_request { params .extended_key_usages .push(ExtendedKeyUsagePurpose::ClientAuth); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(verify_tests::provider()).unwrap(); let csr = params.serialize_request(&key_pair).unwrap(); let (_, parsed_csr) = X509CertificationRequest::from_der(csr.der()).unwrap(); let requested_extensions = parsed_csr @@ -568,11 +578,12 @@ mod test_csr { fn generate_and_test_parsed_csr(params: &CertificateParams) { // Generate a key pair for the CSR - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(verify_tests::provider()).unwrap(); // Serialize the CSR into DER from the given parameters let csr = params.serialize_request(&key_pair).unwrap(); // Parse the CSR we just serialized - let csrp = CertificateSigningRequestParams::from_der(csr.der()).unwrap(); + let csrp = + CertificateSigningRequestParams::from_der(csr.der(), verify_tests::provider()).unwrap(); // Ensure algorithms match. assert_eq!(key_pair.algorithm(), csrp.public_key.algorithm()); @@ -600,7 +611,9 @@ mod test_subject_alternative_name_criticality { "non-empty subject required for test" ); - let cert = params.self_signed(&keypair).unwrap(); + let cert = params + .self_signed(&keypair, verify_tests::provider()) + .unwrap(); let cert = cert.der(); let (_, parsed) = parse_x509_certificate(cert).unwrap(); assert!( @@ -614,7 +627,9 @@ mod test_subject_alternative_name_criticality { let (mut params, keypair) = default_params(); params.distinguished_name = Default::default(); - let cert = params.self_signed(&keypair).unwrap(); + let cert = params + .self_signed(&keypair, verify_tests::provider()) + .unwrap(); let cert = cert.der(); let (_, parsed) = parse_x509_certificate(cert).unwrap(); assert!( diff --git a/verify-tests/tests/openssl.rs b/verify-tests/tests/openssl.rs index c19d22f2..c2210850 100644 --- a/verify-tests/tests/openssl.rs +++ b/verify-tests/tests/openssl.rs @@ -177,7 +177,7 @@ fn verify_csr(params: &CertificateParams, key_pair: &KeyPair) { #[test] fn test_openssl() { let (params, key_pair) = util::default_params(); - let cert = params.self_signed(&key_pair).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); verify_cert(&cert, &key_pair); } @@ -190,8 +190,8 @@ fn test_request() { #[test] fn test_openssl_256() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. verify_cert(&cert, &key_pair); @@ -201,8 +201,8 @@ fn test_openssl_256() { #[test] fn test_openssl_384() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. verify_cert(&cert, &key_pair); @@ -213,8 +213,8 @@ fn test_openssl_384() { #[cfg(feature = "aws_lc_rs")] fn test_openssl_521() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. verify_cert(&cert, &key_pair); @@ -224,8 +224,8 @@ fn test_openssl_521() { #[test] fn test_openssl_25519() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. // TODO openssl doesn't support v2 keys (yet) @@ -238,8 +238,9 @@ fn test_openssl_25519() { #[test] fn test_openssl_25519_v1_given() { let (params, _) = util::default_params(); - let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = + rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate as well as CSR, // but only on OpenSSL >= 1.1.1 @@ -256,8 +257,9 @@ fn test_openssl_25519_v1_given() { #[test] fn test_openssl_25519_v2_given() { let (params, _) = util::default_params(); - let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = + rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. // TODO openssl doesn't support v2 keys (yet) @@ -270,8 +272,8 @@ fn test_openssl_25519_v2_given() { #[test] fn test_openssl_rsa_given() { let (params, _) = util::default_params(); - let key_pair = KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. verify_cert(&cert, &key_pair); @@ -287,9 +289,13 @@ fn test_openssl_rsa_combinations_given() { ]; for (i, alg) in alg_list.iter().enumerate() { let (params, _) = util::default_params(); - let key_pair = - KeyPair::from_pkcs8_pem_and_sign_algo(util::RSA_TEST_KEY_PAIR_PEM, alg).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::from_pkcs8_pem_and_sign_algo( + util::RSA_TEST_KEY_PAIR_PEM, + alg, + util::provider(), + ) + .unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. if i >= 4 { @@ -307,7 +313,7 @@ fn test_openssl_rsa_combinations_given() { fn test_openssl_separate_ca() { let (mut ca_params, ca_key) = util::default_params(); ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let ca_cert_pem = ca_cert.pem(); let ca = Issuer::new(ca_params, ca_key); @@ -318,8 +324,8 @@ fn test_openssl_separate_ca() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate().unwrap(); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let key = cert_key.serialize_der(); verify_cert_ca(&cert.pem(), &key, &ca_cert_pem); @@ -333,7 +339,7 @@ fn test_openssl_separate_ca_with_printable_string() { DnValue::PrintableString("US".try_into().unwrap()), ); ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); params @@ -342,9 +348,9 @@ fn test_openssl_separate_ca_with_printable_string() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate().unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let key = cert_key.serialize_der(); verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); @@ -354,8 +360,8 @@ fn test_openssl_separate_ca_with_printable_string() { fn test_openssl_separate_ca_with_other_signing_alg() { let (mut ca_params, _) = util::default_params(); ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256, util::provider()).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); @@ -365,8 +371,8 @@ fn test_openssl_separate_ca_with_other_signing_alg() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384, util::provider()).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let key = cert_key.serialize_der(); verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); @@ -386,7 +392,7 @@ fn test_openssl_separate_ca_name_constraints() { //excluded_subtrees : vec![GeneralSubtree::DnsName(".v".to_string())], excluded_subtrees: Vec::new(), }); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); @@ -396,8 +402,8 @@ fn test_openssl_separate_ca_name_constraints() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate().unwrap(); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let key = cert_key.serialize_der(); verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); @@ -418,7 +424,7 @@ fn test_openssl_separate_ca_name_constraints_directory_name() { ], excluded_subtrees: Vec::new(), }); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); @@ -429,8 +435,8 @@ fn test_openssl_separate_ca_name_constraints_directory_name() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate().unwrap(); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let key = cert_key.serialize_der(); verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); @@ -544,10 +550,10 @@ fn test_openssl_pkcs1_and_sec1_keys() { let rsa = PKey::from_rsa(rsa).unwrap(); let pkcs1_rsa_key_der = PrivateKeyDer::try_from(rsa.private_key_to_der().unwrap()).unwrap(); - KeyPair::try_from(&pkcs1_rsa_key_der).unwrap(); + KeyPair::from_der(&pkcs1_rsa_key_der, util::provider()).unwrap(); let pkcs8_rsa_key_der = PrivateKeyDer::try_from(rsa.private_key_to_pkcs8().unwrap()).unwrap(); - KeyPair::try_from(&pkcs8_rsa_key_der).unwrap(); + KeyPair::from_der(&pkcs8_rsa_key_der, util::provider()).unwrap(); let group = EcGroup::from_curve_name(Nid::SECP521R1).unwrap(); let ec_key = EcKey::generate(&group).unwrap(); @@ -555,8 +561,8 @@ fn test_openssl_pkcs1_and_sec1_keys() { let ec_key = PKey::from_ec_key(ec_key).unwrap(); let sec1_ec_key_der = PrivateKeyDer::try_from(ec_key.private_key_to_der().unwrap()).unwrap(); - KeyPair::try_from(&sec1_ec_key_der).unwrap(); + KeyPair::from_der(&sec1_ec_key_der, util::provider()).unwrap(); let pkcs8_ec_key_der = PrivateKeyDer::try_from(ec_key.private_key_to_pkcs8().unwrap()).unwrap(); - KeyPair::try_from(&pkcs8_ec_key_der).unwrap(); + KeyPair::from_der(&pkcs8_ec_key_der, util::provider()).unwrap(); } diff --git a/verify-tests/tests/webpki.rs b/verify-tests/tests/webpki.rs index e03cb10c..b4371999 100644 --- a/verify-tests/tests/webpki.rs +++ b/verify-tests/tests/webpki.rs @@ -120,7 +120,7 @@ fn check_cert_ca<'a, 'b, S: SigningKey + 'a>( #[test] fn test_webpki() { let (params, key_pair) = util::default_params(); - let cert = params.self_signed(&key_pair).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. let sign_fn = @@ -137,8 +137,8 @@ fn test_webpki() { #[test] fn test_webpki_256() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); @@ -154,8 +154,8 @@ fn test_webpki_256() { #[test] fn test_webpki_384() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P384_SHA384_ASN1_SIGNING); @@ -171,8 +171,8 @@ fn test_webpki_384() { #[test] fn test_webpki_25519() { let (params, _) = util::default_params(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert( @@ -188,8 +188,9 @@ fn test_webpki_25519() { #[test] fn test_webpki_25519_v1_given() { let (params, _) = util::default_params(); - let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = + rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V1, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert( @@ -205,8 +206,9 @@ fn test_webpki_25519_v1_given() { #[test] fn test_webpki_25519_v2_given() { let (params, _) = util::default_params(); - let key_pair = rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = + rcgen::KeyPair::from_pem(util::ED25519_TEST_KEY_PAIR_PEM_V2, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert( @@ -222,8 +224,8 @@ fn test_webpki_25519_v2_given() { #[test] fn test_webpki_rsa_given() { let (params, _) = util::default_params(); - let key_pair = rcgen::KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = rcgen::KeyPair::from_pem(util::RSA_TEST_KEY_PAIR_PEM, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert( @@ -240,8 +242,8 @@ fn test_webpki_rsa_given() { fn test_webpki_ml_dsa() { let (params, _) = util::default_params(); for (rcgen_alg, webpki_alg, signing_alg) in ML_DSA_ALGS { - let key_pair = KeyPair::generate_for(rcgen_alg).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = KeyPair::generate_for(rcgen_alg, util::provider()).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. let sign_fn = |cert, msg| sign_msg_pq(cert, msg, signing_alg); @@ -294,9 +296,13 @@ fn test_webpki_rsa_combinations_given() { ]; for c in configs { let (params, _) = util::default_params(); - let key_pair = - rcgen::KeyPair::from_pkcs8_pem_and_sign_algo(util::RSA_TEST_KEY_PAIR_PEM, c.0).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let key_pair = rcgen::KeyPair::from_pkcs8_pem_and_sign_algo( + util::RSA_TEST_KEY_PAIR_PEM, + c.0, + util::provider(), + ) + .unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. check_cert(cert.der(), &cert, &key_pair, c.1, |msg, cert| { @@ -309,7 +315,7 @@ fn test_webpki_rsa_combinations_given() { fn test_webpki_separate_ca() { let (mut ca_params, ca_key) = util::default_params(); ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); params @@ -319,9 +325,9 @@ fn test_webpki_separate_ca() { .distinguished_name .push(DnType::CommonName, "Dev domain"); - let key_pair = KeyPair::generate().unwrap(); + let key_pair = KeyPair::generate(util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); - let cert = params.signed_by(&key_pair, &ca).unwrap(); + let cert = params.signed_by(&key_pair, &ca, util::provider()).unwrap(); let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); check_cert_ca( cert.der(), @@ -337,8 +343,8 @@ fn test_webpki_separate_ca() { fn test_webpki_separate_ca_with_other_signing_alg() { let (mut ca_params, _) = util::default_params(); ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256, util::provider()).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); params @@ -348,9 +354,9 @@ fn test_webpki_separate_ca_with_other_signing_alg() { .distinguished_name .push(DnType::CommonName, "Dev domain"); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519, util::provider()).unwrap(); let ca = Issuer::new(ca_params, ca_key); - let cert = params.signed_by(&key_pair, &ca).unwrap(); + let cert = params.signed_by(&key_pair, &ca, util::provider()).unwrap(); check_cert_ca( cert.der(), &key_pair, @@ -386,7 +392,7 @@ fn from_remote() { } let rng = ring::rand::SystemRandom::new(); - let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); + let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256, util::provider()).unwrap(); let remote = EcdsaKeyPair::from_pkcs8( &signature::ECDSA_P256_SHA256_ASN1_SIGNING, &key_pair.serialize_der(), @@ -402,7 +408,7 @@ fn from_remote() { let remote = Remote(remote); let (params, _) = util::default_params(); - let cert = params.self_signed(&remote).unwrap(); + let cert = params.self_signed(&remote, util::provider()).unwrap(); // Now verify the certificate. let sign_fn = move |_, msg| { @@ -463,7 +469,7 @@ fn test_webpki_imported_ca() { let (mut params, ca_key) = util::default_params(); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); params.key_usages.push(KeyUsagePurpose::KeyCertSign); - let ca_cert = params.self_signed(&ca_key).unwrap(); + let ca_cert = params.self_signed(&ca_key, util::provider()).unwrap(); let ca = Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap(); assert_eq!(ca.key_usages(), &[KeyUsagePurpose::KeyCertSign]); @@ -475,8 +481,8 @@ fn test_webpki_imported_ca() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate().unwrap(); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); check_cert_ca( @@ -498,7 +504,7 @@ fn test_webpki_imported_ca_with_printable_string() { DnValue::PrintableString("US".try_into().unwrap()), ); params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); - let ca_cert = params.self_signed(&ca_key).unwrap(); + let ca_cert = params.self_signed(&ca_key, util::provider()).unwrap(); let ca = Issuer::from_ca_cert_der(ca_cert.der(), ca_key).unwrap(); let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); @@ -508,8 +514,8 @@ fn test_webpki_imported_ca_with_printable_string() { params .distinguished_name .push(DnType::CommonName, "Dev domain"); - let cert_key = KeyPair::generate().unwrap(); - let cert = params.signed_by(&cert_key, &ca).unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); + let cert = params.signed_by(&cert_key, &ca, util::provider()).unwrap(); let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); check_cert_ca( @@ -546,9 +552,9 @@ fn test_certificate_from_csr() { params.insert_extended_key_usage(eku.clone()); } - let cert_key = KeyPair::generate().unwrap(); + let cert_key = KeyPair::generate(util::provider()).unwrap(); let csr = params.serialize_request(&cert_key).unwrap(); - let csr = CertificateSigningRequestParams::from_der(csr.der()).unwrap(); + let csr = CertificateSigningRequestParams::from_der(csr.der(), util::provider()).unwrap(); let ekus_contained = &csr.params.extended_key_usages; for eku in &eku_test { @@ -565,7 +571,7 @@ fn test_certificate_from_csr() { assert!(ekus_contained.contains(eku)); } - let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca_cert = ca_params.self_signed(&ca_key, util::provider()).unwrap(); let ekus_contained = &ca_params.extended_key_usages; for eku in &eku_test { @@ -574,7 +580,7 @@ fn test_certificate_from_csr() { let ekus = ca_params.extended_key_usages.clone(); let ca = Issuer::new(ca_params, ca_key); - let cert = csr.signed_by(&ca).unwrap(); + let cert = csr.signed_by(&ca, util::provider()).unwrap(); let ekus_contained = &csr.params.extended_key_usages; for eku in &eku_test { @@ -601,7 +607,7 @@ fn test_certificate_from_csr() { fn test_webpki_serial_number() { let (mut params, key_pair) = util::default_params(); params.serial_number = Some(vec![0, 1, 2].into()); - let cert = params.self_signed(&key_pair).unwrap(); + let cert = params.self_signed(&key_pair, util::provider()).unwrap(); // Now verify the certificate. let sign_fn = |cert, msg| sign_msg_ecdsa(cert, msg, &signature::ECDSA_P256_SHA256_ASN1_SIGNING); @@ -657,17 +663,17 @@ fn test_webpki_crl_revoke() { KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign, ]; - let issuer_key = KeyPair::generate_for(alg).unwrap(); - let issuer_cert = issuer.self_signed(&issuer_key).unwrap(); + let issuer_key = KeyPair::generate_for(alg, util::provider()).unwrap(); + let issuer_cert = issuer.self_signed(&issuer_key, util::provider()).unwrap(); // Create an end entity cert issued by the issuer. let (mut ee, _) = util::default_params(); ee.is_ca = IsCa::NoCa; ee.extended_key_usages = vec![ExtendedKeyUsagePurpose::ClientAuth]; ee.serial_number = Some(SerialNumber::from(99999)); - let ee_key = KeyPair::generate_for(alg).unwrap(); + let ee_key = KeyPair::generate_for(alg, util::provider()).unwrap(); let issuer = Issuer::new(issuer, issuer_key); - let ee_cert = ee.signed_by(&ee_key, &issuer).unwrap(); + let ee_cert = ee.signed_by(&ee_key, &issuer, util::provider()).unwrap(); // Set up webpki's verification requirements. let trust_anchor = anchor_from_trusted_cert(issuer_cert.der()).unwrap(); @@ -704,7 +710,7 @@ fn test_webpki_crl_revoke() { }], key_identifier_method: rcgen::KeyIdMethod::Sha256, } - .signed_by(&issuer) + .signed_by(&issuer, util::provider()) .unwrap(); let crl = CertRevocationList::from(BorrowedCertRevocationList::from_der(crl.der()).unwrap()); From 76af07cab0f7b0f52f043f28dca9e69657a34250 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Thu, 3 Sep 2026 14:30:48 +0000 Subject: [PATCH 09/20] bump to 0.15 since this is a breaking change --- Cargo.lock | 2 +- README.md | 2 +- rcgen/Cargo.toml | 2 +- rcgen/src/string.rs | 2 -- rustls-cert-gen/Cargo.toml | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f31d1cb6..dfe547a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -821,7 +821,7 @@ checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" [[package]] name = "rcgen" -version = "0.14.10" +version = "0.15.0" dependencies = [ "aws-lc-rs", "openssl", diff --git a/README.md b/README.md index 86090093..1b43a628 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Enable a built-in provider explicitly: ```toml [dependencies] -rcgen = { version = "0.14", features = ["ring"] } +rcgen = { version = "0.15", features = ["ring"] } ``` Applications pass the selected provider explicitly to APIs that perform cryptographic work. A diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 607cc873..abf86f66 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rcgen" -version = "0.14.10" +version = "0.15.0" documentation = "https://docs.rs/rcgen" description.workspace = true repository.workspace = true diff --git a/rcgen/src/string.rs b/rcgen/src/string.rs index 21afa866..48ea4662 100644 --- a/rcgen/src/string.rs +++ b/rcgen/src/string.rs @@ -425,7 +425,6 @@ impl BmpString { ))); } - // FIXME: Update this when `array_chunks` is stabilized. for maybe_char in char::decode_utf16( vec.as_chunks::<2>() .0 @@ -546,7 +545,6 @@ impl UniversalString { )); } - // FIXME: Update this when `array_chunks` is stabilized. for maybe_char in vec .as_chunks::<4>() .0 diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index e31dcb43..8ab49f8d 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -23,7 +23,7 @@ aws-lc-rs = { workspace = true, optional = true } bpaf = { workspace = true } pem = { workspace = true } pki-types = { workspace = true } -rcgen = { version = "0.14.2", path = "../rcgen", default-features = false, features = ["pem"] } +rcgen = { version = "0.15", path = "../rcgen", default-features = false, features = ["pem"] } ring = { workspace = true, optional = true } [dev-dependencies] From 9366cbeccfd5ffa6a12451b985c69460c9c1c2e7 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Thu, 3 Sep 2026 17:22:48 +0000 Subject: [PATCH 10/20] fix aws-lc-rs-unstable feature --- rustls-cert-gen/Cargo.toml | 2 +- verify-tests/Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index 8ab49f8d..b4f8579a 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -13,7 +13,7 @@ keywords.workspace = true [features] default = ["ring"] aws_lc_rs = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/aws-lc-sys"] -aws_lc_rs_unstable = ["rcgen/aws_lc_rs_unstable"] +aws_lc_rs_unstable = ["aws_lc_rs", "rcgen/aws_lc_rs_unstable"] fips = ["aws_lc_rs", "rcgen/fips"] ring = ["dep:ring", "rcgen/ring"] diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml index b4a895cd..886dd6c6 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -7,6 +7,7 @@ publish = false [features] default = ["ring"] aws_lc_rs = ["rcgen/aws_lc_rs", "rustls-webpki/aws-lc-rs", "dep:aws-lc-rs"] +aws_lc_rs_unstable = ["aws_lc_rs", "rcgen/aws_lc_rs_unstable"] fips = ["aws_lc_rs", "rcgen/fips"] pem = ["dep:pem", "rcgen/pem"] ring = ["rcgen/ring"] From a8830d89bd98cfdcf11b56c94df29b142962f313 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Fri, 4 Sep 2026 17:20:42 +0000 Subject: [PATCH 11/20] remove "crypto" flag --- .github/workflows/ci.yml | 11 ++-- rcgen/Cargo.toml | 11 ++-- rcgen/src/certificate.rs | 104 ++++++++------------------------- rcgen/src/crl.rs | 43 ++------------ rcgen/src/crypto/mod.rs | 22 +++---- rcgen/src/csr.rs | 16 ++--- rcgen/src/error.rs | 5 -- rcgen/src/key_pair.rs | 14 +---- rcgen/src/lib.rs | 41 ++----------- rcgen/tests/custom_provider.rs | 2 - verify-tests/Cargo.toml | 2 +- 11 files changed, 64 insertions(+), 207 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2018cff9..5a877678 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,12 +47,9 @@ jobs: - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets - - run: cargo clippy -p rcgen --no-default-features --features crypto,pem,x509-parser --all-targets + - run: cargo clippy -p rcgen --no-default-features --features pem,x509-parser --all-targets - name: Ensure backend-free builds have no built-in crypto dependencies - run: | - if cargo tree -p rcgen --no-default-features --features crypto --edges normal,build | grep -E 'ring v|aws-lc'; then - exit 1 - fi + run: "! cargo tree -p rcgen --no-default-features --edges normal,build | grep -E 'ring v|aws-lc'" - name: Ensure FIPS requires an explicit provider run: | if output=$(cargo check -p rcgen --no-default-features --features fips 2>&1); then @@ -85,7 +82,7 @@ jobs: env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (provider API without a built-in backend) - run: cargo doc -p rcgen --no-default-features --features crypto,pem,x509-parser + run: cargo doc -p rcgen --no-default-features --features pem,x509-parser env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (aws_lc_rs_unstable) @@ -181,7 +178,7 @@ jobs: - name: Run the tests with aws_lc_rs backend enabled run: cargo test --no-default-features --features aws_lc_rs,pem - name: Run the tests with no built-in backend - run: cargo test -p rcgen --no-default-features --features crypto,pem,x509-parser + run: cargo test -p rcgen --no-default-features --features pem,x509-parser # rustls-cert-gen require either aws_lc_rs or ring feature - name: Run the tests with no features enabled run: cargo test -p rcgen --no-default-features diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index abf86f66..d53626af 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -11,12 +11,11 @@ rust-version.workspace = true keywords.workspace = true [features] -default = ["crypto", "pem"] -aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] +default = ["pem"] +aws_lc_rs = ["dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] aws_lc_rs_unstable = ["aws_lc_rs"] # For backwards compatibility only -fips = ["crypto", "aws-lc-rs?/fips"] -crypto = [] -ring = ["crypto", "dep:ring"] +fips = ["aws-lc-rs?/fips"] +ring = ["dep:ring"] [dependencies] aws-lc-rs = { workspace = true, optional = true } @@ -48,7 +47,7 @@ name = "simple" required-features = ["pem", "ring"] [package.metadata.docs.rs] -features = ["aws_lc_rs", "aws_lc_rs_unstable", "crypto", "ring", "x509-parser"] +features = ["aws_lc_rs", "aws_lc_rs_unstable", "ring", "x509-parser"] rustdoc-args = ["--cfg", "rcgen_docsrs"] [package.metadata.cargo_check_external_types] diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 56f1d366..4b5e04d2 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -9,7 +9,6 @@ use yasna::models::ObjectIdentifier; use yasna::{DERWriter, DERWriterSeq, Tag}; use crate::crl::CrlDistributionPoint; -#[cfg(feature = "crypto")] use crate::crypto::{CryptoProvider, HashAlgorithm}; use crate::csr::CertificateSigningRequest; use crate::key_pair::{serialize_public_key_der, sign_der, PublicKeyData}; @@ -98,10 +97,7 @@ impl Default for CertificateParams { crl_distribution_points: Vec::new(), custom_extensions: Vec::new(), use_authority_key_identifier_extension: false, - #[cfg(feature = "crypto")] key_identifier_method: KeyIdMethod::Sha256, - #[cfg(not(feature = "crypto"))] - key_identifier_method: KeyIdMethod::PreSpecified(Vec::new()), } } } @@ -141,15 +137,10 @@ impl CertificateParams { &self, public_key: &(impl PublicKeyData + ?Sized), issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { Ok(Certificate { - der: self.serialize_der_with_signer( - public_key, - issuer, - #[cfg(feature = "crypto")] - provider, - )?, + der: self.serialize_der_with_signer(public_key, issuer, provider)?, }) } @@ -160,16 +151,11 @@ impl CertificateParams { pub fn self_signed( &self, signing_key: &(impl SigningKey + ?Sized), - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { let issuer = Issuer::from_params(self, signing_key); Ok(Certificate { - der: self.serialize_der_with_signer( - signing_key, - &issuer, - #[cfg(feature = "crypto")] - provider, - )?, + der: self.serialize_der_with_signer(signing_key, &issuer, provider)?, }) } @@ -178,21 +164,16 @@ impl CertificateParams { pub fn key_identifier( &self, key: &(impl PublicKeyData + ?Sized), - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Vec { - #[cfg(feature = "crypto")] - return self - .key_identifier_method - .derive(provider, key.subject_public_key_info()); - #[cfg(not(feature = "crypto"))] self.key_identifier_method - .derive(key.subject_public_key_info()) + .derive(provider, key.subject_public_key_info()) } #[cfg(all( test, feature = "x509-parser", - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs") ))] pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) @@ -226,12 +207,7 @@ impl CertificateParams { self.write_key_usage(writer.next()); self.write_subject_alt_names(writer.next()); self.write_extended_key_usage(writer.next()); - self.write_ca_extensions( - writer, - None, - #[cfg(feature = "crypto")] - None, - ); + self.write_ca_extensions(writer, None, None); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { writer.write_der(ext.content()) @@ -286,7 +262,7 @@ impl CertificateParams { &self, writer: &mut DERWriterSeq, pub_key_spki: Option<&[u8]>, - #[cfg(feature = "crypto")] provider: Option<&dyn CryptoProvider>, + provider: Option<&dyn CryptoProvider>, ) { let is_ca = match &self.is_ca { IsCa::Ca(bc) => Some(bc), @@ -295,13 +271,10 @@ impl CertificateParams { }; if let Some(pub_key_spki) = pub_key_spki { - #[cfg(feature = "crypto")] let subject_key_identifier = self.key_identifier_method.derive( provider.expect("a provider is required with public key data"), pub_key_spki, ); - #[cfg(not(feature = "crypto"))] - let subject_key_identifier = self.key_identifier_method.derive(pub_key_spki); write_x509_extension( writer.next(), oid::SUBJECT_KEY_IDENTIFIER, @@ -479,7 +452,7 @@ impl CertificateParams { &self, pub_key: &K, issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result, Error> { // An empty distribution point would be encoded as an empty fullName, // violating GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName @@ -502,18 +475,11 @@ impl CertificateParams { if let Some(ref serial) = self.serial_number { writer.next().write_bigint_bytes(serial.as_ref(), true); } else { - #[cfg(feature = "crypto")] - { - let hash = provider.hash(HashAlgorithm::Sha256, pub_key.der_bytes()); - // RFC 5280 specifies at most 20 bytes for a serial number - let mut sl = hash.as_ref()[0..20].to_vec(); - sl[0] &= 0x7f; // MSB must be 0 to ensure encoding bignum in 20 bytes - writer.next().write_bigint_bytes(&sl, true); - } - #[cfg(not(feature = "crypto"))] - if self.serial_number.is_none() { - return Err(Error::MissingSerialNumber); - } + let hash = provider.hash(HashAlgorithm::Sha256, pub_key.der_bytes()); + // RFC 5280 specifies at most 20 bytes for a serial number + let mut sl = hash.as_ref()[0..20].to_vec(); + sl[0] &= 0x7f; // MSB must be 0 to ensure encoding bignum in 20 bytes + writer.next().write_bigint_bytes(&sl, true); }; // Write signature algorithm issuer @@ -549,12 +515,9 @@ impl CertificateParams { } writer.next().write_tagged(Tag::context(3), |writer| { - #[cfg(feature = "crypto")] - return writer.write_sequence(|writer| { + writer.write_sequence(|writer| { self.write_extensions(writer, &pub_key_spki, issuer, provider) - }); - #[cfg(not(feature = "crypto"))] - writer.write_sequence(|writer| self.write_extensions(writer, &pub_key_spki, issuer)) + }) })?; Ok(()) @@ -568,14 +531,13 @@ impl CertificateParams { writer: &mut DERWriterSeq, pub_key_spki: &[u8], issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result<(), Error> { if self.use_authority_key_identifier_extension { write_x509_authority_key_identifier( writer.next(), match issuer.key_identifier_method.as_ref() { KeyIdMethod::PreSpecified(aki) => aki.clone(), - #[cfg(feature = "crypto")] _ => issuer .key_identifier_method .derive(provider, issuer.signing_key.subject_public_key_info()), @@ -626,12 +588,7 @@ impl CertificateParams { ); } - self.write_ca_extensions( - writer, - Some(pub_key_spki), - #[cfg(feature = "crypto")] - Some(provider), - ); + self.write_ca_extensions(writer, Some(pub_key_spki), Some(provider)); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { @@ -826,7 +783,7 @@ impl ExtendedKeyUsagePurpose { #[cfg(all( test, feature = "x509-parser", - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 @@ -895,7 +852,7 @@ impl NameConstraints { #[cfg(all( test, feature = "x509-parser", - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs") ))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, @@ -952,7 +909,7 @@ impl GeneralSubtree { #[cfg(all( test, feature = "x509-parser", - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs") ))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], @@ -1128,7 +1085,7 @@ impl IsCa { #[cfg(all( test, feature = "x509-parser", - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 @@ -1178,27 +1135,21 @@ pub enum BasicConstraints { Constrained(u8), } -#[cfg(all( - test, - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") -))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] mod tests { #[cfg(feature = "x509-parser")] use std::net::Ipv4Addr; #[cfg(feature = "x509-parser")] use pki_types::pem::PemObject; - #[cfg(feature = "crypto")] use x509_parser::oid_registry::OID_X509_EXT_BASIC_CONSTRAINTS; #[cfg(feature = "pem")] use super::*; #[cfg(feature = "x509-parser")] use crate::DnValue; - #[cfg(feature = "crypto")] use crate::KeyPair; - #[cfg(feature = "crypto")] #[test] fn test_with_key_usages() { let params = CertificateParams { @@ -1245,7 +1196,6 @@ mod tests { assert!(found); } - #[cfg(feature = "crypto")] #[test] fn test_explicit_no_ca() { let params = CertificateParams { @@ -1281,7 +1231,6 @@ mod tests { assert!(found); } - #[cfg(feature = "crypto")] #[test] fn test_empty_crl_distribution_point_uris_rejected() { let params = CertificateParams { @@ -1301,7 +1250,6 @@ mod tests { ); } - #[cfg(feature = "crypto")] #[test] fn test_with_key_usages_only() { // The KeyUsage extension must be present even when it is the only @@ -1323,7 +1271,6 @@ mod tests { assert!(cert.key_usage().unwrap().is_some()); } - #[cfg(feature = "crypto")] #[test] fn test_with_crl_distribution_points_only() { // The CRL distribution points extension must be present even when it @@ -1347,7 +1294,6 @@ mod tests { ))); } - #[cfg(feature = "crypto")] #[test] fn test_with_key_usages_decipheronly_only() { let params = CertificateParams { @@ -1387,7 +1333,6 @@ mod tests { assert!(found); } - #[cfg(feature = "crypto")] #[test] fn test_with_extended_key_usages_any() { let params = CertificateParams { @@ -1410,7 +1355,6 @@ mod tests { assert!(extension.value.any); } - #[cfg(feature = "crypto")] #[test] fn test_with_extended_key_usages_other() { use x509_parser::der_parser::asn1_rs::Oid; diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index d439b198..80567192 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -4,7 +4,6 @@ use pki_types::CertificateRevocationListDer; use time::OffsetDateTime; use yasna::{DERWriter, Tag}; -#[cfg(feature = "crypto")] use crate::crypto::CryptoProvider; use crate::key_pair::sign_der; #[cfg(feature = "pem")] @@ -23,30 +22,15 @@ use crate::{ /// extern crate rcgen; /// use rcgen::*; /// -/// #[cfg(not(feature = "crypto"))] -/// struct MyKeyPair { public_key: Vec } -/// #[cfg(not(feature = "crypto"))] -/// impl SigningKey for MyKeyPair { -/// fn sign(&self, _: &[u8]) -> Result, rcgen::Error> { Ok(vec![]) } -/// } -/// #[cfg(not(feature = "crypto"))] -/// impl PublicKeyData for MyKeyPair { -/// fn der_bytes(&self) -> &[u8] { &self.public_key } -/// fn algorithm(&self) -> &'static SignatureAlgorithm { &PKCS_ED25519 } -/// } -/// # #[cfg(any(not(feature = "crypto"), feature = "ring"))] +/// # #[cfg(feature = "ring")] /// # fn main () { /// // Generate a CRL issuer. /// let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]).unwrap(); /// issuer_params.serial_number = Some(SerialNumber::from(9999)); /// issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); /// issuer_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign]; -/// #[cfg(feature = "crypto")] /// let provider = rcgen::crypto::ring::default_provider(); -/// #[cfg(feature = "crypto")] /// let key_pair = KeyPair::generate(provider).unwrap(); -/// #[cfg(not(feature = "crypto"))] -/// let key_pair = MyKeyPair { public_key: vec![] }; /// let issuer = Issuer::new(issuer_params, key_pair); /// /// // Describe a revoked certificate. @@ -63,17 +47,13 @@ use crate::{ /// crl_number: SerialNumber::from(1234), /// issuing_distribution_point: None, /// revoked_certs: vec![revoked_cert], -/// #[cfg(feature = "crypto")] /// key_identifier_method: KeyIdMethod::Sha256, -/// #[cfg(not(feature = "crypto"))] -/// key_identifier_method: KeyIdMethod::PreSpecified(vec![]), /// }.signed_by( /// &issuer, -/// #[cfg(feature = "crypto")] /// provider, /// ).unwrap(); ///# } -/// # #[cfg(all(feature = "crypto", not(feature = "ring")))] +/// # #[cfg(not(feature = "ring"))] /// # fn main() {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateRevocationList { @@ -198,18 +178,12 @@ impl CertificateRevocationListParams { pub fn signed_by( &self, issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { self.validate(issuer)?; Ok(CertificateRevocationList { - der: self - .serialize_der( - issuer, - #[cfg(feature = "crypto")] - provider, - )? - .into(), + der: self.serialize_der(issuer, provider)?.into(), }) } @@ -238,16 +212,11 @@ impl CertificateRevocationListParams { fn serialize_der( &self, issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result, Error> { - #[cfg(feature = "crypto")] let key_identifier = self .key_identifier_method .derive(provider, issuer.signing_key.subject_public_key_info()); - #[cfg(not(feature = "crypto"))] - let key_identifier = self - .key_identifier_method - .derive(issuer.signing_key.subject_public_key_info()); sign_der(&issuer.signing_key, |writer| { // Write CRL version. @@ -454,7 +423,7 @@ impl RevokedCertParams { } } -#[cfg(all(test, feature = "crypto", any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] mod tests { use x509_parser::num_bigint::BigUint; use x509_parser::{oid_registry, parse_x509_crl}; diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index b8f052d9..ed1ec603 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -10,6 +10,14 @@ use pki_types::PrivateKeyDer; use crate::{Error, KeyPair, RsaKeySize, SignatureAlgorithm}; +/// `ring`-based cryptography provider. +#[cfg(feature = "ring")] +pub mod ring; + +/// AWS-LC-based cryptography provider. +#[cfg(feature = "aws_lc_rs")] +pub mod aws_lc_rs; + /// Cryptographic operations used by rcgen. pub trait CryptoProvider: std::fmt::Debug + Send + Sync { /// Hash `input` with `algorithm`. @@ -26,8 +34,9 @@ pub trait CryptoProvider: std::fmt::Debug + Send + Sync { /// Decode and validate an exportable private key. /// - /// If `algorithm` is `Some`, the key must be loaded for exactly that signature algorithm. - /// If it is `None`, the provider detects a supported algorithm from the key. + /// The same key material can support multiple signature algorithms. If `algorithm` is `Some`, + /// the key must be loaded for exactly that signature algorithm. If it is `None`, the provider + /// detects a supported algorithm from the key. fn load_private_key( &self, key_der: PrivateKeyDer<'static>, @@ -47,15 +56,6 @@ pub trait CryptoProvider: std::fmt::Debug + Send + Sync { signature: &[u8], ) -> Result<(), Error>; } - -/// `ring`-based cryptography provider. -#[cfg(feature = "ring")] -pub mod ring; - -/// AWS-LC-based cryptography provider. -#[cfg(feature = "aws_lc_rs")] -pub mod aws_lc_rs; - /// A hash algorithm required by rcgen. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index 0c0da745..38a4da1a 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -4,7 +4,6 @@ use std::hash::Hash; use pem::Pem; use pki_types::CertificateSigningRequestDer; -#[cfg(feature = "crypto")] use crate::crypto::CryptoProvider; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; @@ -85,7 +84,7 @@ impl CertificateSigningRequestParams { /// Parse and verify a certificate signing request from the ASCII PEM format /// /// See [`from_der`](Self::from_der) for more details. - #[cfg(all(feature = "pem", feature = "x509-parser", feature = "crypto"))] + #[cfg(all(feature = "pem", feature = "x509-parser"))] pub fn from_pem(pem_str: &str, provider: &dyn CryptoProvider) -> Result { let csr = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificationRequest)?; Self::from_der(&csr.contents().into(), provider) @@ -108,7 +107,7 @@ impl CertificateSigningRequestParams { /// into [`CertificateSigningRequestDer`] using the [`Into`] trait. /// /// [`PemObject`]: pki_types::pem::PemObject - #[cfg(all(feature = "x509-parser", feature = "crypto"))] + #[cfg(feature = "x509-parser")] pub fn from_der( csr: &CertificateSigningRequestDer<'_>, provider: &dyn CryptoProvider, @@ -234,15 +233,12 @@ impl CertificateSigningRequestParams { pub fn signed_by( &self, issuer: &Issuer, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { Ok(Certificate { - der: self.params.serialize_der_with_signer( - &self.public_key, - issuer, - #[cfg(feature = "crypto")] - provider, - )?, + der: self + .params + .serialize_der_with_signer(&self.public_key, issuer, provider)?, }) } } diff --git a/rcgen/src/error.rs b/rcgen/src/error.rs index 10a4db9e..1e4155f9 100644 --- a/rcgen/src/error.rs +++ b/rcgen/src/error.rs @@ -51,9 +51,6 @@ pub enum Error { IssuerNotCrlSigner, /// A CRL distribution point was specified without any URIs. EmptyCrlDistributionPointUris, - #[cfg(not(feature = "crypto"))] - /// Missing serial number - MissingSerialNumber, /// X509 parsing error #[cfg(feature = "x509-parser")] X509(String), @@ -108,8 +105,6 @@ impl fmt::Display for Error { EmptyCrlDistributionPointUris => { write!(f, "CRL distribution points must include at least one URI")? }, - #[cfg(not(feature = "crypto"))] - MissingSerialNumber => write!(f, "A serial number must be specified")?, #[cfg(feature = "x509-parser")] X509(e) => write!(f, "X.509 parsing error: {e}")?, }; diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 88cf5d97..6d01de08 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -1,17 +1,13 @@ -#[cfg(feature = "crypto")] use std::fmt; #[cfg(feature = "pem")] use pem::Pem; -#[cfg(feature = "crypto")] use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; use yasna::{DERWriter, DERWriterSeq}; -#[cfg(feature = "crypto")] use crate::crypto::CryptoProvider; #[cfg(feature = "pem")] use crate::error::ExternalError; -#[cfg(feature = "crypto")] use crate::sign_algo::algo::*; use crate::sign_algo::SignatureAlgorithm; use crate::Error; @@ -21,13 +17,11 @@ use crate::ENCODE_CONFIG; /// A key pair used to sign certificates and CSRs /// /// The cryptographic implementation is supplied by the selected [`CryptoProvider`]. -#[cfg(feature = "crypto")] pub struct KeyPair { pub(crate) signing_key: Box, pub(crate) serialized_der: Vec, } -#[cfg(feature = "crypto")] impl fmt::Debug for KeyPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("KeyPair") @@ -37,7 +31,6 @@ impl fmt::Debug for KeyPair { } } -#[cfg(feature = "crypto")] impl KeyPair { /// Construct a key pair from a provider-specific signing key and its PKCS#8 DER encoding. /// @@ -254,14 +247,12 @@ impl KeyPair { } } -#[cfg(feature = "crypto")] impl SigningKey for KeyPair { fn sign(&self, msg: &[u8]) -> Result, Error> { self.signing_key.sign(msg) } } -#[cfg(feature = "crypto")] impl PublicKeyData for KeyPair { fn der_bytes(&self) -> &[u8] { self.signing_key.der_bytes() @@ -272,14 +263,12 @@ impl PublicKeyData for KeyPair { } } -#[cfg(feature = "crypto")] impl From for PrivatePkcs8KeyDer<'static> { fn from(val: KeyPair) -> Self { val.serialize_der().into() } } -#[cfg(feature = "crypto")] impl From for PrivateKeyDer<'static> { fn from(val: KeyPair) -> Self { Self::from(PrivatePkcs8KeyDer::from(val)) @@ -287,7 +276,6 @@ impl From for PrivateKeyDer<'static> { } /// The key size used for RSA key generation -#[cfg(feature = "crypto")] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RsaKeySize { @@ -446,7 +434,7 @@ pub(crate) fn serialize_public_key_der(key: &(impl PublicKeyData + ?Sized), writ }) } -#[cfg(all(test, feature = "crypto", any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] mod test { use super::*; diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 2cc45a01..74bb1cca 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -57,15 +57,10 @@ pub use crl::{ CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams, }; -#[cfg(feature = "crypto")] pub use crypto::{CryptoProvider, HashAlgorithm, HashOutput}; pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, PublicKey}; pub use error::{Error, InvalidAsn1String}; -#[cfg(feature = "crypto")] -pub use key_pair::KeyPair; -#[cfg(feature = "crypto")] -pub use key_pair::RsaKeySize; -pub use key_pair::{PublicKeyData, SigningKey, SubjectPublicKeyInfo}; +pub use key_pair::{KeyPair, PublicKeyData, RsaKeySize, SigningKey, SubjectPublicKeyInfo}; #[cfg(feature = "pem")] use pem::Pem; use pki_types::CertificateDer; @@ -80,7 +75,6 @@ use crate::string::{BmpString, Ia5String, PrintableString, TeletexString, Univer mod certificate; mod crl; -#[cfg(feature = "crypto")] pub mod crypto; mod csr; mod error; @@ -89,7 +83,7 @@ mod oid; mod sign_algo; pub mod string; -#[cfg(all(test, feature = "crypto", any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] pub(crate) fn test_provider() -> &'static dyn CryptoProvider { #[cfg(feature = "aws_lc_rs")] return crypto::aws_lc_rs::default_provider(); @@ -120,7 +114,6 @@ this function fills in the other generation parameters with reasonable defaults and generates a self signed certificate and key pair as output. */ -#[cfg(feature = "crypto")] #[cfg_attr( feature = "pem", doc = r##" @@ -168,12 +161,9 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { pub fn self_signed( params: CertificateParams, signing_key: S, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { - #[cfg(feature = "crypto")] let certificate = params.self_signed(&signing_key, provider)?; - #[cfg(not(feature = "crypto"))] - let certificate = params.self_signed(&signing_key)?; Ok(Self { certificate, issuer: Issuer::new(params, signing_key), @@ -185,12 +175,9 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { params: CertificateParams, signing_key: S, issuer: &Issuer<'_, impl SigningKey>, - #[cfg(feature = "crypto")] provider: &dyn CryptoProvider, + provider: &dyn CryptoProvider, ) -> Result { - #[cfg(feature = "crypto")] let certificate = params.signed_by(&signing_key, issuer, provider)?; - #[cfg(not(feature = "crypto"))] - let certificate = params.signed_by(&signing_key, issuer)?; Ok(Self { certificate, issuer: Issuer::new(params, signing_key), @@ -354,7 +341,7 @@ impl SanType { #[cfg(all( test, feature = "x509-parser", - any(not(feature = "crypto"), feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 @@ -715,13 +702,10 @@ impl KeyUsagePurpose { #[non_exhaustive] pub enum KeyIdMethod { /// RFC 7093 method 1 - a truncated SHA256 digest. - #[cfg(feature = "crypto")] Sha256, /// RFC 7093 method 2 - a truncated SHA384 digest. - #[cfg(feature = "crypto")] Sha384, /// RFC 7093 method 3 - a truncated SHA512 digest. - #[cfg(feature = "crypto")] Sha512, /// Pre-specified identifier. The exact given value is used as the key identifier. PreSpecified(Vec), @@ -741,12 +725,7 @@ impl KeyIdMethod { Ok(match key_identifier_method { Some(method) => method, - None => { - #[cfg(not(feature = "crypto"))] - return Err(Error::UnsupportedSignatureAlgorithm); - #[cfg(feature = "crypto")] - KeyIdMethod::Sha256 - }, + None => KeyIdMethod::Sha256, }) } @@ -757,7 +736,6 @@ impl KeyIdMethod { /// /// This key identifier is used in the SubjectKeyIdentifier and AuthorityKeyIdentifier /// X.509v3 extensions. - #[cfg(feature = "crypto")] pub(crate) fn derive( &self, provider: &dyn CryptoProvider, @@ -772,13 +750,6 @@ impl KeyIdMethod { let digest = provider.hash(algorithm, subject_public_key_info.as_ref()); digest.as_ref()[..20].to_vec() } - - #[cfg(not(feature = "crypto"))] - pub(crate) fn derive(&self, _subject_public_key_info: impl AsRef<[u8]>) -> Vec { - match self { - Self::PreSpecified(value) => value.clone(), - } - } } fn dt_strip_nanos(dt: OffsetDateTime) -> OffsetDateTime { diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs index 319c402d..b71b4fbd 100644 --- a/rcgen/tests/custom_provider.rs +++ b/rcgen/tests/custom_provider.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "crypto")] - use std::sync::atomic::{AtomicUsize, Ordering}; use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml index 886dd6c6..4efd5563 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -16,7 +16,7 @@ x509-parser = ["dep:x509-parser", "rcgen/x509-parser"] [dependencies] aws-lc-rs = { workspace = true, optional = true } pem = { workspace = true, optional = true } -rcgen = { path = "../rcgen", default-features = false, features = ["crypto", "pem", "x509-parser"] } +rcgen = { path = "../rcgen", default-features = false, features = ["pem", "x509-parser"] } ring = { workspace = true } rustls-webpki = { workspace = true } time = { workspace = true } From 6253efedbf9ec71454c27d2868b93f0b72be4424 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 14:54:27 +0000 Subject: [PATCH 12/20] use ring for digest generation --- rcgen/examples/rsa-irc-openssl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rcgen/examples/rsa-irc-openssl.rs b/rcgen/examples/rsa-irc-openssl.rs index e72c8b67..e7408d63 100644 --- a/rcgen/examples/rsa-irc-openssl.rs +++ b/rcgen/examples/rsa-irc-openssl.rs @@ -19,8 +19,8 @@ fn main() -> Result<(), Box> { let pem_serialized = cert.pem(); let pem = pem::parse(&pem_serialized)?; let der_serialized = pem.contents(); - let hash = openssl::sha::sha512(der_serialized); - let hash_hex = hash.iter().fold(String::new(), |mut output, b| { + let hash = ring::digest::digest(&ring::digest::SHA512, der_serialized); + let hash_hex = hash.as_ref().iter().fold(String::new(), |mut output, b| { let _ = write!(output, "{b:02x}"); output }); From 3d34d224232e30c6e21ed75065e516ea4c492890 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 14:58:38 +0000 Subject: [PATCH 13/20] nit: add empty line --- rcgen/src/crypto/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index ed1ec603..eaecc211 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -56,6 +56,7 @@ pub trait CryptoProvider: std::fmt::Debug + Send + Sync { signature: &[u8], ) -> Result<(), Error>; } + /// A hash algorithm required by rcgen. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] From db5eb073e13013d032372a708648dbbe95e5656c Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 15:16:03 +0000 Subject: [PATCH 14/20] switch argument order for the verify method on the provider --- rcgen/src/crypto/aws_lc_rs.rs | 6 +++--- rcgen/src/crypto/mod.rs | 4 ++-- rcgen/src/crypto/ring.rs | 4 ++-- rcgen/src/csr.rs | 4 ++-- rcgen/tests/custom_provider.rs | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index 1e45ba47..e726c532 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -298,10 +298,10 @@ impl CryptoProvider for AwsLcProvider { fn verify( &self, - algorithm: &'static SignatureAlgorithm, - public_key: &[u8], message: &[u8], signature_bytes: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, ) -> Result<(), Error> { #[cfg(feature = "aws_lc_rs")] { @@ -443,7 +443,7 @@ mod tests { let message = b"stable ML-DSA provider"; let signature = loaded.sign(message).unwrap(); provider - .verify(algorithm, loaded.der_bytes(), message, &signature) + .verify(message, &signature, loaded.der_bytes(), algorithm) .unwrap(); #[cfg(feature = "x509-parser")] diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index eaecc211..ae7d8f6f 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -50,10 +50,10 @@ pub trait CryptoProvider: std::fmt::Debug + Send + Sync { /// STRING contents, matching [`PublicKeyData::der_bytes`](crate::PublicKeyData::der_bytes). fn verify( &self, - algorithm: &'static SignatureAlgorithm, - public_key: &[u8], message: &[u8], signature: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, ) -> Result<(), Error>; } diff --git a/rcgen/src/crypto/ring.rs b/rcgen/src/crypto/ring.rs index 3643009b..d8e71033 100644 --- a/rcgen/src/crypto/ring.rs +++ b/rcgen/src/crypto/ring.rs @@ -167,10 +167,10 @@ impl CryptoProvider for RingProvider { fn verify( &self, - algorithm: &'static SignatureAlgorithm, - public_key: &[u8], message: &[u8], signature_bytes: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, ) -> Result<(), Error> { let verification_algorithm: &'static dyn VerificationAlgorithm = if algorithm == &PKCS_ECDSA_P256_SHA256 { diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index 38a4da1a..482c372c 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -141,10 +141,10 @@ impl CertificateSigningRequestParams { provider .verify( - alg, - info.subject_pki.subject_public_key.data.as_ref(), info.raw, csr.signature_value.data.as_ref(), + info.subject_pki.subject_public_key.data.as_ref(), + alg, ) .map_err(|error| match error { Error::UnsupportedSignatureAlgorithm => error, diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs index b71b4fbd..deb19a6d 100644 --- a/rcgen/tests/custom_provider.rs +++ b/rcgen/tests/custom_provider.rs @@ -58,10 +58,10 @@ impl CryptoProvider for TestBackend { fn verify( &self, - algorithm: &'static SignatureAlgorithm, - public_key: &[u8], message: &[u8], signature: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, ) -> Result<(), Error> { assert_eq!(algorithm, &PKCS_ED25519); assert_eq!(public_key, [7; 32]); From 4e251d199b2bb46db9858c1bcf3d0f0781a964b1 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 15:47:17 +0000 Subject: [PATCH 15/20] remove sized bounds --- rcgen/src/certificate.rs | 8 ++++---- rcgen/src/key_pair.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 4b5e04d2..05047009 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -135,7 +135,7 @@ impl CertificateParams { /// [`Certificate::pem`]. pub fn signed_by( &self, - public_key: &(impl PublicKeyData + ?Sized), + public_key: &impl PublicKeyData, issuer: &Issuer<'_, impl SigningKey>, provider: &dyn CryptoProvider, ) -> Result { @@ -150,7 +150,7 @@ impl CertificateParams { /// [`Certificate::pem`]. pub fn self_signed( &self, - signing_key: &(impl SigningKey + ?Sized), + signing_key: &impl SigningKey, provider: &dyn CryptoProvider, ) -> Result { let issuer = Issuer::from_params(self, signing_key); @@ -163,7 +163,7 @@ impl CertificateParams { /// This key identifier is used in the SubjectKeyIdentifier X.509v3 extension. pub fn key_identifier( &self, - key: &(impl PublicKeyData + ?Sized), + key: &impl PublicKeyData, provider: &dyn CryptoProvider, ) -> Vec { self.key_identifier_method @@ -448,7 +448,7 @@ impl CertificateParams { }) } - pub(crate) fn serialize_der_with_signer( + pub(crate) fn serialize_der_with_signer( &self, pub_key: &K, issuer: &Issuer<'_, impl SigningKey>, diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 6d01de08..31463f36 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -288,7 +288,7 @@ pub enum RsaKeySize { } pub(crate) fn sign_der( - key: &(impl SigningKey + ?Sized), + key: &impl SigningKey, f: impl FnOnce(&mut DERWriterSeq<'_>) -> Result<(), Error>, ) -> Result, Error> { yasna::try_construct_der(|writer| { From 848e7f90b5b7f3486a32ec4fa477f685255be92f Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 16:20:31 +0000 Subject: [PATCH 16/20] remove validate function --- rcgen/src/crl.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index 80567192..cb95d81f 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -180,14 +180,6 @@ impl CertificateRevocationListParams { issuer: &Issuer<'_, impl SigningKey>, provider: &dyn CryptoProvider, ) -> Result { - self.validate(issuer)?; - - Ok(CertificateRevocationList { - der: self.serialize_der(issuer, provider)?.into(), - }) - } - - fn validate(&self, issuer: &Issuer<'_, impl SigningKey>) -> Result<(), Error> { if self.next_update.le(&self.this_update) { return Err(Error::InvalidCrlNextUpdate); } @@ -206,7 +198,10 @@ impl CertificateRevocationListParams { { return Err(Error::EmptyCrlDistributionPointUris); } - Ok(()) + + Ok(CertificateRevocationList { + der: self.serialize_der(issuer, provider)?.into(), + }) } fn serialize_der( From 38a40ef391a24438eece11e6ff5ad8283d81f640 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 16:49:46 +0000 Subject: [PATCH 17/20] remove provider feature flag cfg from some x509 parser fuctions --- rcgen/src/certificate.rs | 30 +++++------------------------- rcgen/src/lib.rs | 6 +----- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 05047009..4df97d50 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -170,11 +170,7 @@ impl CertificateParams { .derive(provider, key.subject_public_key_info()) } - #[cfg(all( - test, - feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") - ))] + #[cfg(all(test, feature = "x509-parser"))] pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) .map_err(|_| Error::CouldNotParseCertificate)?; @@ -780,11 +776,7 @@ pub enum ExtendedKeyUsagePurpose { } impl ExtendedKeyUsagePurpose { - #[cfg(all( - test, - feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") - ))] + #[cfg(all(test, feature = "x509-parser"))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 .extended_key_usage() @@ -849,11 +841,7 @@ pub struct NameConstraints { } impl NameConstraints { - #[cfg(all( - test, - feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") - ))] + #[cfg(all(test, feature = "x509-parser"))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, ) -> Result, Error> { @@ -906,11 +894,7 @@ pub enum GeneralSubtree { } impl GeneralSubtree { - #[cfg(all( - test, - feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") - ))] + #[cfg(all(test, feature = "x509-parser"))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], ) -> Result, Error> { @@ -1082,11 +1066,7 @@ pub enum IsCa { } impl IsCa { - #[cfg(all( - test, - feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") - ))] + #[cfg(all(test, feature = "x509-parser"))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 .basic_constraints() diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 74bb1cca..ea3adf33 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -338,11 +338,7 @@ pub enum SanType { } impl SanType { - #[cfg(all( - test, - feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") - ))] + #[cfg(all(test, feature = "x509-parser"))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 .subject_alternative_name() From 3d6d00b3b3fd304f49ca643dfc6178906f144569 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Tue, 8 Sep 2026 17:22:53 +0000 Subject: [PATCH 18/20] add back provider feature flags to x509 parser functions --- rcgen/src/certificate.rs | 44 +++++++++++++++++++++++++++------------- rcgen/src/lib.rs | 6 +++++- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 4df97d50..591764a5 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -170,7 +170,11 @@ impl CertificateParams { .derive(provider, key.subject_public_key_info()) } - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs") + ))] pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) .map_err(|_| Error::CouldNotParseCertificate)?; @@ -203,7 +207,7 @@ impl CertificateParams { self.write_key_usage(writer.next()); self.write_subject_alt_names(writer.next()); self.write_extended_key_usage(writer.next()); - self.write_ca_extensions(writer, None, None); + self.write_ca_extensions(writer, None); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { writer.write_der(ext.content()) @@ -257,8 +261,7 @@ impl CertificateParams { fn write_ca_extensions( &self, writer: &mut DERWriterSeq, - pub_key_spki: Option<&[u8]>, - provider: Option<&dyn CryptoProvider>, + pub_key_spki_and_provider: Option<(&[u8], &dyn CryptoProvider)>, ) { let is_ca = match &self.is_ca { IsCa::Ca(bc) => Some(bc), @@ -266,11 +269,8 @@ impl CertificateParams { IsCa::NoCa => return, }; - if let Some(pub_key_spki) = pub_key_spki { - let subject_key_identifier = self.key_identifier_method.derive( - provider.expect("a provider is required with public key data"), - pub_key_spki, - ); + if let Some((pub_key_spki, provider)) = pub_key_spki_and_provider { + let subject_key_identifier = self.key_identifier_method.derive(provider, pub_key_spki); write_x509_extension( writer.next(), oid::SUBJECT_KEY_IDENTIFIER, @@ -584,7 +584,7 @@ impl CertificateParams { ); } - self.write_ca_extensions(writer, Some(pub_key_spki), Some(provider)); + self.write_ca_extensions(writer, Some((pub_key_spki, provider))); for ext in &self.custom_extensions { write_x509_extension(writer.next(), &ext.oid, ext.critical, |writer| { @@ -776,7 +776,11 @@ pub enum ExtendedKeyUsagePurpose { } impl ExtendedKeyUsagePurpose { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs") + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 .extended_key_usage() @@ -841,7 +845,11 @@ pub struct NameConstraints { } impl NameConstraints { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs") + ))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, ) -> Result, Error> { @@ -894,7 +902,11 @@ pub enum GeneralSubtree { } impl GeneralSubtree { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs") + ))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], ) -> Result, Error> { @@ -1066,7 +1078,11 @@ pub enum IsCa { } impl IsCa { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs") + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 .basic_constraints() diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index ea3adf33..74bb1cca 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -338,7 +338,11 @@ pub enum SanType { } impl SanType { - #[cfg(all(test, feature = "x509-parser"))] + #[cfg(all( + test, + feature = "x509-parser", + any(feature = "ring", feature = "aws_lc_rs") + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 .subject_alternative_name() From c08332c82e0e6c6d7358fbf50dc4d40248e76b39 Mon Sep 17 00:00:00 2001 From: jgreeer Date: Wed, 9 Sep 2026 13:48:34 +0000 Subject: [PATCH 19/20] restore original code --- .github/workflows/ci.yml | 13 ++-- README.md | 7 +- rcgen/Cargo.toml | 2 +- rcgen/src/certificate.rs | 17 ++--- rcgen/src/crl.rs | 12 ++-- rcgen/src/crypto/aws_lc_rs.rs | 126 +++++++++++++++++++-------------- rcgen/src/crypto/mod.rs | 2 +- rcgen/src/crypto/ring.rs | 105 ++++++++++++++++----------- rcgen/src/csr.rs | 2 +- rcgen/src/key_pair.rs | 59 ++++++++------- rcgen/src/lib.rs | 21 +++--- rcgen/src/string.rs | 2 + rcgen/tests/custom_provider.rs | 4 -- rustls-cert-gen/Cargo.toml | 2 +- rustls-cert-gen/src/cert.rs | 4 +- verify-tests/Cargo.toml | 2 +- verify-tests/src/lib.rs | 4 +- 17 files changed, 207 insertions(+), 177 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a877678..45951aca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,23 +44,18 @@ jobs: uses: dtolnay/rust-toolchain@stable with: components: clippy + # `fips` and `aws_lc_rs_unstable` cannot be used together, so avoid `--all-features` - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets - run: cargo clippy -p rcgen --no-default-features --features pem,x509-parser --all-targets - name: Ensure backend-free builds have no built-in crypto dependencies run: "! cargo tree -p rcgen --no-default-features --edges normal,build | grep -E 'ring v|aws-lc'" - - name: Ensure FIPS requires an explicit provider - run: | - if output=$(cargo check -p rcgen --no-default-features --features fips 2>&1); then - exit 1 - fi - grep -Fq "the 'fips' feature currently requires the 'aws_lc_rs' feature" <<<"$output" - run: cargo clippy --no-default-features --features ring --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs_unstable,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs --all-targets - - run: cargo clippy --no-default-features --features aws_lc_rs,fips,pem,x509-parser --all-targets + - run: cargo clippy --no-default-features --features fips,pem,x509-parser --all-targets rustdoc: name: Documentation @@ -90,7 +85,7 @@ jobs: env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} - name: cargo doc (fips) - run: cargo doc --no-default-features --features aws_lc_rs,fips --document-private-items + run: cargo doc --no-default-features --features fips --document-private-items env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=rcgen_docsrs' || '-Dwarnings' }} @@ -139,7 +134,7 @@ jobs: toolchain: 1.88.0 - run: cargo check --locked --lib --features ring,pem,x509-parser - run: cargo check --locked --lib --features aws_lc_rs_unstable - - run: cargo check --locked --lib --features aws_lc_rs,fips + - run: cargo check --locked --lib --features fips build-windows: runs-on: windows-latest diff --git a/README.md b/README.md index 1b43a628..213e0a2a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Simple Rust library to generate X.509 certificates. -```rust +```Rust use rcgen::{generate_simple_self_signed, CertifiedKey}; let provider = rcgen::crypto::ring::default_provider(); // Generate a certificate that's valid for "localhost" and "hello.world.example" @@ -21,9 +21,8 @@ println!("{}", signing_key.serialize_pem()); ## Cryptography providers -Rcgen does not select a cryptography provider. Ring and AWS-LC are available through the `ring` -and `aws_lc_rs` features, respectively. AWS-LC FIPS mode requires both the `aws_lc_rs` and -`fips` features. +Rcgen does not select a cryptography provider by default. Ring and AWS-LC are available through +the `ring` and `aws_lc_rs` features, respectively. Enable a built-in provider explicitly: diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index d53626af..73ee4303 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -14,7 +14,7 @@ keywords.workspace = true default = ["pem"] aws_lc_rs = ["dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] aws_lc_rs_unstable = ["aws_lc_rs"] # For backwards compatibility only -fips = ["aws-lc-rs?/fips"] +fips = ["dep:aws-lc-rs", "aws-lc-rs/fips"] ring = ["dep:ring"] [dependencies] diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index 591764a5..f3b9c7ad 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -173,7 +173,7 @@ impl CertificateParams { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] pub(crate) fn from_ca_cert_der(ca_cert: &CertificateDer<'_>) -> Result { let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert) @@ -270,12 +270,13 @@ impl CertificateParams { }; if let Some((pub_key_spki, provider)) = pub_key_spki_and_provider { - let subject_key_identifier = self.key_identifier_method.derive(provider, pub_key_spki); write_x509_extension( writer.next(), oid::SUBJECT_KEY_IDENTIFIER, false, - |writer| writer.write_bytes(&subject_key_identifier), + |writer| { + writer.write_bytes(&self.key_identifier_method.derive(provider, pub_key_spki)); + }, ); } @@ -779,7 +780,7 @@ impl ExtendedKeyUsagePurpose { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 @@ -848,7 +849,7 @@ impl NameConstraints { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, @@ -905,7 +906,7 @@ impl GeneralSubtree { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], @@ -1081,7 +1082,7 @@ impl IsCa { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 @@ -1131,7 +1132,7 @@ pub enum BasicConstraints { Constrained(u8), } -#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] mod tests { #[cfg(feature = "x509-parser")] use std::net::Ipv4Addr; diff --git a/rcgen/src/crl.rs b/rcgen/src/crl.rs index cb95d81f..c18bba47 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -209,10 +209,6 @@ impl CertificateRevocationListParams { issuer: &Issuer<'_, impl SigningKey>, provider: &dyn CryptoProvider, ) -> Result, Error> { - let key_identifier = self - .key_identifier_method - .derive(provider, issuer.signing_key.subject_public_key_info()); - sign_der(&issuer.signing_key, |writer| { // Write CRL version. // RFC 5280 §5.1.2.1: @@ -273,7 +269,11 @@ impl CertificateRevocationListParams { writer.next().write_tagged(Tag::context(0), |writer| { writer.write_sequence(|writer| { // Write authority key identifier. - write_x509_authority_key_identifier(writer.next(), key_identifier.clone()); + write_x509_authority_key_identifier( + writer.next(), + self.key_identifier_method + .derive(provider, issuer.signing_key.subject_public_key_info()), + ); // Write CRL number. write_x509_extension(writer.next(), oid::CRL_NUMBER, false, |writer| { @@ -418,7 +418,7 @@ impl RevokedCertParams { } } -#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] mod tests { use x509_parser::num_bigint::BigUint; use x509_parser::{oid_registry, parse_x509_crl}; diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index e726c532..2b77b331 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -122,14 +122,17 @@ impl AwsLcProvider { PqdsaKeyPair::from_pkcs8(signing_algorithm, key_der) .map_err(|e| Error::RingKeyRejected(e.to_string()))?, ), - algorithm, + alg: algorithm, }); } } return Err(Error::UnsupportedSignatureAlgorithm); }; - Ok(AwsLcSigningKey { kind, algorithm }) + Ok(AwsLcSigningKey { + kind, + alg: algorithm, + }) } fn detect(&self, key_der: &[u8], is_pkcs8: bool) -> Result { @@ -155,61 +158,69 @@ impl AwsLcProvider { fn generate_ecdsa( &self, - algorithm: &'static SignatureAlgorithm, - signing_algorithm: &'static signature::EcdsaSigningAlgorithm, + alg: &'static SignatureAlgorithm, + sign_alg: &'static signature::EcdsaSigningAlgorithm, ) -> Result { - let document = EcdsaKeyPair::generate_pkcs8(signing_algorithm, &SystemRandom::new()) + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, &SystemRandom::new()) .map_err(|_| Error::RingUnspecified)?; - let serialized_der = document.as_ref().to_vec(); - let signing_key = self.load_with_algorithm(&serialized_der, true, algorithm)?; + let key_pair_serialized = key_pair_doc.as_ref().to_vec(); + let key_pair = EcdsaKeyPair::from_pkcs8(sign_alg, key_pair_doc.as_ref()).unwrap(); Ok(KeyPair::from_signing_key( - Box::new(signing_key), - serialized_der, + Box::new(AwsLcSigningKey { + kind: AwsLcKeyKind::Ec(key_pair), + alg, + }), + key_pair_serialized, )) } fn generate_rsa_inner( &self, - algorithm: &'static SignatureAlgorithm, + alg: &'static SignatureAlgorithm, key_size: KeySize, ) -> Result { - if algorithm != &PKCS_RSA_SHA256 - && algorithm != &PKCS_RSA_SHA384 - && algorithm != &PKCS_RSA_SHA512 - { + let sign_alg: &'static dyn RsaEncoding = if alg == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_SHA256 + } else if alg == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_SHA384 + } else if alg == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_SHA512 + } else { return Err(Error::KeyGenerationUnavailable); - } - let key = RsaKeyPair::generate(key_size).map_err(|_| Error::RingUnspecified)?; - let serialized_der = key + }; + let key_pair = RsaKeyPair::generate(key_size).map_err(|_| Error::RingUnspecified)?; + let key_pair_serialized = key_pair .as_der() .map_err(|_| Error::RingUnspecified)? .as_ref() .to_vec(); - let signing_key = self.load_with_algorithm(&serialized_der, true, algorithm)?; Ok(KeyPair::from_signing_key( - Box::new(signing_key), - serialized_der, + Box::new(AwsLcSigningKey { + kind: AwsLcKeyKind::Rsa(key_pair, sign_alg), + alg, + }), + key_pair_serialized, )) } #[cfg(feature = "aws_lc_rs")] fn generate_pqdsa( &self, - algorithm: &'static SignatureAlgorithm, - signing_algorithm: &'static PqdsaSigningAlgorithm, + alg: &'static SignatureAlgorithm, + sign_alg: &'static PqdsaSigningAlgorithm, ) -> Result { - let key = PqdsaKeyPair::generate(signing_algorithm).map_err(|_| Error::RingUnspecified)?; - let serialized_der = key + let key_pair = PqdsaKeyPair::generate(sign_alg).map_err(|_| Error::RingUnspecified)?; + let key_pair_serialized = key_pair .to_pkcs8v1() .map_err(|_| Error::RingUnspecified)? .as_ref() .to_vec(); Ok(KeyPair::from_signing_key( Box::new(AwsLcSigningKey { - kind: AwsLcKeyKind::Pq(key), - algorithm, + kind: AwsLcKeyKind::Pq(key_pair), + alg, }), - serialized_der, + key_pair_serialized, )) } } @@ -247,13 +258,16 @@ impl CryptoProvider for AwsLcProvider { } else if algorithm == &PKCS_ECDSA_P521_SHA512 { self.generate_ecdsa(algorithm, &signature::ECDSA_P521_SHA512_ASN1_SIGNING) } else if algorithm == &PKCS_ED25519 { - let document = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()) + let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()) .map_err(|_| Error::RingUnspecified)?; - let serialized_der = document.as_ref().to_vec(); - let signing_key = self.load_with_algorithm(&serialized_der, true, algorithm)?; + let key_pair_serialized = key_pair_doc.as_ref().to_vec(); + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); Ok(KeyPair::from_signing_key( - Box::new(signing_key), - serialized_der, + Box::new(AwsLcSigningKey { + kind: AwsLcKeyKind::Ed(key_pair), + alg: algorithm, + }), + key_pair_serialized, )) } else if is_rsa { let key_size = match key_size.unwrap_or(RsaKeySize::_2048) { @@ -360,47 +374,51 @@ enum AwsLcKeyKind { struct AwsLcSigningKey { kind: AwsLcKeyKind, - algorithm: &'static SignatureAlgorithm, + alg: &'static SignatureAlgorithm, } impl PublicKeyData for AwsLcSigningKey { fn der_bytes(&self) -> &[u8] { match &self.kind { - AwsLcKeyKind::Ec(key) => key.public_key().as_ref(), - AwsLcKeyKind::Ed(key) => key.public_key().as_ref(), + AwsLcKeyKind::Ec(kp) => kp.public_key().as_ref(), + AwsLcKeyKind::Ed(kp) => kp.public_key().as_ref(), #[cfg(feature = "aws_lc_rs")] - AwsLcKeyKind::Pq(key) => key.public_key().as_ref(), - AwsLcKeyKind::Rsa(key, _) => key.public_key().as_ref(), + AwsLcKeyKind::Pq(kp) => kp.public_key().as_ref(), + AwsLcKeyKind::Rsa(kp, _) => kp.public_key().as_ref(), } } fn algorithm(&self) -> &'static SignatureAlgorithm { - self.algorithm + self.alg } } impl SigningKey for AwsLcSigningKey { - fn sign(&self, message: &[u8]) -> Result, Error> { - match &self.kind { - AwsLcKeyKind::Ec(key) => key - .sign(&SystemRandom::new(), message) - .map(|signature| signature.as_ref().to_vec()) - .map_err(|_| Error::RingUnspecified), - AwsLcKeyKind::Ed(key) => Ok(key.sign(message).as_ref().to_vec()), + fn sign(&self, msg: &[u8]) -> Result, Error> { + Ok(match &self.kind { + AwsLcKeyKind::Ec(kp) => { + let system_random = SystemRandom::new(); + let signature = kp + .sign(&system_random, msg) + .map_err(|_| Error::RingUnspecified)?; + signature.as_ref().to_owned() + }, + AwsLcKeyKind::Ed(kp) => kp.sign(msg).as_ref().to_owned(), #[cfg(feature = "aws_lc_rs")] - AwsLcKeyKind::Pq(key) => { - let mut signature = vec![0; key.algorithm().signature_len()]; - key.sign(message, &mut signature) + AwsLcKeyKind::Pq(kp) => { + let mut signature = vec![0; kp.algorithm().signature_len()]; + kp.sign(msg, &mut signature) .map_err(|_| Error::RingUnspecified)?; - Ok(signature) + signature }, - AwsLcKeyKind::Rsa(key, encoding) => { - let mut signature = vec![0; key.public_modulus_len()]; - key.sign(*encoding, &SystemRandom::new(), message, &mut signature) + AwsLcKeyKind::Rsa(kp, padding_alg) => { + let system_random = SystemRandom::new(); + let mut signature = vec![0; kp.public_modulus_len()]; + kp.sign(*padding_alg, &system_random, msg, &mut signature) .map_err(|_| Error::RingUnspecified)?; - Ok(signature) + signature }, - } + }) } } diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index ae7d8f6f..035afb1b 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -15,7 +15,7 @@ use crate::{Error, KeyPair, RsaKeySize, SignatureAlgorithm}; pub mod ring; /// AWS-LC-based cryptography provider. -#[cfg(feature = "aws_lc_rs")] +#[cfg(any(feature = "aws_lc_rs", feature = "fips"))] pub mod aws_lc_rs; /// Cryptographic operations used by rcgen. diff --git a/rcgen/src/crypto/ring.rs b/rcgen/src/crypto/ring.rs index d8e71033..38a0cbc4 100644 --- a/rcgen/src/crypto/ring.rs +++ b/rcgen/src/crypto/ring.rs @@ -71,7 +71,10 @@ impl RingProvider { return Err(Error::UnsupportedSignatureAlgorithm); }; - Ok(RingSigningKey { kind, algorithm }) + Ok(RingSigningKey { + kind, + alg: algorithm, + }) } fn detect(&self, pkcs8: &[u8]) -> Result { @@ -108,42 +111,60 @@ impl CryptoProvider for RingProvider { return Err(Error::KeyGenerationUnavailable); } let rng = SystemRandom::new(); - let (signing_key, serialized_der) = if algorithm == &PKCS_ECDSA_P256_SHA256 { - let document = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, &rng) .map_err(|_| Error::RingUnspecified)?; - ( - self.load_with_algorithm(document.as_ref(), algorithm)?, - document.as_ref().to_vec(), + let key_pair_serialized = key_pair_doc.as_ref().to_vec(); + let key_pair = Self::ecdsa_from_pkcs8( + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + key_pair_doc.as_ref(), ) + .unwrap(); + Ok(KeyPair::from_signing_key( + Box::new(RingSigningKey { + kind: RingKeyKind::Ec(key_pair), + alg: algorithm, + }), + key_pair_serialized, + )) } else if algorithm == &PKCS_ECDSA_P384_SHA384 { - let document = + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, &rng) .map_err(|_| Error::RingUnspecified)?; - ( - self.load_with_algorithm(document.as_ref(), algorithm)?, - document.as_ref().to_vec(), + let key_pair_serialized = key_pair_doc.as_ref().to_vec(); + let key_pair = Self::ecdsa_from_pkcs8( + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + key_pair_doc.as_ref(), ) + .unwrap(); + Ok(KeyPair::from_signing_key( + Box::new(RingSigningKey { + kind: RingKeyKind::Ec(key_pair), + alg: algorithm, + }), + key_pair_serialized, + )) } else if algorithm == &PKCS_ED25519 { - let document = + let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rng).map_err(|_| Error::RingUnspecified)?; - ( - self.load_with_algorithm(document.as_ref(), algorithm)?, - document.as_ref().to_vec(), - ) + let key_pair_serialized = key_pair_doc.as_ref().to_vec(); + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); + Ok(KeyPair::from_signing_key( + Box::new(RingSigningKey { + kind: RingKeyKind::Ed(key_pair), + alg: algorithm, + }), + key_pair_serialized, + )) } else if algorithm == &PKCS_RSA_SHA256 || algorithm == &PKCS_RSA_SHA384 || algorithm == &PKCS_RSA_SHA512 { - return Err(Error::KeyGenerationUnavailable); + Err(Error::KeyGenerationUnavailable) } else { - return Err(Error::UnsupportedSignatureAlgorithm); - }; - - Ok(KeyPair::from_signing_key( - Box::new(signing_key), - serialized_der, - )) + Err(Error::UnsupportedSignatureAlgorithm) + } } fn load_private_key( @@ -203,38 +224,42 @@ enum RingKeyKind { struct RingSigningKey { kind: RingKeyKind, - algorithm: &'static SignatureAlgorithm, + alg: &'static SignatureAlgorithm, } impl PublicKeyData for RingSigningKey { fn der_bytes(&self) -> &[u8] { match &self.kind { - RingKeyKind::Ec(key) => key.public_key().as_ref(), - RingKeyKind::Ed(key) => key.public_key().as_ref(), - RingKeyKind::Rsa(key, _) => key.public_key().as_ref(), + RingKeyKind::Ec(kp) => kp.public_key().as_ref(), + RingKeyKind::Ed(kp) => kp.public_key().as_ref(), + RingKeyKind::Rsa(kp, _) => kp.public_key().as_ref(), } } fn algorithm(&self) -> &'static SignatureAlgorithm { - self.algorithm + self.alg } } impl SigningKey for RingSigningKey { - fn sign(&self, message: &[u8]) -> Result, Error> { - match &self.kind { - RingKeyKind::Ec(key) => key - .sign(&SystemRandom::new(), message) - .map(|signature| signature.as_ref().to_vec()) - .map_err(|_| Error::RingUnspecified), - RingKeyKind::Ed(key) => Ok(key.sign(message).as_ref().to_vec()), - RingKeyKind::Rsa(key, encoding) => { - let mut signature = vec![0; key.public().modulus_len()]; - key.sign(*encoding, &SystemRandom::new(), message, &mut signature) + fn sign(&self, msg: &[u8]) -> Result, Error> { + Ok(match &self.kind { + RingKeyKind::Ec(kp) => { + let system_random = SystemRandom::new(); + let signature = kp + .sign(&system_random, msg) .map_err(|_| Error::RingUnspecified)?; - Ok(signature) + signature.as_ref().to_owned() }, - } + RingKeyKind::Ed(kp) => kp.sign(msg).as_ref().to_owned(), + RingKeyKind::Rsa(kp, padding_alg) => { + let system_random = SystemRandom::new(); + let mut signature = vec![0; kp.public().modulus_len()]; + kp.sign(*padding_alg, &system_random, msg, &mut signature) + .map_err(|_| Error::RingUnspecified)?; + signature + }, + }) } } diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index 482c372c..d792dd43 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -246,7 +246,7 @@ impl CertificateSigningRequestParams { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] mod tests { use x509_parser::certification_request::X509CertificationRequest; diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 31463f36..d01bb723 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -98,9 +98,11 @@ impl KeyPair { #[cfg(feature = "pem")] pub fn from_pem(pem_str: &str, provider: &dyn CryptoProvider) -> Result { let private_key = pem::parse(pem_str)._err()?; - let private_key = PrivateKeyDer::try_from(private_key.into_contents()) - .map_err(|_| Error::CouldNotParseKeyPair)?; - Self::from_der(&private_key, provider) + let private_key: &[_] = private_key.contents(); + Self::from_der( + &PrivateKeyDer::try_from(private_key).map_err(|_| Error::CouldNotParseKeyPair)?, + provider, + ) } /// Obtains the key pair from a PEM formatted key @@ -117,8 +119,12 @@ impl KeyPair { provider: &dyn CryptoProvider, ) -> Result { let private_key = pem::parse(pem_str)._err()?; - let private_key = PrivatePkcs8KeyDer::from(private_key.into_contents()); - Self::from_pkcs8_der_and_sign_algo(&private_key, alg, provider) + let private_key_der: &[_] = private_key.contents(); + Self::from_pkcs8_der_and_sign_algo( + &PrivatePkcs8KeyDer::from(private_key_der), + alg, + provider, + ) } /// Obtains the key pair from a DER formatted key using the specified [`SignatureAlgorithm`] @@ -162,9 +168,12 @@ impl KeyPair { provider: &dyn CryptoProvider, ) -> Result { let private_key = pem::parse(pem_str)._err()?; - let private_key = PrivateKeyDer::try_from(private_key.into_contents()) - .map_err(|_| Error::CouldNotParseKeyPair)?; - Self::from_der_and_sign_algo(&private_key, alg, provider) + let private_key: &[_] = private_key.contents(); + Self::from_der_and_sign_algo( + &PrivateKeyDer::try_from(private_key).map_err(|_| Error::CouldNotParseKeyPair)?, + alg, + provider, + ) } /// Obtains the key pair from a DER formatted key @@ -242,7 +251,8 @@ impl KeyPair { /// Serializes the key pair (including the private key) in PKCS#8 format in PEM #[cfg(feature = "pem")] pub fn serialize_pem(&self) -> String { - let p = Pem::new("PRIVATE KEY", self.serialize_der()); + let contents = self.serialize_der(); + let p = Pem::new("PRIVATE KEY", contents); pem::encode_config(&p, ENCODE_CONFIG) } } @@ -315,12 +325,6 @@ impl SigningKey for &S { } } -impl SigningKey for Box { - fn sign(&self, msg: &[u8]) -> Result, Error> { - (**self).sign(msg) - } -} - /// A key that can be used to sign messages pub trait SigningKey: PublicKeyData { /// Signs `msg` using the selected algorithm @@ -364,11 +368,16 @@ impl SubjectPublicKeyInfo { let alg = SignatureAlgorithm::iter() .find(|alg| { - let bytes = yasna::construct_der(|writer| alg.write_oids_sign_alg(writer)); + let bytes = yasna::construct_der(|writer| { + alg.write_oids_sign_alg(writer); + }); let Ok((rest, aid)) = AlgorithmIdentifier::from_der(&bytes) else { return false; }; - rest.is_empty() && aid == spki.algorithm + if !rest.is_empty() { + return false; + } + aid == spki.algorithm }) .ok_or(Error::UnsupportedSignatureAlgorithm)?; @@ -399,16 +408,6 @@ impl PublicKeyData for &K { } } -impl PublicKeyData for Box { - fn der_bytes(&self) -> &[u8] { - (**self).der_bytes() - } - - fn algorithm(&self) -> &'static SignatureAlgorithm { - (**self).algorithm() - } -} - /// The public key data of a key pair pub trait PublicKeyData { /// The public key data in DER format @@ -434,7 +433,7 @@ pub(crate) fn serialize_public_key_der(key: &(impl PublicKeyData + ?Sized), writ }) } -#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] mod test { use super::*; @@ -445,9 +444,9 @@ mod test { &PKCS_ED25519, &PKCS_ECDSA_P256_SHA256, &PKCS_ECDSA_P384_SHA384, - #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))] + #[cfg(all(any(feature = "aws_lc_rs", feature = "fips"), not(feature = "ring")))] &PKCS_ECDSA_P521_SHA512, - #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))] + #[cfg(all(any(feature = "aws_lc_rs", feature = "fips"), not(feature = "ring")))] &PKCS_RSA_SHA256, ] { let kp = KeyPair::generate_for(alg, crate::test_provider()).expect("keygen"); diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 74bb1cca..5d4dd3ed 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -37,9 +37,6 @@ println!("{}", signing_key.serialize_pem()); #![cfg_attr(rcgen_docsrs, feature(doc_cfg))] #![warn(unreachable_pub)] -#[cfg(all(feature = "fips", not(feature = "aws_lc_rs")))] -compile_error!("the 'fips' feature currently requires the 'aws_lc_rs' feature"); - use std::borrow::Cow; use std::collections::HashMap; use std::fmt; @@ -83,11 +80,11 @@ mod oid; mod sign_algo; pub mod string; -#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] pub(crate) fn test_provider() -> &'static dyn CryptoProvider { - #[cfg(feature = "aws_lc_rs")] + #[cfg(any(feature = "aws_lc_rs", feature = "fips"))] return crypto::aws_lc_rs::default_provider(); - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] + #[cfg(all(feature = "ring", not(any(feature = "aws_lc_rs", feature = "fips"))))] return crypto::ring::default_provider(); } @@ -163,9 +160,8 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { signing_key: S, provider: &dyn CryptoProvider, ) -> Result { - let certificate = params.self_signed(&signing_key, provider)?; Ok(Self { - certificate, + certificate: params.self_signed(&signing_key, provider)?, issuer: Issuer::new(params, signing_key), }) } @@ -177,9 +173,8 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { issuer: &Issuer<'_, impl SigningKey>, provider: &dyn CryptoProvider, ) -> Result { - let certificate = params.signed_by(&signing_key, issuer, provider)?; Ok(Self { - certificate, + certificate: params.signed_by(&signing_key, issuer, provider)?, issuer: Issuer::new(params, signing_key), }) } @@ -341,7 +336,7 @@ impl SanType { #[cfg(all( test, feature = "x509-parser", - any(feature = "ring", feature = "aws_lc_rs") + any(feature = "ring", feature = "aws_lc_rs", feature = "fips") ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 @@ -745,10 +740,10 @@ impl KeyIdMethod { Self::Sha256 => HashAlgorithm::Sha256, Self::Sha384 => HashAlgorithm::Sha384, Self::Sha512 => HashAlgorithm::Sha512, - Self::PreSpecified(value) => return value.clone(), + Self::PreSpecified(b) => return b.to_vec(), }; let digest = provider.hash(algorithm, subject_public_key_info.as_ref()); - digest.as_ref()[..20].to_vec() + digest.as_ref()[0..20].to_vec() } } diff --git a/rcgen/src/string.rs b/rcgen/src/string.rs index 48ea4662..21afa866 100644 --- a/rcgen/src/string.rs +++ b/rcgen/src/string.rs @@ -425,6 +425,7 @@ impl BmpString { ))); } + // FIXME: Update this when `array_chunks` is stabilized. for maybe_char in char::decode_utf16( vec.as_chunks::<2>() .0 @@ -545,6 +546,7 @@ impl UniversalString { )); } + // FIXME: Update this when `array_chunks` is stabilized. for maybe_char in vec .as_chunks::<4>() .0 diff --git a/rcgen/tests/custom_provider.rs b/rcgen/tests/custom_provider.rs index deb19a6d..dcea99d4 100644 --- a/rcgen/tests/custom_provider.rs +++ b/rcgen/tests/custom_provider.rs @@ -151,8 +151,4 @@ fn explicit_provider_covers_all_rcgen_crypto() { assert_eq!(parsed.public_key.algorithm(), &PKCS_ED25519); assert_eq!(VERIFICATIONS.load(Ordering::Relaxed), 1); } - - let generated = KeyPair::generate_for(&PKCS_ED25519, custom_provider).unwrap(); - assert_eq!(generated.algorithm(), &PKCS_ED25519); - assert_eq!(GENERATIONS.load(Ordering::Relaxed), 2); } diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index b4f8579a..b47184b3 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -14,7 +14,7 @@ keywords.workspace = true default = ["ring"] aws_lc_rs = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/aws-lc-sys"] aws_lc_rs_unstable = ["aws_lc_rs", "rcgen/aws_lc_rs_unstable"] -fips = ["aws_lc_rs", "rcgen/fips"] +fips = ["dep:aws-lc-rs", "rcgen/fips", "aws-lc-rs/fips"] ring = ["dep:ring", "rcgen/ring"] [dependencies] diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 4b8bb9ce..f294fa29 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -12,9 +12,9 @@ use rcgen::{ }; fn provider() -> &'static dyn CryptoProvider { - #[cfg(feature = "aws_lc_rs")] + #[cfg(any(feature = "aws_lc_rs", feature = "fips"))] return rcgen::crypto::aws_lc_rs::default_provider(); - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] + #[cfg(all(feature = "ring", not(any(feature = "aws_lc_rs", feature = "fips"))))] return rcgen::crypto::ring::default_provider(); } diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml index 4efd5563..c8009667 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -8,7 +8,7 @@ publish = false default = ["ring"] aws_lc_rs = ["rcgen/aws_lc_rs", "rustls-webpki/aws-lc-rs", "dep:aws-lc-rs"] aws_lc_rs_unstable = ["aws_lc_rs", "rcgen/aws_lc_rs_unstable"] -fips = ["aws_lc_rs", "rcgen/fips"] +fips = ["rcgen/fips"] pem = ["dep:pem", "rcgen/pem"] ring = ["rcgen/ring"] x509-parser = ["dep:x509-parser", "rcgen/x509-parser"] diff --git a/verify-tests/src/lib.rs b/verify-tests/src/lib.rs index e78d1494..de940450 100644 --- a/verify-tests/src/lib.rs +++ b/verify-tests/src/lib.rs @@ -62,9 +62,9 @@ YPTHy8SWRA2sMII3ArhHJ8A= "#; pub fn provider() -> &'static dyn CryptoProvider { - #[cfg(feature = "aws_lc_rs")] + #[cfg(any(feature = "aws_lc_rs", feature = "fips"))] return rcgen::crypto::aws_lc_rs::default_provider(); - #[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] + #[cfg(all(feature = "ring", not(any(feature = "aws_lc_rs", feature = "fips"))))] return rcgen::crypto::ring::default_provider(); } From cbc3a7d372afc0c673da18603a7f1cc63573a5ae Mon Sep 17 00:00:00 2001 From: jgreeer Date: Thu, 10 Sep 2026 19:02:28 +0000 Subject: [PATCH 20/20] swap method order + remove cargo tree step --- .github/workflows/ci.yml | 2 - rcgen/src/crypto/aws_lc_rs.rs | 146 +++++++++++++++++----------------- rcgen/src/crypto/mod.rs | 34 ++++---- rcgen/src/crypto/ring.rs | 102 ++++++++++++------------ 4 files changed, 141 insertions(+), 143 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45951aca..88f61c8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,6 @@ jobs: # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets - run: cargo clippy -p rcgen --no-default-features --features pem,x509-parser --all-targets - - name: Ensure backend-free builds have no built-in crypto dependencies - run: "! cargo tree -p rcgen --no-default-features --edges normal,build | grep -E 'ring v|aws-lc'" - run: cargo clippy --no-default-features --features ring --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs_unstable,pem,x509-parser --all-targets diff --git a/rcgen/src/crypto/aws_lc_rs.rs b/rcgen/src/crypto/aws_lc_rs.rs index 2b77b331..2b2fa128 100644 --- a/rcgen/src/crypto/aws_lc_rs.rs +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -226,13 +226,74 @@ impl AwsLcProvider { } impl CryptoProvider for AwsLcProvider { - fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { - let algorithm = match algorithm { - HashAlgorithm::Sha256 => &digest::SHA256, - HashAlgorithm::Sha384 => &digest::SHA384, - HashAlgorithm::Sha512 => &digest::SHA512, + fn verify( + &self, + message: &[u8], + signature_bytes: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, + ) -> Result<(), Error> { + #[cfg(feature = "aws_lc_rs")] + { + let pqdsa_algorithm = if algorithm == &PKCS_ML_DSA_44 { + Some(&ML_DSA_44) + } else if algorithm == &PKCS_ML_DSA_65 { + Some(&ML_DSA_65) + } else if algorithm == &PKCS_ML_DSA_87 { + Some(&ML_DSA_87) + } else { + None + }; + if let Some(pqdsa_algorithm) = pqdsa_algorithm { + return pqdsa_algorithm + .verify_sig(public_key, message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed); + } + } + + let verification_algorithm: &'static dyn VerificationAlgorithm = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + &signature::ECDSA_P256_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + &signature::ECDSA_P384_SHA384_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA256 { + &signature::ECDSA_P521_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA384 { + &signature::ECDSA_P521_SHA384_ASN1 + } else if algorithm == &PKCS_ECDSA_P521_SHA512 { + &signature::ECDSA_P521_SHA512_ASN1 + } else if algorithm == &PKCS_ED25519 { + &signature::ED25519 + } else if algorithm == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if algorithm == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if algorithm == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + signature::UnparsedPublicKey::new(verification_algorithm, public_key) + .verify(message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed) + } + + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result { + let is_pkcs8 = matches!(key_der, PrivateKeyDer::Pkcs8(_)); + let serialized_der = key_der.secret_der().to_vec(); + let signing_key = match algorithm { + Some(algorithm) => self.load_with_algorithm(&serialized_der, is_pkcs8, algorithm)?, + None => self.detect(&serialized_der, is_pkcs8)?, }; - HashOutput::new(digest::digest(algorithm, input).as_ref()) + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) } fn generate( @@ -293,74 +354,13 @@ impl CryptoProvider for AwsLcProvider { } } - fn load_private_key( - &self, - key_der: PrivateKeyDer<'static>, - algorithm: Option<&'static SignatureAlgorithm>, - ) -> Result { - let is_pkcs8 = matches!(key_der, PrivateKeyDer::Pkcs8(_)); - let serialized_der = key_der.secret_der().to_vec(); - let signing_key = match algorithm { - Some(algorithm) => self.load_with_algorithm(&serialized_der, is_pkcs8, algorithm)?, - None => self.detect(&serialized_der, is_pkcs8)?, + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { + let algorithm = match algorithm { + HashAlgorithm::Sha256 => &digest::SHA256, + HashAlgorithm::Sha384 => &digest::SHA384, + HashAlgorithm::Sha512 => &digest::SHA512, }; - Ok(KeyPair::from_signing_key( - Box::new(signing_key), - serialized_der, - )) - } - - fn verify( - &self, - message: &[u8], - signature_bytes: &[u8], - public_key: &[u8], - algorithm: &'static SignatureAlgorithm, - ) -> Result<(), Error> { - #[cfg(feature = "aws_lc_rs")] - { - let pqdsa_algorithm = if algorithm == &PKCS_ML_DSA_44 { - Some(&ML_DSA_44) - } else if algorithm == &PKCS_ML_DSA_65 { - Some(&ML_DSA_65) - } else if algorithm == &PKCS_ML_DSA_87 { - Some(&ML_DSA_87) - } else { - None - }; - if let Some(pqdsa_algorithm) = pqdsa_algorithm { - return pqdsa_algorithm - .verify_sig(public_key, message, signature_bytes) - .map_err(|_| Error::SignatureVerificationFailed); - } - } - - let verification_algorithm: &'static dyn VerificationAlgorithm = - if algorithm == &PKCS_ECDSA_P256_SHA256 { - &signature::ECDSA_P256_SHA256_ASN1 - } else if algorithm == &PKCS_ECDSA_P384_SHA384 { - &signature::ECDSA_P384_SHA384_ASN1 - } else if algorithm == &PKCS_ECDSA_P521_SHA256 { - &signature::ECDSA_P521_SHA256_ASN1 - } else if algorithm == &PKCS_ECDSA_P521_SHA384 { - &signature::ECDSA_P521_SHA384_ASN1 - } else if algorithm == &PKCS_ECDSA_P521_SHA512 { - &signature::ECDSA_P521_SHA512_ASN1 - } else if algorithm == &PKCS_ED25519 { - &signature::ED25519 - } else if algorithm == &PKCS_RSA_SHA256 { - &signature::RSA_PKCS1_2048_8192_SHA256 - } else if algorithm == &PKCS_RSA_SHA384 { - &signature::RSA_PKCS1_2048_8192_SHA384 - } else if algorithm == &PKCS_RSA_SHA512 { - &signature::RSA_PKCS1_2048_8192_SHA512 - } else { - return Err(Error::UnsupportedSignatureAlgorithm); - }; - - signature::UnparsedPublicKey::new(verification_algorithm, public_key) - .verify(message, signature_bytes) - .map_err(|_| Error::SignatureVerificationFailed) + HashOutput::new(digest::digest(algorithm, input).as_ref()) } } diff --git a/rcgen/src/crypto/mod.rs b/rcgen/src/crypto/mod.rs index 035afb1b..822a8332 100644 --- a/rcgen/src/crypto/mod.rs +++ b/rcgen/src/crypto/mod.rs @@ -20,17 +20,18 @@ pub mod aws_lc_rs; /// Cryptographic operations used by rcgen. pub trait CryptoProvider: std::fmt::Debug + Send + Sync { - /// Hash `input` with `algorithm`. - fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput; - - /// Generate an exportable key pair for `algorithm`. + /// Verify `signature` over `message` using `public_key` and `algorithm`. /// - /// `key_size` selects an explicit RSA key size. It must be `None` for non-RSA algorithms. - fn generate( + /// rcgen uses this operation to verify the self-signature on a parsed PKCS#10 certificate + /// signing request. `public_key` contains the SubjectPublicKeyInfo `subjectPublicKey` BIT + /// STRING contents, matching [`PublicKeyData::der_bytes`](crate::PublicKeyData::der_bytes). + fn verify( &self, + message: &[u8], + signature: &[u8], + public_key: &[u8], algorithm: &'static SignatureAlgorithm, - key_size: Option, - ) -> Result; + ) -> Result<(), Error>; /// Decode and validate an exportable private key. /// @@ -43,18 +44,17 @@ pub trait CryptoProvider: std::fmt::Debug + Send + Sync { algorithm: Option<&'static SignatureAlgorithm>, ) -> Result; - /// Verify `signature` over `message` using `public_key` and `algorithm`. + /// Generate an exportable key pair for `algorithm`. /// - /// rcgen uses this operation to verify the self-signature on a parsed PKCS#10 certificate - /// signing request. `public_key` contains the SubjectPublicKeyInfo `subjectPublicKey` BIT - /// STRING contents, matching [`PublicKeyData::der_bytes`](crate::PublicKeyData::der_bytes). - fn verify( + /// `key_size` selects an explicit RSA key size. It must be `None` for non-RSA algorithms. + fn generate( &self, - message: &[u8], - signature: &[u8], - public_key: &[u8], algorithm: &'static SignatureAlgorithm, - ) -> Result<(), Error>; + key_size: Option, + ) -> Result; + + /// Hash `input` with `algorithm`. + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput; } /// A hash algorithm required by rcgen. diff --git a/rcgen/src/crypto/ring.rs b/rcgen/src/crypto/ring.rs index 38a0cbc4..8a933ce5 100644 --- a/rcgen/src/crypto/ring.rs +++ b/rcgen/src/crypto/ring.rs @@ -93,13 +93,52 @@ impl RingProvider { } impl CryptoProvider for RingProvider { - fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { - let algorithm = match algorithm { - HashAlgorithm::Sha256 => &digest::SHA256, - HashAlgorithm::Sha384 => &digest::SHA384, - HashAlgorithm::Sha512 => &digest::SHA512, + fn verify( + &self, + message: &[u8], + signature_bytes: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, + ) -> Result<(), Error> { + let verification_algorithm: &'static dyn VerificationAlgorithm = + if algorithm == &PKCS_ECDSA_P256_SHA256 { + &signature::ECDSA_P256_SHA256_ASN1 + } else if algorithm == &PKCS_ECDSA_P384_SHA384 { + &signature::ECDSA_P384_SHA384_ASN1 + } else if algorithm == &PKCS_ED25519 { + &signature::ED25519 + } else if algorithm == &PKCS_RSA_SHA256 { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if algorithm == &PKCS_RSA_SHA384 { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if algorithm == &PKCS_RSA_SHA512 { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else { + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + signature::UnparsedPublicKey::new(verification_algorithm, public_key) + .verify(message, signature_bytes) + .map_err(|_| Error::SignatureVerificationFailed) + } + + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result { + let PrivateKeyDer::Pkcs8(pkcs8) = key_der else { + return Err(Error::CouldNotParseKeyPair); }; - HashOutput::new(digest::digest(algorithm, input).as_ref()) + let serialized_der = pkcs8.secret_pkcs8_der().to_vec(); + let signing_key = match algorithm { + Some(algorithm) => self.load_with_algorithm(&serialized_der, algorithm)?, + None => self.detect(&serialized_der)?, + }; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) } fn generate( @@ -167,52 +206,13 @@ impl CryptoProvider for RingProvider { } } - fn load_private_key( - &self, - key_der: PrivateKeyDer<'static>, - algorithm: Option<&'static SignatureAlgorithm>, - ) -> Result { - let PrivateKeyDer::Pkcs8(pkcs8) = key_der else { - return Err(Error::CouldNotParseKeyPair); - }; - let serialized_der = pkcs8.secret_pkcs8_der().to_vec(); - let signing_key = match algorithm { - Some(algorithm) => self.load_with_algorithm(&serialized_der, algorithm)?, - None => self.detect(&serialized_der)?, + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { + let algorithm = match algorithm { + HashAlgorithm::Sha256 => &digest::SHA256, + HashAlgorithm::Sha384 => &digest::SHA384, + HashAlgorithm::Sha512 => &digest::SHA512, }; - Ok(KeyPair::from_signing_key( - Box::new(signing_key), - serialized_der, - )) - } - - fn verify( - &self, - message: &[u8], - signature_bytes: &[u8], - public_key: &[u8], - algorithm: &'static SignatureAlgorithm, - ) -> Result<(), Error> { - let verification_algorithm: &'static dyn VerificationAlgorithm = - if algorithm == &PKCS_ECDSA_P256_SHA256 { - &signature::ECDSA_P256_SHA256_ASN1 - } else if algorithm == &PKCS_ECDSA_P384_SHA384 { - &signature::ECDSA_P384_SHA384_ASN1 - } else if algorithm == &PKCS_ED25519 { - &signature::ED25519 - } else if algorithm == &PKCS_RSA_SHA256 { - &signature::RSA_PKCS1_2048_8192_SHA256 - } else if algorithm == &PKCS_RSA_SHA384 { - &signature::RSA_PKCS1_2048_8192_SHA384 - } else if algorithm == &PKCS_RSA_SHA512 { - &signature::RSA_PKCS1_2048_8192_SHA512 - } else { - return Err(Error::UnsupportedSignatureAlgorithm); - }; - - signature::UnparsedPublicKey::new(verification_algorithm, public_key) - .verify(message, signature_bytes) - .map_err(|_| Error::SignatureVerificationFailed) + HashOutput::new(digest::digest(algorithm, input).as_ref()) } }