diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1354ceb..88f61c8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,7 @@ 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 pem,x509-parser --all-targets - 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 +74,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 (provider API without a built-in backend) + 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) run: cargo doc --features aws_lc_rs_unstable,pem,x509-parser --document-private-items env: @@ -165,6 +170,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 no built-in backend + 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/Cargo.lock b/Cargo.lock index 2faca654..dfe547a3 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", ] @@ -822,7 +821,7 @@ checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" [[package]] name = "rcgen" -version = "0.14.10" +version = "0.15.0" dependencies = [ "aws-lc-rs", "openssl", @@ -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..213e0a2a 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,33 @@ Simple Rust library to generate X.509 certificates. ```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 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: + +```toml +[dependencies] +rcgen = { version = "0.15", features = ["ring"] } +``` + +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 You can do this: diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 68ad52d7..73ee4303 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 @@ -11,12 +11,11 @@ rust-version.workspace = true 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"] +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", "dep:aws-lc-rs", "aws-lc-rs/fips"] -crypto = [] -ring = ["crypto", "dep:ring", "x509-parser?/verify"] +fips = ["dep:aws-lc-rs", "aws-lc-rs/fips"] +ring = ["dep:ring"] [dependencies] aws-lc-rs = { workspace = true, optional = true } @@ -33,22 +32,22 @@ 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"] +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/examples/rsa-irc-openssl.rs b/rcgen/examples/rsa-irc-openssl.rs index aacb1791..e7408d63 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 6c518018..f3b9c7ad 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -9,10 +9,9 @@ use yasna::models::ObjectIdentifier; use yasna::{DERWriter, DERWriterSeq, Tag}; use crate::crl::CrlDistributionPoint; +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::{ @@ -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,9 +137,10 @@ impl CertificateParams { &self, public_key: &impl PublicKeyData, issuer: &Issuer<'_, impl SigningKey>, + provider: &dyn CryptoProvider, ) -> Result { Ok(Certificate { - der: self.serialize_der_with_signer(public_key, issuer)?, + der: self.serialize_der_with_signer(public_key, issuer, provider)?, }) } @@ -151,21 +148,33 @@ impl CertificateParams { /// /// The returned [`Certificate`] may be serialized using [`Certificate::der`] and /// [`Certificate::pem`]. - pub fn self_signed(&self, signing_key: &impl SigningKey) -> Result { + pub fn self_signed( + &self, + signing_key: &impl SigningKey, + provider: &dyn CryptoProvider, + ) -> Result { let issuer = Issuer::from_params(self, signing_key); Ok(Certificate { - der: self.serialize_der_with_signer(signing_key, &issuer)?, + der: self.serialize_der_with_signer(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 { + pub fn key_identifier( + &self, + key: &impl PublicKeyData, + provider: &dyn CryptoProvider, + ) -> Vec { self.key_identifier_method - .derive(key.subject_public_key_info()) + .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", 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,20 +258,24 @@ 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, + pub_key_spki_and_provider: Option<(&[u8], &dyn CryptoProvider)>, + ) { 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((pub_key_spki, provider)) = pub_key_spki_and_provider { write_x509_extension( writer.next(), oid::SUBJECT_KEY_IDENTIFIER, false, |writer| { - writer.write_bytes(&self.key_identifier_method.derive(pub_key_spki)); + writer.write_bytes(&self.key_identifier_method.derive(provider, pub_key_spki)); }, ); } @@ -436,6 +449,7 @@ impl CertificateParams { &self, pub_key: &K, issuer: &Issuer<'_, impl SigningKey>, + provider: &dyn CryptoProvider, ) -> Result, Error> { // An empty distribution point would be encoded as an empty fullName, // violating GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName @@ -458,18 +472,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 = digest::digest(&digest::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 @@ -505,7 +512,9 @@ impl CertificateParams { } writer.next().write_tagged(Tag::context(3), |writer| { - writer.write_sequence(|writer| self.write_extensions(writer, &pub_key_spki, issuer)) + writer.write_sequence(|writer| { + self.write_extensions(writer, &pub_key_spki, issuer, provider) + }) })?; Ok(()) @@ -519,16 +528,16 @@ impl CertificateParams { writer: &mut DERWriterSeq, pub_key_spki: &[u8], issuer: &Issuer<'_, impl SigningKey>, + 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(issuer.signing_key.subject_public_key_info()), + .derive(provider, issuer.signing_key.subject_public_key_info()), }, ); } @@ -576,7 +585,7 @@ impl CertificateParams { ); } - self.write_ca_extensions(writer, Some(pub_key_spki)); + 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| { @@ -768,7 +777,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", feature = "fips") + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let extended_key_usage = x509 .extended_key_usage() @@ -833,7 +846,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", feature = "fips") + ))] fn from_x509( x509: &x509_parser::certificate::X509Certificate<'_>, ) -> Result, Error> { @@ -886,7 +903,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", feature = "fips") + ))] fn from_x509( subtrees: &[x509_parser::extensions::GeneralSubtree<'_>], ) -> Result, Error> { @@ -1058,7 +1079,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", feature = "fips") + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result { let basic_constraints = x509 .basic_constraints() @@ -1107,24 +1132,21 @@ pub enum BasicConstraints { Constrained(u8), } -#[cfg(test)] +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] 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 { @@ -1140,8 +1162,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(); @@ -1169,7 +1193,6 @@ mod tests { assert!(found); } - #[cfg(feature = "crypto")] #[test] fn test_explicit_no_ca() { let params = CertificateParams { @@ -1178,8 +1201,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(); @@ -1203,7 +1228,6 @@ mod tests { assert!(found); } - #[cfg(feature = "crypto")] #[test] fn test_empty_crl_distribution_point_uris_rejected() { let params = CertificateParams { @@ -1214,14 +1238,15 @@ 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 ); } - #[cfg(feature = "crypto")] #[test] fn test_with_key_usages_only() { // The KeyUsage extension must be present even when it is the only @@ -1234,14 +1259,15 @@ 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()); } - #[cfg(feature = "crypto")] #[test] fn test_with_crl_distribution_points_only() { // The CRL distribution points extension must be present even when it @@ -1253,8 +1279,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!( @@ -1263,7 +1291,6 @@ mod tests { ))); } - #[cfg(feature = "crypto")] #[test] fn test_with_key_usages_decipheronly_only() { let params = CertificateParams { @@ -1275,8 +1302,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(); @@ -1301,7 +1330,6 @@ mod tests { assert!(found); } - #[cfg(feature = "crypto")] #[test] fn test_with_extended_key_usages_any() { let params = CertificateParams { @@ -1310,8 +1338,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(); @@ -1322,7 +1352,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; @@ -1338,8 +1367,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(); @@ -1359,16 +1390,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')); } } @@ -1380,8 +1415,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()); @@ -1410,8 +1447,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()); @@ -1435,7 +1474,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()); @@ -1444,7 +1483,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)); @@ -1540,7 +1579,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, @@ -1567,12 +1606,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 3addf637..c18bba47 100644 --- a/rcgen/src/crl.rs +++ b/rcgen/src/crl.rs @@ -4,6 +4,7 @@ use pki_types::CertificateRevocationListDer; use time::OffsetDateTime; use yasna::{DERWriter, Tag}; +use crate::crypto::CryptoProvider; use crate::key_pair::sign_der; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; @@ -21,27 +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(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 key_pair = KeyPair::generate().unwrap(); -/// #[cfg(not(feature = "crypto"))] -/// let key_pair = MyKeyPair { public_key: vec![] }; +/// let provider = rcgen::crypto::ring::default_provider(); +/// let key_pair = KeyPair::generate(provider).unwrap(); /// let issuer = Issuer::new(issuer_params, key_pair); /// /// // Describe a revoked certificate. @@ -58,12 +47,14 @@ 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).unwrap(); +/// }.signed_by( +/// &issuer, +/// provider, +/// ).unwrap(); ///# } +/// # #[cfg(not(feature = "ring"))] +/// # fn main() {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct CertificateRevocationList { der: CertificateRevocationListDer<'static>, @@ -187,6 +178,7 @@ impl CertificateRevocationListParams { pub fn signed_by( &self, issuer: &Issuer<'_, impl SigningKey>, + provider: &dyn CryptoProvider, ) -> Result { if self.next_update.le(&self.this_update) { return Err(Error::InvalidCrlNextUpdate); @@ -208,11 +200,15 @@ impl CertificateRevocationListParams { } Ok(CertificateRevocationList { - der: self.serialize_der(issuer)?.into(), + der: self.serialize_der(issuer, provider)?.into(), }) } - fn serialize_der(&self, issuer: &Issuer<'_, impl SigningKey>) -> Result, Error> { + fn serialize_der( + &self, + issuer: &Issuer<'_, impl SigningKey>, + provider: &dyn CryptoProvider, + ) -> Result, Error> { sign_der(&issuer.signing_key, |writer| { // Write CRL version. // RFC 5280 §5.1.2.1: @@ -276,7 +272,7 @@ impl CertificateRevocationListParams { write_x509_authority_key_identifier( writer.next(), self.key_identifier_method - .derive(issuer.signing_key.subject_public_key_info()), + .derive(provider, issuer.signing_key.subject_public_key_info()), ); // Write CRL number. @@ -422,7 +418,7 @@ impl RevokedCertParams { } } -#[cfg(all(test, feature = "crypto"))] +#[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}; @@ -448,7 +444,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 ); } @@ -505,7 +502,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() } @@ -519,6 +516,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 new file mode 100644 index 00000000..2b2fa128 --- /dev/null +++ b/rcgen/src/crypto/aws_lc_rs.rs @@ -0,0 +1,479 @@ +//! 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(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, +}; +use pki_types::PrivateKeyDer; + +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, + PKCS_ECDSA_P521_SHA512, PKCS_ED25519, PKCS_RSA_SHA256, PKCS_RSA_SHA384, PKCS_RSA_SHA512, +}; +#[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. +pub fn default_provider() -> &'static dyn CryptoProvider { + &AwsLcProvider +} + +#[derive(Debug)] +struct AwsLcProvider; + +impl AwsLcProvider { + 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(feature = "aws_lc_rs")] + { + 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()))?, + ), + alg: algorithm, + }); + } + } + return Err(Error::UnsupportedSignatureAlgorithm); + }; + + Ok(AwsLcSigningKey { + kind, + alg: 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(feature = "aws_lc_rs")] + &PKCS_ML_DSA_44, + #[cfg(feature = "aws_lc_rs")] + &PKCS_ML_DSA_65, + #[cfg(feature = "aws_lc_rs")] + &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, + alg: &'static SignatureAlgorithm, + sign_alg: &'static signature::EcdsaSigningAlgorithm, + ) -> Result { + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, &SystemRandom::new()) + .map_err(|_| Error::RingUnspecified)?; + 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(AwsLcSigningKey { + kind: AwsLcKeyKind::Ec(key_pair), + alg, + }), + key_pair_serialized, + )) + } + + fn generate_rsa_inner( + &self, + alg: &'static SignatureAlgorithm, + key_size: KeySize, + ) -> Result { + 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_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(); + Ok(KeyPair::from_signing_key( + Box::new(AwsLcSigningKey { + kind: AwsLcKeyKind::Rsa(key_pair, sign_alg), + alg, + }), + key_pair_serialized, + )) + } + + #[cfg(feature = "aws_lc_rs")] + fn generate_pqdsa( + &self, + alg: &'static SignatureAlgorithm, + sign_alg: &'static PqdsaSigningAlgorithm, + ) -> Result { + 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_pair), + alg, + }), + key_pair_serialized, + )) + } +} + +impl CryptoProvider for AwsLcProvider { + 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)?, + }; + Ok(KeyPair::from_signing_key( + Box::new(signing_key), + serialized_der, + )) + } + + 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 { + 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 key_pair_doc = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new()) + .map_err(|_| Error::RingUnspecified)?; + 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(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) { + 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")] + { + 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 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()) + } +} + +enum AwsLcKeyKind { + Ec(EcdsaKeyPair), + Ed(Ed25519KeyPair), + #[cfg(feature = "aws_lc_rs")] + Pq(PqdsaKeyPair), + Rsa(RsaKeyPair, &'static dyn RsaEncoding), +} + +struct AwsLcSigningKey { + kind: AwsLcKeyKind, + alg: &'static SignatureAlgorithm, +} + +impl PublicKeyData for AwsLcSigningKey { + fn der_bytes(&self) -> &[u8] { + match &self.kind { + AwsLcKeyKind::Ec(kp) => kp.public_key().as_ref(), + AwsLcKeyKind::Ed(kp) => kp.public_key().as_ref(), + #[cfg(feature = "aws_lc_rs")] + AwsLcKeyKind::Pq(kp) => kp.public_key().as_ref(), + AwsLcKeyKind::Rsa(kp, _) => kp.public_key().as_ref(), + } + } + + fn algorithm(&self) -> &'static SignatureAlgorithm { + self.alg + } +} + +impl SigningKey for AwsLcSigningKey { + 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(kp) => { + let mut signature = vec![0; kp.algorithm().signature_len()]; + kp.sign(msg, &mut signature) + .map_err(|_| Error::RingUnspecified)?; + 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)?; + signature + }, + }) + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "aws_lc_rs")] + use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; + + use super::*; + + #[test] + fn sha256_known_answer() { + assert_eq!( + default_provider() + .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, + 0xf2, 0x00, 0x15, 0xad, + ] + ); + } + + #[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(algorithm, provider).unwrap(); + let private_key = PrivatePkcs8KeyDer::from(generated.serialize_der()); + + let loaded = + KeyPair::from_pkcs8_der_and_sign_algo(&private_key, algorithm, provider).unwrap(); + assert_eq!(loaded.algorithm(), algorithm); + + 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 + .verify(message, &signature, loaded.der_bytes(), algorithm) + .unwrap(); + + #[cfg(feature = "x509-parser")] + { + let request = crate::CertificateParams::default() + .serialize_request(&loaded) + .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 new file mode 100644 index 00000000..822a8332 --- /dev/null +++ b/rcgen/src/crypto/mod.rs @@ -0,0 +1,110 @@ +//! 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 select a provider explicitly for each API that performs cryptographic work. + +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(any(feature = "aws_lc_rs", feature = "fips"))] +pub mod aws_lc_rs; + +/// Cryptographic operations used by rcgen. +pub trait CryptoProvider: std::fmt::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, + message: &[u8], + signature: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, + ) -> Result<(), Error>; + + /// Decode and validate an exportable private 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>, + algorithm: Option<&'static SignatureAlgorithm>, + ) -> Result; + + /// Generate an exportable key pair for `algorithm`. + /// + /// `key_size` selects an explicit RSA key size. It must be `None` for non-RSA algorithms. + fn generate( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: Option, + ) -> Result; + + /// Hash `input` with `algorithm`. + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput; +} + +/// 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, + } + } +} + +/// 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 new file mode 100644 index 00000000..8a933ce5 --- /dev/null +++ b/rcgen/src/crypto/ring.rs @@ -0,0 +1,283 @@ +//! 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, 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, + PKCS_RSA_SHA512, +}; + +/// Return rcgen's built-in `ring` provider. +pub fn default_provider() -> &'static dyn CryptoProvider { + &RingProvider +} + +#[derive(Debug)] +struct RingProvider; + +impl RingProvider { + 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, + alg: 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 CryptoProvider for RingProvider { + 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); + }; + 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( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: Option, + ) -> Result { + if key_size.is_some() { + return Err(Error::KeyGenerationUnavailable); + } + let rng = SystemRandom::new(); + if algorithm == &PKCS_ECDSA_P256_SHA256 { + let key_pair_doc = + EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, &rng) + .map_err(|_| Error::RingUnspecified)?; + 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 key_pair_doc = + EcdsaKeyPair::generate_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, &rng) + .map_err(|_| Error::RingUnspecified)?; + 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 key_pair_doc = + Ed25519KeyPair::generate_pkcs8(&rng).map_err(|_| Error::RingUnspecified)?; + 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 + { + Err(Error::KeyGenerationUnavailable) + } else { + Err(Error::UnsupportedSignatureAlgorithm) + } + } + + 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()) + } +} + +enum RingKeyKind { + Ec(EcdsaKeyPair), + Ed(Ed25519KeyPair), + Rsa(RsaKeyPair, &'static dyn RsaEncoding), +} + +struct RingSigningKey { + kind: RingKeyKind, + alg: &'static SignatureAlgorithm, +} + +impl PublicKeyData for RingSigningKey { + fn der_bytes(&self) -> &[u8] { + match &self.kind { + 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.alg + } +} + +impl SigningKey for RingSigningKey { + 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)?; + 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 + }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sha256_known_answer() { + assert_eq!( + default_provider() + .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, + 0xf2, 0x00, 0x15, 0xad, + ] + ); + } +} diff --git a/rcgen/src/csr.rs b/rcgen/src/csr.rs index 28bd5c34..d792dd43 100644 --- a/rcgen/src/csr.rs +++ b/rcgen/src/csr.rs @@ -4,6 +4,7 @@ use std::hash::Hash; use pem::Pem; use pki_types::CertificateSigningRequestDer; +use crate::crypto::CryptoProvider; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; use crate::{ @@ -84,9 +85,9 @@ impl CertificateSigningRequestParams { /// /// See [`from_der`](Self::from_der) for more details. #[cfg(all(feature = "pem", feature = "x509-parser"))] - pub fn from_pem(pem_str: &str) -> 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(&csr.contents().into()) + Self::from_der(&csr.contents().into(), provider) } /// Parse and verify a certificate signing request from DER-encoded bytes @@ -107,23 +108,49 @@ impl CertificateSigningRequestParams { /// /// [`PemObject`]: pki_types::pem::PemObject #[cfg(feature = "x509-parser")] - pub fn from_der(csr: &CertificateSigningRequestDer<'_>) -> Result { + pub fn from_der( + csr: &CertificateSigningRequestDer<'_>, + provider: &dyn 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 + .verify( + 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, + _ => Error::InvalidCertificationRequestSignature, + })?; + let mut params = CertificateParams { distinguished_name: DistinguishedName::from_name(&info.subject)?, ..CertificateParams::default() @@ -203,16 +230,24 @@ impl CertificateSigningRequestParams { /// /// The returned [`Certificate`] may be serialized using [`Certificate::der`] and /// [`Certificate::pem`]. - pub fn signed_by(&self, issuer: &Issuer) -> Result { + pub fn signed_by( + &self, + issuer: &Issuer, + provider: &dyn CryptoProvider, + ) -> Result { Ok(Certificate { der: self .params - .serialize_der_with_signer(&self.public_key, issuer)?, + .serialize_der_with_signer(&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}; @@ -226,7 +261,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 @@ -241,7 +276,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 @@ -262,7 +297,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 @@ -285,9 +320,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 9ba0b30e..1e4155f9 100644 --- a/rcgen/src/error.rs +++ b/rcgen/src/error.rs @@ -10,6 +10,8 @@ pub enum Error { CouldNotParseCertificationRequest, /// The given key pair couldn't be parsed CouldNotParseKeyPair, + /// A cryptography provider failed an operation. + CryptoProviderError(String), /// The CSR signature is invalid #[cfg(feature = "x509-parser")] InvalidCertificationRequestSignature, @@ -28,6 +30,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 @@ -47,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), @@ -66,6 +67,7 @@ impl fmt::Display for Error { request" )?, CouldNotParseKeyPair => write!(f, "Could not parse key pair")?, + CryptoProviderError(e) => write!(f, "Cryptography provider error: {e}")?, #[cfg(feature = "x509-parser")] InvalidCertificationRequestSignature => write!(f, "Invalid CSR signature")?, #[cfg(feature = "x509-parser")] @@ -84,6 +86,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")?, @@ -102,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}")?, }; @@ -147,7 +148,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..d01bb723 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -1,225 +1,137 @@ -#[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"))] +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 -#[cfg(feature = "crypto")] +/// +/// The cryptographic implementation is supplied by the selected [`CryptoProvider`]. pub struct KeyPair { - pub(crate) kind: KeyPairKind, - pub(crate) alg: &'static SignatureAlgorithm, + 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") - .field("kind", &self.kind) - .field("alg", &self.alg) + .field("alg", &self.algorithm()) .field("serialized_der", &"[secret key elided]") .finish() } } -#[cfg(feature = "crypto")] impl KeyPair { - /// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair - #[cfg(feature = "crypto")] - pub fn generate() -> Result { - Self::generate_for(&PKCS_ECDSA_P256_SHA256) + /// Construct a key pair from a provider-specific signing key and its PKCS#8 DER encoding. + /// + /// 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, + serialized_der: Vec, + ) -> Self { + Self { + signing_key, + serialized_der, + } + } + + /// 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 /// /// 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 + /// 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, only `aws-lc-rs` supports RSA key generation. - #[cfg(feature = "crypto")] - 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), - } + /// 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, + provider: &dyn CryptoProvider, + ) -> Result { + provider.generate(alg, None) } /// 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"))] + /// + /// It depends on the selected provider whether RSA key generation is available. pub fn generate_rsa_for( alg: &'static SignatureAlgorithm, key_size: RsaKeySize, + provider: &dyn CryptoProvider, ) -> 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), - } - } - - #[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] - fn generate_rsa_inner( - alg: &'static SignatureAlgorithm, - sign_alg: &'static dyn RsaEncoding, - key_size: KeySize, - ) -> 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.generate(alg, Some(key_size)) } /// 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. + /// The accepted private-key encodings depend on the selected provider. /// - /// 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"))] - pub fn from_pem(pem_str: &str) -> Result { + /// 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, provider: &dyn CryptoProvider) -> Result { let private_key = pem::parse(pem_str)._err()?; - Self::try_from(private_key.contents()) + 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 DER formatted key + /// 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; + /// 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. /// - /// 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"))] + /// 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, alg: &'static SignatureAlgorithm, + provider: &dyn 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) + 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`] /// - /// 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 @@ -228,194 +140,91 @@ impl KeyPair { /// /// [`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")] pub fn from_pkcs8_der_and_sign_algo( pkcs8: &PrivatePkcs8KeyDer<'_>, alg: &'static SignatureAlgorithm, + provider: &dyn CryptoProvider, ) -> 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, - }) + provider.load_private_key(PrivateKeyDer::Pkcs8(pkcs8.clone_key()), Some(alg)) } /// 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. + /// The accepted private-key encodings depend on the selected provider. /// - /// 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. + /// 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. /// - /// Same as [from_pem_and_sign_algo](Self::from_pem_and_sign_algo). - #[cfg(all(feature = "pem", feature = "crypto"))] + /// 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, alg: &'static SignatureAlgorithm, + provider: &dyn 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, + 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. + /// 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`. + /// 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 have already a byte slice, just calling `try_into()` will convert it to a [`PrivateKeyDer`]. + /// 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 - #[cfg(feature = "crypto")] pub fn from_der_and_sign_algo( key: &PrivateKeyDer<'_>, alg: &'static SignatureAlgorithm, + provider: &dyn CryptoProvider, ) -> 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, - }) - } + provider.load_private_key(key.clone_key(), Some(alg)) } - /// Get the raw public key of this key pair + /// Obtains the key pair from a DER formatted key using `provider`. /// - /// The key is in raw format, as how [`KeyPair::public_key()`][public_key] - /// would output, and how [`UnparsedPublicKey::verify()`][verify] - /// would accept. + /// The provider determines the correct [`SignatureAlgorithm`] for the key. + 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 /// - /// [public_key]: crate::ring_like::signature::KeyPair::public_key() - /// [verify]: crate::ring_like::signature::UnparsedPublicKey::verify() + /// 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 + /// [`CryptoProvider::verify`]. pub fn public_key_raw(&self) -> &[u8] { self.der_bytes() } /// 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 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 @@ -448,167 +257,28 @@ 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 - } -} - -#[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)?; - - key.try_into() - } -} - -#[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)?; - - key.try_into() - } -} - -#[cfg(feature = "crypto")] -impl TryFrom<&PrivatePkcs8KeyDer<'_>> for KeyPair { - type Error = Error; - - fn try_from(key: &PrivatePkcs8KeyDer) -> Result { - key.secret_pkcs8_der().try_into() - } -} - -#[cfg(feature = "crypto")] -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(), - }) + self.signing_key.algorithm() } } -#[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)) @@ -616,7 +286,6 @@ impl From for PrivateKeyDer<'static> { } /// The key size used for RSA key generation -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum RsaKeySize { @@ -662,20 +331,6 @@ pub trait SigningKey: PublicKeyData { 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())) - } -} - -#[cfg(feature = "crypto")] -impl ExternalError for Result { - fn _err(self) -> Result { - self.map_err(|_| Error::RingUnspecified) - } -} - #[cfg(feature = "pem")] impl ExternalError for Result { fn _err(self) -> Result { @@ -778,11 +433,9 @@ pub(crate) fn serialize_public_key_der(key: &(impl PublicKeyData + ?Sized), writ }) } -#[cfg(all(test, feature = "crypto"))] +#[cfg(all(test, 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,12 +444,12 @@ 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"); + let kp = KeyPair::generate_for(alg, crate::test_provider()).expect("keygen"); let pem = kp.public_key_pem(); let der = kp.subject_public_key_info(); @@ -810,11 +463,10 @@ 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, 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 83816182..5d4dd3ed 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -15,15 +15,20 @@ a key pair to call [`CertificateParams::signed_by()`] or [`CertificateParams::se ``` use rcgen::{generate_simple_self_signed, CertifiedKey}; +# #[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(feature = "ring"))] +# fn main() {} ```"## )] #![forbid(unsafe_code)] @@ -49,18 +54,13 @@ pub use crl::{ CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams, }; +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(all(feature = "crypto", feature = "aws_lc_rs"))] -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; -#[cfg(feature = "crypto")] -use ring_like::digest; pub use sign_algo::algo::*; pub use sign_algo::SignatureAlgorithm; use time::{OffsetDateTime, Time}; @@ -72,14 +72,22 @@ use crate::string::{BmpString, Ia5String, PrintableString, TeletexString, Univer mod certificate; mod crl; +pub mod crypto; mod csr; mod error; mod key_pair; mod oid; -mod ring_like; mod sign_algo; pub mod string; +#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs", feature = "fips")))] +pub(crate) fn test_provider() -> &'static dyn CryptoProvider { + #[cfg(any(feature = "aws_lc_rs", feature = "fips"))] + return crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "ring", not(any(feature = "aws_lc_rs", feature = "fips"))))] + 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`." @@ -103,7 +111,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##" @@ -111,25 +118,31 @@ and key pair as output. ``` use rcgen::{generate_simple_self_signed, CertifiedKey}; +# #[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(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)?; + let signing_key = KeyPair::generate(provider)?; + let cert = CertificateParams::new(subject_alt_names)?.self_signed(&signing_key, provider)?; Ok(CertifiedKey { cert, signing_key }) } @@ -142,9 +155,13 @@ 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 { + pub fn self_signed( + params: CertificateParams, + signing_key: S, + provider: &dyn CryptoProvider, + ) -> Result { Ok(Self { - certificate: params.self_signed(&signing_key)?, + certificate: params.self_signed(&signing_key, provider)?, issuer: Issuer::new(params, signing_key), }) } @@ -154,9 +171,10 @@ impl<'a, S: SigningKey> CertifiedIssuer<'a, S> { params: CertificateParams, signing_key: S, issuer: &Issuer<'_, impl SigningKey>, + provider: &dyn CryptoProvider, ) -> Result { Ok(Self { - certificate: params.signed_by(&signing_key, issuer)?, + certificate: params.signed_by(&signing_key, issuer, provider)?, issuer: Issuer::new(params, signing_key), }) } @@ -315,7 +333,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", feature = "fips") + ))] fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result, Error> { let sans = x509 .subject_alternative_name() @@ -675,13 +697,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), @@ -701,12 +720,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, }) } @@ -717,25 +731,19 @@ 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(); - }, + pub(crate) fn derive( + &self, + provider: &dyn CryptoProvider, + subject_public_key_info: impl AsRef<[u8]>, + ) -> Vec { + let algorithm = match self { + Self::Sha256 => HashAlgorithm::Sha256, + Self::Sha384 => HashAlgorithm::Sha384, + Self::Sha512 => HashAlgorithm::Sha512, + Self::PreSpecified(b) => return b.to_vec(), }; - #[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.hash(algorithm, subject_public_key_info.as_ref()); + digest.as_ref()[0..20].to_vec() } } diff --git a/rcgen/src/oid.rs b/rcgen/src/oid.rs index d61e858f..7851a77e 100644 --- a/rcgen/src/oid.rs +++ b/rcgen/src/oid.rs @@ -21,8 +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 only supported with the `aws_lc_rs` feature -#[cfg(feature = "aws_lc_rs")] +/// 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/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..e877c533 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(feature = "aws_lc_rs")] + &PKCS_ML_DSA_44, + #[cfg(feature = "aws_lc_rs")] + &PKCS_ML_DSA_65, + #[cfg(feature = "aws_lc_rs")] + &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")] + /// 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], - #[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")] + /// 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], - #[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")] + /// 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], - #[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..dcea99d4 --- /dev/null +++ b/rcgen/tests/custom_provider.rs @@ -0,0 +1,154 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; +use rcgen::crypto::{CryptoProvider, HashAlgorithm, HashOutput}; +use rcgen::{ + BasicConstraints, CertificateParams, CertificateRevocationListParams, Error, IsCa, Issuer, + KeyIdMethod, KeyPair, PublicKeyData, RsaKeySize, SerialNumber, SignatureAlgorithm, SigningKey, + 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); + +#[derive(Debug)] +struct TestBackend; + +static TEST_BACKEND: TestBackend = TestBackend; + +fn provider() -> &'static dyn CryptoProvider { + &TEST_BACKEND +} + +impl CryptoProvider for TestBackend { + fn hash(&self, algorithm: HashAlgorithm, input: &[u8]) -> HashOutput { + assert!(!input.is_empty()); + DIGESTS.fetch_add(1, Ordering::Relaxed); + HashOutput::new(&vec![0x42; algorithm.output_len()]) + } + + fn generate( + &self, + algorithm: &'static SignatureAlgorithm, + key_size: Option, + ) -> Result { + 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( + &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(), + )) + } + + fn verify( + &self, + message: &[u8], + signature: &[u8], + public_key: &[u8], + algorithm: &'static SignatureAlgorithm, + ) -> 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() { + let custom_provider = provider(); + let key = KeyPair::generate_for(&PKCS_ED25519, custom_provider).unwrap(); + assert_eq!(GENERATIONS.load(Ordering::Relaxed), 1); + assert_eq!( + 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(&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(&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(&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(request.der(), custom_provider) + .unwrap(); + assert_eq!(parsed.public_key.algorithm(), &PKCS_ED25519); + assert_eq!(VERIFICATIONS.load(Ordering::Relaxed), 1); + } +} diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index 28f7c300..b47184b3 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -13,8 +13,8 @@ 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"] -fips = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/fips"] +aws_lc_rs_unstable = ["aws_lc_rs", "rcgen/aws_lc_rs_unstable"] +fips = ["dep:aws-lc-rs", "rcgen/fips", "aws-lc-rs/fips"] ring = ["dep:ring", "rcgen/ring"] [dependencies] @@ -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] diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 3b3625d2..f294fa29 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(any(feature = "aws_lc_rs", feature = "fips"))] + return rcgen::crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "ring", not(any(feature = "aws_lc_rs", feature = "fips"))))] + 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 8e6b1bcf..c8009667 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -5,8 +5,9 @@ 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"] +aws_lc_rs_unstable = ["aws_lc_rs", "rcgen/aws_lc_rs_unstable"] fips = ["rcgen/fips"] pem = ["dep:pem", "rcgen/pem"] ring = ["rcgen/ring"] @@ -15,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", features = ["pem", "x509-parser"] } +rcgen = { path = "../rcgen", default-features = false, features = ["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..de940450 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(any(feature = "aws_lc_rs", feature = "fips"))] + return rcgen::crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "ring", not(any(feature = "aws_lc_rs", feature = "fips"))))] + 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());