From aa51a5ca466e85f382a32b212c551385be1ea248 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Thu, 5 Mar 2026 12:23:38 +0200 Subject: [PATCH] Feature gate algorithms So you can make the crate more lightweight for embedded use. --- Cargo.lock | 2 +- Cargo.toml | 18 +++++++++++------- src/aead.rs | 1 + src/kx.rs | 12 +++++++++++- src/lib.rs | 12 ++++++++++-- src/sign.rs | 39 ++++++++++++++++++++++++++++++++------- src/sign/ecdsa.rs | 2 ++ src/sign/eddsa.rs | 4 ++++ src/verify.rs | 41 ++++++++++++++++++++++++++++++++++------- src/verify/ecdsa.rs | 4 ++++ 10 files changed, 110 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf81148..d22ef42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,7 +684,7 @@ checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" [[package]] name = "rustls-rustcrypto" -version = "0.0.1-alpha" +version = "0.0.2-alpha" dependencies = [ "aead", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 6ff6103..142cd6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,22 +20,22 @@ resolver = "1" # Hack to enable the `custom` feature of `getrandom` [dependencies] aead = { version = "0.5.2", default-features = false } aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "alloc"] } -chacha20poly1305 = { version = "0.10.1", default-features = false } +chacha20poly1305 = { version = "0.10.1", default-features = false, optional = true } crypto-common = { version = "0.1.6", default-features = false } der = { version = "0.7.9", default-features = false } digest = { version = "0.10.7", default-features = false } -ecdsa = { version = "0.16.8", default-features = false, features = ["alloc"] } +ecdsa = { version = "0.16.8", default-features = false, features = ["alloc"], optional = true } ed25519-dalek = { version = "2", default-features = false, features = ["pkcs8"] } hmac = { version = "0.12.1", default-features = false } -p256 = { version = "0.13.2", default-features = false, features = ["pem", "ecdsa", "ecdh"] } -p384 = { version = "0.13.0", default-features = false, features = ["pem", "ecdsa", "ecdh"] } +p256 = { version = "0.13.2", default-features = false, features = ["pem", "ecdsa", "ecdh"], optional = true } +p384 = { version = "0.13.0", default-features = false, features = ["pem", "ecdsa", "ecdh"], optional = true } paste = { version = "1.0.14", default-features = false } pkcs8 = { version = "0.10.2", default-features = false, features = ["pem", "pkcs5"] } pki-types = { package = "rustls-pki-types", version = "1.0.1", default-features = false } rand_core = { version = "0.6.4", default-features = false, features = ["getrandom"] } -rsa = { version = "0.9.2", default-features = false, features = ["sha2"] } +rsa = { version = "0.9.2", default-features = false, features = ["sha2"], optional = true } rustls = { version = "0.23.0", default-features = false } -sec1 = { version = "0.7.3", default-features = false, features = ["pkcs8", "pem"] } +sec1 = { version = "0.7.3", default-features = false, features = ["pkcs8", "pem"], optional = true } sha2 = { version = "0.10.7", default-features = false } signature = { version = "2.1.0", default-features = false } webpki = { package = "rustls-webpki", version = "0.102.0", default-features = false } @@ -45,9 +45,13 @@ x25519-dalek = { version = "2", default-features = false } getrandom = { version = "0.2", features = ["custom"] } # workaround to build on no_std targets [features] -default = ["std", "tls12", "zeroize"] +default = ["std", "tls12", "zeroize", "chacha20poly1305", "p256", "p384", "rsa"] logging = ["rustls/logging"] tls12 = ["rustls/tls12"] +chacha20poly1305 = ["dep:chacha20poly1305"] +p256 = ["dep:p256", "dep:ecdsa", "dep:sec1"] +p384 = ["dep:p384", "dep:ecdsa", "dep:sec1"] +rsa = ["dep:rsa"] # Only enable feature in upstream if there is an overall effect e.g. aead/alloc in-place # zeroize is another typical that can be turned off diff --git a/src/aead.rs b/src/aead.rs index 6ff57b7..1b42417 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -1,6 +1,7 @@ use aead::Buffer; use rustls::crypto::cipher::{BorrowedPayload, PrefixedPayload}; +#[cfg(feature = "chacha20poly1305")] pub mod chacha20; pub mod gcm; diff --git a/src/kx.rs b/src/kx.rs index 66341f2..c66db3f 100644 --- a/src/kx.rs +++ b/src/kx.rs @@ -2,6 +2,7 @@ use alloc::boxed::Box; use crypto::{SharedSecret, SupportedKxGroup}; +#[cfg(any(feature = "p256", feature = "p384"))] use paste::paste; use rustls::crypto; @@ -46,6 +47,7 @@ impl crypto::ActiveKeyExchange for X25519KeyExchange { } } +#[cfg(any(feature = "p256", feature = "p384"))] macro_rules! impl_kx { ($name:ident, $kx_name:ty, $secret:ty, $public_key:ty) => { paste! { @@ -102,7 +104,15 @@ macro_rules! impl_kx { }; } +#[cfg(feature = "p256")] impl_kx! {SecP256R1, rustls::NamedGroup::secp256r1, p256::ecdh::EphemeralSecret, p256::PublicKey} +#[cfg(feature = "p384")] impl_kx! {SecP384R1, rustls::NamedGroup::secp384r1, p384::ecdh::EphemeralSecret, p384::PublicKey} -pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X25519, &SecP256R1, &SecP384R1]; +pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ + &X25519, + #[cfg(feature = "p256")] + &SecP256R1, + #[cfg(feature = "p384")] + &SecP384R1, +]; diff --git a/src/lib.rs b/src/lib.rs index 1b108b8..2d44559 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,7 +126,7 @@ pub const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = aead_alg: &aead::gcm::Tls12Aes256Gcm, }); -#[cfg(feature = "tls12")] +#[cfg(all(feature = "tls12", feature = "chacha20poly1305"))] pub const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { common: CipherSuiteCommon { @@ -144,6 +144,7 @@ pub const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = const TLS_ECDHE_ECDSA_SUITES: &[SupportedCipherSuite] = &[ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "chacha20poly1305")] TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, ]; @@ -175,7 +176,7 @@ pub const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = aead_alg: &aead::gcm::Tls12Aes256Gcm, }); -#[cfg(feature = "tls12")] +#[cfg(all(feature = "tls12", feature = "chacha20poly1305"))] pub const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { common: CipherSuiteCommon { @@ -193,6 +194,7 @@ pub const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = const TLS_ECDHE_RSA_SUITES: &[SupportedCipherSuite] = &[ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "chacha20poly1305")] TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, ]; @@ -233,6 +235,7 @@ pub const TLS13_AES_256_GCM_SHA384: SupportedCipherSuite = const TLS13_AES_SUITES: &[SupportedCipherSuite] = &[TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384]; +#[cfg(feature = "chacha20poly1305")] pub const TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls13(&Tls13CipherSuite { common: CipherSuiteCommon { @@ -245,12 +248,16 @@ pub const TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = quic: None, }); +#[cfg(feature = "chacha20poly1305")] const TLS13_SUITES: &[SupportedCipherSuite] = misc::const_concat_slices!( SupportedCipherSuite, TLS13_AES_SUITES, &[TLS13_CHACHA20_POLY1305_SHA256] ); +#[cfg(not(feature = "chacha20poly1305"))] +const TLS13_SUITES: &[SupportedCipherSuite] = TLS13_AES_SUITES; + static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = misc::const_concat_slices!( SupportedCipherSuite, if cfg!(feature = "tls12") { @@ -266,6 +273,7 @@ mod hash; mod hmac; mod kx; mod misc; +#[cfg(feature = "chacha20poly1305")] pub mod quic; pub mod sign; mod verify; diff --git a/src/sign.rs b/src/sign.rs index e6109f5..9551b35 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -2,15 +2,23 @@ use alloc::{sync::Arc, vec::Vec}; use core::marker::PhantomData; -use self::ecdsa::{EcdsaSigningKeyP256, EcdsaSigningKeyP384}; +#[cfg(feature = "p256")] +use self::ecdsa::EcdsaSigningKeyP256; +#[cfg(feature = "p384")] +use self::ecdsa::EcdsaSigningKeyP384; use self::eddsa::Ed25519SigningKey; +#[cfg(feature = "rsa")] use self::rsa::RsaSigningKey; use pki_types::PrivateKeyDer; use rustls::sign::{Signer, SigningKey}; use rustls::{Error, SignatureScheme}; -use signature::{RandomizedSigner, SignatureEncoding}; +use signature::SignatureEncoding; +#[cfg(any(feature = "p256", feature = "p384", feature = "rsa"))] +use signature::RandomizedSigner; + +#[cfg(any(feature = "p256", feature = "p384", feature = "rsa"))] #[derive(Debug)] pub struct GenericRandomizedSigner where @@ -22,6 +30,7 @@ where scheme: SignatureScheme, } +#[cfg(any(feature = "p256", feature = "p384", feature = "rsa"))] impl Signer for GenericRandomizedSigner where S: SignatureEncoding + Send + Sync + core::fmt::Debug, @@ -73,10 +82,15 @@ where /// /// Returns an error if the key couldn't be decoded. pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result, rustls::Error> { - RsaSigningKey::try_from(der) - .map(|x| Arc::new(x) as _) - .or_else(|_| any_ecdsa_type(der)) - .or_else(|_| any_eddsa_type(der)) + #[cfg(feature = "rsa")] + if let Ok(key) = RsaSigningKey::try_from(der) { + return Ok(Arc::new(key) as _); + } + #[cfg(any(feature = "p256", feature = "p384"))] + if let Ok(key) = any_ecdsa_type(der) { + return Ok(key); + } + any_eddsa_type(der) } /// Extract any supported ECDSA key from the given DER input. @@ -84,10 +98,19 @@ pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result /// # Errors /// /// Returns an error if the key couldn't be decoded. +#[cfg(any(feature = "p256", feature = "p384"))] pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result, rustls::Error> { + #[cfg(feature = "p256")] let p256 = |_| EcdsaSigningKeyP256::try_from(der).map(|x| Arc::new(x) as _); + #[cfg(feature = "p384")] let p384 = |_| EcdsaSigningKeyP384::try_from(der).map(|x| Arc::new(x) as _); - p256(()).or_else(p384) + + #[cfg(all(feature = "p256", feature = "p384"))] + { p256(()).or_else(p384) } + #[cfg(all(feature = "p256", not(feature = "p384")))] + { p256(()) } + #[cfg(all(not(feature = "p256"), feature = "p384"))] + { p384(()) } } /// Extract any supported EDDSA key from the given DER input. @@ -100,6 +123,8 @@ pub fn any_eddsa_type(der: &PrivateKeyDer<'_>) -> Result, ru Ed25519SigningKey::try_from(der).map(|x| Arc::new(x) as _) } +#[cfg(any(feature = "p256", feature = "p384"))] pub mod ecdsa; pub mod eddsa; +#[cfg(feature = "rsa")] pub mod rsa; diff --git a/src/sign/ecdsa.rs b/src/sign/ecdsa.rs index 3f4a2bc..b1e21d0 100644 --- a/src/sign/ecdsa.rs +++ b/src/sign/ecdsa.rs @@ -62,5 +62,7 @@ macro_rules! impl_ecdsa { }; } +#[cfg(feature = "p256")] impl_ecdsa! {P256, SignatureScheme::ECDSA_NISTP256_SHA256, p256::ecdsa::SigningKey, p256::ecdsa::DerSignature} +#[cfg(feature = "p384")] impl_ecdsa! {P384, SignatureScheme::ECDSA_NISTP384_SHA384, p384::ecdsa::SigningKey, p384::ecdsa::DerSignature} diff --git a/src/sign/eddsa.rs b/src/sign/eddsa.rs index e6f0d1a..56fb364 100644 --- a/src/sign/eddsa.rs +++ b/src/sign/eddsa.rs @@ -6,6 +6,7 @@ use pkcs8::DecodePrivateKey; use pki_types::PrivateKeyDer; use rustls::sign::{Signer, SigningKey}; use rustls::{SignatureAlgorithm, SignatureScheme}; +#[cfg(any(feature = "p256", feature = "p384"))] use sec1::DecodeEcPrivateKey; #[derive(Debug)] @@ -23,10 +24,13 @@ impl TryFrom<&PrivateKeyDer<'_>> for Ed25519SigningKey { ed25519_dalek::SigningKey::from_pkcs8_der(der.secret_pkcs8_der()) .map_err(|e| format!("failed to decrypt private key: {e}")) } + #[cfg(any(feature = "p256", feature = "p384"))] PrivateKeyDer::Sec1(sec1) => { ed25519_dalek::SigningKey::from_sec1_der(sec1.secret_sec1_der()) .map_err(|e| format!("failed to decrypt private key: {e}")) } + #[cfg(not(any(feature = "p256", feature = "p384")))] + PrivateKeyDer::Sec1(_) => Err("SEC1 key support requires p256 or p384 feature".into()), PrivateKeyDer::Pkcs1(_) => Err("ED25519 does not support PKCS#1 key".to_string()), _ => Err("not supported".into()), }; diff --git a/src/verify.rs b/src/verify.rs index 2043b64..9cf9edd 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,46 +1,73 @@ use rustls::crypto::WebPkiSupportedAlgorithms; use rustls::SignatureScheme; -use self::ecdsa::{ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256, ECDSA_P384_SHA384}; +#[cfg(any(feature = "p256", feature = "p384"))] +use self::ecdsa::*; use self::eddsa::ED25519; -use self::rsa::{ - RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, RSA_PSS_SHA256, RSA_PSS_SHA384, - RSA_PSS_SHA512, -}; +#[cfg(feature = "rsa")] +use self::rsa::*; pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { all: &[ + #[cfg(feature = "p256")] ECDSA_P256_SHA256, + #[cfg(feature = "p256")] ECDSA_P256_SHA384, + #[cfg(feature = "p384")] ECDSA_P384_SHA256, + #[cfg(feature = "p384")] ECDSA_P384_SHA384, ED25519, + #[cfg(feature = "rsa")] RSA_PKCS1_SHA256, + #[cfg(feature = "rsa")] RSA_PKCS1_SHA384, + #[cfg(feature = "rsa")] RSA_PKCS1_SHA512, + #[cfg(feature = "rsa")] RSA_PSS_SHA256, + #[cfg(feature = "rsa")] RSA_PSS_SHA384, + #[cfg(feature = "rsa")] RSA_PSS_SHA512, ], mapping: &[ + #[cfg(feature = "p384")] ( SignatureScheme::ECDSA_NISTP384_SHA384, - &[ECDSA_P384_SHA384, ECDSA_P256_SHA384], + &[ + ECDSA_P384_SHA384, + #[cfg(feature = "p256")] + ECDSA_P256_SHA384, + ], ), + #[cfg(feature = "p256")] ( SignatureScheme::ECDSA_NISTP256_SHA256, - &[ECDSA_P256_SHA256, ECDSA_P384_SHA256], + &[ + ECDSA_P256_SHA256, + #[cfg(feature = "p384")] + ECDSA_P384_SHA256, + ], ), (SignatureScheme::ED25519, &[ED25519]), + #[cfg(feature = "rsa")] (SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]), + #[cfg(feature = "rsa")] (SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]), + #[cfg(feature = "rsa")] (SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]), + #[cfg(feature = "rsa")] (SignatureScheme::RSA_PSS_SHA256, &[RSA_PSS_SHA256]), + #[cfg(feature = "rsa")] (SignatureScheme::RSA_PSS_SHA384, &[RSA_PSS_SHA384]), + #[cfg(feature = "rsa")] (SignatureScheme::RSA_PSS_SHA512, &[RSA_PSS_SHA512]), ], }; +#[cfg(any(feature = "p256", feature = "p384"))] pub mod ecdsa; pub mod eddsa; +#[cfg(feature = "rsa")] pub mod rsa; diff --git a/src/verify/ecdsa.rs b/src/verify/ecdsa.rs index 669f03d..69bdf3d 100644 --- a/src/verify/ecdsa.rs +++ b/src/verify/ecdsa.rs @@ -48,7 +48,11 @@ macro_rules! impl_generic_ecdsa_verifer { }; } +#[cfg(feature = "p256")] impl_generic_ecdsa_verifer! {ECDSA_P256_SHA256, alg_id::ECDSA_P256, alg_id::ECDSA_SHA256, p256::ecdsa::VerifyingKey, p256::ecdsa::DerSignature, sha2::Sha256} +#[cfg(feature = "p256")] impl_generic_ecdsa_verifer! {ECDSA_P256_SHA384, alg_id::ECDSA_P256, alg_id::ECDSA_SHA384, p256::ecdsa::VerifyingKey, p256::ecdsa::DerSignature, sha2::Sha384} +#[cfg(feature = "p384")] impl_generic_ecdsa_verifer! {ECDSA_P384_SHA256, alg_id::ECDSA_P384, alg_id::ECDSA_SHA256, p384::ecdsa::VerifyingKey, p384::ecdsa::DerSignature, sha2::Sha256} +#[cfg(feature = "p384")] impl_generic_ecdsa_verifer! {ECDSA_P384_SHA384, alg_id::ECDSA_P384, alg_id::ECDSA_SHA384, p384::ecdsa::VerifyingKey, p384::ecdsa::DerSignature, sha2::Sha384}