From ceefe478dfe8a6c14fa4ad42f2fb4d814d429392 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 3 Sep 2025 09:34:52 +0200 Subject: [PATCH 01/11] cli: avoid duplication key generation logic --- rustls-cert-gen/src/cert.rs | 58 ++++--------------------------------- 1 file changed, 6 insertions(+), 52 deletions(-) diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 352623ef..25ddfac2 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -6,11 +6,6 @@ use rcgen::{ DnValue::PrintableString, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, }; -#[cfg(feature = "aws_lc_rs")] -use aws_lc_rs as ring_like; -#[cfg(all(feature = "ring", not(feature = "aws_lc_rs")))] -use ring as ring_like; - #[derive(Debug, Clone)] /// PEM serialized Certificate and PEM serialized corresponding private key pub struct PemCertifiedKey { @@ -226,55 +221,14 @@ pub enum KeyPairAlgorithm { impl KeyPairAlgorithm { /// Return an `rcgen::KeyPair` for the given varient - fn to_key_pair(self) -> Result { + fn to_key_pair(self) -> Result { match self { - KeyPairAlgorithm::Rsa => rcgen::KeyPair::generate_for(&rcgen::PKCS_RSA_SHA256), - KeyPairAlgorithm::Ed25519 => { - use ring_like::signature::Ed25519KeyPair; - - let rng = ring_like::rand::SystemRandom::new(); - let alg = &rcgen::PKCS_ED25519; - let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng) - .map_err(|_| rcgen::Error::RingUnspecified)?; - - rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) - }, - KeyPairAlgorithm::EcdsaP256 => { - use ring_like::signature::EcdsaKeyPair; - use ring_like::signature::ECDSA_P256_SHA256_ASN1_SIGNING; - - let rng = ring_like::rand::SystemRandom::new(); - let alg = &rcgen::PKCS_ECDSA_P256_SHA256; - let pkcs8_bytes = - EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, &rng) - .map_err(|_| rcgen::Error::RingUnspecified)?; - rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) - }, - KeyPairAlgorithm::EcdsaP384 => { - use ring_like::signature::EcdsaKeyPair; - use ring_like::signature::ECDSA_P384_SHA384_ASN1_SIGNING; - - let rng = ring_like::rand::SystemRandom::new(); - let alg = &rcgen::PKCS_ECDSA_P384_SHA384; - let pkcs8_bytes = - EcdsaKeyPair::generate_pkcs8(&ECDSA_P384_SHA384_ASN1_SIGNING, &rng) - .map_err(|_| rcgen::Error::RingUnspecified)?; - - rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) - }, + KeyPairAlgorithm::Rsa => KeyPair::generate_for(&rcgen::PKCS_RSA_SHA256), + KeyPairAlgorithm::Ed25519 => KeyPair::generate_for(&rcgen::PKCS_ED25519), + KeyPairAlgorithm::EcdsaP256 => KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256), + KeyPairAlgorithm::EcdsaP384 => KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384), #[cfg(feature = "aws_lc_rs")] - KeyPairAlgorithm::EcdsaP521 => { - use ring_like::signature::EcdsaKeyPair; - use ring_like::signature::ECDSA_P521_SHA512_ASN1_SIGNING; - - let rng = ring_like::rand::SystemRandom::new(); - let alg = &rcgen::PKCS_ECDSA_P521_SHA512; - let pkcs8_bytes = - EcdsaKeyPair::generate_pkcs8(&ECDSA_P521_SHA512_ASN1_SIGNING, &rng) - .map_err(|_| rcgen::Error::RingUnspecified)?; - - rcgen::KeyPair::from_pkcs8_der_and_sign_algo(&pkcs8_bytes.as_ref().into(), alg) - }, + KeyPairAlgorithm::EcdsaP521 => KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512), } } } From bcd87a649f0f31a292e909b100147318b01c303e Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 3 Sep 2025 09:38:33 +0200 Subject: [PATCH 02/11] cli: simplify KeyPair generation API --- rustls-cert-gen/src/cert.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 25ddfac2..034962a5 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -4,6 +4,7 @@ use bpaf::Bpaf; use rcgen::{ BasicConstraints, Certificate, CertificateParams, CertifiedIssuer, DistinguishedName, DnType, DnValue::PrintableString, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, + SignatureAlgorithm, }; #[derive(Debug, Clone)] @@ -108,7 +109,7 @@ impl CaBuilder { } /// build `Ca` Certificate. pub fn build(self) -> Result { - let key_pair = self.alg.to_key_pair()?; + let key_pair = KeyPair::generate_for(self.alg.into())?; Ok(Ca { issuer: CertifiedIssuer::self_signed(self.params, key_pair)?, }) @@ -201,7 +202,7 @@ impl EndEntityBuilder { } /// build `EndEntity` Certificate. pub fn build(self, issuer: &Ca) -> Result { - let key_pair = self.alg.to_key_pair()?; + let key_pair = KeyPair::generate_for(self.alg.into())?; let cert = self.params.signed_by(&key_pair, &issuer.issuer)?; Ok(EndEntity { cert, key_pair }) } @@ -219,16 +220,15 @@ pub enum KeyPairAlgorithm { EcdsaP521, } -impl KeyPairAlgorithm { - /// Return an `rcgen::KeyPair` for the given varient - fn to_key_pair(self) -> Result { - match self { - KeyPairAlgorithm::Rsa => KeyPair::generate_for(&rcgen::PKCS_RSA_SHA256), - KeyPairAlgorithm::Ed25519 => KeyPair::generate_for(&rcgen::PKCS_ED25519), - KeyPairAlgorithm::EcdsaP256 => KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256), - KeyPairAlgorithm::EcdsaP384 => KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384), +impl From for &'static SignatureAlgorithm { + fn from(alg: KeyPairAlgorithm) -> Self { + match alg { + KeyPairAlgorithm::Rsa => &rcgen::PKCS_RSA_SHA256, + KeyPairAlgorithm::Ed25519 => &rcgen::PKCS_ED25519, + KeyPairAlgorithm::EcdsaP256 => &rcgen::PKCS_ECDSA_P256_SHA256, + KeyPairAlgorithm::EcdsaP384 => &rcgen::PKCS_ECDSA_P384_SHA384, #[cfg(feature = "aws_lc_rs")] - KeyPairAlgorithm::EcdsaP521 => KeyPair::generate_for(&rcgen::PKCS_ECDSA_P521_SHA512), + KeyPairAlgorithm::EcdsaP521 => &rcgen::PKCS_ECDSA_P521_SHA512, } } } @@ -432,14 +432,14 @@ mod tests { #[test] fn key_pair_algorithm_to_keypair() -> anyhow::Result<()> { - let keypair = KeyPairAlgorithm::Ed25519.to_key_pair()?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::Ed25519.into())?; assert_eq!(format!("{:?}", keypair.algorithm()), "PKCS_ED25519"); - let keypair = KeyPairAlgorithm::EcdsaP256.to_key_pair()?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP256.into())?; assert_eq!( format!("{:?}", keypair.algorithm()), "PKCS_ECDSA_P256_SHA256" ); - let keypair = KeyPairAlgorithm::EcdsaP384.to_key_pair()?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP384.into())?; assert_eq!( format!("{:?}", keypair.algorithm()), "PKCS_ECDSA_P384_SHA384" @@ -447,7 +447,7 @@ mod tests { #[cfg(feature = "aws_lc_rs")] { - let keypair = KeyPairAlgorithm::EcdsaP521.to_key_pair()?; + let keypair = KeyPair::generate_for(KeyPairAlgorithm::EcdsaP521.into())?; assert_eq!( format!("{:?}", keypair.algorithm()), "PKCS_ECDSA_P521_SHA512" From 11baa2e792f0cb5b9e3a35c78343ef9915a2407d Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 3 Sep 2025 09:41:50 +0200 Subject: [PATCH 03/11] cli: move PemCertifiedKey type down --- rustls-cert-gen/src/cert.rs | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 034962a5..e35860ee 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -7,30 +7,6 @@ use rcgen::{ SignatureAlgorithm, }; -#[derive(Debug, Clone)] -/// PEM serialized Certificate and PEM serialized corresponding private key -pub struct PemCertifiedKey { - pub cert_pem: String, - pub private_key_pem: String, -} - -impl PemCertifiedKey { - pub fn write(&self, dir: &Path, name: &str) -> Result<(), io::Error> { - use std::io::Write; - std::fs::create_dir_all(dir)?; - - let key_path = dir.join(format!("{name}.key.pem")); - let mut key_out = File::create(key_path)?; - write!(key_out, "{}", &self.private_key_pem)?; - - let cert_path = dir.join(format!("{name}.pem")); - let mut cert_out = File::create(cert_path)?; - write!(cert_out, "{}", &self.cert_pem)?; - - Ok(()) - } -} - /// Builder to configure TLS [CertificateParams] to be finalized /// into either a [Ca] or an [EndEntity]. #[derive(Clone, Debug, Default)] @@ -246,6 +222,30 @@ impl fmt::Display for KeyPairAlgorithm { } } +#[derive(Debug, Clone)] +/// PEM serialized Certificate and PEM serialized corresponding private key +pub struct PemCertifiedKey { + pub cert_pem: String, + pub private_key_pem: String, +} + +impl PemCertifiedKey { + pub fn write(&self, dir: &Path, name: &str) -> Result<(), io::Error> { + use std::io::Write; + std::fs::create_dir_all(dir)?; + + let key_path = dir.join(format!("{name}.key.pem")); + let mut key_out = File::create(key_path)?; + write!(key_out, "{}", &self.private_key_pem)?; + + let cert_path = dir.join(format!("{name}.pem")); + let mut cert_out = File::create(cert_path)?; + write!(cert_out, "{}", &self.cert_pem)?; + + Ok(()) + } +} + #[cfg(test)] mod tests { use x509_parser::prelude::{FromDer, X509Certificate}; From 84db67458453499fdc7f38144784a06975d022a2 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 3 Sep 2025 09:51:31 +0200 Subject: [PATCH 04/11] cli: implement explicit FromStr impl for KeyPairAlgorithm --- rustls-cert-gen/src/cert.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index e35860ee..88444782 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -1,4 +1,4 @@ -use std::{fmt, fs::File, io, path::Path}; +use std::{fmt, fs::File, io, path::Path, str::FromStr}; use bpaf::Bpaf; use rcgen::{ @@ -222,6 +222,22 @@ impl fmt::Display for KeyPairAlgorithm { } } +impl FromStr for KeyPairAlgorithm { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "rsa" => Ok(Self::Rsa), + "ed25519" => Ok(Self::Ed25519), + "ecdsa-p256" => Ok(Self::EcdsaP256), + "ecdsa-p384" => Ok(Self::EcdsaP384), + #[cfg(feature = "aws_lc_rs")] + "ecdsa-p521" => Ok(Self::EcdsaP521), + _ => Err(anyhow::anyhow!("unknown key algorithm: {s}")), + } + } +} + #[derive(Debug, Clone)] /// PEM serialized Certificate and PEM serialized corresponding private key pub struct PemCertifiedKey { From e590d32b978e76ebc0f1b0b10883ce0cb492389e Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 3 Sep 2025 10:19:25 +0200 Subject: [PATCH 05/11] Remove ring-specific documentation links --- rcgen/src/key_pair.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 8226c0d7..2c6ad727 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -52,12 +52,6 @@ impl fmt::Debug for KeyPairKind { } /// A key pair used to sign certificates and CSRs -/// -/// Note that ring, the underlying library to handle RSA keys -/// requires them to be in a special format, meaning that -/// `openssl genrsa` doesn't work. See ring's [documentation](ring::signature::RsaKeyPair::from_pkcs8) -/// for how to generate RSA keys in the wanted format -/// and conversion between the formats. #[cfg(feature = "crypto")] pub struct KeyPair { pub(crate) kind: KeyPairKind, @@ -378,9 +372,12 @@ impl KeyPair { /// Get the raw public key of this key pair /// - /// The key is in raw format, as how [`ring::signature::KeyPair::public_key`] - /// would output, and how [`ring::signature::UnparsedPublicKey::verify`] + /// The key is in raw format, as how [`KeyPair::public_key()`][public_key] + /// would output, and how [`UnparsedPublicKey::verify()`][verify] /// would accept. + /// + /// [public_key]: crate::ring_like::signature::KeyPair::public_key() + /// [verify]: crate::ring_like::signature::UnparsedPublicKey::verify() pub fn public_key_raw(&self) -> &[u8] { self.der_bytes() } From 2c6166f1b4efa0da258a19d2233d457872cd7fb0 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Thu, 4 Sep 2025 13:45:19 +0200 Subject: [PATCH 06/11] Move all dependency specifications to the workspace --- Cargo.toml | 9 +++++++++ rcgen/Cargo.toml | 14 +++++++------- rustls-cert-gen/Cargo.toml | 6 +++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3bf90ea..2a3acf0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,20 @@ members = ["rcgen", "rustls-cert-gen"] resolver = "2" [workspace.dependencies] +anyhow = "1.0.75" +assert_fs = "1.0.13" aws-lc-rs = { version = "1.6.0", default-features = false } +botan = { version = "0.12", features = ["vendored"] } +bpaf = { version = "0.9.5", features = ["derive"] } +openssl = "0.10" pem = "3.0.2" pki-types = { package = "rustls-pki-types", version = "1.4.1" } ring = "0.17" +rustls-webpki = { version = "0.103", features = ["ring", "std"] } +time = { version = "0.3.6", default-features = false } x509-parser = "0.18" +yasna = { version = "0.5.2", features = ["time", "std"] } +zeroize = { version = "1.2" } [workspace.package] license = "MIT OR Apache-2.0" diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 85f499d9..27edb68d 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -24,13 +24,13 @@ required-features = ["crypto", "pem"] [dependencies] aws-lc-rs = { workspace = true, optional = true } -yasna = { version = "0.5.2", features = ["time", "std"] } +yasna = { workspace = true } ring = { workspace = true, optional = true } pem = { workspace = true, optional = true } pki-types = { workspace = true } -time = { version = "0.3.6", default-features = false } +time = { workspace = true } x509-parser = { workspace = true, features = ["verify"], optional = true } -zeroize = { version = "1.2", optional = true } +zeroize = { workspace = true, optional = true } [features] default = ["crypto", "pem", "ring"] @@ -50,11 +50,11 @@ allowed_external_types = [ ] [dev-dependencies] -pki-types = { package = "rustls-pki-types", version = "1" } +pki-types = { workspace = true } x509-parser = { workspace = true, features = ["verify"] } -rustls-webpki = { version = "0.103", features = ["ring", "std"] } -botan = { version = "0.12", features = ["vendored"] } +rustls-webpki = { workspace = true } +botan = { workspace = true } ring = { workspace = true } [target."cfg(unix)".dev-dependencies] -openssl = "0.10" +openssl = { workspace = true } diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index a9e9e18b..f38a11f7 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -18,13 +18,13 @@ fips = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/fips"] [dependencies] rcgen = { version = "0.14.2", path = "../rcgen", default-features = false, features = ["pem"] } -bpaf = { version = "0.9.5", features = ["derive"] } +bpaf = { workspace = true } pem = { workspace = true } pki-types = { workspace = true } ring = { workspace = true, optional = true } -anyhow = "1.0.75" +anyhow = { workspace = true } aws-lc-rs = { workspace = true, optional = true } [dev-dependencies] -assert_fs = "1.0.13" +assert_fs = { workspace = true } x509-parser = { workspace = true, features = ["verify"] } From 9252526bdaa72a76b404e28068e03eedb329b668 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Thu, 4 Sep 2025 13:46:27 +0200 Subject: [PATCH 07/11] Use more conventional order in Cargo.toml files --- Cargo.lock | 4 ++-- Cargo.toml | 18 ++++++++-------- rcgen/Cargo.toml | 54 ++++++++++++++++++++++++------------------------ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d402911..76c231d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -851,9 +851,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", diff --git a/Cargo.toml b/Cargo.toml index 2a3acf0e..cf9812fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,15 @@ members = ["rcgen", "rustls-cert-gen"] resolver = "2" +[workspace.package] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.71" +readme = "README.md" +description = "Rust X.509 certificate generator" +repository = "https://github.com/rustls/rcgen" +keywords = ["mkcert", "ca", "certificate"] + [workspace.dependencies] anyhow = "1.0.75" assert_fs = "1.0.13" @@ -17,12 +26,3 @@ time = { version = "0.3.6", default-features = false } x509-parser = "0.18" yasna = { version = "0.5.2", features = ["time", "std"] } zeroize = { version = "1.2" } - -[workspace.package] -license = "MIT OR Apache-2.0" -edition = "2021" -rust-version = "1.71" -readme = "README.md" -description = "Rust X.509 certificate generator" -repository = "https://github.com/rustls/rcgen" -keywords = ["mkcert", "ca", "certificate"] diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 27edb68d..68f1b869 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -10,17 +10,12 @@ edition.workspace = true rust-version.workspace = true keywords.workspace = true -[[example]] -name = "rsa-irc-openssl" -required-features = ["pem"] - -[[example]] -name = "sign-leaf-with-ca" -required-features = ["pem", "x509-parser"] - -[[example]] -name = "simple" -required-features = ["crypto", "pem"] +[features] +default = ["crypto", "pem", "ring"] +crypto = [] +aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] +ring = ["crypto", "dep:ring"] +fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] [dependencies] aws-lc-rs = { workspace = true, optional = true } @@ -32,12 +27,27 @@ time = { workspace = true } x509-parser = { workspace = true, features = ["verify"], optional = true } zeroize = { workspace = true, optional = true } -[features] -default = ["crypto", "pem", "ring"] -crypto = [] -aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] -ring = ["crypto", "dep:ring"] -fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] +[dev-dependencies] +pki-types = { workspace = true } +x509-parser = { workspace = true, features = ["verify"] } +rustls-webpki = { workspace = true } +botan = { workspace = true } +ring = { workspace = true } + +[target."cfg(unix)".dev-dependencies] +openssl = { workspace = true } + +[[example]] +name = "rsa-irc-openssl" +required-features = ["pem"] + +[[example]] +name = "sign-leaf-with-ca" +required-features = ["pem", "x509-parser"] + +[[example]] +name = "simple" +required-features = ["crypto", "pem"] [package.metadata.docs.rs] features = ["x509-parser"] @@ -48,13 +58,3 @@ allowed_external_types = [ "zeroize::Zeroize", "rustls_pki_types::*", ] - -[dev-dependencies] -pki-types = { workspace = true } -x509-parser = { workspace = true, features = ["verify"] } -rustls-webpki = { workspace = true } -botan = { workspace = true } -ring = { workspace = true } - -[target."cfg(unix)".dev-dependencies] -openssl = { workspace = true } From 4ff17ff81c5957e00cf40af8edc918245aaee6c1 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Thu, 4 Sep 2025 13:53:23 +0200 Subject: [PATCH 08/11] Alphabetize features and dependencies --- rcgen/Cargo.toml | 14 +++++++------- rustls-cert-gen/Cargo.toml | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 68f1b869..0372f84b 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -12,27 +12,27 @@ keywords.workspace = true [features] default = ["crypto", "pem", "ring"] -crypto = [] aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] -ring = ["crypto", "dep:ring"] fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] +crypto = [] +ring = ["crypto", "dep:ring"] [dependencies] aws-lc-rs = { workspace = true, optional = true } -yasna = { workspace = true } -ring = { workspace = true, optional = true } pem = { workspace = true, optional = true } pki-types = { workspace = true } +ring = { workspace = true, optional = true } time = { workspace = true } x509-parser = { workspace = true, features = ["verify"], optional = true } +yasna = { workspace = true } zeroize = { workspace = true, optional = true } [dev-dependencies] -pki-types = { workspace = true } -x509-parser = { workspace = true, features = ["verify"] } -rustls-webpki = { workspace = true } botan = { workspace = true } +pki-types = { workspace = true } ring = { workspace = true } +rustls-webpki = { workspace = true } +x509-parser = { workspace = true, features = ["verify"] } [target."cfg(unix)".dev-dependencies] openssl = { workspace = true } diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index f38a11f7..f8a6640f 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -13,17 +13,17 @@ keywords.workspace = true [features] default = ["ring"] aws_lc_rs = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/aws-lc-sys"] -ring = ["dep:ring", "rcgen/ring"] fips = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/fips"] +ring = ["dep:ring", "rcgen/ring"] [dependencies] -rcgen = { version = "0.14.2", path = "../rcgen", default-features = false, features = ["pem"] } +anyhow = { workspace = true } +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"] } ring = { workspace = true, optional = true } -anyhow = { workspace = true } -aws-lc-rs = { workspace = true, optional = true } [dev-dependencies] assert_fs = { workspace = true } From 80dc94811e0e72dace2bf24d1f2a758e2305693d Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Thu, 4 Sep 2025 14:19:08 +0200 Subject: [PATCH 09/11] Move verification tests into a separate crate --- Cargo.lock | 19 ++++++++++-- Cargo.toml | 2 +- rcgen/Cargo.toml | 7 ----- verify-tests/Cargo.toml | 31 +++++++++++++++++++ .../tests/util.rs => verify-tests/src/lib.rs | 6 ---- {rcgen => verify-tests}/tests/botan.rs | 4 +-- {rcgen => verify-tests}/tests/generic.rs | 14 +++------ {rcgen => verify-tests}/tests/openssl.rs | 7 ++--- {rcgen => verify-tests}/tests/webpki.rs | 5 ++- 9 files changed, 60 insertions(+), 35 deletions(-) create mode 100644 verify-tests/Cargo.toml rename rcgen/tests/util.rs => verify-tests/src/lib.rs (97%) rename {rcgen => verify-tests}/tests/botan.rs (99%) rename {rcgen => verify-tests}/tests/generic.rs (99%) rename {rcgen => verify-tests}/tests/openssl.rs (99%) rename {rcgen => verify-tests}/tests/webpki.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 76c231d4..418c3876 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -729,12 +729,10 @@ name = "rcgen" version = "0.14.3" dependencies = [ "aws-lc-rs", - "botan", "openssl", "pem", "ring", "rustls-pki-types", - "rustls-webpki", "time", "x509-parser", "yasna", @@ -1005,6 +1003,23 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "verify-tests" +version = "0.0.1" +dependencies = [ + "aws-lc-rs", + "botan", + "openssl", + "pem", + "rcgen", + "ring", + "rustls-pki-types", + "rustls-webpki", + "time", + "x509-parser", + "yasna", +] + [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index cf9812fc..10f99db5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["rcgen", "rustls-cert-gen"] +members = ["verify-tests", "rcgen", "rustls-cert-gen"] resolver = "2" [workspace.package] diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 0372f84b..ada35238 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -27,13 +27,6 @@ x509-parser = { workspace = true, features = ["verify"], optional = true } yasna = { workspace = true } zeroize = { workspace = true, optional = true } -[dev-dependencies] -botan = { workspace = true } -pki-types = { workspace = true } -ring = { workspace = true } -rustls-webpki = { workspace = true } -x509-parser = { workspace = true, features = ["verify"] } - [target."cfg(unix)".dev-dependencies] openssl = { workspace = true } diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml new file mode 100644 index 00000000..5999d731 --- /dev/null +++ b/verify-tests/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "verify-tests" +version = "0.0.1" +edition = { workspace = true } +publish = false + +[features] +default = [] +aws_lc_rs = ["rcgen/aws_lc_rs"] +fips = ["rcgen/fips"] +pem = ["dep:pem", "rcgen/pem"] +ring = ["rcgen/ring"] +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"] } +ring = { workspace = true } +time = { workspace = true } +x509-parser = { workspace = true, features = ["verify"], optional = true } +yasna = { workspace = true } + +[dev-dependencies] +pki-types = { workspace = true } +rustls-webpki = { workspace = true } +botan = { workspace = true } +ring = { workspace = true } + +[target."cfg(unix)".dev-dependencies] +openssl = { workspace = true } diff --git a/rcgen/tests/util.rs b/verify-tests/src/lib.rs similarity index 97% rename from rcgen/tests/util.rs rename to verify-tests/src/lib.rs index 65a98c78..4c2cc5db 100644 --- a/rcgen/tests/util.rs +++ b/verify-tests/src/lib.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "crypto")] - use time::{Duration, OffsetDateTime}; use rcgen::{BasicConstraints, Certificate, CertificateParams, Issuer, KeyPair}; @@ -13,7 +11,6 @@ use rcgen::{KeyUsagePurpose, RevocationReason, RevokedCertParams, SerialNumber}; // to the test_webpki_25519 test and panicing explicitly. // This is a "v2" key containing the public key as well as the // private one. -#[allow(unused)] pub const ED25519_TEST_KEY_PAIR_PEM_V2: &str = r#" -----BEGIN PRIVATE KEY----- MFMCAQEwBQYDK2VwBCIEIC2pHJYjFHhK8V7mj6BnHWUVMS4CRolUlDdRXKCtguDu @@ -23,7 +20,6 @@ oSMDIQDrvH/x8Nx9untsuc6ET+ce3w7PSuLY8BLWcHdXDGvkQA== // Generated with `openssl genpkey -algorithm ED25519` // A "v1" key as it doesn't contain the public key (which can be // derived from the private one) -#[allow(unused)] pub const ED25519_TEST_KEY_PAIR_PEM_V1: &str = r#" -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIDSat0MacDt2fokpnzuBaXvAQR6RJGS9rgIYOU2mZKld @@ -36,8 +32,6 @@ Generated by: openssl genpkey -algorithm RSA \ -pkeyopt rsa_keygen_pubexp:65537 | \ openssl pkcs8 -topk8 -nocrypt -outform pem */ -#[allow(dead_code)] // Used in some but not all test compilation units. -#[cfg(feature = "pem")] pub const RSA_TEST_KEY_PAIR_PEM: &str = r#" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYjmgyV3/LSizJ diff --git a/rcgen/tests/botan.rs b/verify-tests/tests/botan.rs similarity index 99% rename from rcgen/tests/botan.rs rename to verify-tests/tests/botan.rs index c9c38560..29715732 100644 --- a/rcgen/tests/botan.rs +++ b/verify-tests/tests/botan.rs @@ -1,4 +1,4 @@ -#![cfg(all(feature = "crypto", feature = "x509-parser"))] +#![cfg(feature = "x509-parser")] use time::{Duration, OffsetDateTime}; @@ -7,7 +7,7 @@ use rcgen::{CertificateRevocationListParams, RevocationReason, RevokedCertParams use rcgen::{DnValue, KeyPair}; use rcgen::{KeyUsagePurpose, SerialNumber}; -mod util; +use verify_tests as util; fn default_params() -> (CertificateParams, KeyPair) { let (mut params, key_pair) = util::default_params(); diff --git a/rcgen/tests/generic.rs b/verify-tests/tests/generic.rs similarity index 99% rename from rcgen/tests/generic.rs rename to verify-tests/tests/generic.rs index 5d6d8aac..108d5eaa 100644 --- a/rcgen/tests/generic.rs +++ b/verify-tests/tests/generic.rs @@ -1,7 +1,3 @@ -#![cfg(feature = "crypto")] - -mod util; - #[cfg(feature = "pem")] mod test_key_params_mismatch { use std::collections::hash_map::DefaultHasher; @@ -40,9 +36,8 @@ mod test_key_params_mismatch { #[cfg(feature = "x509-parser")] mod test_x509_custom_ext { - use crate::util; - use rcgen::CustomExtension; + use verify_tests as util; use x509_parser::oid_registry::asn1_rs; use x509_parser::prelude::{ FromDer, ParsedCriAttribute, X509Certificate, X509CertificationRequest, @@ -166,7 +161,7 @@ mod test_csr_custom_attributes { #[cfg(feature = "x509-parser")] mod test_x509_parser_crl { - use crate::util; + use verify_tests as util; use x509_parser::extensions::{DistributionPointName, ParsedExtension}; use x509_parser::num_bigint::BigUint; use x509_parser::prelude::{FromDer, GeneralName, IssuingDistributionPoint, X509Certificate}; @@ -248,7 +243,7 @@ mod test_x509_parser_crl { #[cfg(feature = "x509-parser")] mod test_parse_crl_dps { - use crate::util; + use verify_tests as util; use x509_parser::extensions::{DistributionPointName, ParsedExtension}; #[test] @@ -425,12 +420,11 @@ mod test_csr { #[cfg(feature = "x509-parser")] mod test_subject_alternative_name_criticality { + use verify_tests::default_params; use x509_parser::certificate::X509Certificate; use x509_parser::extensions::X509Extension; use x509_parser::{oid_registry, parse_x509_certificate}; - use crate::util::default_params; - #[test] fn with_subject_sans_not_critical() { let (params, keypair) = default_params(); diff --git a/rcgen/tests/openssl.rs b/verify-tests/tests/openssl.rs similarity index 99% rename from rcgen/tests/openssl.rs rename to verify-tests/tests/openssl.rs index 1b75fae6..4286d200 100644 --- a/rcgen/tests/openssl.rs +++ b/verify-tests/tests/openssl.rs @@ -1,4 +1,4 @@ -#![cfg(all(unix, feature = "pem"))] +#![cfg(unix)] use std::cell::RefCell; use std::io::{Error, ErrorKind, Read, Result as ioResult, Write}; @@ -16,8 +16,7 @@ use rcgen::{ BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa, Issuer, KeyPair, NameConstraints, }; - -mod util; +use verify_tests as util; fn verify_cert_basic(cert: &Certificate) { let cert_pem = cert.pem(); @@ -502,7 +501,7 @@ fn test_openssl_crl_dps_parse() { } #[test] -#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))] +#[cfg(feature = "aws_lc_rs")] fn test_openssl_pkcs1_and_sec1_keys() { use openssl::ec::{EcGroup, EcKey}; use openssl::nid::Nid; diff --git a/rcgen/tests/webpki.rs b/verify-tests/tests/webpki.rs similarity index 99% rename from rcgen/tests/webpki.rs rename to verify-tests/tests/webpki.rs index 27d59759..57fd58af 100644 --- a/rcgen/tests/webpki.rs +++ b/verify-tests/tests/webpki.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "crypto")] - use std::time::Duration as StdDuration; use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime}; @@ -22,7 +20,7 @@ use rcgen::{CertificateRevocationListParams, RevocationReason, RevokedCertParams use rcgen::{CertificateSigningRequestParams, DnValue}; use rcgen::{ExtendedKeyUsagePurpose, KeyUsagePurpose, SerialNumber}; -mod util; +use verify_tests as util; fn sign_msg_ecdsa(key_pair: &KeyPair, msg: &[u8], alg: &'static EcdsaSigningAlgorithm) -> Vec { let pk_der = key_pair.serialize_der(); @@ -52,6 +50,7 @@ fn sign_msg_rsa(key_pair: &KeyPair, msg: &[u8], encoding: &'static dyn RsaEncodi signature } +#[cfg_attr(not(feature = "pem"), allow(unused))] fn check_cert<'a, 'b, S: SigningKey + 'a>( cert_der: &CertificateDer<'_>, cert: &'a Certificate, From be7c7370690c8f35a5b57b86385c78a214cfa202 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 2 Sep 2025 14:09:54 +0200 Subject: [PATCH 10/11] Add unstable support for ML-DSA algorithms --- .github/workflows/ci.yml | 30 +++++++++++++++++----- Cargo.lock | 9 ++++--- Cargo.toml | 2 +- rcgen/Cargo.toml | 1 + rcgen/src/key_pair.rs | 26 +++++++++++++++++++ rcgen/src/oid.rs | 7 +++++ rcgen/src/sign_algo.rs | 36 ++++++++++++++++++++++++++ rustls-cert-gen/Cargo.toml | 1 + rustls-cert-gen/src/cert.rs | 24 +++++++++++++++++ verify-tests/Cargo.toml | 3 ++- verify-tests/tests/webpki.rs | 50 ++++++++++++++++++++++++++++++++++++ 11 files changed, 176 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 273cf359..d9abb155 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,12 +27,15 @@ jobs: with: components: clippy, rustfmt - run: cargo fmt -- --check - - run: cargo clippy --all-features --all-targets + # `fips` and `aws_lc_rs_unstable` cannot be used together, so avoid `--all-features` + - run: cargo clippy --features ring,pem,x509-parser --all-targets # rustls-cert-gen require either aws_lc_rs or ring feature - run: cargo clippy -p rcgen --no-default-features --all-targets - run: cargo clippy --no-default-features --features ring --all-targets + - run: cargo clippy --no-default-features --features aws_lc_rs,pem,x509-parser --all-targets + - run: cargo clippy --no-default-features --features aws_lc_rs_unstable,pem,x509-parser --all-targets - run: cargo clippy --no-default-features --features aws_lc_rs --all-targets - - run: cargo clippy --no-default-features --features aws_lc_rs,pem --all-targets + - run: cargo clippy --no-default-features --features fips,pem,x509-parser --all-targets rustdoc: name: Documentation @@ -49,8 +52,16 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - - name: cargo doc (all features) - run: cargo doc --all-features --document-private-items + - name: cargo doc (ring) + run: cargo doc --features ring,pem,x509-parser --document-private-items + env: + RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }} + - name: cargo doc (aws_lc_rs_unstable) + run: cargo doc --features aws_lc_rs_unstable,pem,x509-parser --document-private-items + env: + RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }} + - name: cargo doc (fips) + run: cargo doc --no-default-features --features fips --document-private-items env: RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }} @@ -70,7 +81,10 @@ jobs: - run: cargo install --locked cargo-check-external-types - name: run cargo-check-external-types for rcgen/ working-directory: rcgen/ - run: cargo check-external-types --all-features + run: cargo check-external-types --features ring,pem,x509-parser + - name: run cargo-check-external-types for rcgen/ + working-directory: rcgen/ + run: cargo check-external-types --features aws_lc_rs_unstable,pem,x509-parser semver: name: Check semver compatibility @@ -94,7 +108,9 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: 1.71.0 - - run: cargo check --locked --lib --all-features + - run: cargo check --locked --lib --features ring,pem,x509-parser + - run: cargo check --locked --lib --features aws_lc_rs_unstable + - run: cargo check --locked --lib --features fips build-windows: runs-on: windows-latest @@ -214,7 +230,7 @@ jobs: with: components: llvm-tools - name: Measure coverage - run: cargo llvm-cov --all-features --lcov --output-path ./lcov.info + run: cargo llvm-cov --features ring,pem,x509-parser --lcov --output-path ./lcov.info - name: Report to codecov.io uses: codecov/codecov-action@v5 with: diff --git a/Cargo.lock b/Cargo.lock index 418c3876..06a506ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.13.1" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" dependencies = [ "bindgen", "cc", @@ -853,6 +853,7 @@ version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", diff --git a/Cargo.toml b/Cargo.toml index 10f99db5..d2eadc39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["mkcert", "ca", "certificate"] [workspace.dependencies] anyhow = "1.0.75" assert_fs = "1.0.13" -aws-lc-rs = { version = "1.6.0", default-features = false } +aws-lc-rs = { version = "1.13.3", default-features = false } botan = { version = "0.12", features = ["vendored"] } bpaf = { version = "0.9.5", features = ["derive"] } openssl = "0.10" diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index ada35238..ffcfb1dc 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -13,6 +13,7 @@ keywords.workspace = true [features] default = ["crypto", "pem", "ring"] aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"] +aws_lc_rs_unstable = ["aws_lc_rs", "aws-lc-rs/unstable"] fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"] crypto = [] ring = ["crypto", "dep:ring"] diff --git a/rcgen/src/key_pair.rs b/rcgen/src/key_pair.rs index 2c6ad727..1df39f46 100644 --- a/rcgen/src/key_pair.rs +++ b/rcgen/src/key_pair.rs @@ -27,6 +27,8 @@ use crate::sign_algo::{algo::*, SignAlgo}; #[cfg(feature = "pem")] use crate::ENCODE_CONFIG; use crate::{sign_algo::SignatureAlgorithm, Error}; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +use aws_lc_rs::unstable::signature::PqdsaKeyPair; /// A key pair variant #[allow(clippy::large_enum_variant)] @@ -36,6 +38,9 @@ pub(crate) enum KeyPairKind { Ec(EcdsaKeyPair), /// A Ed25519 key pair Ed(Ed25519KeyPair), + /// A Pqdsa key pair + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + Pq(PqdsaKeyPair), /// A RSA key pair Rsa(RsaKeyPair, &'static dyn RsaEncoding), } @@ -46,6 +51,8 @@ impl fmt::Debug for KeyPairKind { match self { Self::Ec(key_pair) => write!(f, "{key_pair:?}"), Self::Ed(key_pair) => write!(f, "{key_pair:?}"), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + Self::Pq(key_pair) => write!(f, "{key_pair:?}"), Self::Rsa(key_pair, _) => write!(f, "{key_pair:?}"), } } @@ -111,6 +118,17 @@ impl KeyPair { serialized_der: key_pair_serialized, }) }, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + SignAlgo::PqDsa(sign_alg) => { + let key_pair = PqdsaKeyPair::generate(sign_alg)._err()?; + let key_pair_serialized = key_pair.to_pkcs8()._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: @@ -433,6 +451,12 @@ impl SigningKey for KeyPair { signature.as_ref().to_owned() }, KeyPairKind::Ed(kp) => kp.sign(msg).as_ref().to_owned(), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + 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)]; @@ -450,6 +474,8 @@ impl PublicKeyData for KeyPair { match &self.kind { KeyPairKind::Ec(kp) => kp.public_key().as_ref(), KeyPairKind::Ed(kp) => kp.public_key().as_ref(), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairKind::Pq(kp) => kp.public_key().as_ref(), KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(), } } diff --git a/rcgen/src/oid.rs b/rcgen/src/oid.rs index a757559c..3b1c0eb9 100644 --- a/rcgen/src/oid.rs +++ b/rcgen/src/oid.rs @@ -25,6 +25,13 @@ pub(crate) const EC_SECP_384_R1: &[u64] = &[1, 3, 132, 0, 34]; #[cfg(feature = "aws_lc_rs")] pub(crate) const EC_SECP_521_R1: &[u64] = &[1, 3, 132, 0, 35]; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +pub(crate) const ML_DSA_44: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 3, 17]; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +pub(crate) const ML_DSA_65: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 3, 18]; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +pub(crate) const ML_DSA_87: &[u64] = &[2, 16, 840, 1, 101, 3, 4, 3, 19]; + /// rsaEncryption in [RFC 4055](https://www.rfc-editor.org/rfc/rfc4055#section-6) pub(crate) const RSA_ENCRYPTION: &[u64] = &[1, 2, 840, 113549, 1, 1, 1]; diff --git a/rcgen/src/sign_algo.rs b/rcgen/src/sign_algo.rs index 9fd74767..2894cd5e 100644 --- a/rcgen/src/sign_algo.rs +++ b/rcgen/src/sign_algo.rs @@ -8,12 +8,18 @@ use yasna::Tag; #[cfg(feature = "crypto")] use crate::ring_like::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters, RsaEncoding}; use crate::Error; +#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] +use aws_lc_rs::unstable::signature::{ + PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, +}; #[cfg(feature = "crypto")] #[derive(Clone, Copy, Debug)] pub(crate) enum SignAlgo { EcDsa(&'static EcdsaSigningAlgorithm), EdDsa(&'static EdDSAParameters), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + PqDsa(&'static PqdsaSigningAlgorithm), Rsa(&'static dyn RsaEncoding), } @@ -209,6 +215,36 @@ pub(crate) mod algo { oid_components: &[1, 3, 101, 112], params: SignatureAlgorithmParams::None, }; + + /// ML-DSA-44 signing as per . + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + pub static PKCS_ML_DSA_44: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[ML_DSA_44], + #[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))] + sign_alg: SignAlgo::PqDsa(&ML_DSA_44_SIGNING), + oid_components: ML_DSA_44, + params: SignatureAlgorithmParams::None, + }; + + /// ML-DSA-44 signing as per . + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + pub static PKCS_ML_DSA_65: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[ML_DSA_65], + #[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))] + sign_alg: SignAlgo::PqDsa(&ML_DSA_65_SIGNING), + oid_components: ML_DSA_65, + params: SignatureAlgorithmParams::None, + }; + + /// ML-DSA-44 signing as per . + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + pub static PKCS_ML_DSA_87: SignatureAlgorithm = SignatureAlgorithm { + oids_sign_alg: &[ML_DSA_87], + #[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))] + sign_alg: SignAlgo::PqDsa(&ML_DSA_87_SIGNING), + oid_components: ML_DSA_87, + params: SignatureAlgorithmParams::None, + }; } // Signature algorithm IDs as per https://tools.ietf.org/html/rfc4055 impl SignatureAlgorithm { diff --git a/rustls-cert-gen/Cargo.toml b/rustls-cert-gen/Cargo.toml index f8a6640f..28f7c300 100644 --- a/rustls-cert-gen/Cargo.toml +++ b/rustls-cert-gen/Cargo.toml @@ -13,6 +13,7 @@ keywords.workspace = true [features] default = ["ring"] aws_lc_rs = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/aws-lc-sys"] +aws_lc_rs_unstable = ["rcgen/aws_lc_rs_unstable"] fips = ["dep:aws-lc-rs", "rcgen/aws_lc_rs", "aws-lc-rs/fips"] ring = ["dep:ring", "rcgen/ring"] diff --git a/rustls-cert-gen/src/cert.rs b/rustls-cert-gen/src/cert.rs index 88444782..624c71d5 100644 --- a/rustls-cert-gen/src/cert.rs +++ b/rustls-cert-gen/src/cert.rs @@ -194,6 +194,12 @@ pub enum KeyPairAlgorithm { EcdsaP384, #[cfg(feature = "aws_lc_rs")] EcdsaP521, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + MlDsa44, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + MlDsa65, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + MlDsa87, } impl From for &'static SignatureAlgorithm { @@ -205,6 +211,12 @@ impl From for &'static SignatureAlgorithm { KeyPairAlgorithm::EcdsaP384 => &rcgen::PKCS_ECDSA_P384_SHA384, #[cfg(feature = "aws_lc_rs")] KeyPairAlgorithm::EcdsaP521 => &rcgen::PKCS_ECDSA_P521_SHA512, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairAlgorithm::MlDsa44 => &rcgen::PKCS_ML_DSA_44, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairAlgorithm::MlDsa65 => &rcgen::PKCS_ML_DSA_65, + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairAlgorithm::MlDsa87 => &rcgen::PKCS_ML_DSA_87, } } } @@ -218,6 +230,12 @@ impl fmt::Display for KeyPairAlgorithm { KeyPairAlgorithm::EcdsaP384 => write!(f, "ecdsa-p384"), #[cfg(feature = "aws_lc_rs")] KeyPairAlgorithm::EcdsaP521 => write!(f, "ecdsa-p521"), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairAlgorithm::MlDsa44 => write!(f, "ml-dsa-44"), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairAlgorithm::MlDsa65 => write!(f, "ml-dsa-65"), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + KeyPairAlgorithm::MlDsa87 => write!(f, "ml-dsa-87"), } } } @@ -233,6 +251,12 @@ impl FromStr for KeyPairAlgorithm { "ecdsa-p384" => Ok(Self::EcdsaP384), #[cfg(feature = "aws_lc_rs")] "ecdsa-p521" => Ok(Self::EcdsaP521), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + "ml-dsa-44" => Ok(Self::MlDsa44), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + "ml-dsa-65" => Ok(Self::MlDsa65), + #[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))] + "ml-dsa-87" => Ok(Self::MlDsa87), _ => Err(anyhow::anyhow!("unknown key algorithm: {s}")), } } diff --git a/verify-tests/Cargo.toml b/verify-tests/Cargo.toml index 5999d731..ae2c93f2 100644 --- a/verify-tests/Cargo.toml +++ b/verify-tests/Cargo.toml @@ -7,6 +7,7 @@ publish = false [features] default = [] aws_lc_rs = ["rcgen/aws_lc_rs"] +aws_lc_rs_unstable = ["dep:aws-lc-rs", "rcgen/aws_lc_rs_unstable", "rustls-webpki/aws-lc-rs-unstable"] fips = ["rcgen/fips"] pem = ["dep:pem", "rcgen/pem"] ring = ["rcgen/ring"] @@ -17,13 +18,13 @@ aws-lc-rs = { workspace = true, optional = true } pem = { workspace = true, optional = true } rcgen = { path = "../rcgen", features = ["pem", "x509-parser"] } ring = { workspace = true } +rustls-webpki = { workspace = true } time = { workspace = true } x509-parser = { workspace = true, features = ["verify"], optional = true } yasna = { workspace = true } [dev-dependencies] pki-types = { workspace = true } -rustls-webpki = { workspace = true } botan = { workspace = true } ring = { workspace = true } diff --git a/verify-tests/tests/webpki.rs b/verify-tests/tests/webpki.rs index 57fd58af..89bc7ff5 100644 --- a/verify-tests/tests/webpki.rs +++ b/verify-tests/tests/webpki.rs @@ -1,5 +1,9 @@ use std::time::Duration as StdDuration; +#[cfg(feature = "aws_lc_rs_unstable")] +use aws_lc_rs::unstable::signature::{ + PqdsaKeyPair, PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING, +}; use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime}; use ring::rand::SystemRandom; use ring::signature::{self, EcdsaKeyPair, EcdsaSigningAlgorithm, Ed25519KeyPair, KeyPair as _}; @@ -38,6 +42,15 @@ fn sign_msg_ed25519(key_pair: &KeyPair, msg: &[u8]) -> Vec { signature.as_ref().to_vec() } +#[cfg(feature = "aws_lc_rs_unstable")] +fn sign_msg_pq(key_pair: &KeyPair, msg: &[u8], alg: &'static PqdsaSigningAlgorithm) -> Vec { + let pk_der = key_pair.serialize_der(); + let key_pair = PqdsaKeyPair::from_pkcs8(alg, &pk_der).unwrap(); + let mut sig = vec![0; alg.signature_len()]; + key_pair.sign(msg, &mut sig).unwrap(); + sig.to_owned() +} + #[cfg(feature = "pem")] fn sign_msg_rsa(key_pair: &KeyPair, msg: &[u8], encoding: &'static dyn RsaEncoding) -> Vec { let pk_der = key_pair.serialize_der(); @@ -225,6 +238,43 @@ fn test_webpki_rsa_given() { ); } +#[cfg(all(feature = "pem", feature = "aws_lc_rs_unstable"))] +#[test] +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(); + + // Now verify the certificate. + let sign_fn = |cert, msg| sign_msg_pq(cert, msg, signing_alg); + check_cert(cert.der(), &cert, &key_pair, *webpki_alg, sign_fn); + } +} + +#[cfg(feature = "aws_lc_rs_unstable")] +const ML_DSA_ALGS: &[( + &rcgen::SignatureAlgorithm, + &dyn SignatureVerificationAlgorithm, + &PqdsaSigningAlgorithm, +)] = &[ + ( + &rcgen::PKCS_ML_DSA_44, + webpki::aws_lc_rs::ML_DSA_44, + &ML_DSA_44_SIGNING, + ), + ( + &rcgen::PKCS_ML_DSA_65, + webpki::aws_lc_rs::ML_DSA_65, + &ML_DSA_65_SIGNING, + ), + ( + &rcgen::PKCS_ML_DSA_87, + webpki::aws_lc_rs::ML_DSA_87, + &ML_DSA_87_SIGNING, + ), +]; + #[cfg(feature = "pem")] #[test] fn test_webpki_rsa_combinations_given() { From f8cd3ab49775e64e5ab67259de7f5168b4298523 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Tue, 2 Sep 2025 14:10:21 +0200 Subject: [PATCH 11/11] Bump rcgen to 0.14.4 --- Cargo.lock | 2 +- rcgen/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06a506ff..846db8ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -726,7 +726,7 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rcgen" -version = "0.14.3" +version = "0.14.4" dependencies = [ "aws-lc-rs", "openssl", diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index ffcfb1dc..4095d4ed 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rcgen" -version = "0.14.3" +version = "0.14.4" documentation = "https://docs.rs/rcgen" description.workspace = true repository.workspace = true