Skip to content

Commit

Permalink
Implement signature verification methods of ClientCertVerifier and …
Browse files Browse the repository at this point in the history
…`ServerSertVerifier`
  • Loading branch information
Arash Sahebolamri committed Nov 1, 2023
1 parent 617e7cd commit f3a3252
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 48 deletions.
24 changes: 20 additions & 4 deletions rustls-mbedpki-provider/src/client_cert_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use rustls::{
};

use crate::{
common::verify_certificates_active, mbedtls_err_into_rustls_err,
mbedtls_err_into_rustls_err_with_error_msg, rustls_cert_to_mbedtls_cert,
mbedtls_err_into_rustls_err, mbedtls_err_into_rustls_err_with_error_msg,
rustls_cert_to_mbedtls_cert, verify_certificates_active, verify_tls_signature,
};

/// A `rustls` `ClientCertVerifier` implemented using the PKI functionality of
Expand Down Expand Up @@ -76,6 +76,24 @@ impl ClientCertVerifier for MbedTlsClientCertVerifier {

Ok(ClientCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::Certificate,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
verify_tls_signature(message, cert, dss, false)
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::Certificate,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
verify_tls_signature(message, cert, dss, true)
}
}


Expand Down Expand Up @@ -123,7 +141,6 @@ mod tests {
get_key(include_bytes!("../test-data/rsa/client.key"))
).unwrap();


let client_cert_verifier = MbedTlsClientCertVerifier::new([&root_ca]).unwrap();

let server_config = server_config_with_verifier(client_cert_verifier);
Expand All @@ -134,7 +151,6 @@ mod tests {
assert!(do_handshake_until_error(&mut client_conn, &mut server_conn).is_ok());
}


fn test_connection_client_cert_verifier_with_invalid_certs(invalid_cert_chain: Vec<Certificate>) {
let client_config = ClientConfig::builder().with_safe_defaults();
let root_ca = Certificate(include_bytes!("../test-data/rsa/ca.der").to_vec());
Expand Down
26 changes: 0 additions & 26 deletions rustls-mbedpki-provider/src/common.rs

This file was deleted.

158 changes: 156 additions & 2 deletions rustls-mbedpki-provider/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use chrono::NaiveDateTime;
use mbedtls::hash::Type;
use rustls::SignatureScheme;
use std::sync::Arc;

mod common;
mod tests_common;

pub mod client_cert_verifier;
Expand Down Expand Up @@ -30,4 +32,156 @@ pub fn mbedtls_err_into_rustls_err_with_error_msg(err: mbedtls::Error, msg: &str
mbedtls::Error::X509FatalError => rustls::Error::InvalidCertificate(rustls::CertificateError::Other(Arc::new(err))),
_ => rustls::Error::General(format!("{err}\n{msg}")),
}
}
}

fn rustls_signature_scheme_to_mbedtls_hash_type(signature_scheme: SignatureScheme) -> mbedtls::hash::Type {
match signature_scheme {
SignatureScheme::RSA_PKCS1_SHA1 => Type::Sha1,
SignatureScheme::ECDSA_SHA1_Legacy => Type::Sha1,
SignatureScheme::RSA_PKCS1_SHA256 => Type::Sha256,
SignatureScheme::ECDSA_NISTP256_SHA256 => Type::Sha256,
SignatureScheme::RSA_PKCS1_SHA384 => Type::Sha384,
SignatureScheme::ECDSA_NISTP384_SHA384 => Type::Sha384,
SignatureScheme::RSA_PKCS1_SHA512 => Type::Sha512,
SignatureScheme::ECDSA_NISTP521_SHA512 => Type::Sha512,
SignatureScheme::RSA_PSS_SHA256 => Type::Sha256,
SignatureScheme::RSA_PSS_SHA384 => Type::Sha384,
SignatureScheme::RSA_PSS_SHA512 => Type::Sha512,
SignatureScheme::ED25519 => Type::None,
SignatureScheme::ED448 => Type::None,
SignatureScheme::Unknown(_) => Type::None,
_ => Type::None,
}
}

fn rustls_signature_scheme_to_mbedtls_pk_options(signature_scheme: SignatureScheme) -> Option<mbedtls::pk::Options> {
use mbedtls::pk::Options;
use mbedtls::pk::RsaPadding;
// reference: https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3
match signature_scheme {
SignatureScheme::RSA_PKCS1_SHA1 => None,
SignatureScheme::ECDSA_SHA1_Legacy => None,
SignatureScheme::ECDSA_NISTP256_SHA256 => None,
SignatureScheme::ECDSA_NISTP384_SHA384 => None,
SignatureScheme::ECDSA_NISTP521_SHA512 => None,
SignatureScheme::RSA_PKCS1_SHA256 |
SignatureScheme::RSA_PKCS1_SHA384 |
SignatureScheme::RSA_PKCS1_SHA512 => Some(Options::Rsa { padding: RsaPadding::Pkcs1V15 }),
SignatureScheme::RSA_PSS_SHA256 => Some( Options::Rsa { padding: RsaPadding::Pkcs1V21 { mgf: Type::Sha256 } }),
SignatureScheme::RSA_PSS_SHA384 => Some( Options::Rsa { padding: RsaPadding::Pkcs1V21 { mgf: Type::Sha384 } }),
SignatureScheme::RSA_PSS_SHA512 => Some( Options::Rsa { padding: RsaPadding::Pkcs1V21 { mgf: Type::Sha512 } }),
SignatureScheme::ED25519 => None,
SignatureScheme::ED448 => None,
SignatureScheme::Unknown(_) => None,
_ => None,
}
}

