From 140d73d16c6970cf33a57279669535705e46aa72 Mon Sep 17 00:00:00 2001 From: Tom Fay Date: Fri, 8 Nov 2024 07:23:56 +0000 Subject: [PATCH] Test against aws-lc-rs provider (#4) Add signing/verification of ECDSA/ED25119 Remove unwraps from key exchanges --- Cargo.toml | 6 +- README.md | 10 ++- src/cipher_suites.rs | 98 +++++++++++------------ src/ecdh.rs | 87 +++++++++++--------- src/lib.rs | 108 ++++++------------------- src/signer.rs | 49 +++++++++--- src/tls12.rs | 10 +-- src/tls13.rs | 5 +- src/verify.rs | 184 ++++++++++++++++++++++++++++++------------- tests/it.rs | 158 ++++++++++++++++++++++++++++++++++++- 10 files changed, 464 insertions(+), 251 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79c89bc..d73f47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustls-openssl" authors = ["Tom Fay "] -version = "0.1.0" +version = "0.0.1" edition = "2021" license = "MIT" description = "Rustls crypto provider for OpenSSL" @@ -15,7 +15,7 @@ rustls = { version = "0.23.0", features = [ "tls12", "std", ], default-features = false } -rustls-webpki = "0.102.2" +rustls-webpki = { version = "0.102.2", default-features = false } [features] default = [] @@ -27,5 +27,7 @@ antidote = "1.0.0" lazy_static = "1.4.0" once_cell = "1.8.0" rstest = "0.23.0" +# Use aws_lc_rs to test our provider +rustls = { version = "0.23.0", features = ["aws_lc_rs"] } rustls-pemfile = "2" webpki-roots = "0.26" diff --git a/README.md b/README.md index 2ade7a7..341afcf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # rustls-openssl -An experimental [rustls Crypto Provider](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html) that uses OpenSSL for cryptographic operations. +A [rustls Crypto Provider](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html) that uses OpenSSL for cryptographic operations. + +## Status +**Early in development.** ## Usage The main entry points are the `rustls_openssl::default_provider` and `rustls_openssl::custom_provider` functions. @@ -20,6 +23,9 @@ TLS13_CHACHA20_POLY1305_SHA256 // Requires the `chacha` feature ### TLS 1.2 ``` +TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Requires the `chacha` feature TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 // Requires the `chacha` feature @@ -36,4 +42,4 @@ X25519 // Requires the `x25519` feature ## Signature verification algorithms -ECDSA signature verification is done using the webpki ring implementation. ED25119 and RSA signature verification is done using openssl. +ECDSA signature verification is done using the webpki ring implementation. ED25519 and RSA signature verification is done using openssl. diff --git a/src/cipher_suites.rs b/src/cipher_suites.rs index 37110f7..ec0e1db 100644 --- a/src/cipher_suites.rs +++ b/src/cipher_suites.rs @@ -53,20 +53,20 @@ pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = /// TLS 1.2 -// /// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256. -// #[cfg(feature = "chacha")] -// pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = -// SupportedCipherSuite::Tls12(&Tls12CipherSuite { -// common: CipherSuiteCommon { -// suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, -// hash_provider: &HashAlgorithm::SHA256, -// confidentiality_limit: u64::MAX, -// }, -// kx: KeyExchangeAlgorithm::ECDHE, -// sign: TLS12_ECDSA_SCHEMES, -// aead_alg: &crate::tls12::Tls12ChaCha, -// prf_provider: &PrfUsingHmac(&HmacSha256), -// }); +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256. +#[cfg(feature = "chacha")] +pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: &HashAlgorithm::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &crate::tls12::Tls12ChaCha, + prf_provider: &PrfUsingHmac(&HmacSha256), + }); /// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 #[cfg(feature = "chacha")] @@ -115,43 +115,43 @@ pub static TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = prf_provider: &PrfUsingHmac(&HmacSha384), }); -// /// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -// pub static TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = -// SupportedCipherSuite::Tls12(&Tls12CipherSuite { -// common: CipherSuiteCommon { -// suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, -// hash_provider: &HashAlgorithm::SHA256, -// confidentiality_limit: 1 << 23, -// }, -// kx: KeyExchangeAlgorithm::ECDHE, -// sign: TLS12_ECDSA_SCHEMES, -// aead_alg: &Tls12Gcm { -// algo_type: AesGcm::Aes128Gcm, -// }, -// prf_provider: &PrfUsingHmac(&HmacSha256), -// }); +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +pub static TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + hash_provider: &HashAlgorithm::SHA256, + confidentiality_limit: 1 << 23, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Tls12Gcm { + algo_type: AesGcm::Aes128Gcm, + }, + prf_provider: &PrfUsingHmac(&HmacSha256), + }); -// /// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -// pub static TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = -// SupportedCipherSuite::Tls12(&Tls12CipherSuite { -// common: CipherSuiteCommon { -// suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, -// hash_provider: &HashAlgorithm::SHA384, -// confidentiality_limit: 1 << 23, -// }, -// kx: KeyExchangeAlgorithm::ECDHE, -// sign: TLS12_ECDSA_SCHEMES, -// aead_alg: &Tls12Gcm { -// algo_type: AesGcm::Aes256Gcm, -// }, -// prf_provider: &PrfUsingHmac(&HmacSha384), -// }); +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +pub static TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + hash_provider: &HashAlgorithm::SHA384, + confidentiality_limit: 1 << 23, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Tls12Gcm { + algo_type: AesGcm::Aes256Gcm, + }, + prf_provider: &PrfUsingHmac(&HmacSha384), + }); -// static TLS12_ECDSA_SCHEMES: &[SignatureScheme] = &[ -// SignatureScheme::ECDSA_NISTP521_SHA512, -// SignatureScheme::ECDSA_NISTP384_SHA384, -// SignatureScheme::ECDSA_NISTP256_SHA256, -// ]; +static TLS12_ECDSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP256_SHA256, +]; /// RSA schemes in descending order of preference pub(crate) static TLS12_RSA_SCHEMES: &[SignatureScheme] = &[ diff --git a/src/ecdh.rs b/src/ecdh.rs index 375d66e..f393168 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -1,6 +1,7 @@ use openssl::bn::BigNumContext; use openssl::derive::Deriver; use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm}; +use openssl::error::ErrorStack; use openssl::nid::Nid; #[cfg(feature = "x25519")] use openssl::pkey::Id; @@ -62,20 +63,23 @@ pub const SECP384R1: &dyn SupportedKxGroup = &EcKxGroup { impl SupportedKxGroup for EcKxGroup { fn start(&self) -> Result, Error> { - let group = EcGroup::from_curve_name(self.nid).unwrap(); - let priv_key = EcKey::generate(&group).unwrap(); - let mut ctx = BigNumContext::new().unwrap(); - let pub_key = priv_key - .public_key() - .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx) - .unwrap(); - - Ok(Box::new(EcKeyExchange { - priv_key, - name: self.name, - group, - pub_key, - })) + EcGroup::from_curve_name(self.nid) + .and_then(|group| { + let priv_key = EcKey::generate(&group)?; + let mut ctx = BigNumContext::new()?; + let pub_key = priv_key.public_key().to_bytes( + &group, + PointConversionForm::UNCOMPRESSED, + &mut ctx, + )?; + Ok(Box::new(EcKeyExchange { + priv_key, + name: self.name, + group, + pub_key, + }) as Box) + }) + .map_err(|e| Error::General(format!("OpenSSL error: {}", e))) } fn name(&self) -> NamedGroup { @@ -84,24 +88,27 @@ impl SupportedKxGroup for EcKxGroup { } impl EcKeyExchange { - fn load_peer_key(&self, peer_pub_key: &[u8]) -> Result, Error> { - let mut ctx = BigNumContext::new().unwrap(); - let point = EcPoint::from_bytes(&self.group, peer_pub_key, &mut ctx).unwrap(); - let peer_key = EcKey::from_public_key(&self.group, &point).unwrap(); - peer_key.check_key().unwrap(); - let peer_key: PKey<_> = peer_key.try_into().unwrap(); + fn load_peer_key(&self, peer_pub_key: &[u8]) -> Result, ErrorStack> { + let mut ctx = BigNumContext::new()?; + let point = EcPoint::from_bytes(&self.group, peer_pub_key, &mut ctx)?; + let peer_key = EcKey::from_public_key(&self.group, &point)?; + peer_key.check_key()?; + let peer_key: PKey<_> = peer_key.try_into()?; Ok(peer_key) } } impl ActiveKeyExchange for EcKeyExchange { fn complete(self: Box, peer_pub_key: &[u8]) -> Result { - let peer_key = self.load_peer_key(peer_pub_key).unwrap(); - let key: PKey<_> = self.priv_key.try_into().unwrap(); - let mut deriver = Deriver::new(&key).unwrap(); - deriver.set_peer(&peer_key).unwrap(); - let secret = deriver.derive_to_vec().unwrap(); - Ok(SharedSecret::from(secret.as_slice())) + self.load_peer_key(peer_pub_key) + .and_then(|peer_key| { + let key: PKey<_> = self.priv_key.try_into()?; + let mut deriver = Deriver::new(&key)?; + deriver.set_peer(&peer_key)?; + let secret = deriver.derive_to_vec()?; + Ok(SharedSecret::from(secret.as_slice())) + }) + .map_err(|e| Error::General(format!("OpenSSL error: {}", e))) } fn pub_key(&self) -> &[u8] { @@ -116,12 +123,15 @@ impl ActiveKeyExchange for EcKeyExchange { #[cfg(feature = "x25519")] impl SupportedKxGroup for X25519KxGroup { fn start(&self) -> Result, Error> { - let private_key = PKey::generate_x25519().unwrap(); - let public_key = private_key.raw_public_key().unwrap(); - Ok(Box::new(X25519KeyExchange { - private_key, - public_key, - })) + PKey::generate_x25519() + .and_then(|private_key| { + let public_key = private_key.raw_public_key()?; + Ok(Box::new(X25519KeyExchange { + private_key, + public_key, + }) as Box) + }) + .map_err(|e| Error::General(format!("OpenSSL error: {}", e))) } fn name(&self) -> NamedGroup { @@ -132,11 +142,14 @@ impl SupportedKxGroup for X25519KxGroup { #[cfg(feature = "x25519")] impl ActiveKeyExchange for X25519KeyExchange { fn complete(self: Box, peer_pub_key: &[u8]) -> Result { - let peer_public_key = PKey::public_key_from_raw_bytes(peer_pub_key, Id::X25519).unwrap(); - let mut deriver = Deriver::new(&self.private_key).unwrap(); - deriver.set_peer(&peer_public_key).unwrap(); - let secret = deriver.derive_to_vec().unwrap(); - Ok(SharedSecret::from(secret.as_slice())) + PKey::public_key_from_raw_bytes(peer_pub_key, Id::X25519) + .and_then(|peer_pub_key| { + let mut deriver = Deriver::new(&self.private_key)?; + deriver.set_peer(&peer_pub_key)?; + let secret = deriver.derive_to_vec()?; + Ok(SharedSecret::from(secret.as_slice())) + }) + .map_err(|e| Error::General(format!("OpenSSL error: {}", e))) } fn pub_key(&self) -> &[u8] { diff --git a/src/lib.rs b/src/lib.rs index efc62a5..bd841a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ //! //! ## Limitations //! -//! - TLS 1.2: No ECDSA support. //! - QUIC Protocol: Not supported. //! //! ## Supported Ciphers @@ -22,9 +21,9 @@ //! ### TLS 1.2 //! //! ```ignore -// //! TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -// //! TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -// //! TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Requires the `chacha` feature +//! TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +//! TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +//! TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Requires the `chacha` feature //! TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //! TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 //! TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 // Requires the `chacha` feature @@ -104,10 +103,8 @@ //! - `x25519`: Enables X25519 key exchange group. use openssl::rand::rand_bytes; -use rustls::crypto::{ - CryptoProvider, GetRandomFailed, SecureRandom, SupportedKxGroup, WebPkiSupportedAlgorithms, -}; -use rustls::{SignatureScheme, SupportedCipherSuite}; +use rustls::crypto::{CryptoProvider, GetRandomFailed, SecureRandom, SupportedKxGroup}; +use rustls::SupportedCipherSuite; mod cipher_suites; mod ecdh; @@ -123,13 +120,15 @@ pub use cipher_suites::{TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384}; /// Exporting default cipher suites for TLS 1.2 pub use cipher_suites::{ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, }; /// Exporting ChaCha suites for TLS 1.2 and TLS 1.3 #[cfg(feature = "chacha")] pub use cipher_suites::{ - TLS13_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS13_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, }; /// Exporting default key exchange groups @@ -165,9 +164,9 @@ pub fn default_provider() -> CryptoProvider { CryptoProvider { cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(), kx_groups: ecdh::ALL_KX_GROUPS.to_vec(), - signature_verification_algorithms: SUPPORTED_SIG_ALGS, - secure_random: &Rng, - key_provider: &signer::Provider, + signature_verification_algorithms: verify::SUPPORTED_SIG_ALGS, + secure_random: &Provider, + key_provider: &Provider, } } @@ -225,9 +224,9 @@ pub fn custom_provider( CryptoProvider { cipher_suites, kx_groups: kx_group, - signature_verification_algorithms: SUPPORTED_SIG_ALGS, - secure_random: &Rng, - key_provider: &signer::Provider, + signature_verification_algorithms: verify::SUPPORTED_SIG_ALGS, + secure_random: &Provider, + key_provider: &Provider, } } @@ -239,9 +238,9 @@ pub fn custom_provider( /// TLS13_AES_128_GCM_SHA256 /// TLS13_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature /// // TLS 1.2 suites -/// // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -/// // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -/// // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature +/// TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +/// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +/// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature /// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 /// TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 /// TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature @@ -255,81 +254,20 @@ static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[ #[cfg(feature = "chacha")] TLS13_CHACHA20_POLY1305_SHA256, // TLS 1.2 suites - // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - // #[cfg(feature = "chacha")] - // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + #[cfg(feature = "chacha")] + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, #[cfg(feature = "chacha")] TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, ]; -// TODO implement ECDSA verification. For now reuse webpki's ring implementation. -use webpki::ring as webpki_algs; - -static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { - all: &[ - webpki_algs::ECDSA_P256_SHA256, - webpki_algs::ECDSA_P256_SHA384, - webpki_algs::ECDSA_P384_SHA256, - webpki_algs::ECDSA_P384_SHA384, - verify::ED25519, - verify::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, - verify::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, - verify::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, - verify::RSA_PKCS1_2048_8192_SHA256, - verify::RSA_PKCS1_2048_8192_SHA384, - verify::RSA_PKCS1_2048_8192_SHA512, - verify::RSA_PKCS1_3072_8192_SHA384, - ], - mapping: &[ - //Note: for TLS1.2 the curve is not fixed by SignatureScheme. For TLS1.3 it is. - ( - SignatureScheme::ECDSA_NISTP384_SHA384, - &[ - webpki_algs::ECDSA_P384_SHA384, - webpki_algs::ECDSA_P256_SHA384, - ], - ), - ( - SignatureScheme::ECDSA_NISTP256_SHA256, - &[ - webpki_algs::ECDSA_P256_SHA256, - webpki_algs::ECDSA_P384_SHA256, - ], - ), - (SignatureScheme::ED25519, &[verify::ED25519]), - ( - SignatureScheme::RSA_PSS_SHA512, - &[verify::RSA_PSS_2048_8192_SHA512_LEGACY_KEY], - ), - ( - SignatureScheme::RSA_PSS_SHA384, - &[verify::RSA_PSS_2048_8192_SHA384_LEGACY_KEY], - ), - ( - SignatureScheme::RSA_PSS_SHA256, - &[verify::RSA_PSS_2048_8192_SHA256_LEGACY_KEY], - ), - ( - SignatureScheme::RSA_PKCS1_SHA512, - &[verify::RSA_PKCS1_2048_8192_SHA512], - ), - ( - SignatureScheme::RSA_PKCS1_SHA384, - &[verify::RSA_PKCS1_2048_8192_SHA384], - ), - ( - SignatureScheme::RSA_PKCS1_SHA256, - &[verify::RSA_PKCS1_2048_8192_SHA256], - ), - ], -}; #[derive(Debug)] -struct Rng; +struct Provider; -impl SecureRandom for Rng { +impl SecureRandom for Provider { fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> { rand_bytes(buf).map_err(|_| GetRandomFailed) } diff --git a/src/signer.rs b/src/signer.rs index dc0e681..2bb9869 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -1,6 +1,7 @@ use openssl::hash::MessageDigest; use openssl::pkey::{Id, Private}; use openssl::rsa::Padding; +use openssl::sign::RsaPssSaltlen; use rustls::crypto::KeyProvider; use rustls::pki_types::PrivateKeyDer; use rustls::sign::SigningKey; @@ -8,9 +9,13 @@ use rustls::{Error, SignatureAlgorithm, SignatureScheme}; use std::sync::Arc; use crate::cipher_suites::TLS12_RSA_SCHEMES; +use crate::Provider; -#[derive(Debug)] -pub(crate) struct Provider; +static ALL_ECDSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP521_SHA512, +]; #[derive(Debug)] struct PKey(Arc>); @@ -44,6 +49,24 @@ fn message_digest(scheme: &SignatureScheme) -> Option { } } +fn mgf1(scheme: &SignatureScheme) -> Option { + match scheme { + SignatureScheme::RSA_PSS_SHA256 => Some(MessageDigest::sha256()), + SignatureScheme::RSA_PSS_SHA384 => Some(MessageDigest::sha384()), + SignatureScheme::RSA_PSS_SHA512 => Some(MessageDigest::sha512()), + _ => None, + } +} + +fn pss_salt_len(scheme: &SignatureScheme) -> Option { + match scheme { + SignatureScheme::RSA_PSS_SHA256 + | SignatureScheme::RSA_PSS_SHA384 + | SignatureScheme::RSA_PSS_SHA512 => Some(RsaPssSaltlen::DIGEST_LENGTH), + _ => None, + } +} + impl PKey { fn signer(&self, scheme: &SignatureScheme) -> Signer { Signer { @@ -58,9 +81,9 @@ impl KeyProvider for Provider { &self, key_der: PrivateKeyDer<'static>, ) -> Result, Error> { - Ok(Arc::new(PKey(Arc::new( - openssl::pkey::PKey::private_key_from_der(key_der.secret_der()).unwrap(), - )))) + let pkey = openssl::pkey::PKey::private_key_from_der(key_der.secret_der()) + .map_err(|e| Error::General(format!("OpenSSL error: {}", e)))?; + Ok(Arc::new(PKey(Arc::new(pkey)))) } } @@ -92,12 +115,10 @@ impl SigningKey for PKey { None } } - // SignatureAlgorithm::ECDSA => ALL_ECDSA_SCHEMES - // .iter() - // .find(|scheme| offered.contains(scheme)) - // .map(|scheme| { - // Box::new(self.digest_signer(scheme)) as Box - // }), + SignatureAlgorithm::ECDSA => ALL_ECDSA_SCHEMES + .iter() + .find(|scheme| offered.contains(scheme)) + .map(|scheme| Box::new(self.signer(scheme)) as Box), _ => None, } } @@ -127,6 +148,12 @@ impl rustls::sign::Signer for Signer { if let Some(padding) = rsa_padding(&self.scheme) { signer.set_rsa_padding(padding)?; } + if let Some(mgf1) = mgf1(&self.scheme) { + signer.set_rsa_mgf1_md(mgf1)?; + } + if let Some(len) = pss_salt_len(&self.scheme) { + signer.set_rsa_pss_saltlen(len)?; + } signer.update(message)?; signer.sign_to_vec() }) diff --git a/src/tls12.rs b/src/tls12.rs index 43fe534..66773d2 100644 --- a/src/tls12.rs +++ b/src/tls12.rs @@ -136,10 +136,10 @@ impl MessageDecrypter for Tls12ChaCha20Poly1305 { Some(self.key.as_ref()), Some(&nonce.0), )?; - ctx.cipher_update(&aad, None).unwrap(); - let count = ctx.cipher_update_inplace(payload, message_len).unwrap(); - ctx.set_tag(&tag).unwrap(); - let rest = ctx.cipher_final(&mut payload[count..]).unwrap(); + ctx.cipher_update(&aad, None)?; + let count = ctx.cipher_update_inplace(payload, message_len)?; + ctx.set_tag(&tag)?; + let rest = ctx.cipher_final(&mut payload[count..])?; payload.truncate(count + rest); Ok(()) }) @@ -263,7 +263,7 @@ impl MessageEncrypter for Gcm12Encrypt { CipherCtx::new() .and_then(|mut ctx| { ctx.encrypt_init(Some(cipher), Some(self.key.as_ref()), Some(&nonce.0))?; - ctx.cipher_update(&aad, None).unwrap(); + ctx.cipher_update(&aad, None)?; let count = ctx.cipher_update_inplace( &mut payload.as_mut()[GCM_EXPLICIT_NONCE_LENGTH..], msg.payload.len(), diff --git a/src/tls13.rs b/src/tls13.rs index d1f839d..1005dd8 100644 --- a/src/tls13.rs +++ b/src/tls13.rs @@ -148,12 +148,11 @@ impl MessageDecrypter for AeadMessageCrypter { Some(self.algo.openssl_cipher()), Some(self.key.as_ref()), Some(&nonce.0), - ) - .unwrap(); + )?; ctx.cipher_update(&aad, None)?; let count = ctx.cipher_update_inplace(payload, message_len)?; ctx.set_tag(&tag)?; - let rest = ctx.cipher_final(&mut payload[count..]).unwrap(); + let rest = ctx.cipher_final(&mut payload[count..])?; payload.truncate(count + rest); Ok(()) }) diff --git a/src/verify.rs b/src/verify.rs index 1115710..32d9264 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,104 +1,185 @@ use core::fmt; -use std::ops::RangeInclusive; - use openssl::{ + bn::BigNumContext, + ec::{EcGroup, EcKey, EcPoint}, hash::MessageDigest, - pkey::{PKey, Public}, + nid::Nid, + pkey::{Id, PKey, Public}, rsa::{Padding, Rsa}, sign::{RsaPssSaltlen, Verifier}, }; -use rustls::pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; +use rustls::{ + crypto::WebPkiSupportedAlgorithms, + pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}, + SignatureScheme, +}; use webpki::alg_id; +pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: &[ + ECDSA_P256_SHA256, + ECDSA_P256_SHA384, + ECDSA_P384_SHA256, + ECDSA_P384_SHA384, + ECDSA_P521_SHA256, + ECDSA_P521_SHA384, + ECDSA_P521_SHA512, + ED25519, + RSA_PSS_SHA512, + RSA_PSS_SHA384, + RSA_PSS_SHA256, + RSA_PKCS1_SHA512, + RSA_PKCS1_SHA384, + RSA_PKCS1_SHA256, + ], + mapping: &[ + //Note: for TLS1.2 the curve is not fixed by SignatureScheme. For TLS1.3 it is. + ( + SignatureScheme::ECDSA_NISTP384_SHA384, + &[ECDSA_P384_SHA384, ECDSA_P256_SHA384, ECDSA_P521_SHA384], + ), + ( + SignatureScheme::ECDSA_NISTP256_SHA256, + &[ECDSA_P256_SHA256, ECDSA_P384_SHA256, ECDSA_P521_SHA256], + ), + (SignatureScheme::ECDSA_NISTP521_SHA512, &[ECDSA_P521_SHA512]), + (SignatureScheme::ED25519, &[ED25519]), + (SignatureScheme::RSA_PSS_SHA512, &[RSA_PSS_SHA512]), + (SignatureScheme::RSA_PSS_SHA384, &[RSA_PSS_SHA384]), + (SignatureScheme::RSA_PSS_SHA256, &[RSA_PSS_SHA256]), + (SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]), + (SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]), + (SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]), + ], +}; + /// RSA PKCS#1 1.5 signatures using SHA-256 for keys of 2048-8192 bits. -pub static RSA_PKCS1_2048_8192_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { +pub static RSA_PKCS1_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PKCS1_SHA256, - range: Some(2048..=8192), }; /// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 2048-8192 bits. -pub static RSA_PKCS1_2048_8192_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { +pub static RSA_PKCS1_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PKCS1_SHA384, - range: Some(2048..=8192), }; /// RSA PKCS#1 1.5 signatures using SHA-512 for keys of 2048-8192 bits. -pub static RSA_PKCS1_2048_8192_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { +pub static RSA_PKCS1_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PKCS1_SHA512, - range: Some(2048..=8192), }; -/// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 3072-8192 bits. -pub static RSA_PKCS1_3072_8192_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { +/// RSA PSS signatures using SHA-256 +/// +/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 +pub static RSA_PSS_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, - signature_alg_id: alg_id::RSA_PKCS1_SHA384, - range: Some(3072..=8192), + signature_alg_id: alg_id::RSA_PSS_SHA256, }; -/// RSA PSS signatures using SHA-256 for keys of 2048-8192 bits and of -/// type rsaEncryption; see [RFC 4055 Section 1.2]. -/// -/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 -pub static RSA_PSS_2048_8192_SHA256_LEGACY_KEY: &dyn SignatureVerificationAlgorithm = - &OpenSslAlgorithm { - public_key_alg_id: alg_id::RSA_ENCRYPTION, - signature_alg_id: alg_id::RSA_PSS_SHA256, - range: Some(2048..=8192), - }; - -/// RSA PSS signatures using SHA-384 for keys of 2048-8192 bits and of -/// type rsaEncryption; see [RFC 4055 Section 1.2]. +/// RSA PSS signatures using SHA-384 /// /// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 -pub static RSA_PSS_2048_8192_SHA384_LEGACY_KEY: &dyn SignatureVerificationAlgorithm = - &OpenSslAlgorithm { - public_key_alg_id: alg_id::RSA_ENCRYPTION, - signature_alg_id: alg_id::RSA_PSS_SHA384, - range: Some(2048..=8192), - }; - -/// RSA PSS signatures using SHA-512 for keys of 2048-8192 bits and of -/// type rsaEncryption; see [RFC 4055 Section 1.2]. +pub static RSA_PSS_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::RSA_ENCRYPTION, + signature_alg_id: alg_id::RSA_PSS_SHA384, +}; + +/// RSA PSS signatures using SHA-512 /// /// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 -pub static RSA_PSS_2048_8192_SHA512_LEGACY_KEY: &dyn SignatureVerificationAlgorithm = - &OpenSslAlgorithm { - public_key_alg_id: alg_id::RSA_ENCRYPTION, - signature_alg_id: alg_id::RSA_PSS_SHA512, - range: Some(2048..=8192), - }; +pub static RSA_PSS_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::RSA_ENCRYPTION, + signature_alg_id: alg_id::RSA_PSS_SHA512, +}; /// ED25519 signatures according to RFC 8410 pub static ED25519: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::ED25519, signature_alg_id: alg_id::ED25519, - range: None, +}; + +/// ECDSA signatures using the P-256 curve and SHA-256. +pub static ECDSA_P256_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P256, + signature_alg_id: alg_id::ECDSA_SHA256, +}; + +/// ECDSA signatures using the P-256 curve and SHA-384. Deprecated. +pub static ECDSA_P256_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P256, + signature_alg_id: alg_id::ECDSA_SHA384, +}; + +/// ECDSA signatures using the P-384 curve and SHA-256. Deprecated. +pub static ECDSA_P384_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P384, + signature_alg_id: alg_id::ECDSA_SHA256, +}; + +/// ECDSA signatures using the P-384 curve and SHA-384. +pub static ECDSA_P384_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P384, + signature_alg_id: alg_id::ECDSA_SHA384, +}; + +/// ECDSA signatures using the P-521 curve and SHA-256. +pub static ECDSA_P521_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P521, + signature_alg_id: alg_id::ECDSA_SHA256, +}; + +/// ECDSA signatures using the P-521 curve and SHA-384. +pub static ECDSA_P521_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P521, + signature_alg_id: alg_id::ECDSA_SHA384, +}; + +/// ECDSA signatures using the P-521 curve and SHA-512. +pub static ECDSA_P521_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { + public_key_alg_id: alg_id::ECDSA_P521, + signature_alg_id: alg_id::ECDSA_SHA512, }; struct OpenSslAlgorithm { public_key_alg_id: AlgorithmIdentifier, signature_alg_id: AlgorithmIdentifier, - range: Option>, } impl fmt::Debug for OpenSslAlgorithm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OpensslAlgorithm") + f.debug_struct("OpenSSLAlgorithm") .field("public_key_alg_id", &self.public_key_alg_id) .field("signature_alg_id", &self.signature_alg_id) .finish() } } +fn ecdsa_public_key(curve_name: Nid, public_key: &[u8]) -> Result, InvalidSignature> { + EcGroup::from_curve_name(curve_name) + .and_then(|group| { + let mut ctx = BigNumContext::new()?; + let point = EcPoint::from_bytes(&group, public_key, &mut ctx)?; + let key = EcKey::from_public_key(&group, &point)?; + key.try_into() + }) + .map_err(|_| InvalidSignature) +} + impl OpenSslAlgorithm { fn public_key(&self, public_key: &[u8]) -> Result, InvalidSignature> { match self.public_key_alg_id { alg_id::RSA_ENCRYPTION => Rsa::public_key_from_der_pkcs1(public_key) .and_then(|rsa| rsa.try_into()) .map_err(|_| InvalidSignature), + alg_id::ECDSA_P521 => ecdsa_public_key(Nid::SECP521R1, public_key), + alg_id::ECDSA_P384 => ecdsa_public_key(Nid::SECP384R1, public_key), + alg_id::ECDSA_P256 => ecdsa_public_key(Nid::X9_62_PRIME256V1, public_key), + alg_id::ED25519 => PKey::public_key_from_raw_bytes(public_key, Id::ED25519) + .map_err(|_| InvalidSignature), + _ => Err(InvalidSignature), } } @@ -111,6 +192,9 @@ impl OpenSslAlgorithm { alg_id::RSA_PSS_SHA256 => Some(MessageDigest::sha256()), alg_id::RSA_PSS_SHA384 => Some(MessageDigest::sha384()), alg_id::RSA_PSS_SHA512 => Some(MessageDigest::sha512()), + alg_id::ECDSA_SHA256 => Some(MessageDigest::sha256()), + alg_id::ECDSA_SHA384 => Some(MessageDigest::sha384()), + alg_id::ECDSA_SHA512 => Some(MessageDigest::sha512()), _ => None, } } @@ -182,16 +266,6 @@ impl SignatureVerificationAlgorithm for OpenSslAlgorithm { } let pkey = self.public_key(public_key)?; - // Check the length is in the range. - if !self - .range - .as_ref() - .map(|range| range.contains(&pkey.bits())) - .unwrap_or(true) - { - return Err(InvalidSignature); - } - if let Some(message_digest) = self.message_digest() { Verifier::new(message_digest, &pkey).and_then(|mut verifier| { if let Some(padding) = self.rsa_padding() { diff --git a/tests/it.rs b/tests/it.rs index fe25b63..45d0f59 100644 --- a/tests/it.rs +++ b/tests/it.rs @@ -1,8 +1,14 @@ //! Integration tests, based on rustls-symcrypt integration tests - +use openssl::bn::BigNumContext; +use openssl::ec::{EcGroup, EcKey, PointConversionForm}; +use openssl::nid::Nid; +use openssl::pkey::PKey; +use openssl::rsa::Rsa; use rstest::rstest; use rustls::crypto::SupportedKxGroup; -use rustls::{CipherSuite, SupportedCipherSuite}; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::PrivateKeyDer; +use rustls::{CipherSuite, SignatureScheme, SupportedCipherSuite}; use rustls_openssl::{ custom_provider, default_provider, SECP256R1, SECP384R1, TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, @@ -226,11 +232,17 @@ fn test_with_custom_config_to_internet( SECP384R1, CipherSuite::TLS13_AES_256_GCM_SHA384 )] +// TODO: setup ECDSA certs // #[case::tls_ecdhe_ecdsa_with_aes_256_gcm_sha384( // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, // SECP384R1, // CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 // )] +// #[case::tls_ecdhe_ecdsa_with_aes_128_gcm_sha256( +// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, +// SECP256R1, +// CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +// )] fn test_tls( #[case] suite: SupportedCipherSuite, #[case] group: &'static dyn SupportedKxGroup, @@ -317,3 +329,145 @@ fn test_default_client() { assert_eq!(ciphersuite.suite(), CipherSuite::TLS13_AES_256_GCM_SHA384); drop(lock); } + +static RSA_SIGNING_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, +]; + +#[test] +fn test_rsa_sign_and_verify() { + let ours = rustls_openssl::default_provider(); + let theirs = rustls::crypto::aws_lc_rs::default_provider(); + + let private_key = Rsa::generate(2048).unwrap(); + let rustls_private_key = + PrivateKeyDer::from_pem_slice(&private_key.private_key_to_pem().unwrap()).unwrap(); + let pub_key = private_key.public_key_to_der_pkcs1().unwrap(); + + for scheme in RSA_SIGNING_SCHEMES { + eprintln!("Testing scheme {:?}", scheme); + + sign_and_verify( + &ours, + &theirs, + *scheme, + rustls_private_key.clone_key(), + &pub_key, + ); + sign_and_verify( + &theirs, + &ours, + *scheme, + rustls_private_key.clone_key(), + &pub_key, + ); + } +} + +#[rstest] +#[case::ecdsa_nistp256_sha256(SignatureScheme::ECDSA_NISTP256_SHA256, Nid::X9_62_PRIME256V1)] +#[case::ecdsa_nistp384_sha384(SignatureScheme::ECDSA_NISTP384_SHA384, Nid::SECP384R1)] +#[case::ecdsa_nistp521_sha512(SignatureScheme::ECDSA_NISTP521_SHA512, Nid::SECP521R1)] + +fn test_ec_sign_and_verify(#[case] scheme: SignatureScheme, #[case] curve: Nid) { + let ours = rustls_openssl::default_provider(); + let theirs = rustls::crypto::aws_lc_rs::default_provider(); + + let group = EcGroup::from_curve_name(curve).unwrap(); + + let private_key = EcKey::generate(&group).unwrap(); + let rustls_private_key = + PrivateKeyDer::from_pem_slice(&private_key.private_key_to_pem().unwrap()).unwrap(); + + eprintln!("private_key: {:?}", rustls_private_key); + + let mut ctx = BigNumContext::new().unwrap(); + let pub_key = private_key + .public_key() + // ring doesn't work if PointConversionForm::Compression, aws_lc_rs does + .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx) + .unwrap(); + + eprintln!("verifying using theirs"); + sign_and_verify( + &ours, + &theirs, + scheme, + rustls_private_key.clone_key(), + &pub_key, + ); + eprintln!("verifying using ours"); + sign_and_verify( + &theirs, + &ours, + scheme, + rustls_private_key.clone_key(), + &pub_key, + ); +} + +#[test] +fn test_ed25119_sign_and_verify() { + let ours = rustls_openssl::default_provider(); + let theirs = rustls::crypto::aws_lc_rs::default_provider(); + let scheme = SignatureScheme::ED25519; + + let private_key = PKey::generate_ed25519().unwrap(); + let pub_key = private_key.raw_public_key().unwrap(); + let rustls_private_key = + PrivateKeyDer::from_pem_slice(&private_key.private_key_to_pem_pkcs8().unwrap()).unwrap(); + eprintln!("verifying using theirs"); + sign_and_verify( + &ours, + &theirs, + scheme, + rustls_private_key.clone_key(), + &pub_key, + ); + eprintln!("verifying using ours"); + sign_and_verify( + &theirs, + &ours, + scheme, + rustls_private_key.clone_key(), + &pub_key, + ); +} + +fn sign_and_verify( + signing_provider: &rustls::crypto::CryptoProvider, + verifying_provider: &rustls::crypto::CryptoProvider, + scheme: SignatureScheme, + rustls_private_key: PrivateKeyDer<'static>, + pub_key: &[u8], +) { + let data = b"hello, world!"; + + // sign + let signing_key = signing_provider + .key_provider + .load_private_key(rustls_private_key) + .unwrap(); + let signer = signing_key + .choose_scheme(&[scheme]) + .expect("signing provider supports this scheme"); + let signature = signer.sign(data).unwrap(); + + // verify + let algs = verifying_provider + .signature_verification_algorithms + .mapping + .iter() + .find(|(k, _v)| *k == scheme) + .map(|(_k, v)| *v) + .expect("verifying provider supports this scheme"); + assert!(!algs.is_empty()); + assert!(algs + .iter() + .any(|alg| { alg.verify_signature(pub_key, data, &signature).is_ok() })); +}