Skip to content

Commit

Permalink
Add support for custom certificate verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Demi-Marie committed Apr 2, 2020
1 parent bdaf35b commit 785944b
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 33 deletions.
3 changes: 2 additions & 1 deletion rustls/Cargo.toml
Expand Up @@ -19,10 +19,11 @@ sct = "0.6.0"
webpki = "0.21.0"

[features]
default = ["logging"]
default = ["logging", "builtin_validation"]
logging = ["log"]
dangerous_configuration = []
quic = []
builtin_validation = []

[dev-dependencies]
env_logger = "0.7.1"
Expand Down
7 changes: 4 additions & 3 deletions rustls/src/client/tls12.rs
Expand Up @@ -503,9 +503,10 @@ impl hs::State for ExpectServerDone {
return Err(TLSError::PeerMisbehavedError(error_message));
}

verify::verify_signed_struct(&message,
&st.server_cert.cert_chain[0],
sig)
verify::verify_tls12_signature(&message,
&st.server_cert.cert_chain[0],
sig.scheme,
&sig.sig.0)
.map_err(|err| hs::send_cert_error_alert(sess, err))?
};
sess.server_cert_chain = st.server_cert.take_chain();
Expand Down
8 changes: 4 additions & 4 deletions rustls/src/client/tls13.rs
Expand Up @@ -585,8 +585,7 @@ impl hs::State for ExpectCertificateVerify {
return Err(TLSError::NoCertificatesPresented);
}

