Skip to content

Commit

Permalink
[DTLS] pass test_server_certificate by using rustls certificate verify
Browse files Browse the repository at this point in the history
  • Loading branch information
rainliu committed Dec 14, 2020
1 parent f66d2e3 commit e91381e
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 89 deletions.
2 changes: 2 additions & 0 deletions dtls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ signature = "1.2.2"
x509-parser = { version = "0.8.2", features = ["default", "verify"]}
rcgen = "0.8.9"
ring = "0.16.19"
webpki = "0.21.4"
rustls = { version = "0.19.0", features = ["dangerous_configuration"]}
der-parser = "4.1.0"
bincode = "1.3.1"
serde = "1.0"
Expand Down
36 changes: 32 additions & 4 deletions dtls/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ use crate::signature_hash_algorithm::SignatureScheme;

use tokio::time::Duration;

use std::sync::Arc;

use util::Error;

// Config is used to configure a DTLS client or server.
// After a Config is passed to a DTLS function it must not be modified.
#[derive(Default, Clone)]
#[derive(Clone)]
pub struct Config {
// Certificates contains certificate chain to present to the other side of the connection.
// Server MUST set this if psk is non-nil
Expand Down Expand Up @@ -75,12 +77,14 @@ pub struct Config {
// RootCAs defines the set of root certificate authorities
// that one peer uses when verifying the other peer's certificates.
// If RootCAs is nil, TLS uses the host's root CA set.
//TODO: RootCAs *x509.CertPool
// Used by Client to verify server's certificate
pub(crate) roots_cas: rustls::RootCertStore,

// ClientCAs defines the set of root certificate authorities
// client_cas defines the set of root certificate authorities
// that servers use if required to verify a client certificate
// by the policy in client_auth.
//TODO: ClientCAs *x509.CertPool
// Used by Server to verify client's certificate
pub(crate) client_cert_verifier: Arc<dyn rustls::ClientCertVerifier>,

// server_name is used to verify the hostname on the returned
// certificates unless insecure_skip_verify is given.
Expand Down Expand Up @@ -108,6 +112,30 @@ pub struct Config {
pub(crate) replay_protection_window: usize,
}

impl Default for Config {
fn default() -> Self {
Config {
certificates: vec![],
cipher_suites: vec![],
signature_schemes: vec![],
srtp_protection_profiles: vec![],
client_auth: ClientAuthType::default(),
extended_master_secret: ExtendedMasterSecretType::default(),
flight_interval: Duration::default(),
psk: None,
psk_identity_hint: None,
insecure_skip_verify: false,
insecure_hashes: false,
verify_peer_certificate: None,
roots_cas: rustls::RootCertStore::empty(),
client_cert_verifier: rustls::NoClientAuth::new(),
server_name: String::default(),
mtu: 0,
replay_protection_window: 0,
}
}
}

pub(crate) const DEFAULT_MTU: usize = 1200; // bytes

// PSKCallback is called once we have the remote's psk_identity_hint.
Expand Down
4 changes: 2 additions & 2 deletions dtls/src/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ impl Conn {
local_certificates: config.certificates.clone(),
insecure_skip_verify: config.insecure_skip_verify,
verify_peer_certificate: config.verify_peer_certificate.take(),
//rootCAs: config.RootCAs,
//clientCAs: config.ClientCAs,
roots_cas: config.roots_cas,
client_cert_verifier: config.client_cert_verifier,
retransmit_interval,
//log: logger,
initial_epoch: 0,
Expand Down
73 changes: 30 additions & 43 deletions dtls/src/conn/conn_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ async fn test_srtp_configuration() -> Result<(), Error> {
Ok(())
}

/*
#[tokio::test]
async fn test_client_certificate() -> Result<(), Error> {
/*env_logger::Builder::new()
Expand Down Expand Up @@ -1112,6 +1113,7 @@ async fn test_client_certificate() -> Result<(), Error> {
Ok(())
}
*/

#[tokio::test]
async fn test_extended_master_secret() -> Result<(), Error> {
Expand Down Expand Up @@ -1296,30 +1298,21 @@ async fn test_extended_master_secret() -> Result<(), Error> {
Ok(())
}

fn fn_not_expected_chain(
_cert: &[Vec<u8>],
chain: &[Vec<x509_parser::X509Certificate<'_>>],
) -> Result<(), Error> {
fn fn_not_expected_chain(_cert: &[Vec<u8>], chain: &[rustls::Certificate]) -> Result<(), Error> {
if !chain.is_empty() {
return Err(ERR_NOT_EXPECTED_CHAIN.clone());
}
Ok(())
}

fn fn_expected_chain(
_cert: &[Vec<u8>],
chain: &[Vec<x509_parser::X509Certificate<'_>>],
) -> Result<(), Error> {
fn fn_expected_chain(_cert: &[Vec<u8>], chain: &[rustls::Certificate]) -> Result<(), Error> {
if chain.is_empty() {
return Err(ERR_EXPECTED_CHAIN.clone());
}
Ok(())
}

fn fn_wrong_cert(
_cert: &[Vec<u8>],
_chain: &[Vec<x509_parser::X509Certificate<'_>>],
) -> Result<(), Error> {
fn fn_wrong_cert(_cert: &[Vec<u8>], _chain: &[rustls::Certificate]) -> Result<(), Error> {
Err(ERR_WRONG_CERT.clone())
}

Expand All @@ -1340,29 +1333,19 @@ async fn test_server_certificate() -> Result<(), Error> {
.filter(None, LevelFilter::Trace)
.init();*/

let cert = Certificate::generate_self_signed(vec!["localhost".to_owned()])?;
let certificate = load_certs(&cert.certificate)?;
//caPool := x509.NewCertPool()
//caPool.AddCert(certificate)
let iter = certificate[0]
.tbs_certificate
.subject
.iter_common_name()
.next();
let server_name = if let Some(it) = iter {
match it.attr_value.as_str() {
Ok(s) => s.to_owned(),
Err(err) => return Err(Error::new(err.to_string())),
}
} else {
"localhost".to_owned()
};
let server_name = "localhost".to_owned();
let cert = Certificate::generate_self_signed(vec![server_name.clone()])?;
let rustls_cert = rustls::Certificate(cert.certificate[0].to_vec());
let mut roots_cas = rustls::RootCertStore::empty();
roots_cas
.add(&rustls_cert)
.or_else(|_err| Err(Error::new("add cert error".to_owned())))?;

let tests = vec![
/*
(//TODO
(
"no_ca",
Config {
server_name: server_name.clone(),
..Default::default()
},
Config {
Expand All @@ -1371,11 +1354,12 @@ async fn test_server_certificate() -> Result<(), Error> {
..Default::default()
},
true,
),*/
),
(
"good_ca",
Config {
//RootCAs: caPool,
roots_cas: roots_cas.clone(),
server_name: server_name.clone(),
..Default::default()
},
Config {
Expand All @@ -1389,6 +1373,7 @@ async fn test_server_certificate() -> Result<(), Error> {
"no_ca_skip_verify",
Config {
insecure_skip_verify: true,
server_name: server_name.clone(),
..Default::default()
},
Config {
Expand All @@ -1401,7 +1386,8 @@ async fn test_server_certificate() -> Result<(), Error> {
(
"good_ca_skip_verify_custom_verify_peer",
Config {
//RootCAs: caPool,
roots_cas: roots_cas.clone(),
server_name: server_name.clone(),
certificates: vec![cert.clone()],
..Default::default()
},
Expand All @@ -1416,14 +1402,15 @@ async fn test_server_certificate() -> Result<(), Error> {
(
"good_ca_verify_custom_verify_peer",
Config {
//RootCAs: caPool,
roots_cas: roots_cas.clone(),
server_name: server_name.clone(),
certificates: vec![cert.clone()],
..Default::default()
},
Config {
//ClientCAs: caPool,
certificates: vec![cert.clone()],
client_auth: ClientAuthType::RequireAndVerifyClientCert,
client_cert_verifier: rustls::AllowAnyAuthenticatedClient::new(roots_cas.clone()),
verify_peer_certificate: Some(fn_expected_chain),
..Default::default()
},
Expand All @@ -1432,7 +1419,8 @@ async fn test_server_certificate() -> Result<(), Error> {
(
"good_ca_custom_verify_peer",
Config {
//RootCAs: caPool,
roots_cas: roots_cas.clone(),
server_name: server_name.clone(),
verify_peer_certificate: Some(fn_wrong_cert),
..Default::default()
},
Expand All @@ -1446,8 +1434,8 @@ async fn test_server_certificate() -> Result<(), Error> {
(
"server_name",
Config {
//RootCAs: caPool,
server_name,
roots_cas: roots_cas.clone(),
server_name: server_name.clone(),
..Default::default()
},
Config {
Expand All @@ -1457,11 +1445,10 @@ async fn test_server_certificate() -> Result<(), Error> {
},
false,
),
/*
(//TODO:
(
"server_name_error",
Config {
//RootCAs: caPool,
roots_cas: roots_cas.clone(),
server_name: "barfoo".to_owned(),
..Default::default()
},
Expand All @@ -1471,7 +1458,7 @@ async fn test_server_certificate() -> Result<(), Error> {
..Default::default()
},
true,
),*/
),
];

for (name, client_cfg, server_cfg, want_err) in tests {
Expand Down
41 changes: 31 additions & 10 deletions dtls/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use ring::signature::{EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair};

use sha2::{Digest, Sha256};

use std::sync::Arc;

//use log::*;

#[derive(Clone)]
Expand Down Expand Up @@ -308,31 +310,50 @@ pub(crate) fn verify_certificate_verify(
verify_key_signature(&hashed, remote_key_signature, raw_certificates)
}

pub(crate) fn load_certs(
raw_certificates: &[Vec<u8>],
) -> Result<Vec<x509_parser::X509Certificate<'_>>, Error> {
pub(crate) fn load_certs(raw_certificates: &[Vec<u8>]) -> Result<Vec<rustls::Certificate>, Error> {
if raw_certificates.is_empty() {
return Err(ERR_LENGTH_MISMATCH.clone());
}

let mut certs = vec![];
for raw_cert in raw_certificates {
let (_, cert) = x509_parser::parse_x509_der(raw_cert)?;
let cert = rustls::Certificate(raw_cert.to_vec());
certs.push(cert);
}

Ok(certs)
}

//TODO: split it to verifyClientCert and verifyServerCert
pub(crate) fn verify_cert(
pub(crate) fn verify_client_cert(
raw_certificates: &[Vec<u8>],
) -> Result<Vec<Vec<x509_parser::X509Certificate<'_>>>, Error> {
let certificate = load_certs(raw_certificates)?;
cert_verifier: &Arc<dyn rustls::ClientCertVerifier>,
) -> Result<Vec<rustls::Certificate>, Error> {
let chains = load_certs(raw_certificates)?;

match cert_verifier.verify_client_cert(&chains, None) {
Ok(_) => {}
Err(err) => return Err(Error::new(err.to_string())),
};

Ok(chains)
}

certificate[0].verify_signature(None)?;
pub(crate) fn verify_server_cert(
raw_certificates: &[Vec<u8>],
cert_verifier: &Arc<dyn rustls::ServerCertVerifier>,
roots: &rustls::RootCertStore,
server_name: &str,
) -> Result<Vec<rustls::Certificate>, Error> {
let chains = load_certs(raw_certificates)?;
let dns_name = match webpki::DNSNameRef::try_from_ascii_str(server_name) {
Ok(dns_name) => dns_name,
Err(err) => return Err(Error::new(err.to_string())),
};

let chains = vec![certificate];
match cert_verifier.verify_server_cert(roots, &chains, dns_name, &[]) {
Ok(_) => {}
Err(err) => return Err(Error::new(err.to_string())),
};

Ok(chains)
}
Expand Down
26 changes: 14 additions & 12 deletions dtls/src/flight/flight4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,20 @@ impl Flight for Flight4 {
let mut chains = vec![];
let mut verified = false;
if cfg.client_auth as u8 >= ClientAuthType::VerifyClientCertIfGiven as u8 {
chains = match verify_cert(&state.peer_certificates /*, cfg.clientCAs*/) {
Ok(chains) => chains,
Err(err) => {
return Err((
Some(Alert {
alert_level: AlertLevel::Fatal,
alert_description: AlertDescription::BadCertificate,
}),
Some(err),
))
}
};
chains =
match verify_client_cert(&state.peer_certificates, &cfg.client_cert_verifier) {
Ok(chains) => chains,
Err(err) => {
return Err((
Some(Alert {
alert_level: AlertLevel::Fatal,
alert_description: AlertDescription::BadCertificate,
}),
Some(err),
))
}
};

verified = true
}
if let Some(verify_peer_certificate) = &cfg.verify_peer_certificate {
Expand Down

0 comments on commit e91381e

Please sign in to comment.