| // This is a test shim for the BoringSSL-Go ('bogo') TLS |
| // test suite. See bogo/ for this in action. |
| // |
| // https://boringssl.googlesource.com/boringssl/+/master/ssl/test |
| // |
| |
| extern crate rustls; |
| extern crate webpki; |
| extern crate env_logger; |
| extern crate base64; |
| extern crate sct; |
| |
| use std::env; |
| use std::process; |
| use std::net; |
| use std::fs; |
| use std::io; |
| use std::io::BufReader; |
| use std::io::{Write, Read}; |
| use std::sync::Arc; |
| use rustls::internal::msgs::enums::ProtocolVersion; |
| use rustls::quic::ClientQuicExt; |
| use rustls::quic::ServerQuicExt; |
| |
| static BOGO_NACK: i32 = 89; |
| |
| macro_rules! println_err( |
| ($($arg:tt)*) => { { |
| writeln!(&mut ::std::io::stderr(), $($arg)*).unwrap(); |
| } } |
| ); |
| |
| #[derive(Debug)] |
| struct Options { |
| port: u16, |
| server: bool, |
| mtu: Option<usize>, |
| resumes: usize, |
| verify_peer: bool, |
| require_any_client_cert: bool, |
| offer_no_client_cas: bool, |
| tickets: bool, |
| queue_data: bool, |
| shut_down_after_handshake: bool, |
| check_close_notify: bool, |
| host_name: String, |
| use_sni: bool, |
| send_sct: bool, |
| key_file: String, |
| cert_file: String, |
| protocols: Vec<String>, |
| support_tls13: bool, |
| support_tls12: bool, |
| min_version: Option<ProtocolVersion>, |
| max_version: Option<ProtocolVersion>, |
| server_ocsp_response: Vec<u8>, |
| server_sct_list: Vec<u8>, |
| use_signing_scheme: u16, |
| expect_curve: u16, |
| export_keying_material: usize, |
| export_keying_material_label: String, |
| export_keying_material_context: String, |
| export_keying_material_context_used: bool, |
| read_size: usize, |
| quic_transport_params: Vec<u8>, |
| expect_quic_transport_params: Vec<u8>, |
| } |
| |
| impl Options { |
| fn new() -> Options { |
| Options { |
| port: 0, |
| server: false, |
| mtu: None, |
| resumes: 0, |
| verify_peer: false, |
| tickets: true, |
| host_name: "example.com".to_string(), |
| use_sni: false, |
| send_sct: false, |
| queue_data: false, |
| shut_down_after_handshake: false, |
| check_close_notify: false, |
| require_any_client_cert: false, |
| offer_no_client_cas: false, |
| key_file: "".to_string(), |
| cert_file: "".to_string(), |
| protocols: vec![], |
| support_tls13: true, |
| support_tls12: true, |
| min_version: None, |
| max_version: None, |
| server_ocsp_response: vec![], |
| server_sct_list: vec![], |
| use_signing_scheme: 0, |
| expect_curve: 0, |
| export_keying_material: 0, |
| export_keying_material_label: "".to_string(), |
| export_keying_material_context: "".to_string(), |
| export_keying_material_context_used: false, |
| read_size: 512, |
| quic_transport_params: vec![], |
| expect_quic_transport_params: vec![], |
| } |
| } |
| |
| fn version_allowed(&self, vers: ProtocolVersion) -> bool { |
| (self.min_version.is_none() || vers.get_u16() >= self.min_version.unwrap().get_u16()) && |
| (self.max_version.is_none() || vers.get_u16() <= self.max_version.unwrap().get_u16()) |
| } |
| |
| fn tls13_supported(&self) -> bool { |
| self.support_tls13 && (self.version_allowed(ProtocolVersion::TLSv1_3) || |
| self.version_allowed(ProtocolVersion::Unknown(0x7f12))) |
| } |
| |
| fn tls12_supported(&self) -> bool { |
| self.support_tls12 && self.version_allowed(ProtocolVersion::TLSv1_2) |
| } |
| } |
| |
| fn load_cert(filename: &str) -> Vec<rustls::Certificate> { |
| let certfile = fs::File::open(filename).expect("cannot open certificate file"); |
| let mut reader = BufReader::new(certfile); |
| rustls::internal::pemfile::certs(&mut reader).unwrap() |
| } |
| |
| fn load_key(filename: &str) -> rustls::PrivateKey { |
| let keyfile = fs::File::open(filename).expect("cannot open private key file"); |
| let mut reader = BufReader::new(keyfile); |
| let keys = rustls::internal::pemfile::pkcs8_private_keys(&mut reader).unwrap(); |
| assert!(keys.len() == 1); |
| keys[0].clone() |
| } |
| |
| fn split_protocols(protos: &str) -> Vec<String> { |
| let mut ret = Vec::new(); |
| |
| let mut offs = 0; |
| while offs < protos.len() { |
| let len = protos.as_bytes()[offs] as usize; |
| let item = protos[offs + 1..offs + 1 + len].to_string(); |
| ret.push(item); |
| offs += 1 + len; |
| } |
| |
| ret |
| } |
| |
| struct DummyClientAuth { |
| mandatory: bool, |
| } |
| |
| impl rustls::ClientCertVerifier for DummyClientAuth { |
| fn offer_client_auth(&self) -> bool { true } |
| |
| fn client_auth_mandatory(&self) -> bool { self.mandatory } |
| |
| fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames { |
| rustls::DistinguishedNames::new() |
| } |
| |
| fn verify_client_cert(&self, |
| _certs: &[rustls::Certificate]) -> Result<rustls::ClientCertVerified, rustls::TLSError> { |
| Ok(rustls::ClientCertVerified::assertion()) |
| } |
| } |
| |
| struct DummyServerAuth {} |
| |
| impl rustls::ServerCertVerifier for DummyServerAuth { |
| fn verify_server_cert(&self, |
| _roots: &rustls::RootCertStore, |
| _certs: &[rustls::Certificate], |
| _hostname: webpki::DNSNameRef, |
| _ocsp: &[u8]) -> Result<rustls::ServerCertVerified, rustls::TLSError> { |
| Ok(rustls::ServerCertVerified::assertion()) |
| } |
| } |
| |
| struct FixedSignatureSchemeSigningKey { |
| key: Arc<Box<rustls::sign::SigningKey>>, |
| scheme: rustls::SignatureScheme, |
| } |
| |
| impl rustls::sign::SigningKey for FixedSignatureSchemeSigningKey { |
| fn choose_scheme(&self, offered: &[rustls::SignatureScheme]) -> Option<Box<rustls::sign::Signer>> { |
| if offered.contains(&self.scheme) { |
| self.key.choose_scheme(&[self.scheme]) |
| } else { |
| self.key.choose_scheme(&[]) |
| } |
| } |
| fn algorithm(&self) -> rustls::internal::msgs::enums::SignatureAlgorithm { self.key.algorithm() } |
| } |
| |
| struct FixedSignatureSchemeServerCertResolver { |
| resolver: Arc<rustls::ResolvesServerCert>, |
| scheme: rustls::SignatureScheme, |
| } |
| |
| impl rustls::ResolvesServerCert for FixedSignatureSchemeServerCertResolver { |
| fn resolve(&self, |
| server_name: Option<webpki::DNSNameRef>, |
| sigschemes: &[rustls::SignatureScheme]) -> Option<rustls::sign::CertifiedKey> { |
| let mut certkey = self.resolver.resolve(server_name, sigschemes)?; |
| certkey.key = Arc::new(Box::new(FixedSignatureSchemeSigningKey { |
| key: certkey.key.clone(), |
| scheme: self.scheme, |
| })); |
| Some(certkey) |
| } |
| } |
| |
| struct FixedSignatureSchemeClientCertResolver { |
| resolver: Arc<rustls::ResolvesClientCert>, |
| scheme: rustls::SignatureScheme, |
| } |
| |
| impl rustls::ResolvesClientCert for FixedSignatureSchemeClientCertResolver { |
| fn resolve(&self, |
| acceptable_issuers: &[&[u8]], |
| sigschemes: &[rustls::SignatureScheme]) -> Option<rustls::sign::CertifiedKey> { |
| if !sigschemes.contains(&self.scheme) { |
| quit(":NO_COMMON_SIGNATURE_ALGORITHMS:"); |
| } |
| let mut certkey = self.resolver.resolve(acceptable_issuers, sigschemes)?; |
| certkey.key = Arc::new(Box::new(FixedSignatureSchemeSigningKey { |
| key: certkey.key.clone(), |
| scheme: self.scheme, |
| })); |
| Some(certkey) |
| } |
| |
| fn has_certs(&self) -> bool { self.resolver.has_certs() } |
| } |
| |
| fn lookup_scheme(scheme: u16) -> rustls::SignatureScheme { |
| match scheme { |
| 0x0401 => rustls::SignatureScheme::RSA_PKCS1_SHA256, |
| 0x0501 => rustls::SignatureScheme::RSA_PKCS1_SHA384, |
| 0x0601 => rustls::SignatureScheme::RSA_PKCS1_SHA512, |
| 0x0403 => rustls::SignatureScheme::ECDSA_NISTP256_SHA256, |
| 0x0503 => rustls::SignatureScheme::ECDSA_NISTP384_SHA384, |
| 0x0804 => rustls::SignatureScheme::RSA_PSS_SHA256, |
| 0x0805 => rustls::SignatureScheme::RSA_PSS_SHA384, |
| 0x0806 => rustls::SignatureScheme::RSA_PSS_SHA512, |
| _ => { |
| println_err!("Unsupported signature scheme {:04x}", scheme); |
| process::exit(BOGO_NACK); |
| } |
| } |
| } |
| |
| fn make_server_cfg(opts: &Options) -> Arc<rustls::ServerConfig> { |
| let client_auth = |
| if opts.verify_peer || opts.offer_no_client_cas || opts.require_any_client_cert { |
| Arc::new(DummyClientAuth { mandatory: opts.require_any_client_cert }) |
| } else { |
| rustls::NoClientAuth::new() |
| }; |
| |
| let mut cfg = rustls::ServerConfig::new(client_auth); |
| let persist = rustls::ServerSessionMemoryCache::new(32); |
| cfg.set_persistence(persist); |
| |
| cfg.mtu = opts.mtu; |
| |
| let cert = load_cert(&opts.cert_file); |
| let key = load_key(&opts.key_file); |
| cfg.set_single_cert_with_ocsp_and_sct(cert.clone(), key, |
| opts.server_ocsp_response.clone(), |
| opts.server_sct_list.clone()) |
| .unwrap(); |
| if opts.use_signing_scheme > 0 { |
| let scheme = lookup_scheme(opts.use_signing_scheme); |
| cfg.cert_resolver = Arc::new(FixedSignatureSchemeServerCertResolver { |
| resolver: cfg.cert_resolver.clone(), |
| scheme |
| }); |
| } |
| |
| if opts.tickets { |
| cfg.ticketer = rustls::Ticketer::new(); |
| } |
| |
| if !opts.protocols.is_empty() { |
| cfg.set_protocols(&opts.protocols); |
| } |
| |
| cfg.versions.clear(); |
| |
| if opts.tls12_supported() { |
| cfg.versions.push(ProtocolVersion::TLSv1_2); |
| } |
| |
| if opts.tls13_supported() { |
| cfg.versions.push(ProtocolVersion::TLSv1_3); |
| } |
| |
| Arc::new(cfg) |
| } |
| |
| static EMPTY_LOGS: [&sct::Log; 0] = []; |
| |
| struct ClientCacheWithoutKxHints(Arc<rustls::ClientSessionMemoryCache>); |
| |
| impl ClientCacheWithoutKxHints { |
| fn new() -> Arc<ClientCacheWithoutKxHints> { |
| Arc::new(ClientCacheWithoutKxHints(rustls::ClientSessionMemoryCache::new(32))) |
| } |
| } |
| |
| impl rustls::StoresClientSessions for ClientCacheWithoutKxHints { |
| fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool { |
| if key.len() > 2 && key[0] == b'k' && key[1] == b'x' { |
| true |
| } else { |
| self.0.put(key, value) |
| } |
| } |
| |
| fn get(&self, key: &[u8]) -> Option<Vec<u8>> { |
| self.0.get(key) |
| } |
| } |
| |
| fn make_client_cfg(opts: &Options) -> Arc<rustls::ClientConfig> { |
| let mut cfg = rustls::ClientConfig::new(); |
| let persist = ClientCacheWithoutKxHints::new(); |
| cfg.set_persistence(persist); |
| cfg.root_store.add(&load_cert("cert.pem")[0]).unwrap(); |
| cfg.enable_sni = opts.use_sni; |
| cfg.mtu = opts.mtu; |
| |
| if opts.send_sct { |
| cfg.ct_logs = Some(&EMPTY_LOGS); |
| } |
| |
| if !opts.cert_file.is_empty() && !opts.key_file.is_empty() { |
| let cert = load_cert(&opts.cert_file); |
| let key = load_key(&opts.key_file); |
| cfg.set_single_client_cert(cert, key); |
| } |
| |
| if !opts.cert_file.is_empty() && opts.use_signing_scheme > 0 { |
| let scheme = lookup_scheme(opts.use_signing_scheme); |
| cfg.client_auth_cert_resolver = Arc::new(FixedSignatureSchemeClientCertResolver { |
| resolver: cfg.client_auth_cert_resolver.clone(), |
| scheme |
| }); |
| } |
| |
| cfg.dangerous() |
| .set_certificate_verifier(Arc::new(DummyServerAuth {})); |
| |
| if !opts.protocols.is_empty() { |
| cfg.set_protocols(&opts.protocols); |
| } |
| |
| cfg.versions.clear(); |
| |
| if opts.tls12_supported() { |
| cfg.versions.push(ProtocolVersion::TLSv1_2); |
| } |
| |
| if opts.tls13_supported() { |
| cfg.versions.push(ProtocolVersion::TLSv1_3); |
| } |
| |
| Arc::new(cfg) |
| } |
| |
| fn quit(why: &str) -> ! { |
| println_err!("{}", why); |
| process::exit(0) |
| } |
| |
| fn quit_err(why: &str) -> ! { |
| println_err!("{}", why); |
| process::exit(1) |
| } |
| |
| fn handle_err(err: rustls::TLSError) -> ! { |
| use rustls::TLSError; |
| use rustls::internal::msgs::enums::{AlertDescription, ContentType}; |
| use std::{thread, time}; |
| |
| println!("TLS error: {:?}", err); |
| thread::sleep(time::Duration::from_millis(100)); |
| |
| match err { |
| TLSError::InappropriateHandshakeMessage { .. } | |
| TLSError::InappropriateMessage { .. } => quit(":UNEXPECTED_MESSAGE:"), |
| TLSError::AlertReceived(AlertDescription::RecordOverflow) => { |
| quit(":TLSV1_ALERT_RECORD_OVERFLOW:") |
| } |
| TLSError::AlertReceived(AlertDescription::HandshakeFailure) => quit(":HANDSHAKE_FAILURE:"), |
| TLSError::CorruptMessagePayload(ContentType::Alert) => quit(":BAD_ALERT:"), |
| TLSError::CorruptMessagePayload(ContentType::ChangeCipherSpec) => { |
| quit(":BAD_CHANGE_CIPHER_SPEC:") |
| } |
| TLSError::CorruptMessagePayload(ContentType::Handshake) => quit(":BAD_HANDSHAKE_MSG:"), |
| TLSError::CorruptMessagePayload(ContentType::Unknown(42)) => { |
| quit(":GARBAGE:") |
| } |
| TLSError::CorruptMessage => quit(":GARBAGE:"), |
| TLSError::DecryptError => quit(":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"), |
| TLSError::PeerIncompatibleError(_) => quit(":INCOMPATIBLE:"), |
| TLSError::PeerMisbehavedError(_) => quit(":PEER_MISBEHAVIOUR:"), |
| TLSError::NoCertificatesPresented => quit(":NO_CERTS:"), |
| TLSError::AlertReceived(AlertDescription::UnexpectedMessage) => { |
| quit(":BAD_ALERT:") |
| } |
| TLSError::AlertReceived(AlertDescription::DecompressionFailure) => { |
| quit_err(":SSLV3_ALERT_DECOMPRESSION_FAILURE:") |
| } |
| TLSError::WebPKIError(webpki::Error::BadDER) => { |
| quit(":CANNOT_PARSE_LEAF_CERT:") |
| } |
| TLSError::WebPKIError(webpki::Error::InvalidSignatureForPublicKey) => { |
| quit(":BAD_SIGNATURE:") |
| } |
| TLSError::WebPKIError(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => { |
| quit(":WRONG_SIGNATURE_TYPE:") |
| } |
| TLSError::PeerSentOversizedRecord => { |
| quit(":DATA_LENGTH_TOO_LONG:") |
| } |
| _ => { |
| println_err!("unhandled error: {:?}", err); |
| quit(":FIXME:") |
| } |
| } |
| } |
| |
| fn flush(sess: &mut Box<rustls::Session>, conn: &mut net::TcpStream) { |
| while sess.wants_write() { |
| match sess.write_tls(conn) { |
| Err(err) => { |
| println!("IO error: {:?}", err); |
| process::exit(0); |
| } |
| Ok(_) => {} |
| } |
| } |
| conn.flush().unwrap(); |
| } |
| |
| fn exec(opts: &Options, sess: &mut Box<rustls::Session>) { |
| if opts.queue_data { |
| sess.write_all(b"hello world") |
| .unwrap(); |
| } |
| |
| let mut conn = net::TcpStream::connect(("localhost", opts.port)).expect("cannot connect"); |
| let mut sent_shutdown = false; |
| let mut seen_eof = false; |
| let mut sent_exporter = false; |
| |
| loop { |
| flush(sess, &mut conn); |
| |
| if sess.wants_read() { |
| let len = sess.read_tls(&mut conn) |
| .expect("read failed"); |
| |
| if len == 0 { |
| if opts.check_close_notify { |
| if !seen_eof { |
| seen_eof = true; |
| } else { |
| quit_err(":CLOSE_WITHOUT_CLOSE_NOTIFY:"); |
| } |
| } else { |
| println!("EOF (plain)"); |
| return; |
| } |
| } |
| |
| if let Err(err) = sess.process_new_packets() { |
| flush(sess, &mut conn); /* send any alerts before exiting */ |
| handle_err(err); |
| } |
| } |
| |
| if !sess.is_handshaking() && |
| opts.export_keying_material > 0 && |
| !sent_exporter { |
| let mut export = Vec::new(); |
| export.resize(opts.export_keying_material, 0u8); |
| sess.export_keying_material(&mut export, |
| opts.export_keying_material_label.as_bytes(), |
| if opts.export_keying_material_context_used { |
| Some(opts.export_keying_material_context.as_bytes()) |
| } else { None }) |
| .unwrap(); |
| sess.write_all(&export) |
| .unwrap(); |
| sent_exporter = true; |
| } |
| |
| if !sess.is_handshaking() && |
| !opts.expect_quic_transport_params.is_empty() { |
| let their_transport_params = sess.get_quic_transport_parameters() |
| .expect("missing peer quic transport params"); |
| assert_eq!(opts.expect_quic_transport_params, |
| their_transport_params); |
| } |
| |
| let mut buf = [0u8; 1024]; |
| let len = match sess.read(&mut buf[..opts.read_size]) { |
| Ok(len) => len, |
| Err(ref err) if err.kind() == io::ErrorKind::ConnectionAborted => { |
| if opts.check_close_notify { |
| println!("close notify ok"); |
| } |
| println!("EOF (tls)"); |
| return; |
| } |
| Err(err) => panic!("unhandled read error {:?}", err), |
| }; |
| |
| if opts.shut_down_after_handshake && |
| !sent_shutdown && |
| !sess.is_handshaking() { |
| sess.send_close_notify(); |
| sent_shutdown = true; |
| } |
| |
| for b in buf.iter_mut() { |
| *b ^= 0xff; |
| } |
| |
| sess.write_all(&buf[..len]).unwrap(); |
| } |
| } |
| |
| fn main() { |
| let mut args: Vec<_> = env::args().collect(); |
| env_logger::init(); |
| |
| args.remove(0); |
| println!("options: {:?}", args); |
| |
| let mut opts = Options::new(); |
| |
| while !args.is_empty() { |
| let arg = args.remove(0); |
| match arg.as_ref() { |
| "-port" => { |
| opts.port = args.remove(0).parse::<u16>().unwrap(); |
| } |
| "-server" => { |
| opts.server = true; |
| } |
| "-key-file" => { |
| opts.key_file = args.remove(0); |
| } |
| "-cert-file" => { |
| opts.cert_file = args.remove(0); |
| } |
| "-resume-count" => { |
| opts.resumes = args.remove(0).parse::<usize>().unwrap(); |
| } |
| "-no-tls13" => { |
| opts.support_tls13 = false; |
| } |
| "-no-tls12" => { |
| opts.support_tls12 = false; |
| } |
| "-min-version" => { |
| let min = args.remove(0).parse::<u16>().unwrap(); |
| opts.min_version = Some(ProtocolVersion::Unknown(min)); |
| } |
| "-max-version" => { |
| let max = args.remove(0).parse::<u16>().unwrap(); |
| opts.max_version = Some(ProtocolVersion::Unknown(max)); |
| } |
| "-max-send-fragment" => { |
| let mtu = args.remove(0).parse::<usize>().unwrap(); |
| opts.mtu = Some(mtu); |
| } |
| "-read-size" => { |
| let rdsz = args.remove(0).parse::<usize>().unwrap(); |
| opts.read_size = rdsz; |
| } |
| "-tls13-variant" => { |
| let variant = args.remove(0).parse::<u16>().unwrap(); |
| if variant != 5 { |
| println!("NYI TLS1.3 variant selection: {:?} {:?}", arg, variant); |
| process::exit(BOGO_NACK); |
| } |
| } |
| "-signing-prefs" => { |
| let alg = args.remove(0).parse::<u16>().unwrap(); |
| opts.use_signing_scheme = alg; |
| } |
| "-max-cert-list" | |
| "-expect-curve-id" | |
| "-expect-resume-curve-id" | |
| "-expect-peer-signature-algorithm" | |
| "-expect-advertised-alpn" | |
| "-expect-alpn" | |
| "-expect-server-name" | |
| "-expect-ocsp-response" | |
| "-expect-signed-cert-timestamps" | |
| "-expect-certificate-types" | |
| "-expect-client-ca-list" | |
| "-expect-msg-callback" => { |
| println!("not checking {} {}; NYI", arg, args.remove(0)); |
| } |
| |
| "-expect-secure-renegotiation" | |
| "-expect-no-session-id" | |
| "-enable-ed25519" | |
| "-expect-session-id" => { |
| println!("not checking {}; NYI", arg); |
| } |
| |
| "-export-keying-material" => { |
| opts.export_keying_material = args.remove(0).parse::<usize>().unwrap(); |
| } |
| "-export-label" => { |
| opts.export_keying_material_label = args.remove(0); |
| } |
| "-export-context" => { |
| opts.export_keying_material_context = args.remove(0); |
| } |
| "-use-export-context" => { |
| opts.export_keying_material_context_used = true; |
| } |
| "-quic-transport-params" => { |
| opts.quic_transport_params = base64::decode(args.remove(0).as_bytes()) |
| .expect("invalid base64"); |
| } |
| "-expected-quic-transport-params" => { |
| opts.expect_quic_transport_params = base64::decode(args.remove(0).as_bytes()) |
| .expect("invalid base64"); |
| } |
| |
| "-ocsp-response" => { |
| opts.server_ocsp_response = base64::decode(args.remove(0).as_bytes()) |
| .expect("invalid base64"); |
| } |
| "-signed-cert-timestamps" => { |
| opts.server_sct_list = base64::decode(args.remove(0).as_bytes()) |
| .expect("invalid base64"); |
| |
| if opts.server_sct_list.len() == 2 && |
| opts.server_sct_list[0] == 0x00 && |
| opts.server_sct_list[1] == 0x00 { |
| quit(":INVALID_SCT_LIST:"); |
| } |
| } |
| "-select-alpn" => { |
| opts.protocols.push(args.remove(0)); |
| } |
| "-require-any-client-certificate" => { |
| opts.require_any_client_cert = true; |
| } |
| "-verify-peer" => { |
| opts.verify_peer = true; |
| } |
| "-shim-writes-first" => { |
| opts.queue_data = true; |
| } |
| "-shim-shuts-down" => { |
| opts.shut_down_after_handshake = true; |
| } |
| "-check-close-notify" => { |
| opts.check_close_notify = true; |
| } |
| "-host-name" => { |
| opts.host_name = args.remove(0); |
| opts.use_sni = true; |
| } |
| "-advertise-alpn" => { |
| opts.protocols = split_protocols(&args.remove(0)); |
| } |
| "-use-null-client-ca-list" => { |
| opts.offer_no_client_cas = true; |
| } |
| "-enable-signed-cert-timestamps" => { |
| opts.send_sct = true; |
| } |
| |
| // defaults: |
| "-enable-all-curves" | |
| "-renegotiate-ignore" | |
| "-no-tls11" | |
| "-no-tls1" | |
| "-no-ssl3" | |
| "-handoff" | |
| "-decline-alpn" | |
| "-expect-no-session" | |
| "-expect-session-miss" | |
| "-expect-extended-master-secret" | |
| "-expect-ticket-renewal" | |
| "-enable-ocsp-stapling" | |
| // internal openssl details: |
| "-async" | |
| "-implicit-handshake" | |
| "-use-old-client-cert-callback" | |
| "-use-early-callback" => {} |
| |
| // Not implemented things |
| "-dtls" | |
| "-cipher" | |
| "-psk" | |
| "-renegotiate-freely" | |
| "-false-start" | |
| "-fallback-scsv" | |
| "-fail-early-callback" | |
| "-fail-cert-callback" | |
| "-install-ddos-callback" | |
| "-advertise-npn" | |
| "-verify-fail" | |
| "-expect-channel-id" | |
| "-send-channel-id" | |
| "-select-next-proto" | |
| "-p384-only" | |
| "-expect-verify-result" | |
| "-send-alert" | |
| "-digest-prefs" | |
| "-use-exporter-between-reads" | |
| "-ticket-key" | |
| "-tls-unique" | |
| "-enable-server-custom-extension" | |
| "-enable-client-custom-extension" | |
| "-expect-dhe-group-size" | |
| "-use-ticket-callback" | |
| "-enable-grease" | |
| "-enable-channel-id" | |
| "-resumption-delay" | |
| "-expect-early-data-info" | |
| "-enable-early-data" | |
| "-expect-cipher-aes" | |
| "-retain-only-sha256-client-cert-initial" | |
| "-use-client-ca-list" | |
| "-expect-draft-downgrade" | |
| "-allow-unknown-alpn-protos" | |
| "-on-initial-tls13-variant" | |
| "-on-initial-expect-curve-id" | |
| "-on-resume-export-early-keying-material" | |
| "-export-early-keying-material" | |
| "-handshake-twice" | |
| "-verify-prefs" | |
| "-no-op-extra-handshake" | |
| "-on-resume-enable-early-data" | |
| "-read-with-unfinished-write" | |
| "-expect-peer-cert-file" => { |
| println!("NYI option {:?}", arg); |
| process::exit(BOGO_NACK); |
| } |
| |
| _ => { |
| println!("unhandled option {:?}", arg); |
| process::exit(1); |
| } |
| } |
| } |
| |
| println!("opts {:?}", opts); |
| |
| let server_cfg = if opts.server { |
| Some(make_server_cfg(&opts)) |
| } else { |
| None |
| }; |
| let client_cfg = if !opts.server { |
| Some(make_client_cfg(&opts)) |
| } else { |
| None |
| }; |
| |
| let make_session = || { |
| if opts.server { |
| let s = if opts.quic_transport_params.is_empty() { |
| rustls::ServerSession::new(server_cfg.as_ref().unwrap()) |
| } else { |
| rustls::ServerSession::new_quic(server_cfg.as_ref().unwrap(), |
| opts.quic_transport_params.clone()) |
| |
| }; |
| Box::new(s) as Box<rustls::Session> |
| } else { |
| let dns_name = |
| webpki::DNSNameRef::try_from_ascii_str(&opts.host_name).unwrap(); |
| let s = if opts.quic_transport_params.is_empty() { |
| rustls::ClientSession::new(client_cfg.as_ref().unwrap(), |
| dns_name) |
| } else { |
| rustls::ClientSession::new_quic(client_cfg.as_ref().unwrap(), |
| dns_name, |
| opts.quic_transport_params.clone()) |
| }; |
| Box::new(s) as Box<rustls::Session> |
| } |
| }; |
| |
| for _ in 0..opts.resumes + 1 { |
| let mut sess = make_session(); |
| exec(&opts, &mut sess); |
| } |
| } |