let certv = sess.config
.get_verifier()
let certv = sess.config.get_verifier()
.verify_server_cert(&sess.config.root_store,
&self.server_cert.cert_chain,
self.handshake.dns_name.as_ref(),
Expand All @@ -595,10 +594,11 @@ impl hs::State for ExpectCertificateVerify {

// 2. Verify their signature on the handshake.
let handshake_hash = self.handshake.transcript.get_current_hash();
let sigv = verify::verify_tls13(&self.server_cert.cert_chain[0],
let sigv = verify::verify_tls13_client(&self.server_cert.cert_chain[0],
cert_verify,
&handshake_hash,
b"TLS 1.3, server CertificateVerify\x00")
b"TLS 1.3, server CertificateVerify\x00",
sess.config.get_verifier())
.map_err(|err| send_cert_error_alert(sess, err))?;

// 3. Verify any included SCTs.
Expand Down
2 changes: 1 addition & 1 deletion rustls/src/lib.rs
Expand Up @@ -308,4 +308,4 @@ pub use crate::verify::{ServerCertVerifier, ServerCertVerified,
pub use crate::client::danger::DangerousClientConfig;

/// This is the rustls manual.
pub mod manual;
pub mod manual;
2 changes: 1 addition & 1 deletion rustls/src/server/tls12.rs
Expand Up @@ -183,7 +183,7 @@ impl hs::State for ExpectCertificateVerify {
let handshake_msgs = self.handshake.transcript.take_handshake_buf();
let certs = &self.client_cert.cert_chain;

verify::verify_signed_struct(&handshake_msgs, &certs[0], sig)
verify::verify_tls12_signature(&handshake_msgs, &certs[0], sig.scheme, &sig.sig.0)
};

if let Err(e) = rc {
Expand Down
9 changes: 5 additions & 4 deletions rustls/src/server/tls13.rs
Expand Up @@ -713,10 +713,11 @@ impl hs::State for ExpectCertificateVerify {
self.handshake.transcript.abandon_client_auth();
let certs = &self.client_cert.cert_chain;

verify::verify_tls13(&certs[0],
sig,
&handshake_hash,
b"TLS 1.3, client CertificateVerify\x00")
verify::verify_tls13_server(&certs[0],
sig,
&handshake_hash,
b"TLS 1.3, client CertificateVerify\x00",
sess.config.get_verifier())
};

if let Err(e) = rc {
Expand Down
104 changes: 85 additions & 19 deletions rustls/src/verify.rs
Expand Up @@ -10,6 +10,7 @@ use crate::msgs::enums::SignatureScheme;
use crate::error::TLSError;
use crate::anchors::{DistinguishedNames, RootCertStore};
use crate::anchors::OwnedTrustAnchor;
use crate::ProtocolVersion;
#[cfg(feature = "logging")]
use crate::log::{warn, debug};

Expand Down Expand Up @@ -60,6 +61,29 @@ impl ClientCertVerified {
pub fn assertion() -> Self { Self { 0: () } }
}

fn verify_certificate_signature(scheme: SignatureScheme,
version: ProtocolVersion,
cert: &Certificate,
msg: &[u8],
signature: &[u8]) -> Result<HandshakeSignatureValid, TLSError> {
match version {
crate::ProtocolVersion::TLSv1_2 => {
verify_tls12_signature(msg, cert, scheme, signature)
}
crate::ProtocolVersion::TLSv1_3 => {
let signature_alg = convert_alg_tls13(scheme)?;
let cert = webpki::EndEntityCert::from(&cert.0)
.map_err(TLSError::WebPKIError)?;

cert.verify_signature(signature_alg, &msg, signature)
.map_err(TLSError::WebPKIError)
.map(|_| HandshakeSignatureValid::assertion())
}
_ => panic!("other versions of TLS rejected earlier"),
}
}


/// Something that can verify a server certificate chain
pub trait ServerCertVerifier : Send + Sync {
/// Verify a the certificate chain `presented_certs` against the roots
Expand All @@ -70,10 +94,26 @@ pub trait ServerCertVerifier : Send + Sync {
presented_certs: &[Certificate],
dns_name: webpki::DNSNameRef,
ocsp_response: &[u8]) -> Result<ServerCertVerified, TLSError>;

/// Verify a signature against a certificate. `cert` is the certificate from the client.
///
/// This function will only be called after [`ServerCertVerifier::verify_client_cert`] has
/// been called on the same [`Certificate`] and returned `Ok`. Feel free to `panic!` if
/// [`ServerCertVerifier::verify_client_cert`] would have rejected `certificate` no matter what
/// the SNI is. You can also `panic!` if you get a value for `version` that you rejected in your
/// configuration.
fn verify_certificate_signature(&self,
scheme: SignatureScheme,
version: ProtocolVersion,
cert: &Certificate,
msg: &[u8],
signature: &[u8]) -> Result<HandshakeSignatureValid, TLSError> {
verify_certificate_signature(scheme, version, cert, msg, signature)
}
}

/// Something that can verify a client certificate chain
pub trait ClientCertVerifier : Send + Sync {
pub trait ClientCertVerifier: Send + Sync {
/// Returns `true` to enable the server to request a client certificate and
/// `false` to skip requesting a client certificate. Defaults to `true`.
fn offer_client_auth(&self) -> bool { true }
Expand Down Expand Up @@ -104,6 +144,22 @@ pub trait ClientCertVerifier : Send + Sync {
fn verify_client_cert(&self,
presented_certs: &[Certificate],
sni: Option<&webpki::DNSName>) -> Result<ClientCertVerified, TLSError>;

/// Verify a signature against a certificate. `cert` is the certificate from the client.
///
/// This function will only be called after [`ClientCertVerifier::verify_client_cert`] has
/// been called on the same [`Certificate`] and returned `Ok`. Feel free to `panic!` if
/// [`ClientCertVerifier::verify_client_cert`] would have rejected `certificate` no matter what
/// the SNI is. You can also `panic!` if you get a value for `version` that you rejected in your
/// configuration.
fn verify_certificate_signature(&self,
scheme: SignatureScheme,
version: ProtocolVersion,
cert: &Certificate,
msg: &[u8],
signature: &[u8]) -> Result<HandshakeSignatureValid, TLSError> {
verify_certificate_signature(scheme, version, cert, msg, signature)
}
}

/// Default `ServerCertVerifier`, see the trait impl for more information.
Expand Down Expand Up @@ -329,21 +385,25 @@ fn verify_sig_using_any_alg(cert: &webpki::EndEntityCert,
Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey)
}

pub trait SignatureVerifier {

}

/// Verify the signed `message` using the public key quoted in
/// `cert` and algorithm and signature in `dss`.
///
/// `cert` MUST have been authenticated before using this function,
/// typically using `verify_cert`.
pub fn verify_signed_struct(message: &[u8],
pub fn verify_tls12_signature(message: &[u8],
cert: &Certificate,
dss: &DigitallySignedStruct)
scheme: SignatureScheme,
signature: &[u8])
-> Result<HandshakeSignatureValid, TLSError> {

let possible_algs = convert_scheme(dss.scheme)?;
let possible_algs = convert_scheme(scheme)?;
let cert = webpki::EndEntityCert::from(&cert.0)
.map_err(TLSError::WebPKIError)?;

verify_sig_using_any_alg(&cert, possible_algs, message, &dss.sig.0)
verify_sig_using_any_alg(&cert, possible_algs, message, signature)
.map_err(TLSError::WebPKIError)
.map(|_| HandshakeSignatureValid::assertion())
}
Expand All @@ -365,24 +425,30 @@ fn convert_alg_tls13(scheme: SignatureScheme)
}
}

pub fn verify_tls13(cert: &Certificate,
dss: &DigitallySignedStruct,
handshake_hash: &[u8],
context_string_with_0: &[u8])
-> Result<HandshakeSignatureValid, TLSError> {
let alg = convert_alg_tls13(dss.scheme)?;

pub fn verify_tls13_client(cert: &Certificate,
dss: &DigitallySignedStruct,
handshake_hash: &[u8],
context_string_with_0: &[u8],
verifier: &dyn ServerCertVerifier)
-> Result<HandshakeSignatureValid, TLSError> {
let mut msg = Vec::new();
msg.resize(64, 0x20u8);
msg.extend_from_slice(context_string_with_0);
msg.extend_from_slice(handshake_hash);
verifier.verify_certificate_signature(dss.scheme, ProtocolVersion::TLSv1_3, cert, &msg, &dss.sig.0)
}

let cert = webpki::EndEntityCert::from(&cert.0)
.map_err(TLSError::WebPKIError)?;

cert.verify_signature(alg, &msg, &dss.sig.0)
.map_err(TLSError::WebPKIError)
.map(|_| HandshakeSignatureValid::assertion())
pub fn verify_tls13_server(cert: &Certificate,
dss: &DigitallySignedStruct,
handshake_hash: &[u8],
context_string_with_0: &[u8],
verifier: &dyn ClientCertVerifier)
-> Result<HandshakeSignatureValid, TLSError> {
let mut msg = Vec::new();
msg.resize(64, 0x20u8);
msg.extend_from_slice(context_string_with_0);
msg.extend_from_slice(handshake_hash);
verifier.verify_certificate_signature(dss.scheme, ProtocolVersion::TLSv1_3, cert, &msg, &dss.sig.0)
}

fn unix_time_millis() -> Result<u64, TLSError> {
Expand Down

0 comments on commit 785944b

Please sign in to comment.