mirror of https://github.com/ctz/rustls
testing updates
This commit is contained in:
parent
078802cdd7
commit
82bdabbb0a
|
@ -14,5 +14,6 @@ rustc-serialize = "0.3"
|
|||
log = "0.3.6"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.3.3"
|
||||
mio = "0.5.1"
|
||||
docopt = "0.6"
|
||||
|
|
|
@ -4,10 +4,13 @@ use std::process;
|
|||
extern crate mio;
|
||||
use mio::tcp::TcpStream;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::str;
|
||||
use std::io;
|
||||
use std::io::{Read, Write, BufReader};
|
||||
|
||||
extern crate env_logger;
|
||||
|
||||
extern crate rustc_serialize;
|
||||
extern crate docopt;
|
||||
use docopt::Docopt;
|
||||
|
@ -73,9 +76,40 @@ impl io::Read for TlsClient {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_suite(name: &str) -> Option<&'static rustls::suites::SupportedCipherSuite> {
|
||||
for suite in rustls::suites::DEFAULT_CIPHERSUITES.iter() {
|
||||
let sname = format!("{:?}", suite.suite).to_lowercase();
|
||||
|
||||
if sname == name.to_string().to_lowercase() {
|
||||
return Some(suite);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn lookup_suites(suites: &Vec<String>) -> Vec<&'static rustls::suites::SupportedCipherSuite> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
for csname in suites {
|
||||
let scs = find_suite(csname);
|
||||
match scs {
|
||||
Some(s) => out.push(s),
|
||||
None => panic!("cannot look up ciphersuite '{}'", csname)
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
impl TlsClient {
|
||||
fn new(sock: TcpStream, hostname: &str, cafile: &str) -> TlsClient {
|
||||
fn new(sock: TcpStream, hostname: &str, cafile: &str, suites: &Vec<String>) -> TlsClient {
|
||||
let mut config = rustls::client::ClientConfig::default();
|
||||
|
||||
if suites.len() != 0 {
|
||||
config.ciphersuites = lookup_suites(suites);
|
||||
}
|
||||
|
||||
let certfile = std::fs::File::open(cafile)
|
||||
.unwrap();
|
||||
let mut reader = BufReader::new(certfile);
|
||||
|
@ -110,6 +144,7 @@ impl TlsClient {
|
|||
if rc.unwrap() == 0 {
|
||||
println!("EOF");
|
||||
self.closing = true;
|
||||
self.clean_closure = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -181,7 +216,7 @@ before making the connection. --http replaces this with a
|
|||
basic HTTP GET request for /.
|
||||
|
||||
Usage:
|
||||
tlsclient [-p PORT] [--http] [--cafile CAFILE] <hostname>
|
||||
tlsclient [--verbose] [-p PORT] [--http] [--cafile CAFILE] [--suite SUITE...] <hostname>
|
||||
tlsclient --version
|
||||
tlsclient --help
|
||||
|
||||
|
@ -189,6 +224,9 @@ Options:
|
|||
-p, --port PORT Connect to PORT. Default is 443.
|
||||
--http Send a basic HTTP GET request for /.
|
||||
--cafile CAFILE Read root certificates from CAFILE.
|
||||
--suite SUITE Disable default cipher suite list, and use
|
||||
SUITE instead.
|
||||
--verbose Emit log output.
|
||||
--version Show tool version.
|
||||
--help Show this screen.
|
||||
";
|
||||
|
@ -197,13 +235,26 @@ Options:
|
|||
struct Args {
|
||||
flag_port: Option<u16>,
|
||||
flag_http: bool,
|
||||
flag_verbose: bool,
|
||||
flag_suite: Vec<String>,
|
||||
flag_cafile: Option<String>,
|
||||
arg_hostname: String
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn lookup_ipv4(host: &str, port: u16) -> SocketAddr {
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
let addrs = (host, port).to_socket_addrs().unwrap();
|
||||
for addr in addrs {
|
||||
if let SocketAddr::V4(_) = addr {
|
||||
return addr.clone();
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("Cannot lookup address");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let version = env!("CARGO_PKG_NAME").to_string() + ", version: " + env!("CARGO_PKG_VERSION");
|
||||
|
||||
let args: Args = Docopt::new(USAGE)
|
||||
|
@ -212,17 +263,19 @@ fn main() {
|
|||
.and_then(|d| d.decode())
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
|
||||
let port = args.flag_port.unwrap_or(443);
|
||||
if args.flag_verbose {
|
||||
let mut logger = env_logger::LogBuilder::new();
|
||||
logger.parse("debug");
|
||||
logger.init().unwrap();
|
||||
}
|
||||
|
||||
let addr = (args.arg_hostname.as_str(), port).to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap();
|
||||
let port = args.flag_port.unwrap_or(443);
|
||||
let addr = lookup_ipv4(args.arg_hostname.as_str(), port);
|
||||
|
||||
let cafile = args.flag_cafile.unwrap_or("/etc/ssl/certs/ca-certificates.crt".to_string());
|
||||
|
||||
let sock = TcpStream::connect(&addr).unwrap();
|
||||
let mut tlsclient = TlsClient::new(sock, &args.arg_hostname, &cafile);
|
||||
let mut tlsclient = TlsClient::new(sock, &args.arg_hostname, &cafile, &args.flag_suite);
|
||||
|
||||
if args.flag_http {
|
||||
let httpreq = format!("GET / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", args.arg_hostname);
|
||||
|
|
|
@ -8,6 +8,7 @@ use msgs::handshake::ClientExtension;
|
|||
use msgs::handshake::{SupportedSignatureAlgorithms, SupportedMandatedSignatureAlgorithms};
|
||||
use msgs::handshake::{EllipticCurveList, SupportedCurves};
|
||||
use msgs::handshake::{ECPointFormatList, SupportedPointFormats};
|
||||
use msgs::handshake::ServerKeyExchangePayload;
|
||||
use msgs::ccs::ChangeCipherSpecPayload;
|
||||
use client::{ClientSession, ConnState};
|
||||
use suites;
|
||||
|
@ -156,6 +157,11 @@ fn handle_server_kx(sess: &mut ClientSession, m: &Message) -> Result<ConnState,
|
|||
sess.handshake_data.server_kx_sig = decoded_kx.get_sig();
|
||||
decoded_kx.encode_params(&mut sess.handshake_data.server_kx_params);
|
||||
|
||||
match decoded_kx {
|
||||
ServerKeyExchangePayload::ECDHE(ecdhe) => info!("ECDHE curve is {:?}", ecdhe.params.curve_params),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
Ok(ConnState::ExpectServerHelloDone)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@ mod verify;
|
|||
mod handshake;
|
||||
mod server_hs;
|
||||
mod client_hs;
|
||||
mod suites;
|
||||
pub mod suites;
|
||||
pub mod server;
|
||||
pub mod client;
|
||||
|
|
|
@ -15,4 +15,4 @@ keyUsage = cRLSign, keyCertSign, digitalSignature, nonRepudiation,keyEnciphermen
|
|||
[ alt_names ]
|
||||
DNS.1 = testserver.com
|
||||
DNS.2 = second.testserver.com
|
||||
|
||||
DNS.3 = localhost
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
openssl s_server -www -accept 8443 -key test/end.key -cert test/end.cert -CAfile test/end.chain -msg -debug -state > server.log 2>&1 &
|
||||
server=$!
|
||||
sleep 1
|
||||
|
||||
./target/debug/s_client > client.log 2>&1 &
|
||||
client=$!
|
||||
sleep 5
|
||||
|
||||
kill $server
|
||||
kill $client
|
|
@ -4,69 +4,12 @@
|
|||
* each test.
|
||||
*/
|
||||
|
||||
use std::process;
|
||||
#[allow(dead_code)]
|
||||
mod common;
|
||||
use common::{TlsClient, polite};
|
||||
|
||||
struct SClientTest {
|
||||
hostname: String,
|
||||
expect_fails: bool,
|
||||
expect_output: Option<String>
|
||||
}
|
||||
|
||||
fn connect(hostname: &str) -> SClientTest {
|
||||
SClientTest {
|
||||
hostname: hostname.to_string(),
|
||||
expect_fails: false,
|
||||
expect_output: None
|
||||
}
|
||||
}
|
||||
|
||||
impl SClientTest {
|
||||
fn expect(&mut self, expect: &str) -> &mut SClientTest {
|
||||
self.expect_output = Some(expect.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
fn fails(&mut self) -> &mut SClientTest {
|
||||
self.expect_fails = true;
|
||||
self
|
||||
}
|
||||
|
||||
fn go(&mut self) -> Option<()> {
|
||||
println!("cwd {:?}",
|
||||
process::Command::new("pwd")
|
||||
.output()
|
||||
.unwrap());
|
||||
|
||||
let output = process::Command::new("target/debug/examples/tlsclient")
|
||||
.arg("--http")
|
||||
.arg(&self.hostname)
|
||||
.output()
|
||||
.unwrap_or_else(|e| { panic!("failed to execute: {}", e) });
|
||||
|
||||
if self.expect_fails {
|
||||
assert!(output.status.code().unwrap() != 0);
|
||||
} else {
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
let stdout_str = String::from_utf8(output.stdout.clone()).unwrap();
|
||||
|
||||
if self.expect_output.is_some() && stdout_str.find(self.expect_output.as_ref().unwrap()).is_none() {
|
||||
println!("We expected to find '{}' in the following output:", self.expect_output.as_ref().unwrap());
|
||||
println!("{:?}", output);
|
||||
panic!("Test failed");
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
/* For tests which connect to internet servers, don't go crazy. */
|
||||
fn polite() {
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
fn connect(hostname: &str) -> TlsClient {
|
||||
TlsClient::new(hostname)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
|
||||
use std::process;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use std::net;
|
||||
|
||||
/* For tests which connect to internet servers, don't go crazy. */
|
||||
pub fn polite() {
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
pub struct TlsClient {
|
||||
pub hostname: String,
|
||||
pub port: u16,
|
||||
pub http: bool,
|
||||
pub cafile: Option<String>,
|
||||
pub suites: Vec<String>,
|
||||
pub verbose: bool,
|
||||
pub expect_fails: bool,
|
||||
pub expect_output: Option<String>,
|
||||
pub expect_log: Option<String>
|
||||
}
|
||||
|
||||
impl TlsClient {
|
||||
pub fn new(hostname: &str) -> TlsClient {
|
||||
TlsClient {
|
||||
hostname: hostname.to_string(),
|
||||
port: 443,
|
||||
http: true,
|
||||
cafile: None,
|
||||
verbose: false,
|
||||
suites: Vec::new(),
|
||||
expect_fails: false,
|
||||
expect_output: None,
|
||||
expect_log: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cafile(&mut self, cafile: &str) -> &mut TlsClient {
|
||||
self.cafile = Some(cafile.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn verbose(&mut self) -> &mut TlsClient {
|
||||
self.verbose = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn port(&mut self, port: u16) -> &mut TlsClient {
|
||||
self.port = port;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn expect(&mut self, expect: &str) -> &mut TlsClient {
|
||||
self.expect_output = Some(expect.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn expect_log(&mut self, expect: &str) -> &mut TlsClient {
|
||||
self.expect_log = Some(expect.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn suite(&mut self, suite: &str) -> &mut TlsClient {
|
||||
self.suites.push(suite.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fails(&mut self) -> &mut TlsClient {
|
||||
self.expect_fails = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn go(&mut self) -> Option<()> {
|
||||
let portstring = self.port.to_string();
|
||||
let mut args = Vec::<&str>::new();
|
||||
args.push(&self.hostname);
|
||||
|
||||
args.push("--port");
|
||||
args.push(&portstring);
|
||||
|
||||
if self.http {
|
||||
args.push("--http");
|
||||
}
|
||||
|
||||
if self.cafile.is_some() {
|
||||
args.push("--cafile");
|
||||
args.push(self.cafile.as_ref().unwrap());
|
||||
}
|
||||
|
||||
for suite in &self.suites {
|
||||
args.push("--suite");
|
||||
args.push(suite.as_ref());
|
||||
}
|
||||
|
||||
if self.verbose {
|
||||
args.push("--verbose");
|
||||
}
|
||||
|
||||
let output = process::Command::new("target/debug/examples/tlsclient")
|
||||
.args(&args)
|
||||
.output()
|
||||
.unwrap_or_else(|e| { panic!("failed to execute: {}", e) });
|
||||
|
||||
let stdout_str = String::from_utf8(output.stdout.clone()).unwrap();
|
||||
let stderr_str = String::from_utf8(output.stderr.clone()).unwrap();
|
||||
|
||||
if self.expect_output.is_some() && stdout_str.find(self.expect_output.as_ref().unwrap()).is_none() {
|
||||
println!("We expected to find '{}' in the following output:", self.expect_output.as_ref().unwrap());
|
||||
println!("{:?}", output);
|
||||
panic!("Test failed");
|
||||
}
|
||||
|
||||
if self.expect_log.is_some() && stderr_str.find(self.expect_log.as_ref().unwrap()).is_none() {
|
||||
println!("We expected to find '{}' in the following output:", self.expect_log.as_ref().unwrap());
|
||||
println!("{:?}", output);
|
||||
panic!("Test failed");
|
||||
}
|
||||
|
||||
if self.expect_fails {
|
||||
assert!(output.status.code().unwrap() != 0);
|
||||
} else {
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpenSSLServer {
|
||||
pub port: u16,
|
||||
pub http: bool,
|
||||
pub key: String,
|
||||
pub cert: String,
|
||||
pub chain: String,
|
||||
pub extra_args: Vec<&'static str>,
|
||||
pub child: Option<process::Child>
|
||||
}
|
||||
|
||||
fn unused_port(mut port: u16) -> u16 {
|
||||
loop {
|
||||
if let Err(_) = net::TcpStream::connect(("127.0.0.1", port)) {
|
||||
return port;
|
||||
}
|
||||
|
||||
port += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenSSLServer {
|
||||
pub fn new(start_port: u16) -> OpenSSLServer {
|
||||
OpenSSLServer {
|
||||
port: unused_port(start_port),
|
||||
http: true,
|
||||
key: "test-ca/end.key".to_string(),
|
||||
cert: "test-ca/end.cert".to_string(),
|
||||
chain: "test-ca/end.chain".to_string(),
|
||||
extra_args: Vec::new(),
|
||||
child: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arg(&mut self, arg: &'static str) -> &mut Self {
|
||||
self.extra_args.push(arg);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> &mut Self {
|
||||
let mut extra_args = Vec::<&'static str>::new();
|
||||
extra_args.extend(&self.extra_args);
|
||||
if self.http {
|
||||
extra_args.push("-www");
|
||||
}
|
||||
|
||||
println!("args = {:?}", extra_args);
|
||||
|
||||
let child = process::Command::new("openssl")
|
||||
.arg("s_server")
|
||||
.arg("-accept").arg(self.port.to_string())
|
||||
.arg("-key").arg(&self.key)
|
||||
.arg("-cert").arg(&self.cert)
|
||||
.arg("-CAfile").arg(&self.chain)
|
||||
.args(&extra_args)
|
||||
.stdout(process::Stdio::null())
|
||||
.stderr(process::Stdio::null())
|
||||
.spawn()
|
||||
.expect("cannot run openssl server");
|
||||
|
||||
self.wait_for_port().expect("server did not come up");
|
||||
self.child = Some(child);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn kill(&mut self) {
|
||||
self.child.as_mut().unwrap().kill().unwrap();
|
||||
self.child = None;
|
||||
}
|
||||
|
||||
pub fn client(&self) -> TlsClient {
|
||||
let mut c = TlsClient::new("localhost");
|
||||
c.port(self.port);
|
||||
c.cafile("test-ca/ca.cert");
|
||||
c
|
||||
}
|
||||
|
||||
fn wait_for_port(&self) -> Option<()> {
|
||||
let mut count = 0;
|
||||
loop {
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
if let Ok(_) = net::TcpStream::connect(("127.0.0.1", self.port)) {
|
||||
return Some(())
|
||||
}
|
||||
count += 1;
|
||||
if count == 10 {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* Engineer a handshake using each curve. */
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod common;
|
||||
use common::OpenSSLServer;
|
||||
|
||||
#[test]
|
||||
fn curve_nistp256() {
|
||||
let mut server = OpenSSLServer::new(8300);
|
||||
server.arg("-named_curve").arg("prime256v1");
|
||||
server.run();
|
||||
server.client()
|
||||
.verbose()
|
||||
.expect_log("ECDHE curve is ECParameters { curve_type: NamedCurve, named_curve: secp256r1 }")
|
||||
.go();
|
||||
server.kill();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curve_nistp384() {
|
||||
let mut server = OpenSSLServer::new(8400);
|
||||
server.arg("-named_curve").arg("secp384r1");
|
||||
server.run();
|
||||
server.client()
|
||||
.verbose()
|
||||
.expect_log("ECDHE curve is ECParameters { curve_type: NamedCurve, named_curve: secp384r1 }")
|
||||
.go();
|
||||
server.kill();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/* Engineer a handshake using each suite. */
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod common;
|
||||
use common::OpenSSLServer;
|
||||
|
||||
#[test]
|
||||
fn ecdhe_rsa_aes_128_gcm_sha256() {
|
||||
let mut server = OpenSSLServer::new(8100);
|
||||
server.run();
|
||||
server.client()
|
||||
.verbose()
|
||||
.suite("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
|
||||
.expect("Ciphers common between both SSL end points:\nECDHE-RSA-AES128-GCM-SHA256")
|
||||
.go();
|
||||
server.kill();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecdhe_rsa_aes_256_gcm_sha384() {
|
||||
let mut server = OpenSSLServer::new(8200);
|
||||
server.run();
|
||||
server.client()
|
||||
.verbose()
|
||||
.suite("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
|
||||
.expect("Ciphers common between both SSL end points:\nECDHE-RSA-AES256-GCM-SHA384")
|
||||
.go();
|
||||
server.kill();
|
||||
}
|
Loading…
Reference in New Issue