fn rustls_signature_scheme_to_mbedtls_curve_id(signature_scheme: SignatureScheme) -> mbedtls::pk::EcGroupId {
// reference: https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3
use mbedtls::pk::EcGroupId;
match signature_scheme {
SignatureScheme::ECDSA_NISTP256_SHA256 => EcGroupId::SecP256R1,
SignatureScheme::ECDSA_NISTP384_SHA384 => EcGroupId::SecP384R1,
SignatureScheme::ECDSA_NISTP521_SHA512 => EcGroupId::SecP521R1,
SignatureScheme::ECDSA_SHA1_Legacy => EcGroupId::None,
SignatureScheme::RSA_PKCS1_SHA1 => EcGroupId::None,
SignatureScheme::RSA_PKCS1_SHA256 => EcGroupId::None,
SignatureScheme::RSA_PKCS1_SHA384 => EcGroupId::None,
SignatureScheme::RSA_PKCS1_SHA512 => EcGroupId::None,
SignatureScheme::RSA_PSS_SHA256 => EcGroupId::None,
SignatureScheme::RSA_PSS_SHA384 => EcGroupId::None,
SignatureScheme::RSA_PSS_SHA512 => EcGroupId::None,
SignatureScheme::ED25519 => EcGroupId::None,
SignatureScheme::ED448 => EcGroupId::None,
SignatureScheme::Unknown(_) => EcGroupId::None,
_ => EcGroupId::None,
}
}

/// Returns the size of the message digest given the hash type.
fn hash_size_bytes(hash_type: mbedtls::hash::Type) -> Option<usize> {
match hash_type {
mbedtls::hash::Type::None => None,
mbedtls::hash::Type::Md2 => Some(16),
mbedtls::hash::Type::Md4 => Some(16),
mbedtls::hash::Type::Md5 => Some(16),
mbedtls::hash::Type::Sha1 => Some(20),
mbedtls::hash::Type::Sha224 => Some(28),
mbedtls::hash::Type::Sha256 => Some(32),
mbedtls::hash::Type::Sha384 => Some(48),
mbedtls::hash::Type::Sha512 => Some(64),
mbedtls::hash::Type::Ripemd => Some(20), // this is MD_RIPEMD160
}
}

fn buffer_for_hash_type(hash_type: mbedtls::hash::Type) -> Option<Vec<u8>> {
let size = hash_size_bytes(hash_type)?;
Some(vec![0; size])
}

/// Verifies that certificates are active, i.e., `now` is between not_before and not_after for
/// each certificate
fn verify_certificates_active<'a>(
chain: impl IntoIterator<Item = &'a mbedtls::x509::Certificate>,
now: NaiveDateTime
) -> Result<(), rustls::Error> {
fn time_err_to_err(_time_err: mbedtls::x509::InvalidTimeError) -> rustls::Error {
rustls::Error::InvalidCertificate(rustls::CertificateError::BadEncoding)
}

for cert in chain.into_iter() {
let not_after = cert.not_after().map_err(mbedtls_err_into_rustls_err)?
.try_into().map_err(time_err_to_err)?;
if now > not_after {
return Err(rustls::Error::InvalidCertificate(rustls::CertificateError::Expired));
}
let not_before = cert.not_before().map_err(mbedtls_err_into_rustls_err)?
.try_into().map_err(time_err_to_err)?;
if now < not_before {
return Err(rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidYet));
}
}
Ok(())
}

/// Verifies the tls signature, matches verify functions in rustls `ClientCertVerifier` and
/// `ServerCertVerifier`
fn verify_tls_signature(
message: &[u8],
cert: &rustls::Certificate,
dss: &rustls::DigitallySignedStruct,
is_tls13: bool,
) -> Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
let mut cert = rustls_cert_to_mbedtls_cert(cert).map_err(mbedtls_err_into_rustls_err)?;
let pk = cert.public_key_mut();
let hash_type = rustls_signature_scheme_to_mbedtls_hash_type(dss.scheme);

// for tls 1.3, we need to verify the advertised curve in signaure scheme matches the public key
if is_tls13 {
let signature_curve = rustls_signature_scheme_to_mbedtls_curve_id(dss.scheme);
match signature_curve {
mbedtls::pk::EcGroupId::None => (),
_ => {
let curves_match = pk.curve().is_ok_and(|pk_curve| pk_curve == signature_curve);
if !curves_match {
return Err(rustls::Error::PeerMisbehaved(rustls::PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme));
}
}
}
}

if let Some(opts) = rustls_signature_scheme_to_mbedtls_pk_options(dss.scheme) {
pk.set_options(opts);
}

let mut hash = buffer_for_hash_type(hash_type)
.ok_or_else(|| rustls::Error::General("unexpected hash type".into()))?;
let hash_size = mbedtls::hash::Md::hash(hash_type, message, &mut hash)
.map_err(mbedtls_err_into_rustls_err)?;
pk.verify(hash_type, &hash[..hash_size], dss.signature())
.map_err(mbedtls_err_into_rustls_err)?;

Ok(rustls::client::HandshakeSignatureValid::assertion())
}

Loading

0 comments on commit f3a3252

Please sign in to comment.