From 2e7e3db11dcae9763714dfbf6279a5ea2bf7799b Mon Sep 17 00:00:00 2001 From: "sinu.eth" <65924192+sinui0@users.noreply.github.com> Date: Mon, 13 Oct 2025 00:57:34 -0700 Subject: [PATCH] fix: fully identify signature algorithm (#1015) --- crates/core/src/connection.rs | 160 ++++++++++++++++------------------ crates/core/src/fixtures.rs | 7 +- crates/mpc-tls/src/leader.rs | 19 ++-- crates/tls/core/src/verify.rs | 66 +++++++++++++- 4 files changed, 158 insertions(+), 94 deletions(-) diff --git a/crates/core/src/connection.rs b/crates/core/src/connection.rs index 1d9a9cd2f..fb7df37fb 100644 --- a/crates/core/src/connection.rs +++ b/crates/core/src/connection.rs @@ -116,84 +116,75 @@ pub enum KeyType { SECP256R1 = 0x0017, } -/// Signature scheme on the key exchange parameters. +/// Signature algorithm used on the key exchange parameters. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[allow(non_camel_case_types, missing_docs)] -pub enum SignatureScheme { - RSA_PKCS1_SHA1 = 0x0201, - ECDSA_SHA1_Legacy = 0x0203, - RSA_PKCS1_SHA256 = 0x0401, - ECDSA_NISTP256_SHA256 = 0x0403, - RSA_PKCS1_SHA384 = 0x0501, - ECDSA_NISTP384_SHA384 = 0x0503, - RSA_PKCS1_SHA512 = 0x0601, - ECDSA_NISTP521_SHA512 = 0x0603, - RSA_PSS_SHA256 = 0x0804, - RSA_PSS_SHA384 = 0x0805, - RSA_PSS_SHA512 = 0x0806, - ED25519 = 0x0807, +pub enum SignatureAlgorithm { + ECDSA_NISTP256_SHA256, + ECDSA_NISTP256_SHA384, + ECDSA_NISTP384_SHA256, + ECDSA_NISTP384_SHA384, + ED25519, + RSA_PKCS1_2048_8192_SHA256, + RSA_PKCS1_2048_8192_SHA384, + RSA_PKCS1_2048_8192_SHA512, + RSA_PSS_2048_8192_SHA256_LEGACY_KEY, + RSA_PSS_2048_8192_SHA384_LEGACY_KEY, + RSA_PSS_2048_8192_SHA512_LEGACY_KEY, } -impl fmt::Display for SignatureScheme { +impl fmt::Display for SignatureAlgorithm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - SignatureScheme::RSA_PKCS1_SHA1 => write!(f, "RSA_PKCS1_SHA1"), - SignatureScheme::ECDSA_SHA1_Legacy => write!(f, "ECDSA_SHA1_Legacy"), - SignatureScheme::RSA_PKCS1_SHA256 => write!(f, "RSA_PKCS1_SHA256"), - SignatureScheme::ECDSA_NISTP256_SHA256 => write!(f, "ECDSA_NISTP256_SHA256"), - SignatureScheme::RSA_PKCS1_SHA384 => write!(f, "RSA_PKCS1_SHA384"), - SignatureScheme::ECDSA_NISTP384_SHA384 => write!(f, "ECDSA_NISTP384_SHA384"), - SignatureScheme::RSA_PKCS1_SHA512 => write!(f, "RSA_PKCS1_SHA512"), - SignatureScheme::ECDSA_NISTP521_SHA512 => write!(f, "ECDSA_NISTP521_SHA512"), - SignatureScheme::RSA_PSS_SHA256 => write!(f, "RSA_PSS_SHA256"), - SignatureScheme::RSA_PSS_SHA384 => write!(f, "RSA_PSS_SHA384"), - SignatureScheme::RSA_PSS_SHA512 => write!(f, "RSA_PSS_SHA512"), - SignatureScheme::ED25519 => write!(f, "ED25519"), + SignatureAlgorithm::ECDSA_NISTP256_SHA256 => write!(f, "ECDSA_NISTP256_SHA256"), + SignatureAlgorithm::ECDSA_NISTP256_SHA384 => write!(f, "ECDSA_NISTP256_SHA384"), + SignatureAlgorithm::ECDSA_NISTP384_SHA256 => write!(f, "ECDSA_NISTP384_SHA256"), + SignatureAlgorithm::ECDSA_NISTP384_SHA384 => write!(f, "ECDSA_NISTP384_SHA384"), + SignatureAlgorithm::ED25519 => write!(f, "ED25519"), + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256 => { + write!(f, "RSA_PKCS1_2048_8192_SHA256") + } + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA384 => { + write!(f, "RSA_PKCS1_2048_8192_SHA384") + } + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA512 => { + write!(f, "RSA_PKCS1_2048_8192_SHA512") + } + SignatureAlgorithm::RSA_PSS_2048_8192_SHA256_LEGACY_KEY => { + write!(f, "RSA_PSS_2048_8192_SHA256_LEGACY_KEY") + } + SignatureAlgorithm::RSA_PSS_2048_8192_SHA384_LEGACY_KEY => { + write!(f, "RSA_PSS_2048_8192_SHA384_LEGACY_KEY") + } + SignatureAlgorithm::RSA_PSS_2048_8192_SHA512_LEGACY_KEY => { + write!(f, "RSA_PSS_2048_8192_SHA512_LEGACY_KEY") + } } } } -impl TryFrom for SignatureScheme { - type Error = &'static str; - - fn try_from(value: tls_core::msgs::enums::SignatureScheme) -> Result { - use tls_core::msgs::enums::SignatureScheme as Core; - use SignatureScheme::*; - Ok(match value { - Core::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1, - Core::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy, - Core::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256, - Core::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256, - Core::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384, - Core::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384, - Core::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512, - Core::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512, - Core::RSA_PSS_SHA256 => RSA_PSS_SHA256, - Core::RSA_PSS_SHA384 => RSA_PSS_SHA384, - Core::RSA_PSS_SHA512 => RSA_PSS_SHA512, - Core::ED25519 => ED25519, - _ => return Err("unsupported signature scheme"), - }) - } -} - -impl From for tls_core::msgs::enums::SignatureScheme { - fn from(value: SignatureScheme) -> Self { - use tls_core::msgs::enums::SignatureScheme::*; +impl From for SignatureAlgorithm { + fn from(value: tls_core::verify::SignatureAlgorithm) -> Self { + use tls_core::verify::SignatureAlgorithm as Core; match value { - SignatureScheme::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1, - SignatureScheme::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384, - SignatureScheme::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256 => RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384 => RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512 => RSA_PSS_SHA512, - SignatureScheme::ED25519 => ED25519, + Core::ECDSA_NISTP256_SHA256 => SignatureAlgorithm::ECDSA_NISTP256_SHA256, + Core::ECDSA_NISTP256_SHA384 => SignatureAlgorithm::ECDSA_NISTP256_SHA384, + Core::ECDSA_NISTP384_SHA256 => SignatureAlgorithm::ECDSA_NISTP384_SHA256, + Core::ECDSA_NISTP384_SHA384 => SignatureAlgorithm::ECDSA_NISTP384_SHA384, + Core::ED25519 => SignatureAlgorithm::ED25519, + Core::RSA_PKCS1_2048_8192_SHA256 => SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256, + Core::RSA_PKCS1_2048_8192_SHA384 => SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA384, + Core::RSA_PKCS1_2048_8192_SHA512 => SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA512, + Core::RSA_PSS_2048_8192_SHA256_LEGACY_KEY => { + SignatureAlgorithm::RSA_PSS_2048_8192_SHA256_LEGACY_KEY + } + Core::RSA_PSS_2048_8192_SHA384_LEGACY_KEY => { + SignatureAlgorithm::RSA_PSS_2048_8192_SHA384_LEGACY_KEY + } + Core::RSA_PSS_2048_8192_SHA512_LEGACY_KEY => { + SignatureAlgorithm::RSA_PSS_2048_8192_SHA512_LEGACY_KEY + } } } } @@ -201,8 +192,8 @@ impl From for tls_core::msgs::enums::SignatureScheme { /// Server's signature of the key exchange parameters. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerSignature { - /// Signature scheme. - pub scheme: SignatureScheme, + /// Signature algorithm. + pub alg: SignatureAlgorithm, /// Signature data. pub sig: Vec, } @@ -359,20 +350,23 @@ impl HandshakeData { message.extend_from_slice(&server_ephemeral_key.kx_params()); use webpki::ring as alg; - let sig_alg = match self.sig.scheme { - SignatureScheme::RSA_PKCS1_SHA256 => alg::RSA_PKCS1_2048_8192_SHA256, - SignatureScheme::RSA_PKCS1_SHA384 => alg::RSA_PKCS1_2048_8192_SHA384, - SignatureScheme::RSA_PKCS1_SHA512 => alg::RSA_PKCS1_2048_8192_SHA512, - SignatureScheme::RSA_PSS_SHA256 => alg::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, - SignatureScheme::RSA_PSS_SHA384 => alg::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, - SignatureScheme::RSA_PSS_SHA512 => alg::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, - SignatureScheme::ECDSA_NISTP256_SHA256 => alg::ECDSA_P256_SHA256, - SignatureScheme::ECDSA_NISTP384_SHA384 => alg::ECDSA_P384_SHA384, - SignatureScheme::ED25519 => alg::ED25519, - scheme => { - return Err(HandshakeVerificationError::UnsupportedSignatureScheme( - scheme, - )) + let sig_alg = match self.sig.alg { + SignatureAlgorithm::ECDSA_NISTP256_SHA256 => alg::ECDSA_P256_SHA256, + SignatureAlgorithm::ECDSA_NISTP256_SHA384 => alg::ECDSA_P256_SHA384, + SignatureAlgorithm::ECDSA_NISTP384_SHA256 => alg::ECDSA_P384_SHA256, + SignatureAlgorithm::ECDSA_NISTP384_SHA384 => alg::ECDSA_P384_SHA384, + SignatureAlgorithm::ED25519 => alg::ED25519, + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256 => alg::RSA_PKCS1_2048_8192_SHA256, + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA384 => alg::RSA_PKCS1_2048_8192_SHA384, + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA512 => alg::RSA_PKCS1_2048_8192_SHA512, + SignatureAlgorithm::RSA_PSS_2048_8192_SHA256_LEGACY_KEY => { + alg::RSA_PSS_2048_8192_SHA256_LEGACY_KEY + } + SignatureAlgorithm::RSA_PSS_2048_8192_SHA384_LEGACY_KEY => { + alg::RSA_PSS_2048_8192_SHA384_LEGACY_KEY + } + SignatureAlgorithm::RSA_PSS_2048_8192_SHA512_LEGACY_KEY => { + alg::RSA_PSS_2048_8192_SHA512_LEGACY_KEY } }; @@ -402,8 +396,6 @@ pub enum HandshakeVerificationError { InvalidServerEphemeralKey, #[error("server certificate verification failed: {0}")] ServerCert(ServerCertVerifierError), - #[error("unsupported signature scheme: {0}")] - UnsupportedSignatureScheme(SignatureScheme), } #[cfg(test)] diff --git a/crates/core/src/fixtures.rs b/crates/core/src/fixtures.rs index bf397dc80..6ee8caa0b 100644 --- a/crates/core/src/fixtures.rs +++ b/crates/core/src/fixtures.rs @@ -10,7 +10,8 @@ use hex::FromHex; use crate::{ connection::{ CertBinding, CertBindingV1_2, ConnectionInfo, DnsName, HandshakeData, KeyType, - ServerEphemKey, ServerName, ServerSignature, SignatureScheme, TlsVersion, TranscriptLength, + ServerEphemKey, ServerName, ServerSignature, SignatureAlgorithm, TlsVersion, + TranscriptLength, }, transcript::{ encoding::{EncoderSecret, EncodingProvider}, @@ -47,7 +48,7 @@ impl ConnectionFixture { CertificateDer(include_bytes!("fixtures/data/tlsnotary.org/ca.der").to_vec()), ], sig: ServerSignature { - scheme: SignatureScheme::RSA_PKCS1_SHA256, + alg: SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256, sig: Vec::::from_hex(include_bytes!( "fixtures/data/tlsnotary.org/signature" )) @@ -92,7 +93,7 @@ impl ConnectionFixture { CertificateDer(include_bytes!("fixtures/data/appliedzkp.org/ca.der").to_vec()), ], sig: ServerSignature { - scheme: SignatureScheme::ECDSA_NISTP256_SHA256, + alg: SignatureAlgorithm::ECDSA_NISTP256_SHA256, sig: Vec::::from_hex(include_bytes!( "fixtures/data/appliedzkp.org/signature" )) diff --git a/crates/mpc-tls/src/leader.rs b/crates/mpc-tls/src/leader.rs index 926fda993..0a72190cd 100644 --- a/crates/mpc-tls/src/leader.rs +++ b/crates/mpc-tls/src/leader.rs @@ -41,6 +41,7 @@ use tls_core::{ message::{OpaqueMessage, PlainMessage}, }, suites::SupportedCipherSuite, + verify::verify_sig_determine_alg, }; use tlsn_core::{ connection::{CertBinding, CertBindingV1_2, ServerSignature, TlsVersion, VerifyData}, @@ -327,12 +328,20 @@ impl MpcTlsLeader { .map(|cert| CertificateDer(cert.0.clone())) .collect(); + let mut sig_msg = Vec::new(); + sig_msg.extend_from_slice(&client_random.0); + sig_msg.extend_from_slice(&server_random.0); + sig_msg.extend_from_slice(server_kx_details.kx_params()); + + let server_signature_alg = verify_sig_determine_alg( + &server_cert_details.cert_chain()[0], + &sig_msg, + server_kx_details.kx_sig(), + ) + .expect("only supported signature should have been accepted"); + let server_signature = ServerSignature { - scheme: server_kx_details - .kx_sig() - .scheme - .try_into() - .expect("only supported signature scheme should have been accepted"), + alg: server_signature_alg.into(), sig: server_kx_details.kx_sig().sig.0.clone(), }; diff --git a/crates/tls/core/src/verify.rs b/crates/tls/core/src/verify.rs index 859bddcaf..c52deab18 100644 --- a/crates/tls/core/src/verify.rs +++ b/crates/tls/core/src/verify.rs @@ -465,19 +465,81 @@ fn convert_scheme(scheme: SignatureScheme) -> Result } } +/// Signature algorithm. +#[derive(Debug, Clone, Copy, PartialEq)] +#[allow(non_camel_case_types)] +pub enum SignatureAlgorithm { + ECDSA_NISTP256_SHA256, + ECDSA_NISTP256_SHA384, + ECDSA_NISTP384_SHA256, + ECDSA_NISTP384_SHA384, + ED25519, + RSA_PKCS1_2048_8192_SHA256, + RSA_PKCS1_2048_8192_SHA384, + RSA_PKCS1_2048_8192_SHA512, + RSA_PSS_2048_8192_SHA256_LEGACY_KEY, + RSA_PSS_2048_8192_SHA384_LEGACY_KEY, + RSA_PSS_2048_8192_SHA512_LEGACY_KEY, +} + +impl SignatureAlgorithm { + pub fn from_alg(alg: &dyn pki_types::SignatureVerificationAlgorithm) -> Self { + let id = alg.signature_alg_id(); + if id == webpki::ring::ECDSA_P256_SHA256.signature_alg_id() { + SignatureAlgorithm::ECDSA_NISTP256_SHA256 + } else if id == webpki::ring::ECDSA_P256_SHA384.signature_alg_id() { + SignatureAlgorithm::ECDSA_NISTP256_SHA384 + } else if id == webpki::ring::ECDSA_P384_SHA256.signature_alg_id() { + SignatureAlgorithm::ECDSA_NISTP384_SHA256 + } else if id == webpki::ring::ECDSA_P384_SHA384.signature_alg_id() { + SignatureAlgorithm::ECDSA_NISTP384_SHA384 + } else if id == webpki::ring::ED25519.signature_alg_id() { + SignatureAlgorithm::ED25519 + } else if id == webpki::ring::RSA_PKCS1_2048_8192_SHA256.signature_alg_id() { + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA256 + } else if id == webpki::ring::RSA_PKCS1_2048_8192_SHA384.signature_alg_id() { + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA384 + } else if id == webpki::ring::RSA_PKCS1_2048_8192_SHA512.signature_alg_id() { + SignatureAlgorithm::RSA_PKCS1_2048_8192_SHA512 + } else if id == webpki::ring::RSA_PSS_2048_8192_SHA256_LEGACY_KEY.signature_alg_id() { + SignatureAlgorithm::RSA_PSS_2048_8192_SHA256_LEGACY_KEY + } else if id == webpki::ring::RSA_PSS_2048_8192_SHA384_LEGACY_KEY.signature_alg_id() { + SignatureAlgorithm::RSA_PSS_2048_8192_SHA384_LEGACY_KEY + } else if id == webpki::ring::RSA_PSS_2048_8192_SHA512_LEGACY_KEY.signature_alg_id() { + SignatureAlgorithm::RSA_PSS_2048_8192_SHA512_LEGACY_KEY + } else { + unreachable!() + } + } +} + +/// Verify the signature and return the algorithm which passed verification. +pub fn verify_sig_determine_alg( + cert: &Certificate, + message: &[u8], + dss: &DigitallySignedStruct, +) -> Result { + let cert = pki_types::CertificateDer::from(cert.0.as_slice()); + let cert = webpki::EndEntityCert::try_from(&cert).map_err(pki_error)?; + + verify_sig_using_any_alg(&cert, convert_scheme(dss.scheme)?, message, &dss.sig.0) + .map_err(pki_error) +} + fn verify_sig_using_any_alg( cert: &webpki::EndEntityCert, algs: SignatureAlgorithms, message: &[u8], sig: &[u8], -) -> Result<(), webpki::Error> { +) -> Result { // TLS doesn't itself give us enough info to map to a single // webpki::SignatureAlgorithm. Therefore, convert_algs maps to several and // we try them all. for alg in algs { match cert.verify_signature(*alg, message, sig) { + Ok(_) => return Ok(SignatureAlgorithm::from_alg(*alg)), Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKeyContext(_)) => continue, - res => return res, + Err(e) => return Err(e), } }