From eee32457d452bedb5dab9ec3f82699757f2aed36 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Wed, 16 Oct 2019 22:28:50 +0700 Subject: [PATCH 01/20] Add the beginnings of ESNI --- rustls/src/msgs/enums.rs | 3 ++- rustls/src/msgs/handshake.rs | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/rustls/src/msgs/enums.rs b/rustls/src/msgs/enums.rs index f3e4d040daa..6d5f42b149c 100644 --- a/rustls/src/msgs/enums.rs +++ b/rustls/src/msgs/enums.rs @@ -235,7 +235,8 @@ enum_builder! { NextProtocolNegotiation => 0x3374, ChannelId => 0x754f, RenegotiationInfo => 0xff01, - TransportParameters => 0xffa5 + TransportParameters => 0xffa5, + EncryptedServerName => 0xffce } } diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index e342dbe2a03..17cf6b4ee45 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -329,6 +329,55 @@ impl ConvertServerNameList for ServerNameRequest { } } +// --- TLS 1.3 Encrypted SNI + +#[derive(Clone, Debug)] +pub struct ClientEncryptedSNI { + cipher: CipherSuite, + key_share_entry: KeyShareEntry, + record_digest: PayloadU16, + encrypted_sni: PayloadU16, +} + +impl ClientEncryptedSNI { + pub fn new(cipher: CipherSuite, + key_share_entry: KeyShareEntry, + record_digest: PayloadU16, + encrypted_sni: PayloadU16) -> ClientEncryptedSNI { + ClientEncryptedSNI { + cipher, + key_share_entry, + record_digest, + encrypted_sni + } + } +} + +impl Codec for ClientEncryptedSNI { + fn encode(&self, bytes: &mut Vec) { + self.cipher.encode(bytes); + self.key_share_entry.encode(bytes); + self.record_digest.encode(bytes); + self.encrypted_sni.encode(bytes); + } + + fn read(r: &mut Reader) -> Option { + let cipher = CipherSuite::read(r)?; + let key_share_entry = KeyShareEntry::read(r)?; + let record_digest = PayloadU16::read(r)?; + let encrypted_sni = PayloadU16::read(r)?; + + Some(ClientEncryptedSNI { + cipher, + key_share_entry, + record_digest, + encrypted_sni + }) + } +} + +declare_u16_vec!(ESNIRequest, ClientEncryptedSNI); + pub type ProtocolNameList = VecU16OfPayloadU8; pub trait ConvertProtocolNameList { @@ -557,6 +606,7 @@ pub enum ClientExtension { SignedCertificateTimestampRequest, TransportParameters(Vec), EarlyData, + EncryptedServerName(ESNIRequest), Unknown(UnknownExtension), } @@ -580,6 +630,7 @@ impl ClientExtension { ClientExtension::SignedCertificateTimestampRequest => ExtensionType::SCT, ClientExtension::TransportParameters(_) => ExtensionType::TransportParameters, ClientExtension::EarlyData => ExtensionType::EarlyData, + ClientExtension::EncryptedServerName(_) => ExtensionType::EncryptedServerName, ClientExtension::Unknown(ref r) => r.typ, } } @@ -608,6 +659,7 @@ impl Codec for ClientExtension { ClientExtension::Cookie(ref r) => r.encode(&mut sub), ClientExtension::CertificateStatusRequest(ref r) => r.encode(&mut sub), ClientExtension::TransportParameters(ref r) => sub.extend_from_slice(r), + ClientExtension::EncryptedServerName(ref r) => r.encode(&mut sub), ClientExtension::Unknown(ref r) => r.encode(&mut sub), } From 40355a50ce86d58d66cfb959b38b25e186080bc2 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 20 Oct 2019 12:58:00 -0700 Subject: [PATCH 02/20] checkpoint esni --- rustls/src/client/common.rs | 8 +- rustls/src/client/hs.rs | 18 +++- rustls/src/client/mod.rs | 24 ++++- rustls/src/esni.rs | 84 +++++++++++++++ rustls/src/lib.rs | 1 + rustls/src/msgs/enums.rs | 9 ++ rustls/src/msgs/handshake.rs | 170 ++++++++++++++++++++++++++++++ rustls/src/msgs/handshake_test.rs | 13 +++ rustls/src/suites.rs | 5 + 9 files changed, 321 insertions(+), 11 deletions(-) create mode 100644 rustls/src/esni.rs diff --git a/rustls/src/client/common.rs b/rustls/src/client/common.rs index 0c22197a1ea..72659e042cd 100644 --- a/rustls/src/client/common.rs +++ b/rustls/src/client/common.rs @@ -1,4 +1,4 @@ -use crate::msgs::handshake::CertificatePayload; +use crate::msgs::handshake::{CertificatePayload, ESNIRecord}; use crate::msgs::handshake::DigitallySignedStruct; use crate::msgs::handshake::SessionID; use crate::msgs::handshake::SCTList; @@ -60,11 +60,14 @@ pub struct HandshakeDetails { pub session_id: SessionID, pub sent_tls13_fake_ccs: bool, pub dns_name: webpki::DNSName, + pub esni_record: Option, pub extra_exts: Vec, } impl HandshakeDetails { - pub fn new(host_name: webpki::DNSName, extra_exts: Vec) -> HandshakeDetails { + pub fn new(host_name: webpki::DNSName, + esni_record: Option, + extra_exts: Vec) -> HandshakeDetails { HandshakeDetails { resuming_session: None, transcript: hash_hs::HandshakeHash::new(), @@ -74,6 +77,7 @@ impl HandshakeDetails { session_id: SessionID::empty(), sent_tls13_fake_ccs: false, dns_name: host_name, + esni_record, extra_exts, } } diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 872c44114bf..b647ff44780 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -2,7 +2,7 @@ use crate::msgs::enums::{ContentType, HandshakeType, ExtensionType}; use crate::msgs::enums::{Compression, ProtocolVersion, AlertDescription}; use crate::msgs::message::{Message, MessagePayload}; use crate::msgs::base::Payload; -use crate::msgs::handshake::{HandshakePayload, HandshakeMessagePayload, ClientHelloPayload}; +use crate::msgs::handshake::{HandshakePayload, HandshakeMessagePayload, ClientHelloPayload, ESNIRecord}; use crate::msgs::handshake::{SessionID, Random}; use crate::msgs::handshake::{ClientExtension, HasServerExtensions}; use crate::msgs::handshake::{ECPointFormatList, SupportedPointFormats}; @@ -132,9 +132,11 @@ struct InitialState { } impl InitialState { - fn new(host_name: webpki::DNSName, extra_exts: Vec) -> InitialState { + fn new(host_name: webpki::DNSName, + esni_record: Option, + extra_exts: Vec) -> InitialState { InitialState { - handshake: HandshakeDetails::new(host_name, extra_exts), + handshake: HandshakeDetails::new(host_name, esni_record, extra_exts), } } @@ -149,8 +151,9 @@ impl InitialState { pub fn start_handshake(sess: &mut ClientSessionImpl, host_name: webpki::DNSName, + esni_record: Option, extra_exts: Vec) -> NextState { - InitialState::new(host_name, extra_exts) + InitialState::new(host_name, esni_record, extra_exts) .emit_initial_client_hello(sess) } @@ -215,9 +218,14 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, if !supported_versions.is_empty() { exts.push(ClientExtension::SupportedVersions(supported_versions)); } + if sess.config.enable_sni { - exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); + match &sess.config.encrypt_sni { + Some(esni) => exts.push(ClientExtension::make_esni(handshake.dns_name.as_ref(), &esni)), + None => exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())) + } } + exts.push(ClientExtension::ECPointFormats(ECPointFormatList::supported())); exts.push(ClientExtension::NamedGroups(suites::KeyExchange::supported_groups().to_vec())); exts.push(ClientExtension::SignatureAlgorithms(verify::supported_verify_schemes().to_vec())); diff --git a/rustls/src/client/mod.rs b/rustls/src/client/mod.rs index 7705bd9692f..02aadb78246 100644 --- a/rustls/src/client/mod.rs +++ b/rustls/src/client/mod.rs @@ -6,13 +6,14 @@ use crate::suites::{SupportedCipherSuite, ALL_CIPHERSUITES}; use crate::msgs::handshake::CertificatePayload; use crate::msgs::enums::SignatureScheme; use crate::msgs::enums::{ContentType, ProtocolVersion}; -use crate::msgs::handshake::ClientExtension; +use crate::msgs::handshake::{ClientExtension, ESNIRecord}; use crate::msgs::message::Message; use crate::verify; use crate::anchors; use crate::sign; use crate::error::TLSError; use crate::key; +use crate::esni::ESNIHandshakeData; use crate::vecbuf::WriteV; #[cfg(feature = "logging")] use crate::log::trace; @@ -124,6 +125,10 @@ pub struct ClientConfig { /// The default is true. pub enable_sni: bool, + /// If present, encrypt the SNI. Only used if `enable_sni` is true, in which case + /// the clear text SNI will not be sent. + pub encrypt_sni: Option, + /// How to verify the server certificate chain. verifier: Arc, @@ -160,6 +165,7 @@ impl ClientConfig { versions: vec![ProtocolVersion::TLSv1_3, ProtocolVersion::TLSv1_2], ct_logs: None, enable_sni: true, + encrypt_sni: None, verifier: Arc::new(verify::WebPKIVerifier::new()), key_log: Arc::new(NoKeyLog {}), enable_early_data: false, @@ -397,8 +403,8 @@ impl ClientSessionImpl { } } - pub fn start_handshake(&mut self, hostname: webpki::DNSName, extra_exts: Vec) { - self.state = Some(hs::start_handshake(self, hostname, extra_exts)); + pub fn start_handshake(&mut self, hostname: webpki::DNSName, esni_record: Option, extra_exts: Vec) { + self.state = Some(hs::start_handshake(self, hostname, esni_record, extra_exts)); } pub fn get_cipher_suites(&self) -> Vec { @@ -596,7 +602,17 @@ impl ClientSession { /// hostname of who we want to talk to. pub fn new(config: &Arc, hostname: webpki::DNSNameRef) -> ClientSession { let mut imp = ClientSessionImpl::new(config); - imp.start_handshake(hostname.into(), vec![]); + imp.start_handshake(hostname.into(), None,vec![]); + ClientSession { imp } + } + + /// Make a new ClientSession. `config` controls how + /// we behave in the TLS protocol, `hostname` is the + /// hostname of who we want to talk to, and esni_keys are used to + /// encrypt the hostname in the ClientHello. + pub fn new_with_esni(config: &Arc, hostname: webpki::DNSNameRef, esni_record: ESNIRecord) -> ClientSession { + let mut imp = ClientSessionImpl::new(config); + imp.start_handshake(hostname.into(), Some(esni_record),vec![]); ClientSession { imp } } diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs new file mode 100644 index 00000000000..e794a678197 --- /dev/null +++ b/rustls/src/esni.rs @@ -0,0 +1,84 @@ +use crate::client::ClientConfig; +use crate::msgs::handshake::{ESNIRecord, KeyShareEntry, ServerNamePayload, ServerName}; +use crate::msgs::enums::{HashAlgorithm, SignatureAlgorithm, ServerNameType}; +use crate::msgs::enums::{CipherSuite, ProtocolVersion}; +use crate::suites::{KeyExchange, TLS13_CIPHERSUITES, choose_ciphersuite_preferring_server}; +use crate::msgs::codec::Codec; +use crate::rand; + +use std::time::{SystemTime, UNIX_EPOCH}; + +use ring::digest; +use webpki; +use crate::SupportedCipherSuite; + +#[derive(Clone, Debug)] +pub struct ESNIHandshakeData { + pub peer_share: KeyShareEntry, + pub cipher_suite: &'static SupportedCipherSuite, + pub padded_length: u16, + pub record_digest: digest::Digest, +} + +/// Creates a `ClientConfig` with defaults suitable for ESNI extension support. +/// This creates a config that supports TLS 1.3 only. +pub fn create_esniclient_config(record: ESNIRecord) -> Option { + // Check whether the record is still valid + let now = now()?; + if now < record.not_before || now > record.not_after { + return None + } + + let mut config = ClientConfig::new(); + config.versions = vec![ProtocolVersion::TLSv1_3]; + config.ciphersuites = TLS13_CIPHERSUITES.to_vec(); + + let peer_share = match KeyExchange::supported_groups() + .iter() + .flat_map(|group| { + record.keys.iter().find(|key| { key.group == *group }) + }).nth(0) + .cloned() { + Some(entry) => entry, + None => return None, + }; + + let cipher_suite= match + choose_ciphersuite_preferring_server(record.cipher_suites.as_slice(), + &TLS13_CIPHERSUITES) { + Some(entry) => entry, + None => return None, + }; + + config.encrypt_sni = Some(ESNIHandshakeData { + peer_share, + cipher_suite, + padded_length: record.padded_length, + record_digest: digest::digest(cipher_suite.get_hash(), &record.bytes.as_slice()[2..]), + }); + + Some(config) +} + +fn compute_esni_extension_data(dns_name: webpki::DNSNameRef, hs_data: &ESNIHandshakeData) { + let mut nonce = [0u8; 16]; + rand::fill_random(&mut random_id); + + let name = ServerName { + typ: ServerNameType::HostName, + payload: ServerNamePayload::HostName(dns_name.into()), + }; + + let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { + Some(entry) => entry, + None => return None, + }; +} + +fn now() -> Option { + let start = SystemTime::now(); + match start.duration_since(UNIX_EPOCH) { + Err(_e) => None, + Ok(since_the_epoch) => Some(since_the_epoch.as_secs() * 1000) + } +} \ No newline at end of file diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index 96bd6f8acb7..672ea028d99 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -237,6 +237,7 @@ mod client; mod key; mod bs_debug; mod keylog; +mod esni; /// Internal classes which may be useful outside the library. /// The contents of this section DO NOT form part of the stable interface. diff --git a/rustls/src/msgs/enums.rs b/rustls/src/msgs/enums.rs index 6d5f42b149c..19316c6bc30 100644 --- a/rustls/src/msgs/enums.rs +++ b/rustls/src/msgs/enums.rs @@ -790,3 +790,12 @@ enum_builder! { OCSP => 0x01 } } + +enum_builder! { + /// The `ESNI` protocol version. + @U16 + EnumName: ESNIVersion; + EnumVal{ + V1 => 0xff01 + } +} diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 17cf6b4ee45..293a521a4d4 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -3,6 +3,7 @@ use crate::msgs::enums::{CipherSuite, Compression, ExtensionType, ECPointFormat} use crate::msgs::enums::{HashAlgorithm, SignatureAlgorithm, ServerNameType}; use crate::msgs::enums::{SignatureScheme, KeyUpdateRequest, NamedGroup}; use crate::msgs::enums::{ClientCertificateType, CertificateStatusType}; +use crate::msgs::enums::ESNIVersion; use crate::msgs::enums::ECCurveType; use crate::msgs::enums::PSKKeyExchangeMode; use crate::msgs::base::{Payload, PayloadU8, PayloadU16, PayloadU24}; @@ -17,7 +18,9 @@ use std::fmt; use std::io::Write; use std::collections; use std::mem; +use ring::digest; use webpki; +use crate::esni::ESNIHandshakeData; macro_rules! declare_u8_vec( ($name:ident, $itemtype:ty) => { @@ -331,6 +334,89 @@ impl ConvertServerNameList for ServerNameRequest { // --- TLS 1.3 Encrypted SNI +declare_u16_vec!(CipherSuites, CipherSuite); + +#[derive(Clone, Debug)] +pub struct ESNIRecord { + pub version: ESNIVersion, + pub checksum: Vec, + pub checksum_valid: bool, + pub keys: KeyShareEntries, + pub cipher_suites: CipherSuites, + pub padded_length: u16, + pub not_before: u64, + pub not_after: u64, + pub extensions: PayloadU16, + pub bytes: Vec, +} + +impl ESNIRecord { + pub fn is_checksum_valid(&self) -> bool { + self.checksum_valid + } +} + +impl Codec for ESNIRecord { + fn encode(&self, bytes: &mut Vec) { + self.version.encode(bytes); + for byte in self.checksum.iter() { + byte.encode(bytes); + } + self.keys.encode(bytes); + self.cipher_suites.encode(bytes); + self.padded_length.encode(bytes); + self.not_before.encode(bytes); + self.not_after.encode(bytes); + self.extensions.encode(bytes); + } + + fn read(r: &mut Reader) -> Option { + let first_bytes = r.take(6)?; + let version = ESNIVersion::read(&mut Reader::init(&first_bytes[0..2]))?; + let checksum: Vec = first_bytes[2..6].iter().cloned().collect(); + + // checksum + let mut ctx = digest::Context::new(&digest::SHA256); + ctx.update(&first_bytes[0..2]); + ctx.update(&[0u8, 0u8, 0u8, 0u8]); + let rest = r.rest(); + ctx.update(rest); + let digest = ctx.finish(); + let checksum_valid = slice_eq(checksum.as_slice(), &digest.as_ref()[0..4]); + + let mut bytes = Vec::with_capacity(first_bytes.len() + rest.len() ); + bytes.extend_from_slice(first_bytes); + bytes.extend_from_slice(rest); + let tail_reader = &mut Reader::init(rest); + Some(ESNIRecord { + version, + checksum, + checksum_valid, + keys: KeyShareEntries::read(tail_reader)?, + cipher_suites: CipherSuites::read(tail_reader)?, + padded_length: u16::read(tail_reader)?, + not_before: u64::read(tail_reader)?, + not_after: u64::read(tail_reader)?, + extensions: PayloadU16::read(tail_reader)?, + bytes + }) + } +} + +fn slice_eq<'a, T: PartialEq>(a: &'a [T], b: &'a [T]) -> bool { + if a.len() != b.len() { + return false; + } + + for i in 0..a.len() { + if a[i] != b[i] { + return false; + } + } + + true +} + #[derive(Clone, Debug)] pub struct ClientEncryptedSNI { cipher: CipherSuite, @@ -376,6 +462,80 @@ impl Codec for ClientEncryptedSNI { } } +pub struct ESNIContents { + pub record_digest: PayloadU16, + pub esni_key_share: KeyShareEntry, + pub client_hello_random: Random, +} + +pub struct PaddedServerNameList { + pub sni: ServerName, + pub zeros: Vec, + pub padded_length: u16, +} + +impl PaddedServerNameList { + pub fn new(sni: ServerName, padded_length: u16) -> PaddedServerNameList { + let mut output = Vec::new(); + sni.encode(&output); + let length = padded_length - output.len() as u16; + PaddedServerNameList { + sni, + zeros: vec![0; length as usize], + padded_length, + } + } +} + +impl Codec for PaddedServerNameList { + fn encode(&self, bytes: &mut Vec) { + self.sni.encode(bytes); + bytes.extend_from_slice(self.zeros.as_slice()); + } + + fn read(r: &mut Reader) -> Option { + let count = r.left(); + let sni = ServerName::read(r)?; + let sni_length = count - r.left(); + let mut padding = Vec::with_capacity(r.left()); + padding.extend_from_slice(r.rest()); + + for zero in padding.iter() { + if zero != 0 { + return None; + } + } + + Some(PaddedServerNameList { + sni, + zeros: padding, + padded_length: (sni_length + padding.len()) as u16, + }) + } +} + +pub struct ClientESNIInner { + pub nonce: [u8; 16], + pub real_sni: PaddedServerNameList, +} + +impl Codec for ClientESNIInner { + fn encode(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.nonce); + self.real_sni.encode(bytes); + } + + fn read(r: &mut Reader) -> Option { + let mut nonce = [u8; 16]; + nonce.clone_from_slice(r.take(16)?); + + Some(ClientESNIInner { + nonce, + real_sni: PaddedServerNameList::read(r)? + }) + } +} + declare_u16_vec!(ESNIRequest, ClientEncryptedSNI); pub type ProtocolNameList = VecU16OfPayloadU8; @@ -740,6 +900,16 @@ impl ClientExtension { ClientExtension::ServerName(vec![ name ]) } + + /// Make an ESNI request, encrypting `hostname` with the ESNIRecord + pub fn make_esni(dns_name: webpki::DNSNameRef, esni_record: &ESNIHandshakeData) -> ClientExtension { + let name = ServerName { + typ: ServerNameType::HostName, + payload: ServerNamePayload::HostName(dns_name.into()), + }; + + ClientExtension::ServerName(vec![ name ]) + } } #[derive(Clone, Debug)] diff --git a/rustls/src/msgs/handshake_test.rs b/rustls/src/msgs/handshake_test.rs index b4fc6b8e49e..3dbd256c5dd 100644 --- a/rustls/src/msgs/handshake_test.rs +++ b/rustls/src/msgs/handshake_test.rs @@ -4,6 +4,7 @@ use super::base::{Payload, PayloadU8, PayloadU16, PayloadU24}; use super::codec::{Reader, Codec}; use webpki::DNSNameRef; use crate::key::Certificate; +use base64; use std::mem; @@ -926,3 +927,15 @@ fn can_roundtrip_all_tls13_handshake_payloads() { println!("{:?}", other); } } + +#[test] +fn test_esni() { + // An ESNI record from Cloudflare + let base64_esni = "/wHdBX2/ACQAHQAg+Q5TFpKpR0O9dALVeNC9kDBfNwNBvzHma4VrZgMtKXwAAhMBAQQAAAAAXaSpkAAAAABdrJKQAAA="; + let bytes = base64::decode(&base64_esni).unwrap(); + let record = ESNIRecord::read(&mut Reader::init(&bytes)).unwrap(); + assert!(record.is_checksum_valid()); + let mut output = Vec::new(); + record.encode(&mut output); + assert_eq!(base64_esni, base64::encode(&output)); +} \ No newline at end of file diff --git a/rustls/src/suites.rs b/rustls/src/suites.rs index a1ddc85d3b2..1f5c24b0c3d 100644 --- a/rustls/src/suites.rs +++ b/rustls/src/suites.rs @@ -373,6 +373,11 @@ pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite hkdf_algorithm: ring::hkdf::HKDF_SHA256, }; +pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 3] = + [&TLS13_CHACHA20_POLY1305_SHA256, + &TLS13_AES_256_GCM_SHA384, + &TLS13_AES_128_GCM_SHA256]; + /// A list of all the cipher suites supported by rustls. pub static ALL_CIPHERSUITES: [&'static SupportedCipherSuite; 9] = [// TLS1.3 suites From 7e436777ee8f84848711bcafaa28d19bfdcf4e31 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sat, 26 Oct 2019 13:42:02 -0700 Subject: [PATCH 03/20] Almost working --- rustls-mio/Cargo.toml | 5 ++ rustls-mio/examples/esniclient.rs | 88 +++++++++++++++++++++++ rustls/src/cipher.rs | 1 - rustls/src/client/common.rs | 9 +-- rustls/src/client/hs.rs | 49 +++++++++---- rustls/src/client/mod.rs | 18 ++--- rustls/src/client/tls13.rs | 5 +- rustls/src/esni.rs | 114 ++++++++++++++++++++++++------ rustls/src/lib.rs | 4 +- rustls/src/msgs/handshake.rs | 75 ++++++++++++-------- 10 files changed, 288 insertions(+), 80 deletions(-) create mode 100644 rustls-mio/examples/esniclient.rs diff --git a/rustls-mio/Cargo.toml b/rustls-mio/Cargo.toml index b385afebda9..5e1570d96b6 100644 --- a/rustls-mio/Cargo.toml +++ b/rustls-mio/Cargo.toml @@ -22,6 +22,7 @@ log = { version = "0.4.4", optional = true } rustls = { path = "../rustls" } sct = "0.6" webpki = "0.21.0" +trust-dns-resolver = { version = "0.12.0", features = ["dns-over-rustls", "dns-over-https-rustls"] } [dev-dependencies] ct-logs = "0.6" @@ -47,6 +48,10 @@ path = "examples/tlsserver.rs" name = "simpleclient" path = "examples/simpleclient.rs" +[[example]] +name = "esniclient" +path = "examples/esniclient.rs" + [[example]] name = "simple_0rtt_client" path = "examples/simple_0rtt_client.rs" diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs new file mode 100644 index 00000000000..5d4d956a211 --- /dev/null +++ b/rustls-mio/examples/esniclient.rs @@ -0,0 +1,88 @@ +use std::sync::Arc; + +use std::net::TcpStream; +use std::io::{Read, Write, stdout}; +use std::iter::FromIterator; + +use rustls; +use webpki; +use webpki_roots; +use rustls::Session; +use base64::decode; + +extern crate trust_dns_resolver; +use trust_dns_resolver::config::*; +use trust_dns_resolver::Resolver; + +fn main() { + let domain = "opaque.website"; + let dns_config = ResolverConfig::cloudflare_https(); + let opts = ResolverOpts::default(); + let addr = Address::new(domain); + let esni_bytes = resolve_esni(dns_config, opts, &addr); + let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); + + let mut config = rustls::esni::create_esni_config(); + config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + + let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap(); + let mut sess = rustls::ClientSession::new_with_esni(&Arc::new(config), dns_name, esni_hs); + let mut sock = TcpStream::connect(domain.to_owned() + ":443").unwrap(); + let mut tls = rustls::Stream::new(&mut sess, &mut sock); + tls.write(concat!("GET / HTTP/1.1\r\n", + "Host: opaque.website\r\n", + "Connection: close\r\n", + "Accept-Encoding: identity\r\n", + "\r\n") + .as_bytes()) + .unwrap(); + let ciphersuite = tls.sess.get_negotiated_ciphersuite().unwrap(); + writeln!(&mut std::io::stderr(), "\n\nCurrent ciphersuite: {:?}", ciphersuite.suite).unwrap(); + let mut plaintext = Vec::new(); + tls.read_to_end(&mut plaintext).unwrap(); + stdout().write_all(&plaintext).unwrap(); +} + +pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Address) -> Vec { + let resolver = Resolver::new(config, opts).unwrap(); + let response = resolver.lookup_ip(&address.dns_address()).unwrap(); + println!("response records: {:#?}", response); + + let txt = resolver.txt_lookup(&address.esni_address()).unwrap(); + println!("txt: {:?}", txt); + let text = Vec::from_iter(txt.iter()); + let mut bytes: Vec = Vec::new(); + for txt_record in text.iter() { + for byte_slice in txt_record.txt_data().iter() { + for byte in byte_slice.iter() { + bytes.push(*byte); + } + } + } + + println!("base 64: {}", std::str::from_utf8(&bytes).unwrap()); + let decoded = decode(&bytes).unwrap(); + println!("bytes: {:?}", decoded); + + decoded +} + +pub struct Address { + domain: String +} + +impl Address { + pub fn new(domain: &str) -> Address { + Address { + domain: String::from(domain) + } + } + + pub fn esni_address(&self) -> String { + format!("_esni.{}.", self.domain) + } + + pub fn dns_address(&self) -> String { + format!("{}.", self.domain) + } +} \ No newline at end of file diff --git a/rustls/src/cipher.rs b/rustls/src/cipher.rs index 74d76bb1ba8..982d8171af8 100644 --- a/rustls/src/cipher.rs +++ b/rustls/src/cipher.rs @@ -251,7 +251,6 @@ impl Iv { Self(value) } - #[cfg(test)] pub(crate) fn value(&self) -> &[u8; 12] { &self.0 } } diff --git a/rustls/src/client/common.rs b/rustls/src/client/common.rs index 72659e042cd..5eb16b2e39a 100644 --- a/rustls/src/client/common.rs +++ b/rustls/src/client/common.rs @@ -1,4 +1,4 @@ -use crate::msgs::handshake::{CertificatePayload, ESNIRecord}; +use crate::msgs::handshake::CertificatePayload; use crate::msgs::handshake::DigitallySignedStruct; use crate::msgs::handshake::SessionID; use crate::msgs::handshake::SCTList; @@ -16,6 +16,7 @@ use crate::log::trace; use webpki; use std::mem; +use crate::esni::ESNIHandshakeData; pub struct ServerCertDetails { pub cert_chain: CertificatePayload, @@ -60,13 +61,13 @@ pub struct HandshakeDetails { pub session_id: SessionID, pub sent_tls13_fake_ccs: bool, pub dns_name: webpki::DNSName, - pub esni_record: Option, + pub esni: Option, pub extra_exts: Vec, } impl HandshakeDetails { pub fn new(host_name: webpki::DNSName, - esni_record: Option, + esni: Option, extra_exts: Vec) -> HandshakeDetails { HandshakeDetails { resuming_session: None, @@ -77,7 +78,7 @@ impl HandshakeDetails { session_id: SessionID::empty(), sent_tls13_fake_ccs: false, dns_name: host_name, - esni_record, + esni, extra_exts, } } diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index b647ff44780..92639f1af29 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -2,7 +2,7 @@ use crate::msgs::enums::{ContentType, HandshakeType, ExtensionType}; use crate::msgs::enums::{Compression, ProtocolVersion, AlertDescription}; use crate::msgs::message::{Message, MessagePayload}; use crate::msgs::base::Payload; -use crate::msgs::handshake::{HandshakePayload, HandshakeMessagePayload, ClientHelloPayload, ESNIRecord}; +use crate::msgs::handshake::{HandshakePayload, HandshakeMessagePayload, ClientHelloPayload}; use crate::msgs::handshake::{SessionID, Random}; use crate::msgs::handshake::{ClientExtension, HasServerExtensions}; use crate::msgs::handshake::{ECPointFormatList, SupportedPointFormats}; @@ -10,7 +10,7 @@ use crate::msgs::handshake::{ProtocolNameList, ConvertProtocolNameList}; use crate::msgs::handshake::HelloRetryRequest; use crate::msgs::handshake::{CertificateStatusRequest, SCTList}; use crate::msgs::enums::{PSKKeyExchangeMode, ECPointFormat}; -use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::codec::{Codec, Reader, encode_vec_u16}; use crate::msgs::persist; use crate::client::ClientSessionImpl; use crate::session::SessionSecrets; @@ -35,6 +35,7 @@ use crate::client::common::{ClientHelloDetails, ReceivedTicketDetails}; use crate::client::{tls12, tls13}; use webpki; +use crate::esni::ESNIHandshakeData; macro_rules! extract_handshake( ( $m:expr, $t:path ) => ( @@ -133,10 +134,10 @@ struct InitialState { impl InitialState { fn new(host_name: webpki::DNSName, - esni_record: Option, + esni: Option, extra_exts: Vec) -> InitialState { InitialState { - handshake: HandshakeDetails::new(host_name, esni_record, extra_exts), + handshake: HandshakeDetails::new(host_name, esni, extra_exts), } } @@ -151,9 +152,9 @@ impl InitialState { pub fn start_handshake(sess: &mut ClientSessionImpl, host_name: webpki::DNSName, - esni_record: Option, + esni: Option, extra_exts: Vec) -> NextState { - InitialState::new(host_name, esni_record, extra_exts) + InitialState::new(host_name, esni, extra_exts) .emit_initial_client_hello(sess) } @@ -219,11 +220,35 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, exts.push(ClientExtension::SupportedVersions(supported_versions)); } - if sess.config.enable_sni { - match &sess.config.encrypt_sni { - Some(esni) => exts.push(ClientExtension::make_esni(handshake.dns_name.as_ref(), &esni)), - None => exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())) + let keyshare_entries = if support_tls13 { + Some(tls13::choose_kx_groups(sess, &mut hello, &mut handshake, retryreq)) + } else { + None + }; + + if sess.config.enable_sni && sess.config.encrypt_sni { + if let Some(esni) = &handshake.esni { + if let Some(ks) = &keyshare_entries { + let mut ks_bytes= Vec::new(); + encode_vec_u16(&mut ks_bytes, ks); + let esni_ext = ClientExtension::make_esni(handshake.dns_name.as_ref(), esni, ks_bytes); + if let Some(ext) = esni_ext { + println!("Pushing ESNI..."); + exts.push(ext); + } + // TODO: what if ESNI fails? + } } + + // TODO: what if ESNI is configured but there's no ESNI record? + + } else if sess.config.enable_sni { + println!("REGULAR SNI..."); + exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); + } + + if let Some(ks) = keyshare_entries { + exts.push(ClientExtension::KeyShare(ks)); } exts.push(ClientExtension::ECPointFormats(ECPointFormatList::supported())); @@ -236,10 +261,6 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, exts.push(ClientExtension::SignedCertificateTimestampRequest); } - if support_tls13 { - tls13::choose_kx_groups(sess, &mut exts, &mut hello, &mut handshake, retryreq); - } - if let Some(cookie) = retryreq.and_then(HelloRetryRequest::get_cookie) { exts.push(ClientExtension::Cookie(cookie.clone())); } diff --git a/rustls/src/client/mod.rs b/rustls/src/client/mod.rs index 02aadb78246..9af03e66b4a 100644 --- a/rustls/src/client/mod.rs +++ b/rustls/src/client/mod.rs @@ -6,7 +6,7 @@ use crate::suites::{SupportedCipherSuite, ALL_CIPHERSUITES}; use crate::msgs::handshake::CertificatePayload; use crate::msgs::enums::SignatureScheme; use crate::msgs::enums::{ContentType, ProtocolVersion}; -use crate::msgs::handshake::{ClientExtension, ESNIRecord}; +use crate::msgs::handshake::ClientExtension; use crate::msgs::message::Message; use crate::verify; use crate::anchors; @@ -125,9 +125,11 @@ pub struct ClientConfig { /// The default is true. pub enable_sni: bool, - /// If present, encrypt the SNI. Only used if `enable_sni` is true, in which case + /// If true, encrypt the SNI. Only used if `enable_sni` is true, in which case /// the clear text SNI will not be sent. - pub encrypt_sni: Option, + /// + /// The default is false. + pub encrypt_sni: bool, /// How to verify the server certificate chain. verifier: Arc, @@ -165,7 +167,7 @@ impl ClientConfig { versions: vec![ProtocolVersion::TLSv1_3, ProtocolVersion::TLSv1_2], ct_logs: None, enable_sni: true, - encrypt_sni: None, + encrypt_sni: false, verifier: Arc::new(verify::WebPKIVerifier::new()), key_log: Arc::new(NoKeyLog {}), enable_early_data: false, @@ -403,8 +405,8 @@ impl ClientSessionImpl { } } - pub fn start_handshake(&mut self, hostname: webpki::DNSName, esni_record: Option, extra_exts: Vec) { - self.state = Some(hs::start_handshake(self, hostname, esni_record, extra_exts)); + pub fn start_handshake(&mut self, hostname: webpki::DNSName, esni: Option, extra_exts: Vec) { + self.state = Some(hs::start_handshake(self, hostname, esni, extra_exts)); } pub fn get_cipher_suites(&self) -> Vec { @@ -610,9 +612,9 @@ impl ClientSession { /// we behave in the TLS protocol, `hostname` is the /// hostname of who we want to talk to, and esni_keys are used to /// encrypt the hostname in the ClientHello. - pub fn new_with_esni(config: &Arc, hostname: webpki::DNSNameRef, esni_record: ESNIRecord) -> ClientSession { + pub fn new_with_esni(config: &Arc, hostname: webpki::DNSNameRef, esni: ESNIHandshakeData) -> ClientSession { let mut imp = ClientSessionImpl::new(config); - imp.start_handshake(hostname.into(), Some(esni_record),vec![]); + imp.start_handshake(hostname.into(), Some(esni),vec![]); ClientSession { imp } } diff --git a/rustls/src/client/tls13.rs b/rustls/src/client/tls13.rs index 6b410fcbb34..6484cc1361f 100644 --- a/rustls/src/client/tls13.rs +++ b/rustls/src/client/tls13.rs @@ -82,10 +82,9 @@ fn save_kx_hint(sess: &mut ClientSessionImpl, dns_name: webpki::DNSNameRef, grou } pub fn choose_kx_groups(sess: &mut ClientSessionImpl, - exts: &mut Vec, hello: &mut ClientHelloDetails, handshake: &mut HandshakeDetails, - retryreq: Option<&HelloRetryRequest>) { + retryreq: Option<&HelloRetryRequest>) -> Vec { // Choose our groups: // - if we've been asked via HelloRetryRequest for a specific // one, do that. @@ -115,7 +114,7 @@ pub fn choose_kx_groups(sess: &mut ClientSessionImpl, } } - exts.push(ClientExtension::KeyShare(key_shares)); + key_shares } /// This implements the horrifying TLS1.3 hack where PSK binders have a diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index e794a678197..bc4fb7aecf9 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -1,38 +1,60 @@ use crate::client::ClientConfig; -use crate::msgs::handshake::{ESNIRecord, KeyShareEntry, ServerNamePayload, ServerName}; -use crate::msgs::enums::{HashAlgorithm, SignatureAlgorithm, ServerNameType}; -use crate::msgs::enums::{CipherSuite, ProtocolVersion}; +use crate::msgs::handshake::{ESNIRecord, KeyShareEntry, ServerNamePayload, ServerName, ClientEncryptedSNI, ESNIContents, Random, PaddedServerNameList, ClientESNIInner}; +use crate::msgs::enums::ServerNameType; +use crate::msgs::enums::ProtocolVersion; use crate::suites::{KeyExchange, TLS13_CIPHERSUITES, choose_ciphersuite_preferring_server}; -use crate::msgs::codec::Codec; +use crate::msgs::codec::{Codec, Reader}; use crate::rand; use std::time::{SystemTime, UNIX_EPOCH}; -use ring::digest; +use ring::{digest, hkdf}; use webpki; use crate::SupportedCipherSuite; +use crate::key_schedule::hkdf_expand; +use crate::msgs::base::PayloadU16; +use ring::hkdf::KeyType; +use crate::cipher::{Iv, IvLen}; +/// Data calculated for a client session from a DNS ESNI record. #[derive(Clone, Debug)] pub struct ESNIHandshakeData { + /// The selected Key Share from the DNS record pub peer_share: KeyShareEntry, + + /// The selected CipherSuite from the DNS record pub cipher_suite: &'static SupportedCipherSuite, + + /// The length to pad the ESNI to pub padded_length: u16, - pub record_digest: digest::Digest, + + /// A digest of the DNS record + pub record_digest: Vec, +} + +/// Create a TLS 1.3 Config for ESNI +pub fn create_esni_config() -> ClientConfig { + let mut config = ClientConfig::new(); + config.versions = vec![ProtocolVersion::TLSv1_3]; + config.ciphersuites = TLS13_CIPHERSUITES.to_vec(); + config.encrypt_sni = true; + config } /// Creates a `ClientConfig` with defaults suitable for ESNI extension support. /// This creates a config that supports TLS 1.3 only. -pub fn create_esniclient_config(record: ESNIRecord) -> Option { +pub fn create_esni_handshake(record_bytes: &Vec) -> Option { + let record = ESNIRecord::read(&mut Reader::init(&record_bytes))?; + + println!("record {:?}", record); + // Check whether the record is still valid let now = now()?; + if now < record.not_before || now > record.not_after { return None } - let mut config = ClientConfig::new(); - config.versions = vec![ProtocolVersion::TLSv1_3]; - config.ciphersuites = TLS13_CIPHERSUITES.to_vec(); - let peer_share = match KeyExchange::supported_groups() .iter() .flat_map(|group| { @@ -50,35 +72,85 @@ pub fn create_esniclient_config(record: ESNIRecord) -> Option { None => return None, }; - config.encrypt_sni = Some(ESNIHandshakeData { + let digest = digest::digest(cipher_suite.get_hash(), &record.bytes.as_slice()[2..]); + let bytes: Vec = Vec::from(digest.as_ref()); + + Some(ESNIHandshakeData { peer_share, cipher_suite, padded_length: record.padded_length, - record_digest: digest::digest(cipher_suite.get_hash(), &record.bytes.as_slice()[2..]), - }); - - Some(config) + record_digest: bytes, + }) } -fn compute_esni_extension_data(dns_name: webpki::DNSNameRef, hs_data: &ESNIHandshakeData) { +/// Compute the encrypted SNI +// TODO: this is big and messy, fix it up +pub fn compute_esni(dns_name: webpki::DNSNameRef, + hs_data: &ESNIHandshakeData, + key_share_bytes: Vec) -> Option { let mut nonce = [0u8; 16]; - rand::fill_random(&mut random_id); - + rand::fill_random(&mut nonce); let name = ServerName { typ: ServerNameType::HostName, payload: ServerNamePayload::HostName(dns_name.into()), }; + let psnl = PaddedServerNameList::new(name, hs_data.padded_length); + let client_esni_inner = ClientESNIInner { + nonce, + real_sni: psnl, + }; + let mut sni_bytes = Vec::new(); + client_esni_inner.encode(&mut sni_bytes); + let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { - Some(entry) => entry, + Some(ke) => ke, None => return None, }; + let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; + + let mut random = [0u8; 32]; + rand::fill_random(&mut random); + let contents = ESNIContents { + record_digest: PayloadU16::new(hs_data.record_digest.clone()), + esni_key_share: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), + client_hello_random: Random::from_slice(&random), + }; + + let mut contents_bytes = Vec::new(); + contents.encode(&mut contents_bytes); + let digest = digest::digest(hs_data.cipher_suite.get_hash(), &contents_bytes); + + let algorithm = hs_data.cipher_suite.hkdf_algorithm; + let zeroes = [0u8; digest::MAX_OUTPUT_LEN]; + let zeroes = &zeroes[..algorithm.len()]; + let salt = hkdf::Salt::new(algorithm, &zeroes); + let zx = salt.extract(&exchange_result.premaster_secret); + let key = hkdf_expand(&zx, hs_data.cipher_suite.get_aead_alg(), b"esni key", digest.as_ref()); + let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", digest.as_ref()); + let lsk = ring::aead::LessSafeKey::new(key); + match lsk.seal_in_place_append_tag(ring::aead::Nonce::assume_unique_for_key(*iv.value()), + ring::aead::Aad::from(key_share_bytes), + &mut sni_bytes) { + Ok(_) => { + println!("What's the suite? {:?}", hs_data.cipher_suite.suite); + println!("What's record digest? {:?}", hs_data.record_digest); + println!("what the group? {:?}", hs_data.peer_share.group); + Some (ClientEncryptedSNI { + suite: hs_data.cipher_suite.suite, + key_share_entry: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), + record_digest: PayloadU16(hs_data.record_digest.clone()), + encrypted_sni: PayloadU16(Vec::from(sni_bytes)), + }) + }, + _ => None + } } fn now() -> Option { let start = SystemTime::now(); match start.duration_since(UNIX_EPOCH) { Err(_e) => None, - Ok(since_the_epoch) => Some(since_the_epoch.as_secs() * 1000) + Ok(since_the_epoch) => Some(since_the_epoch.as_secs()) } } \ No newline at end of file diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index 672ea028d99..1de2840b651 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -237,7 +237,9 @@ mod client; mod key; mod bs_debug; mod keylog; -mod esni; + +/// ESNI related functions +pub mod esni; /// Internal classes which may be useful outside the library. /// The contents of this section DO NOT form part of the stable interface. diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 293a521a4d4..a987a9d553d 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -10,6 +10,7 @@ use crate::msgs::base::{Payload, PayloadU8, PayloadU16, PayloadU24}; use crate::msgs::codec; use crate::msgs::codec::{Codec, Reader}; use crate::key; +use crate::esni::*; #[cfg(feature = "logging")] use crate::log::warn; @@ -20,7 +21,6 @@ use std::collections; use std::mem; use ring::digest; use webpki; -use crate::esni::ESNIHandshakeData; macro_rules! declare_u8_vec( ($name:ident, $itemtype:ty) => { @@ -371,21 +371,20 @@ impl Codec for ESNIRecord { } fn read(r: &mut Reader) -> Option { - let first_bytes = r.take(6)?; - let version = ESNIVersion::read(&mut Reader::init(&first_bytes[0..2]))?; - let checksum: Vec = first_bytes[2..6].iter().cloned().collect(); + let version = ESNIVersion::read(r)?; + let checksum: Vec = u32::read(r)?.get_encoding(); // checksum let mut ctx = digest::Context::new(&digest::SHA256); - ctx.update(&first_bytes[0..2]); + ctx.update(version.get_u16().get_encoding().as_slice()); ctx.update(&[0u8, 0u8, 0u8, 0u8]); let rest = r.rest(); ctx.update(rest); let digest = ctx.finish(); let checksum_valid = slice_eq(checksum.as_slice(), &digest.as_ref()[0..4]); - let mut bytes = Vec::with_capacity(first_bytes.len() + rest.len() ); - bytes.extend_from_slice(first_bytes); + let mut bytes = Vec::with_capacity(4 + rest.len() ); + bytes.extend_from_slice(checksum.as_slice()); bytes.extend_from_slice(rest); let tail_reader = &mut Reader::init(rest); Some(ESNIRecord { @@ -419,19 +418,19 @@ fn slice_eq<'a, T: PartialEq>(a: &'a [T], b: &'a [T]) -> bool { #[derive(Clone, Debug)] pub struct ClientEncryptedSNI { - cipher: CipherSuite, - key_share_entry: KeyShareEntry, - record_digest: PayloadU16, - encrypted_sni: PayloadU16, + pub suite: CipherSuite, + pub key_share_entry: KeyShareEntry, + pub record_digest: PayloadU16, + pub encrypted_sni: PayloadU16, } impl ClientEncryptedSNI { - pub fn new(cipher: CipherSuite, + pub fn new(suite: CipherSuite, key_share_entry: KeyShareEntry, record_digest: PayloadU16, encrypted_sni: PayloadU16) -> ClientEncryptedSNI { ClientEncryptedSNI { - cipher, + suite, key_share_entry, record_digest, encrypted_sni @@ -441,20 +440,20 @@ impl ClientEncryptedSNI { impl Codec for ClientEncryptedSNI { fn encode(&self, bytes: &mut Vec) { - self.cipher.encode(bytes); + self.suite.encode(bytes); self.key_share_entry.encode(bytes); self.record_digest.encode(bytes); self.encrypted_sni.encode(bytes); } fn read(r: &mut Reader) -> Option { - let cipher = CipherSuite::read(r)?; + let suite = CipherSuite::read(r)?; let key_share_entry = KeyShareEntry::read(r)?; let record_digest = PayloadU16::read(r)?; let encrypted_sni = PayloadU16::read(r)?; Some(ClientEncryptedSNI { - cipher, + suite, key_share_entry, record_digest, encrypted_sni @@ -462,12 +461,30 @@ impl Codec for ClientEncryptedSNI { } } +#[derive(Clone, Debug)] pub struct ESNIContents { pub record_digest: PayloadU16, pub esni_key_share: KeyShareEntry, pub client_hello_random: Random, } +impl Codec for ESNIContents { + fn encode(&self, bytes: &mut Vec) { + self.record_digest.encode(bytes); + self.esni_key_share.encode(bytes); + self.client_hello_random.encode(bytes); + } + + fn read(r: &mut Reader) -> Option { + Some(ESNIContents { + record_digest: PayloadU16::read(r)?, + esni_key_share: KeyShareEntry::read(r)?, + client_hello_random: Random::read(r)?, + }) + } +} + +#[derive(Clone, Debug)] pub struct PaddedServerNameList { pub sni: ServerName, pub zeros: Vec, @@ -477,7 +494,7 @@ pub struct PaddedServerNameList { impl PaddedServerNameList { pub fn new(sni: ServerName, padded_length: u16) -> PaddedServerNameList { let mut output = Vec::new(); - sni.encode(&output); + sni.encode(&mut output); let length = padded_length - output.len() as u16; PaddedServerNameList { sni, @@ -499,9 +516,9 @@ impl Codec for PaddedServerNameList { let sni_length = count - r.left(); let mut padding = Vec::with_capacity(r.left()); padding.extend_from_slice(r.rest()); - + let len = padding.len(); for zero in padding.iter() { - if zero != 0 { + if *zero != 0u8 { return None; } } @@ -509,11 +526,12 @@ impl Codec for PaddedServerNameList { Some(PaddedServerNameList { sni, zeros: padding, - padded_length: (sni_length + padding.len()) as u16, + padded_length: (sni_length + len) as u16, }) } } +#[derive(Clone, Debug)] pub struct ClientESNIInner { pub nonce: [u8; 16], pub real_sni: PaddedServerNameList, @@ -526,7 +544,7 @@ impl Codec for ClientESNIInner { } fn read(r: &mut Reader) -> Option { - let mut nonce = [u8; 16]; + let mut nonce = [0u8; 16]; nonce.clone_from_slice(r.take(16)?); Some(ClientESNIInner { @@ -846,6 +864,9 @@ impl Codec for ClientExtension { ExtensionType::ServerName => { ClientExtension::ServerName(ServerNameRequest::read(&mut sub)?) } + ExtensionType::EncryptedServerName => { + ClientExtension::EncryptedServerName(ESNIRequest::read(&mut sub)?) + } ExtensionType::SessionTicket => { if sub.any_left() { ClientExtension::SessionTicketOffer(Payload::read(&mut sub)?) @@ -902,13 +923,11 @@ impl ClientExtension { } /// Make an ESNI request, encrypting `hostname` with the ESNIRecord - pub fn make_esni(dns_name: webpki::DNSNameRef, esni_record: &ESNIHandshakeData) -> ClientExtension { - let name = ServerName { - typ: ServerNameType::HostName, - payload: ServerNamePayload::HostName(dns_name.into()), - }; - - ClientExtension::ServerName(vec![ name ]) + pub fn make_esni(dns_name: webpki::DNSNameRef, + hs_data: &ESNIHandshakeData, + key_share_bytes: Vec) -> Option { + let esni = compute_esni(dns_name, hs_data, key_share_bytes)?; + Some(ClientExtension::EncryptedServerName(vec![esni])) } } From ac39f27748a57aa1f0ca2e476aac00741c91c1be Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sat, 26 Oct 2019 19:17:37 -0700 Subject: [PATCH 04/20] Working handshake with only.esni.defo.ie --- rustls-mio/Cargo.toml | 2 +- rustls-mio/examples/esniclient.rs | 35 +++++++++++++++++++++++-------- rustls/src/client/hs.rs | 10 +++++---- rustls/src/msgs/handshake.rs | 8 +++---- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/rustls-mio/Cargo.toml b/rustls-mio/Cargo.toml index 5e1570d96b6..688faa14f72 100644 --- a/rustls-mio/Cargo.toml +++ b/rustls-mio/Cargo.toml @@ -34,7 +34,7 @@ serde = "1.0" serde_derive = "1.0" tempfile = "3.0" vecio = "0.1" -webpki-roots = "0.17" +webpki-roots = "0.18" [[example]] name = "tlsclient" diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index 5d4d956a211..4fab7d3ab92 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -15,38 +15,55 @@ use trust_dns_resolver::config::*; use trust_dns_resolver::Resolver; fn main() { - let domain = "opaque.website"; + let domain = "only.esni.defo.ie"; let dns_config = ResolverConfig::cloudflare_https(); let opts = ResolverOpts::default(); let addr = Address::new(domain); let esni_bytes = resolve_esni(dns_config, opts, &addr); let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); - let mut config = rustls::esni::create_esni_config(); + let mut config = rustls::ClientConfig::default(); + config.encrypt_sni = true; config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap(); + + let dns_name = webpki::DNSNameRef::try_from_ascii_str("defo.ie").unwrap(); let mut sess = rustls::ClientSession::new_with_esni(&Arc::new(config), dns_name, esni_hs); let mut sock = TcpStream::connect(domain.to_owned() + ":443").unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); - tls.write(concat!("GET / HTTP/1.1\r\n", - "Host: opaque.website\r\n", + match tls.write(concat!("GET / HTTP/1.1\r\n", + "Host: only.esni.defo.ie\r\n", "Connection: close\r\n", "Accept-Encoding: identity\r\n", "\r\n") - .as_bytes()) - .unwrap(); + .as_bytes()) { + Ok(size) => { + println!("Received: {} bytes", size); + } , + Err(e) => { + println!("Error: {:?}", e); + return; + } + } let ciphersuite = tls.sess.get_negotiated_ciphersuite().unwrap(); writeln!(&mut std::io::stderr(), "\n\nCurrent ciphersuite: {:?}", ciphersuite.suite).unwrap(); + writeln!(&mut std::io::stderr(), "\n\nReading the the stream is broken...").unwrap(); let mut plaintext = Vec::new(); - tls.read_to_end(&mut plaintext).unwrap(); + match tls.read_to_end(&mut plaintext) { + Ok(success) => { + println!("read bytes: {}", success); + }, + Err(e) => { + println!("failure to read the bytes: {:?}", e); + return; + } + } stdout().write_all(&plaintext).unwrap(); } pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Address) -> Vec { let resolver = Resolver::new(config, opts).unwrap(); let response = resolver.lookup_ip(&address.dns_address()).unwrap(); - println!("response records: {:#?}", response); let txt = resolver.txt_lookup(&address.esni_address()).unwrap(); println!("txt: {:?}", txt); diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 92639f1af29..58c26cdbdff 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -221,7 +221,10 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, } let keyshare_entries = if support_tls13 { - Some(tls13::choose_kx_groups(sess, &mut hello, &mut handshake, retryreq)) + let ks = tls13::choose_kx_groups(sess, &mut hello, &mut handshake, retryreq); + let ret = ks.clone(); + exts.push(ClientExtension::KeyShare(ks)); + Some(ret) } else { None }; @@ -235,6 +238,7 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, if let Some(ext) = esni_ext { println!("Pushing ESNI..."); exts.push(ext); + //exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } // TODO: what if ESNI fails? } @@ -247,9 +251,7 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } - if let Some(ks) = keyshare_entries { - exts.push(ClientExtension::KeyShare(ks)); - } + exts.push(ClientExtension::ECPointFormats(ECPointFormatList::supported())); exts.push(ClientExtension::NamedGroups(suites::KeyExchange::supported_groups().to_vec())); diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index a987a9d553d..6cc2c0f5f2b 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -554,7 +554,7 @@ impl Codec for ClientESNIInner { } } -declare_u16_vec!(ESNIRequest, ClientEncryptedSNI); +// declare_u16_vec!(ESNIRequest, ClientEncryptedSNI); pub type ProtocolNameList = VecU16OfPayloadU8; @@ -784,7 +784,7 @@ pub enum ClientExtension { SignedCertificateTimestampRequest, TransportParameters(Vec), EarlyData, - EncryptedServerName(ESNIRequest), + EncryptedServerName(ClientEncryptedSNI), Unknown(UnknownExtension), } @@ -865,7 +865,7 @@ impl Codec for ClientExtension { ClientExtension::ServerName(ServerNameRequest::read(&mut sub)?) } ExtensionType::EncryptedServerName => { - ClientExtension::EncryptedServerName(ESNIRequest::read(&mut sub)?) + ClientExtension::EncryptedServerName(ClientEncryptedSNI::read(&mut sub)?) } ExtensionType::SessionTicket => { if sub.any_left() { @@ -927,7 +927,7 @@ impl ClientExtension { hs_data: &ESNIHandshakeData, key_share_bytes: Vec) -> Option { let esni = compute_esni(dns_name, hs_data, key_share_bytes)?; - Some(ClientExtension::EncryptedServerName(vec![esni])) + Some(ClientExtension::EncryptedServerName(esni)) } } From e9d5863772449f869372f7dbdadc91055700d989 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sat, 26 Oct 2019 19:29:47 -0700 Subject: [PATCH 05/20] clean up some printing --- rustls-mio/examples/esniclient.rs | 8 +++----- rustls/src/esni.rs | 5 ----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index 4fab7d3ab92..9971fc99597 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -16,6 +16,8 @@ use trust_dns_resolver::Resolver; fn main() { let domain = "only.esni.defo.ie"; + println!("\nContacting {:?} over ESNI\n", domain); + let dns_config = ResolverConfig::cloudflare_https(); let opts = ResolverOpts::default(); let addr = Address::new(domain); @@ -46,7 +48,7 @@ fn main() { } } let ciphersuite = tls.sess.get_negotiated_ciphersuite().unwrap(); - writeln!(&mut std::io::stderr(), "\n\nCurrent ciphersuite: {:?}", ciphersuite.suite).unwrap(); + writeln!(&mut std::io::stderr(), "\n\nNegotiated ciphersuite: {:?}", ciphersuite.suite).unwrap(); writeln!(&mut std::io::stderr(), "\n\nReading the the stream is broken...").unwrap(); let mut plaintext = Vec::new(); match tls.read_to_end(&mut plaintext) { @@ -63,10 +65,8 @@ fn main() { pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Address) -> Vec { let resolver = Resolver::new(config, opts).unwrap(); - let response = resolver.lookup_ip(&address.dns_address()).unwrap(); let txt = resolver.txt_lookup(&address.esni_address()).unwrap(); - println!("txt: {:?}", txt); let text = Vec::from_iter(txt.iter()); let mut bytes: Vec = Vec::new(); for txt_record in text.iter() { @@ -77,9 +77,7 @@ pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Addres } } - println!("base 64: {}", std::str::from_utf8(&bytes).unwrap()); let decoded = decode(&bytes).unwrap(); - println!("bytes: {:?}", decoded); decoded } diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index bc4fb7aecf9..ad0d690128e 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -46,8 +46,6 @@ pub fn create_esni_config() -> ClientConfig { pub fn create_esni_handshake(record_bytes: &Vec) -> Option { let record = ESNIRecord::read(&mut Reader::init(&record_bytes))?; - println!("record {:?}", record); - // Check whether the record is still valid let now = now()?; @@ -133,9 +131,6 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, ring::aead::Aad::from(key_share_bytes), &mut sni_bytes) { Ok(_) => { - println!("What's the suite? {:?}", hs_data.cipher_suite.suite); - println!("What's record digest? {:?}", hs_data.record_digest); - println!("what the group? {:?}", hs_data.peer_share.group); Some (ClientEncryptedSNI { suite: hs_data.cipher_suite.suite, key_share_entry: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), From 2a9373d120de85acf02b6c2b9c92156ca197ebc6 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 27 Oct 2019 14:26:48 -0700 Subject: [PATCH 06/20] medium.com works --- rustls-mio/examples/esniclient.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index 9971fc99597..b434409ec89 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -15,7 +15,7 @@ use trust_dns_resolver::config::*; use trust_dns_resolver::Resolver; fn main() { - let domain = "only.esni.defo.ie"; + let domain = "medium.com"; println!("\nContacting {:?} over ESNI\n", domain); let dns_config = ResolverConfig::cloudflare_https(); @@ -25,16 +25,17 @@ fn main() { let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); let mut config = rustls::ClientConfig::default(); + config.alpn_protocols = vec![b"http/1.1".to_vec()]; config.encrypt_sni = true; config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - let dns_name = webpki::DNSNameRef::try_from_ascii_str("defo.ie").unwrap(); + let dns_name = webpki::DNSNameRef::try_from_ascii_str("medium.com").unwrap(); let mut sess = rustls::ClientSession::new_with_esni(&Arc::new(config), dns_name, esni_hs); let mut sock = TcpStream::connect(domain.to_owned() + ":443").unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); match tls.write(concat!("GET / HTTP/1.1\r\n", - "Host: only.esni.defo.ie\r\n", + "Host: medium.com\r\n", "Connection: close\r\n", "Accept-Encoding: identity\r\n", "\r\n") @@ -49,7 +50,6 @@ fn main() { } let ciphersuite = tls.sess.get_negotiated_ciphersuite().unwrap(); writeln!(&mut std::io::stderr(), "\n\nNegotiated ciphersuite: {:?}", ciphersuite.suite).unwrap(); - writeln!(&mut std::io::stderr(), "\n\nReading the the stream is broken...").unwrap(); let mut plaintext = Vec::new(); match tls.read_to_end(&mut plaintext) { Ok(success) => { From 62d2e80a3ed8b7fc29d273bab90d4bab66aacb8b Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 27 Oct 2019 14:29:39 -0700 Subject: [PATCH 07/20] Use TLS1.3-only config --- rustls-mio/examples/esniclient.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index b434409ec89..cada6d22d1f 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -24,9 +24,7 @@ fn main() { let esni_bytes = resolve_esni(dns_config, opts, &addr); let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); - let mut config = rustls::ClientConfig::default(); - config.alpn_protocols = vec![b"http/1.1".to_vec()]; - config.encrypt_sni = true; + let mut config = rustls::esni::create_esni_config(); config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); From 3ba1a213460c977c8404cf628ff1a7729f15868f Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 27 Oct 2019 18:46:17 -0700 Subject: [PATCH 08/20] Take out bogus list macro --- rustls/src/msgs/handshake.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 6cc2c0f5f2b..5399773aece 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -554,8 +554,6 @@ impl Codec for ClientESNIInner { } } -// declare_u16_vec!(ESNIRequest, ClientEncryptedSNI); - pub type ProtocolNameList = VecU16OfPayloadU8; pub trait ConvertProtocolNameList { From 96d1339a1b539b59b47e1d801ed44c9df4df4291 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 27 Oct 2019 21:44:26 -0700 Subject: [PATCH 09/20] Account for mistakes in the ESNI drafts. See: https://t.co/Jp6wwa9DSX --- rustls/src/esni.rs | 2 +- rustls/src/msgs/handshake.rs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index ad0d690128e..e67dfd11755 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -70,7 +70,7 @@ pub fn create_esni_handshake(record_bytes: &Vec) -> Option return None, }; - let digest = digest::digest(cipher_suite.get_hash(), &record.bytes.as_slice()[2..]); + let digest = digest::digest(cipher_suite.get_hash(), record_bytes); let bytes: Vec = Vec::from(digest.as_ref()); Some(ESNIHandshakeData { diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 5399773aece..b675dcc6a09 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -347,7 +347,6 @@ pub struct ESNIRecord { pub not_before: u64, pub not_after: u64, pub extensions: PayloadU16, - pub bytes: Vec, } impl ESNIRecord { @@ -383,9 +382,6 @@ impl Codec for ESNIRecord { let digest = ctx.finish(); let checksum_valid = slice_eq(checksum.as_slice(), &digest.as_ref()[0..4]); - let mut bytes = Vec::with_capacity(4 + rest.len() ); - bytes.extend_from_slice(checksum.as_slice()); - bytes.extend_from_slice(rest); let tail_reader = &mut Reader::init(rest); Some(ESNIRecord { version, @@ -397,7 +393,6 @@ impl Codec for ESNIRecord { not_before: u64::read(tail_reader)?, not_after: u64::read(tail_reader)?, extensions: PayloadU16::read(tail_reader)?, - bytes }) } } From 5bdfb38182e0dc27638ab4d77e78cd18761d4ed2 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 17 Nov 2019 18:43:42 -0800 Subject: [PATCH 10/20] Testing encrypt routing. problem must be in key calculation. --- rustls-mio/examples/esniclient.rs | 16 +-- rustls/Cargo.toml | 1 + rustls/src/esni.rs | 165 +++++++++++++++++++++++++++--- rustls/src/lib.rs | 3 + rustls/src/msgs/handshake.rs | 3 +- rustls/src/suites.rs | 6 +- rustls/src/verify.rs | 6 +- 7 files changed, 175 insertions(+), 25 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index cada6d22d1f..7ba32c20835 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -15,25 +15,28 @@ use trust_dns_resolver::config::*; use trust_dns_resolver::Resolver; fn main() { - let domain = "medium.com"; + let domain = "canbe.esni.defo.ie"; println!("\nContacting {:?} over ESNI\n", domain); - let dns_config = ResolverConfig::cloudflare_https(); + //let dns_config = ResolverConfig::cloudflare_https(); + let dns_config= ResolverConfig::default(); let opts = ResolverOpts::default(); let addr = Address::new(domain); let esni_bytes = resolve_esni(dns_config, opts, &addr); + println!("esni_bytes: {:02x?}", esni_bytes); + let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); let mut config = rustls::esni::create_esni_config(); config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - let dns_name = webpki::DNSNameRef::try_from_ascii_str("medium.com").unwrap(); + let dns_name = webpki::DNSNameRef::try_from_ascii_str("canbe.esni.defo.ie").unwrap(); let mut sess = rustls::ClientSession::new_with_esni(&Arc::new(config), dns_name, esni_hs); - let mut sock = TcpStream::connect(domain.to_owned() + ":443").unwrap(); + let mut sock = TcpStream::connect(domain.to_owned() + ":8443").unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); - match tls.write(concat!("GET / HTTP/1.1\r\n", - "Host: medium.com\r\n", + match tls.write(concat!("GET /stats HTTP/1.1\r\n", + "Host: canbe.esni.defo.ie\r\n", "Connection: close\r\n", "Accept-Encoding: identity\r\n", "\r\n") @@ -77,6 +80,7 @@ pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Addres let decoded = decode(&bytes).unwrap(); + println!("hmm? {:?}", decoded); decoded } diff --git a/rustls/Cargo.toml b/rustls/Cargo.toml index 4e8e4a639ae..2f18382d371 100644 --- a/rustls/Cargo.toml +++ b/rustls/Cargo.toml @@ -17,6 +17,7 @@ log = { version = "0.4.4", optional = true } ring = "0.16.5" sct = "0.6.0" webpki = "0.21.0" +hex-literal = "0.2.1" [features] default = ["logging"] diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index e67dfd11755..b516e593452 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -13,8 +13,9 @@ use webpki; use crate::SupportedCipherSuite; use crate::key_schedule::hkdf_expand; use crate::msgs::base::PayloadU16; -use ring::hkdf::KeyType; +use ring::hkdf::{KeyType, Prk}; use crate::cipher::{Iv, IvLen}; +use ring::aead::{UnboundKey, Algorithm}; /// Data calculated for a client session from a DNS ESNI record. #[derive(Clone, Debug)] @@ -46,6 +47,7 @@ pub fn create_esni_config() -> ClientConfig { pub fn create_esni_handshake(record_bytes: &Vec) -> Option { let record = ESNIRecord::read(&mut Reader::init(&record_bytes))?; + println!("record {:?}", record); // Check whether the record is still valid let now = now()?; @@ -93,59 +95,196 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, payload: ServerNamePayload::HostName(dns_name.into()), }; let psnl = PaddedServerNameList::new(name, hs_data.padded_length); + + let mut padded_bytes = Vec::new(); + psnl.encode(&mut padded_bytes); + println!("padded length: {}", padded_bytes.len()); + + let client_esni_inner = ClientESNIInner { nonce, real_sni: psnl, }; let mut sni_bytes = Vec::new(); client_esni_inner.encode(&mut sni_bytes); + println!("sni_bytes: {:02x?}, {}", sni_bytes, sni_bytes.len()); + println!("Client key share: {:?}", hs_data.peer_share); + let mut peer_bytes = Vec::new(); + hs_data.peer_share.clone().encode(&mut peer_bytes); + println!("peer_bytes: {:02x?}, {}", peer_bytes, peer_bytes.len()); let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { Some(ke) => ke, None => return None, }; + + println!("group: {:?}", key_exchange.group); + + + let keyex_Bytes = key_exchange.pubkey.as_ref(); + println!(" key_exchange: {:02x?}, {}", keyex_Bytes, keyex_Bytes.len()); let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; + let mut result_bytes = exchange_result.pubkey.as_ref(); + println!(" Z result_bytes: {:02x?}, {}", result_bytes, result_bytes.len()); + + let premaster_bytes = exchange_result.premaster_secret.as_slice(); + println!("Z premaster_bytes: {:02x?}, {}", premaster_bytes, premaster_bytes.len()); + let mut random = [0u8; 32]; rand::fill_random(&mut random); let contents = ESNIContents { record_digest: PayloadU16::new(hs_data.record_digest.clone()), - esni_key_share: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), + esni_key_share: KeyShareEntry { + group: hs_data.peer_share.group, + payload: PayloadU16(exchange_result.pubkey.clone().as_ref().to_vec()) + }, client_hello_random: Random::from_slice(&random), }; let mut contents_bytes = Vec::new(); contents.encode(&mut contents_bytes); + println!("ESNIContents encoded, {:02x?}, {}", contents_bytes, contents_bytes.len()); let digest = digest::digest(hs_data.cipher_suite.get_hash(), &contents_bytes); + let digest_bytes = digest.as_ref(); + println!(" ESNIContents hash, {:02x?}, {}", digest_bytes, digest_bytes.len()); + + + let zx = zx(hs_data.cipher_suite.hkdf_algorithm, &exchange_result.premaster_secret); - let algorithm = hs_data.cipher_suite.hkdf_algorithm; - let zeroes = [0u8; digest::MAX_OUTPUT_LEN]; - let zeroes = &zeroes[..algorithm.len()]; - let salt = hkdf::Salt::new(algorithm, &zeroes); - let zx = salt.extract(&exchange_result.premaster_secret); let key = hkdf_expand(&zx, hs_data.cipher_suite.get_aead_alg(), b"esni key", digest.as_ref()); + println!("Key {:?}", key); let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", digest.as_ref()); - let lsk = ring::aead::LessSafeKey::new(key); - match lsk.seal_in_place_append_tag(ring::aead::Nonce::assume_unique_for_key(*iv.value()), - ring::aead::Aad::from(key_share_bytes), - &mut sni_bytes) { - Ok(_) => { + println!("Iv {:02x?}", iv.value()); + + + println!("key_share_bytes: {:02x?}, {}", key_share_bytes, key_share_bytes.len()); + let aad = ring::aead::Aad::from(key_share_bytes.to_vec()); + let aad_bytes = aad.as_ref(); + println!("AAD: {:02x?}, {}", aad_bytes, aad_bytes.len()); + + match encrypt(key, iv, aad, &mut sni_bytes) { + Some(bytes) => { + println!("cipher: {:02x?}, {}", bytes, bytes.len()); Some (ClientEncryptedSNI { suite: hs_data.cipher_suite.suite, key_share_entry: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), record_digest: PayloadU16(hs_data.record_digest.clone()), - encrypted_sni: PayloadU16(Vec::from(sni_bytes)), + encrypted_sni: PayloadU16(bytes), }) }, _ => None } } +fn zx(algorithm: ring::hkdf::Algorithm, secret: &Vec) -> Prk { + let zeroes = [0u8; digest::MAX_OUTPUT_LEN]; + let zeroes = &zeroes[..algorithm.len()]; + let salt = hkdf::Salt::new(algorithm, &zeroes); + salt.extract(secret) +} + +fn encrypt(unbound: UnboundKey, iv: Iv, aad: ring::aead::Aad>, sni_bytes: &mut Vec) -> Option> { + let lsk = ring::aead::LessSafeKey::new(unbound); + match lsk.seal_in_place_append_tag(ring::aead::Nonce::assume_unique_for_key(*iv.value()), + aad, + sni_bytes) { + Ok(_) => Some(sni_bytes.clone()), + _ => None + } +} + fn now() -> Option { let start = SystemTime::now(); match start.duration_since(UNIX_EPOCH) { Err(_e) => None, Ok(since_the_epoch) => Some(since_the_epoch.as_secs()) } +} + +#[cfg(test)] +mod tests { + use crate::SupportedCipherSuite; + + #[test] + fn test_zx() { + let z_bytes = hex!(" + 97 1a 40 1c cb 08 be 7f 7b de f3 10 c7 9c 1d 36 + 45 bd 27 f6 34 ee 73 e6 5d 1b a0 ff 60 f8 3e 5e"); + + let zx_bytes = hex!(" + 51 98 ef 6a 9d bb af f8 44 42 ac 57 28 69 e6 63 + 60 ce 27 2d c4 30 82 5f 4e 2c eb a4 4e 42 05 0a + "); + + let zx = super::zx(crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm, &z_bytes.to_vec()); + } + + #[test] + fn test_encrypt() { + let key_bytes = hex!("9b 9f f2 2c dd 39 4c f6 20 ac f8 d6 f6 90 99 ab"); + + let iv_bytes = hex!("d0 c2 2c 42 3c 03 a7 1d 3d 36 36 51"); + + let aad_bytes = hex!(" + 00 69 00 1d 00 20 e7 41 + 94 4b 78 8d 6f cd 6b 5b 64 f6 69 35 83 d1 df c7 + e8 21 55 c6 f7 8d a5 c3 25 b9 7a 69 58 7d 00 17 + 00 41 04 d8 75 ac 7c 46 38 c6 eb 35 a9 90 60 6b + 1b be b1 70 dd 18 0c 80 82 8d 83 95 b1 aa a5 2e + 24 2e fb ed 9f 2a bd 7f 86 f0 8c 8b 6b ca db a6 + 28 69 88 1d fb 76 5f 34 d9 da 0b 07 02 64 80 d2 + d3 84 15 "); + + let mut plain_text = hex!(" + ad 1b f4 b3 d3 14 59 48 59 9e be c8 56 42 4f 66 + 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e + 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 "); + + let expected = hex!(" + 6f f6 5d 1e bd 9c 35 2d 2c 1c ca 92 5d 3e 1a 65 + f6 30 fe 97 3b a0 24 9d 92 b8 cb 67 f0 1d 17 a4 + bc 11 9b ac 39 c4 48 f7 bb 86 04 b5 58 ad 76 15 + 10 c3 21 d0 3b 86 ac c9 d6 7e 9f 89 6e b0 73 cb + 69 97 f4 1b f5 17 e9 81 29 86 6f 3e df 49 99 3c + 59 00 24 6c 2d d6 3e 7b d2 b7 bd 3a c0 90 8f b6 + dc 2b 11 08 15 00 41 ca fb 79 ef 57 5b 17 18 00 + bf c2 0c 1b 2b cf 1e 9b f0 0f 9d 67 32 37 e1 06 + 22 f8 cb a8 a3 40 26 6e 50 85 32 29 d7 20 41 a5 + 0f 47 87 d0 af 01 ba 83 62 ad a0 b6 ac 8e d5 dd + 24 42 3d f8 a8 f9 9e 16 40 cf 85 b9 16 39 f8 94 + 4b bd cb a5 59 a8 a9 65 7a 83 95 b2 38 c7 3b d5 + d4 9b 6f f0 e3 18 d0 cb 65 65 c9 0c 8a 07 a1 ce + 5f 39 ed 6a 1b 6f e7 59 11 7d b3 81 e4 4b 51 d4 + db 28 f3 95 eb 16 62 de de 29 c7 dc 79 54 67 24 + d7 4d d1 3f 34 ca 64 6e 6c 12 9a e4 0c 1c ea 33 + c3 81 15 48 04 14 a4 ed ab 44 90 e9 0d c2 56 8a + df 4e 92 eb 3b 93 f5 5c 59 15 0e 7d 85 66 2d b4 + 62 ee 41 8a"); + + let key = ring::aead::UnboundKey::new(crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(), &key_bytes).unwrap(); + let iv = crate::cipher::Iv::new(iv_bytes); + let aad = ring::aead::Aad::from(aad_bytes.to_vec()); + let mut sni_bytes = Vec::from(plain_text.to_vec()); + let encrypted = super::encrypt(key, iv, aad, &mut sni_bytes).unwrap(); + println!("{}, {:02x?}", encrypted.len(), encrypted); + assert_eq!(expected.len(), encrypted.len()); + assert!(crate::msgs::handshake::slice_eq(&expected, encrypted.as_slice())); + } } \ No newline at end of file diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index 1de2840b651..d16096e7ddb 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -211,6 +211,9 @@ mod log { macro_rules! error ( ($($tt:tt)*) => {{}} ); } +#[macro_use] +extern crate hex_literal; + #[allow(missing_docs)] #[macro_use] mod msgs; diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index b675dcc6a09..faa98047e86 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -382,6 +382,7 @@ impl Codec for ESNIRecord { let digest = ctx.finish(); let checksum_valid = slice_eq(checksum.as_slice(), &digest.as_ref()[0..4]); + println!("rest: {:02x?}", rest); let tail_reader = &mut Reader::init(rest); Some(ESNIRecord { version, @@ -397,7 +398,7 @@ impl Codec for ESNIRecord { } } -fn slice_eq<'a, T: PartialEq>(a: &'a [T], b: &'a [T]) -> bool { +pub fn slice_eq<'a, T: PartialEq>(a: &'a [T], b: &'a [T]) -> bool { if a.len() != b.len() { return false; } diff --git a/rustls/src/suites.rs b/rustls/src/suites.rs index 1f5c24b0c3d..95303a8d214 100644 --- a/rustls/src/suites.rs +++ b/rustls/src/suites.rs @@ -373,9 +373,9 @@ pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite hkdf_algorithm: ring::hkdf::HKDF_SHA256, }; -pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 3] = - [&TLS13_CHACHA20_POLY1305_SHA256, - &TLS13_AES_256_GCM_SHA384, +pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 1] = + [//&TLS13_CHACHA20_POLY1305_SHA256, + //&TLS13_AES_256_GCM_SHA384, &TLS13_AES_128_GCM_SHA256]; /// A list of all the cipher suites supported by rustls. diff --git a/rustls/src/verify.rs b/rustls/src/verify.rs index 89bc958bf16..49d65891b5b 100644 --- a/rustls/src/verify.rs +++ b/rustls/src/verify.rs @@ -111,9 +111,11 @@ impl ServerCertVerifier for WebPKIVerifier { debug!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec()); } - cert.verify_is_valid_for_dns_name(dns_name) + println!("here is the problem..."); + /*cert.verify_is_valid_for_dns_name(dns_name) .map_err(TLSError::WebPKIError) - .map(|_| ServerCertVerified::assertion()) + .map(|_| ServerCertVerified::assertion())*/ + Ok(ServerCertVerified::assertion()) } } From 3793a7fe7a713502219f7ef889fc3b4fc6ee3b79 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sat, 23 Nov 2019 19:46:29 -0800 Subject: [PATCH 11/20] Fix plaintext, add more tests. --- rustls/src/esni.rs | 261 +++++++++++++++++++++++++++++++---- rustls/src/msgs/handshake.rs | 6 +- 2 files changed, 235 insertions(+), 32 deletions(-) diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index b516e593452..504db72d801 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -11,7 +11,6 @@ use std::time::{SystemTime, UNIX_EPOCH}; use ring::{digest, hkdf}; use webpki; use crate::SupportedCipherSuite; -use crate::key_schedule::hkdf_expand; use crate::msgs::base::PayloadU16; use ring::hkdf::{KeyType, Prk}; use crate::cipher::{Iv, IvLen}; @@ -45,6 +44,7 @@ pub fn create_esni_config() -> ClientConfig { /// Creates a `ClientConfig` with defaults suitable for ESNI extension support. /// This creates a config that supports TLS 1.3 only. pub fn create_esni_handshake(record_bytes: &Vec) -> Option { + println!("ESNIKeys:{} {:02x?}", record_bytes.len(), record_bytes); let record = ESNIRecord::read(&mut Reader::init(&record_bytes))?; println!("record {:?}", record); @@ -90,23 +90,7 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, key_share_bytes: Vec) -> Option { let mut nonce = [0u8; 16]; rand::fill_random(&mut nonce); - let name = ServerName { - typ: ServerNameType::HostName, - payload: ServerNamePayload::HostName(dns_name.into()), - }; - let psnl = PaddedServerNameList::new(name, hs_data.padded_length); - - let mut padded_bytes = Vec::new(); - psnl.encode(&mut padded_bytes); - println!("padded length: {}", padded_bytes.len()); - - - let client_esni_inner = ClientESNIInner { - nonce, - real_sni: psnl, - }; - let mut sni_bytes = Vec::new(); - client_esni_inner.encode(&mut sni_bytes); + let mut sni_bytes = compute_client_esni_inner(dns_name, hs_data.padded_length, nonce); println!("sni_bytes: {:02x?}, {}", sni_bytes, sni_bytes.len()); println!("Client key share: {:?}", hs_data.peer_share); @@ -114,6 +98,7 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, hs_data.peer_share.clone().encode(&mut peer_bytes); println!("peer_bytes: {:02x?}, {}", peer_bytes, peer_bytes.len()); + println!("dns_name: {:?}", dns_name); let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { Some(ke) => ke, None => return None, @@ -124,6 +109,8 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, let keyex_Bytes = key_exchange.pubkey.as_ref(); println!(" key_exchange: {:02x?}, {}", keyex_Bytes, keyex_Bytes.len()); + let payload = &hs_data.peer_share.payload; + println!("payload length: {:?}", payload); let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; let mut result_bytes = exchange_result.pubkey.as_ref(); println!(" Z result_bytes: {:02x?}, {}", result_bytes, result_bytes.len()); @@ -146,16 +133,15 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, let mut contents_bytes = Vec::new(); contents.encode(&mut contents_bytes); println!("ESNIContents encoded, {:02x?}, {}", contents_bytes, contents_bytes.len()); - let digest = digest::digest(hs_data.cipher_suite.get_hash(), &contents_bytes); - let digest_bytes = digest.as_ref(); - println!(" ESNIContents hash, {:02x?}, {}", digest_bytes, digest_bytes.len()); + let hash = esni_hash(&contents_bytes, hs_data.cipher_suite.get_hash()); + println!(" ESNIContents hash, {:02x?}, {}", hash, hash.len()); let zx = zx(hs_data.cipher_suite.hkdf_algorithm, &exchange_result.premaster_secret); - let key = hkdf_expand(&zx, hs_data.cipher_suite.get_aead_alg(), b"esni key", digest.as_ref()); + let key = hkdf_expand(&zx, hs_data.cipher_suite.get_aead_alg(), b"esni key", &hash); println!("Key {:?}", key); - let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", digest.as_ref()); + let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", &hash); println!("Iv {:02x?}", iv.value()); @@ -178,10 +164,60 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, } } +fn compute_client_esni_inner(dns_name: webpki::DNSNameRef, length: u16, nonce: [u8; 16]) -> Vec { + let name = ServerName { + typ: ServerNameType::HostName, + payload: ServerNamePayload::HostName(dns_name.into()), + }; + let psnl = PaddedServerNameList::new(vec![name], length); + + let mut padded_bytes = Vec::new(); + psnl.encode(&mut padded_bytes); + + let client_esni_inner = ClientESNIInner { + nonce, + real_sni: psnl, + }; + let mut sni_bytes = Vec::new(); + client_esni_inner.encode(&mut sni_bytes); + sni_bytes +} + +fn esni_hash(encoded_esni_contents: &Vec, algorithm: &'static ring::digest::Algorithm) -> Vec { + let digest = digest::digest(algorithm, &encoded_esni_contents); + digest.as_ref().to_vec() +} + +fn hkdf_expand(secret: &Prk, key_type: L, label: &[u8], context: &[u8]) -> T + where + T: for <'a> From>, + L: KeyType, +{ + hkdf_expand_info(secret, key_type, label, context, |okm| okm.into()) +} + +fn hkdf_expand_info(secret: &Prk, key_type: L, label: &[u8], context: &[u8], f: F) + -> T + where + F: for<'b> FnOnce(hkdf::Okm<'b, L>) -> T, + L: KeyType +{ + const LABEL_PREFIX: &[u8] = b"tls13 "; + + let output_len = u16::to_be_bytes(key_type.len() as u16); + let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8); + let context_len = u8::to_be_bytes(context.len() as u8); + + let info = &[&output_len[..], &label_len[..], LABEL_PREFIX, label, &context_len[..], context]; + let okm = secret.expand(info, key_type).unwrap(); + + f(okm) +} + fn zx(algorithm: ring::hkdf::Algorithm, secret: &Vec) -> Prk { let zeroes = [0u8; digest::MAX_OUTPUT_LEN]; let zeroes = &zeroes[..algorithm.len()]; - let salt = hkdf::Salt::new(algorithm, &zeroes); + let salt = hkdf::Salt::new(algorithm, &[]); salt.extract(secret) } @@ -203,22 +239,170 @@ fn now() -> Option { } } +struct ESNILen { + bytes: Vec +} + +impl ESNILen { + fn new(suite: &SupportedCipherSuite) -> ESNILen { + ESNILen { + bytes: vec![0u8; suite.enc_key_len] + } + } +} + #[cfg(test)] mod tests { use crate::SupportedCipherSuite; + use super::hkdf_expand; + use crate::cipher::{Iv, IvLen}; + use crate::esni::ESNILen; + use crate::msgs::handshake::ESNIRecord; + use crate::msgs::codec::{Codec, Reader}; + use webpki; + + #[test] + fn test_compute_client_esni_inner() { + let nonce = hex!("c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28"); + + let dns_name = webpki::DNSNameRef::try_from_ascii(b"canbe.esni.defo.ie").unwrap(); + + let expected = hex!(" + c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28 + 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e + 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00"); + + let result = super::compute_client_esni_inner(dns_name, 260u16, nonce); + // assert_eq!(expected.len(), result.len()); + + println!("expected: {:02x?}, {}", expected.to_vec(), expected.len()); + println!(" result: {:02x?}, {}", result, result.len()); + + + assert!(crate::msgs::handshake::slice_eq(&expected, &result)); + } + + #[test] + fn test_hash() { + let esni_bytes = hex!(" + 00 20 3e 06 06 98 4c 3b a9 70 3a fb a7 a1 2d 75 + 29 5b 05 81 7d 75 8f 40 9b 51 00 c8 37 8e 9d 08 + 7e f1 00 1d 00 20 72 d8 3a 31 da 1c cd c7 e5 89 + c1 c6 24 bd 7a 14 2d 90 de 7f 01 82 73 9d 25 14 + c2 66 e1 97 23 5b 64 c0 c4 7c 5b c8 14 a0 a4 2b + 0c 2f f4 23 51 00 10 f4 1d f4 c1 f4 3c 3e 89 c8 + fe 87 25 d1 9f 00 "); + + let expected = hex!(" + 21 5b ba fe a8 9e da 35 7b 7b 55 e4 6d 01 ac c8 + 94 94 b2 6e e6 55 08 0e 47 21 6a b2 3b 7d 25 f7 + "); + + let result = super::esni_hash(&esni_bytes.to_vec(), &ring::digest::SHA256); + assert!(crate::msgs::handshake::slice_eq(&expected, &result)); + } #[test] fn test_zx() { let z_bytes = hex!(" - 97 1a 40 1c cb 08 be 7f 7b de f3 10 c7 9c 1d 36 - 45 bd 27 f6 34 ee 73 e6 5d 1b a0 ff 60 f8 3e 5e"); + de cf 6a 8c 23 49 e1 8c db d8 48 49 7c 10 16 9a + 77 66 fb 3f f4 8b 54 f7 bd 1f 15 14 74 e1 88 1c"); + + let hash = hex!(" + a5 33 9b 1b a6 ae d2 7f 43 b9 91 5e 5e bc 8e 5a + af d9 fb 1d e2 b4 df 36 13 70 97 14 27 a1 61 25 + "); - let zx_bytes = hex!(" - 51 98 ef 6a 9d bb af f8 44 42 ac 57 28 69 e6 63 - 60 ce 27 2d c4 30 82 5f 4e 2c eb a4 4e 42 05 0a + let expected_iv = hex!(" + 07 d7 77 4c 69 be bd ad 1b 75 49 c7 "); + let aad_bytes = hex!(" + 00 69 00 1d 00 20 70 cb + 7e ce 36 ab c1 b6 e1 92 6a 9a f2 08 d9 91 70 f1 + 98 7a aa 0f e3 9b f0 b3 c5 4d 79 00 a8 07 00 17 + 00 41 04 03 1d 6c 6c e6 f3 28 1f 6f f2 78 d5 5c + 0f 5e f7 be 52 71 9f 7e c0 0e 6e 26 db 85 7b f9 + e0 73 91 e6 b5 3e 06 7b ef c8 f8 b5 f0 46 16 c2 + 9f 0d 52 c3 6a 9e 41 2f 68 ce 7e ee d0 27 99 e5 + 28 aa 9e + "); + + let plain_text = hex!(" + 4f b0 25 11 6b f7 4d f8 ce f3 0f 59 ce d9 d6 df + 00 17 00 00 14 63 64 6e 6a 73 2e 63 6c 6f 75 64 + 66 6c 61 72 65 2e 63 6f 6d 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 + "); + + let expected = hex!(" + 28 0a 3d 56 cd 30 9d 68 ac 98 1b 41 bb fb 85 26 + 48 ef 1a 83 c8 aa bd 12 15 80 44 10 50 2f c0 3d + 68 15 99 e0 47 6a 80 c2 e9 a0 df 86 16 7e a8 a4 + 37 8c 27 62 89 7e f8 60 4f 04 cf b5 ea 60 ed 99 + 51 59 70 a1 a5 ac b9 32 7d 35 86 e9 e2 01 d6 60 + 9d 8d de 81 03 69 13 dd 66 09 e9 18 76 f9 25 65 + 3d b7 ea 22 50 da 50 4d d8 74 31 5a 35 a2 29 7a + 09 31 0a 45 4e b2 29 fd 72 40 04 93 3a e3 a6 7d + 09 46 bb b5 8d e0 0f b5 12 e4 36 7d 38 32 3b b5 + ee 99 6f ad 2c ea af 39 9f a1 dc c9 70 dc 2f ad + 46 de 2a d6 8c 4e 3c e6 31 01 8a 97 f0 1f c9 3c + b8 c8 f1 45 02 c4 d7 3d ee b9 88 6f 53 cc 85 0b + 69 ce 61 dc 30 c8 85 2d e1 d0 d3 d6 10 c2 32 04 + 0d 96 2d d5 4a a4 1f e2 bc a3 77 15 72 61 20 75 + aa 9b 4a ee f7 25 cf 22 95 b9 77 88 48 f3 30 8e + a4 ab 3d b4 bd b4 e4 24 98 b7 ca 7e bf 26 ee 82 + b5 b4 fd f2 f0 65 04 ea 4c 7c 75 25 24 b0 be 92 + 9a a2 b7 e4 82 5a 37 cf 08 3f 0e 9b 6c 89 27 b4 + 33 15 75 24 + "); + + //let expected = hex!("4a 44 bd 36 07 73 51 95 a6 27 cc 81 c9 a5 6c fe"); + let zx = super::zx(crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm, &z_bytes.to_vec()); + println!("{:?}", zx); + let aead_alg = crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(); + let key :ring::aead::UnboundKey = hkdf_expand(&zx, aead_alg, b"esni key", hash.as_ref()); + + let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", hash.as_ref()); + println!("Iv {:02x?}", iv.value()); + assert!(crate::msgs::handshake::slice_eq(&expected_iv, iv.value())); + + let aad = ring::aead::Aad::from(aad_bytes.to_vec()); + let mut sni_bytes = Vec::from(plain_text.to_vec()); + let encrypted = super::encrypt(key, iv, aad, &mut sni_bytes).unwrap(); + println!("{}, {:02x?}", encrypted.len(), encrypted); + + assert!(crate::msgs::handshake::slice_eq(&expected, &encrypted)); } #[test] @@ -287,4 +471,23 @@ mod tests { assert_eq!(expected.len(), encrypted.len()); assert!(crate::msgs::handshake::slice_eq(&expected, encrypted.as_slice())); } + + /// Generic newtype wrapper that lets us implement traits for externally-defined + /// types. + #[derive(Debug, PartialEq)] + struct My(T); + + impl ring::hkdf::KeyType for My { + fn len(&self) -> usize { + self.0 + } + } + + impl From>> for My> { + fn from(okm: ring::hkdf::Okm>) -> Self { + let mut r = vec![0u8; okm.len().0]; + okm.fill(&mut r).unwrap(); + My(r) + } + } } \ No newline at end of file diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index faa98047e86..b4ec1a8d24a 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -482,13 +482,13 @@ impl Codec for ESNIContents { #[derive(Clone, Debug)] pub struct PaddedServerNameList { - pub sni: ServerName, + pub sni: ServerNameRequest, pub zeros: Vec, pub padded_length: u16, } impl PaddedServerNameList { - pub fn new(sni: ServerName, padded_length: u16) -> PaddedServerNameList { + pub fn new(sni: ServerNameRequest, padded_length: u16) -> PaddedServerNameList { let mut output = Vec::new(); sni.encode(&mut output); let length = padded_length - output.len() as u16; @@ -520,7 +520,7 @@ impl Codec for PaddedServerNameList { } Some(PaddedServerNameList { - sni, + sni: vec![sni], zeros: padding, padded_length: (sni_length + len) as u16, }) From 665a83a27c8e4b6cb36d55c64e5317bbf051cd99 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 11:46:48 -0800 Subject: [PATCH 12/20] Working client, still messy --- Cargo.toml | 3 +- rustls/Cargo.toml | 2 +- rustls/src/client/hs.rs | 2 +- rustls/src/esni.rs | 83 ++++++++++++++++++++++++++---------- rustls/src/msgs/handshake.rs | 6 ++- 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 26f7901de79..f31c3c5739d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [workspace] + members = [ # the main library and tests "rustls", # tests and example code that depend on mio "rustls-mio", -] +] \ No newline at end of file diff --git a/rustls/Cargo.toml b/rustls/Cargo.toml index 2f18382d371..338cc90a316 100644 --- a/rustls/Cargo.toml +++ b/rustls/Cargo.toml @@ -14,7 +14,7 @@ autobenches = false [dependencies] base64 = "0.10" log = { version = "0.4.4", optional = true } -ring = "0.16.5" +ring = "0.16.9" sct = "0.6.0" webpki = "0.21.0" hex-literal = "0.2.1" diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 58c26cdbdff..9bba325e8e9 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -234,7 +234,7 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, if let Some(ks) = &keyshare_entries { let mut ks_bytes= Vec::new(); encode_vec_u16(&mut ks_bytes, ks); - let esni_ext = ClientExtension::make_esni(handshake.dns_name.as_ref(), esni, ks_bytes); + let esni_ext = ClientExtension::make_esni(handshake.dns_name.as_ref(), esni, ks_bytes, &handshake.randoms); if let Some(ext) = esni_ext { println!("Pushing ESNI..."); exts.push(ext); diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 504db72d801..639584d9232 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -15,6 +15,7 @@ use crate::msgs::base::PayloadU16; use ring::hkdf::{KeyType, Prk}; use crate::cipher::{Iv, IvLen}; use ring::aead::{UnboundKey, Algorithm}; +use crate::session::SessionRandoms; /// Data calculated for a client session from a DNS ESNI record. #[derive(Clone, Debug)] @@ -72,22 +73,24 @@ pub fn create_esni_handshake(record_bytes: &Vec) -> Option return None, }; - let digest = digest::digest(cipher_suite.get_hash(), record_bytes); - let bytes: Vec = Vec::from(digest.as_ref()); - Some(ESNIHandshakeData { peer_share, cipher_suite, padded_length: record.padded_length, - record_digest: bytes, + record_digest: record_digest(cipher_suite.get_hash(), record_bytes), }) } +fn record_digest(algorithm: &'static ring::digest::Algorithm, bytes: &[u8]) -> Vec { + digest::digest(algorithm, bytes).as_ref().to_vec() +} + /// Compute the encrypted SNI // TODO: this is big and messy, fix it up pub fn compute_esni(dns_name: webpki::DNSNameRef, hs_data: &ESNIHandshakeData, - key_share_bytes: Vec) -> Option { + key_share_bytes: Vec, + randoms: &SessionRandoms) -> Option { let mut nonce = [0u8; 16]; rand::fill_random(&mut nonce); let mut sni_bytes = compute_client_esni_inner(dns_name, hs_data.padded_length, nonce); @@ -98,7 +101,6 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, hs_data.peer_share.clone().encode(&mut peer_bytes); println!("peer_bytes: {:02x?}, {}", peer_bytes, peer_bytes.len()); - println!("dns_name: {:?}", dns_name); let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { Some(ke) => ke, None => return None, @@ -119,20 +121,7 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, println!("Z premaster_bytes: {:02x?}, {}", premaster_bytes, premaster_bytes.len()); - let mut random = [0u8; 32]; - rand::fill_random(&mut random); - let contents = ESNIContents { - record_digest: PayloadU16::new(hs_data.record_digest.clone()), - esni_key_share: KeyShareEntry { - group: hs_data.peer_share.group, - payload: PayloadU16(exchange_result.pubkey.clone().as_ref().to_vec()) - }, - client_hello_random: Random::from_slice(&random), - }; - - let mut contents_bytes = Vec::new(); - contents.encode(&mut contents_bytes); - println!("ESNIContents encoded, {:02x?}, {}", contents_bytes, contents_bytes.len()); + let mut contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); let hash = esni_hash(&contents_bytes, hs_data.cipher_suite.get_hash()); println!(" ESNIContents hash, {:02x?}, {}", hash, hash.len()); @@ -164,6 +153,22 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, } } +fn compute_esni_content(hs_data: &ESNIHandshakeData, pubkey: &Vec, random: [u8; 32]) -> Vec { + let contents = ESNIContents { + record_digest: PayloadU16::new(hs_data.record_digest.clone()), + esni_key_share: KeyShareEntry { + group: hs_data.peer_share.group, + payload: PayloadU16(pubkey.to_vec()) + }, + client_hello_random: Random::from_slice(&random), + }; + + let mut contents_bytes = Vec::new(); + contents.encode(&mut contents_bytes); + println!("ESNIContents encoded, {:02x?}, {}", contents_bytes, contents_bytes.len()); + contents_bytes +} + fn compute_client_esni_inner(dns_name: webpki::DNSNameRef, length: u16, nonce: [u8; 16]) -> Vec { let name = ServerName { typ: ServerNameType::HostName, @@ -261,6 +266,40 @@ mod tests { use crate::msgs::codec::{Codec, Reader}; use webpki; + #[test] + fn test_compute_esni_content() { + let esni_keys = hex!("ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de + ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d + aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 + 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da + 09 a0 00 00"); + + let random = hex!(" + 1a 2b 12 8a 1e 08 7e 12 68 b7 53 90 97 05 21 36 + 6f 1c 27 ce 43 1b f2 1c fb 6e 95 7f af 1a 67 67 + "); + + + let hs_data = super::create_esni_handshake(&esni_keys).unwrap(); + } + + #[test] + fn test_record_digest() { + let esni_keys = hex!("ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de + ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d + aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 + 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da + 09 a0 00 00"); + + let expected = hex!(" + 3b d7 25 90 a7 58 68 16 46 c5 22 93 2a 1e b0 8d + 0c e3 8c 2c 67 21 8e bf ab 88 90 04 49 cc 23 92 + "); + + let result = super::record_digest(&ring::digest::SHA256, &esni_keys); + assert!(crate::msgs::handshake::slice_eq(&expected, &result)); + } + #[test] fn test_compute_client_esni_inner() { let nonce = hex!("c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28"); @@ -288,7 +327,7 @@ mod tests { 00 00 00 00"); let result = super::compute_client_esni_inner(dns_name, 260u16, nonce); - // assert_eq!(expected.len(), result.len()); + assert_eq!(expected.len(), result.len()); println!("expected: {:02x?}, {}", expected.to_vec(), expected.len()); println!(" result: {:02x?}, {}", result, result.len()); @@ -386,8 +425,6 @@ mod tests { 33 15 75 24 "); - //let expected = hex!("4a 44 bd 36 07 73 51 95 a6 27 cc 81 c9 a5 6c fe"); - let zx = super::zx(crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm, &z_bytes.to_vec()); println!("{:?}", zx); let aead_alg = crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(); diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index b4ec1a8d24a..500f7f01961 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -21,6 +21,7 @@ use std::collections; use std::mem; use ring::digest; use webpki; +use crate::session::SessionRandoms; macro_rules! declare_u8_vec( ($name:ident, $itemtype:ty) => { @@ -919,8 +920,9 @@ impl ClientExtension { /// Make an ESNI request, encrypting `hostname` with the ESNIRecord pub fn make_esni(dns_name: webpki::DNSNameRef, hs_data: &ESNIHandshakeData, - key_share_bytes: Vec) -> Option { - let esni = compute_esni(dns_name, hs_data, key_share_bytes)?; + key_share_bytes: Vec, + randoms: &SessionRandoms) -> Option { + let esni = compute_esni(dns_name, hs_data, key_share_bytes, randoms)?; Some(ClientExtension::EncryptedServerName(esni)) } } From cdf514208e79d6769ca006a6b7c31562467c991e Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 13:06:53 -0800 Subject: [PATCH 13/20] Cleaned up esni.rs --- rustls/src/esni.rs | 420 +++++++++++++++++++-------------------------- 1 file changed, 178 insertions(+), 242 deletions(-) diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 639584d9232..63828f506d1 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -16,6 +16,7 @@ use ring::hkdf::{KeyType, Prk}; use crate::cipher::{Iv, IvLen}; use ring::aead::{UnboundKey, Algorithm}; use crate::session::SessionRandoms; +use crate::key_schedule::hkdf_expand; /// Data calculated for a client session from a DNS ESNI record. #[derive(Clone, Debug)] @@ -45,10 +46,7 @@ pub fn create_esni_config() -> ClientConfig { /// Creates a `ClientConfig` with defaults suitable for ESNI extension support. /// This creates a config that supports TLS 1.3 only. pub fn create_esni_handshake(record_bytes: &Vec) -> Option { - println!("ESNIKeys:{} {:02x?}", record_bytes.len(), record_bytes); let record = ESNIRecord::read(&mut Reader::init(&record_bytes))?; - - println!("record {:?}", record); // Check whether the record is still valid let now = now()?; @@ -56,10 +54,14 @@ pub fn create_esni_handshake(record_bytes: &Vec) -> Option) -> Option { let peer_share = match KeyExchange::supported_groups() .iter() .flat_map(|group| { - record.keys.iter().find(|key| { key.group == *group }) + record.keys.iter().find(|key| { key.group == *group }) }).nth(0) .cloned() { Some(entry) => entry, @@ -81,6 +83,14 @@ pub fn create_esni_handshake(record_bytes: &Vec) -> Option Option { + let start = SystemTime::now(); + match start.duration_since(UNIX_EPOCH) { + Err(_e) => None, + Ok(since_the_epoch) => Some(since_the_epoch.as_secs()) + } +} + fn record_digest(algorithm: &'static ring::digest::Algorithm, bytes: &[u8]) -> Vec { digest::digest(algorithm, bytes).as_ref().to_vec() } @@ -94,54 +104,24 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, let mut nonce = [0u8; 16]; rand::fill_random(&mut nonce); let mut sni_bytes = compute_client_esni_inner(dns_name, hs_data.padded_length, nonce); - println!("sni_bytes: {:02x?}, {}", sni_bytes, sni_bytes.len()); - - println!("Client key share: {:?}", hs_data.peer_share); let mut peer_bytes = Vec::new(); hs_data.peer_share.clone().encode(&mut peer_bytes); - println!("peer_bytes: {:02x?}, {}", peer_bytes, peer_bytes.len()); let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { Some(ke) => ke, None => return None, }; - - println!("group: {:?}", key_exchange.group); - - - let keyex_Bytes = key_exchange.pubkey.as_ref(); - println!(" key_exchange: {:02x?}, {}", keyex_Bytes, keyex_Bytes.len()); - let payload = &hs_data.peer_share.payload; - println!("payload length: {:?}", payload); let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; - let mut result_bytes = exchange_result.pubkey.as_ref(); - println!(" Z result_bytes: {:02x?}, {}", result_bytes, result_bytes.len()); - - let premaster_bytes = exchange_result.premaster_secret.as_slice(); - println!("Z premaster_bytes: {:02x?}, {}", premaster_bytes, premaster_bytes.len()); - - let mut contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); let hash = esni_hash(&contents_bytes, hs_data.cipher_suite.get_hash()); - println!(" ESNIContents hash, {:02x?}, {}", hash, hash.len()); - let zx = zx(hs_data.cipher_suite.hkdf_algorithm, &exchange_result.premaster_secret); - let key = hkdf_expand(&zx, hs_data.cipher_suite.get_aead_alg(), b"esni key", &hash); - println!("Key {:?}", key); let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", &hash); - println!("Iv {:02x?}", iv.value()); - - - println!("key_share_bytes: {:02x?}, {}", key_share_bytes, key_share_bytes.len()); let aad = ring::aead::Aad::from(key_share_bytes.to_vec()); - let aad_bytes = aad.as_ref(); - println!("AAD: {:02x?}, {}", aad_bytes, aad_bytes.len()); match encrypt(key, iv, aad, &mut sni_bytes) { Some(bytes) => { - println!("cipher: {:02x?}, {}", bytes, bytes.len()); Some (ClientEncryptedSNI { suite: hs_data.cipher_suite.suite, key_share_entry: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), @@ -165,7 +145,6 @@ fn compute_esni_content(hs_data: &ESNIHandshakeData, pubkey: &Vec, random: [ let mut contents_bytes = Vec::new(); contents.encode(&mut contents_bytes); - println!("ESNIContents encoded, {:02x?}, {}", contents_bytes, contents_bytes.len()); contents_bytes } @@ -193,35 +172,7 @@ fn esni_hash(encoded_esni_contents: &Vec, algorithm: &'static ring::digest:: digest.as_ref().to_vec() } -fn hkdf_expand(secret: &Prk, key_type: L, label: &[u8], context: &[u8]) -> T - where - T: for <'a> From>, - L: KeyType, -{ - hkdf_expand_info(secret, key_type, label, context, |okm| okm.into()) -} - -fn hkdf_expand_info(secret: &Prk, key_type: L, label: &[u8], context: &[u8], f: F) - -> T - where - F: for<'b> FnOnce(hkdf::Okm<'b, L>) -> T, - L: KeyType -{ - const LABEL_PREFIX: &[u8] = b"tls13 "; - - let output_len = u16::to_be_bytes(key_type.len() as u16); - let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8); - let context_len = u8::to_be_bytes(context.len() as u8); - - let info = &[&output_len[..], &label_len[..], LABEL_PREFIX, label, &context_len[..], context]; - let okm = secret.expand(info, key_type).unwrap(); - - f(okm) -} - fn zx(algorithm: ring::hkdf::Algorithm, secret: &Vec) -> Prk { - let zeroes = [0u8; digest::MAX_OUTPUT_LEN]; - let zeroes = &zeroes[..algorithm.len()]; let salt = hkdf::Salt::new(algorithm, &[]); salt.extract(secret) } @@ -236,60 +187,61 @@ fn encrypt(unbound: UnboundKey, iv: Iv, aad: ring::aead::Aad>, sni_bytes } } -fn now() -> Option { - let start = SystemTime::now(); - match start.duration_since(UNIX_EPOCH) { - Err(_e) => None, - Ok(since_the_epoch) => Some(since_the_epoch.as_secs()) - } -} - -struct ESNILen { - bytes: Vec -} - -impl ESNILen { - fn new(suite: &SupportedCipherSuite) -> ESNILen { - ESNILen { - bytes: vec![0u8; suite.enc_key_len] - } - } -} - #[cfg(test)] mod tests { use crate::SupportedCipherSuite; - use super::hkdf_expand; + use crate::key_schedule::hkdf_expand; use crate::cipher::{Iv, IvLen}; - use crate::esni::ESNILen; use crate::msgs::handshake::ESNIRecord; use crate::msgs::codec::{Codec, Reader}; - use webpki; + use webpki::DNSNameRef; #[test] fn test_compute_esni_content() { - let esni_keys = hex!("ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de - ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d - aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 - 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da - 09 a0 00 00"); + let esni_keys = hex!(" + ff 01 a0 1f 3e 02 00 24 00 1d 00 20 f6 27 6f e9 + c5 63 14 c3 d7 0c 12 ce ab ea 55 97 28 9e f3 75 + ee 5a ae 04 41 af 5b ff d4 fa 78 5f 00 02 13 01 + 01 04 00 00 00 00 5d da 02 98 00 00 00 00 5d da + 17 b0 00 00 + "); let random = hex!(" - 1a 2b 12 8a 1e 08 7e 12 68 b7 53 90 97 05 21 36 - 6f 1c 27 ce 43 1b f2 1c fb 6e 95 7f af 1a 67 67 + 8a 96 02 a9 3c 80 de 46 01 22 c8 0a 65 48 fb c2 + b7 f6 be 87 07 8d 2d d8 e8 68 25 aa c3 44 1d 7f "); + let pubkey = hex!(" + b3 cf b7 0e eb e5 d4 40 c7 00 af 31 + ba 1e 32 a4 8a ce 7d 6d 24 ce ed 33 4b 82 b4 c9 + 4e 90 78 17 + "); - let hs_data = super::create_esni_handshake(&esni_keys).unwrap(); + let expected = hex!(" + 00 20 20 4f 08 28 be 7f 73 0e 92 bb 2b 1d 0e 3c + 35 05 86 1d 83 5a a8 7b ad fa 83 3b 17 9a f9 70 + 42 c9 00 1d 00 20 b3 cf b7 0e eb e5 d4 40 c7 00 + af 31 ba 1e 32 a4 8a ce 7d 6d 24 ce ed 33 4b 82 + b4 c9 4e 90 78 17 8a 96 02 a9 3c 80 de 46 01 22 + c8 0a 65 48 fb c2 b7 f6 be 87 07 8d 2d d8 e8 68 + 25 aa c3 44 1d 7f + "); + + let record = ESNIRecord::read(&mut Reader::init(&esni_keys)).unwrap(); + let hs_data = super::record_to_handshake_data(&record, &esni_keys.to_vec()).unwrap(); + let esni_contents = super::compute_esni_content(&hs_data, &pubkey.to_vec(), random); + assert!(crate::msgs::handshake::slice_eq(&expected, &esni_contents)); } #[test] fn test_record_digest() { - let esni_keys = hex!("ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de - ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d - aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 - 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da - 09 a0 00 00"); + let esni_keys = hex!(" + ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de + ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d + aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 + 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da + 09 a0 00 00 + "); let expected = hex!(" 3b d7 25 90 a7 58 68 16 46 c5 22 93 2a 1e b0 8d @@ -304,27 +256,28 @@ mod tests { fn test_compute_client_esni_inner() { let nonce = hex!("c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28"); - let dns_name = webpki::DNSNameRef::try_from_ascii(b"canbe.esni.defo.ie").unwrap(); + let dns_name = DNSNameRef::try_from_ascii(b"canbe.esni.defo.ie").unwrap(); let expected = hex!(" - c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28 - 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e - 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00"); + c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28 + 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e + 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 + "); let result = super::compute_client_esni_inner(dns_name, 260u16, nonce); assert_eq!(expected.len(), result.len()); @@ -339,13 +292,14 @@ mod tests { #[test] fn test_hash() { let esni_bytes = hex!(" - 00 20 3e 06 06 98 4c 3b a9 70 3a fb a7 a1 2d 75 - 29 5b 05 81 7d 75 8f 40 9b 51 00 c8 37 8e 9d 08 - 7e f1 00 1d 00 20 72 d8 3a 31 da 1c cd c7 e5 89 - c1 c6 24 bd 7a 14 2d 90 de 7f 01 82 73 9d 25 14 - c2 66 e1 97 23 5b 64 c0 c4 7c 5b c8 14 a0 a4 2b - 0c 2f f4 23 51 00 10 f4 1d f4 c1 f4 3c 3e 89 c8 - fe 87 25 d1 9f 00 "); + 00 20 3e 06 06 98 4c 3b a9 70 3a fb a7 a1 2d 75 + 29 5b 05 81 7d 75 8f 40 9b 51 00 c8 37 8e 9d 08 + 7e f1 00 1d 00 20 72 d8 3a 31 da 1c cd c7 e5 89 + c1 c6 24 bd 7a 14 2d 90 de 7f 01 82 73 9d 25 14 + c2 66 e1 97 23 5b 64 c0 c4 7c 5b c8 14 a0 a4 2b + 0c 2f f4 23 51 00 10 f4 1d f4 c1 f4 3c 3e 89 c8 + fe 87 25 d1 9f 00 + "); let expected = hex!(" 21 5b ba fe a8 9e da 35 7b 7b 55 e4 6d 01 ac c8 @@ -372,72 +326,70 @@ mod tests { "); let aad_bytes = hex!(" - 00 69 00 1d 00 20 70 cb - 7e ce 36 ab c1 b6 e1 92 6a 9a f2 08 d9 91 70 f1 - 98 7a aa 0f e3 9b f0 b3 c5 4d 79 00 a8 07 00 17 - 00 41 04 03 1d 6c 6c e6 f3 28 1f 6f f2 78 d5 5c - 0f 5e f7 be 52 71 9f 7e c0 0e 6e 26 db 85 7b f9 - e0 73 91 e6 b5 3e 06 7b ef c8 f8 b5 f0 46 16 c2 - 9f 0d 52 c3 6a 9e 41 2f 68 ce 7e ee d0 27 99 e5 - 28 aa 9e + 00 69 00 1d 00 20 70 cb + 7e ce 36 ab c1 b6 e1 92 6a 9a f2 08 d9 91 70 f1 + 98 7a aa 0f e3 9b f0 b3 c5 4d 79 00 a8 07 00 17 + 00 41 04 03 1d 6c 6c e6 f3 28 1f 6f f2 78 d5 5c + 0f 5e f7 be 52 71 9f 7e c0 0e 6e 26 db 85 7b f9 + e0 73 91 e6 b5 3e 06 7b ef c8 f8 b5 f0 46 16 c2 + 9f 0d 52 c3 6a 9e 41 2f 68 ce 7e ee d0 27 99 e5 + 28 aa 9e "); let plain_text = hex!(" - 4f b0 25 11 6b f7 4d f8 ce f3 0f 59 ce d9 d6 df - 00 17 00 00 14 63 64 6e 6a 73 2e 63 6c 6f 75 64 - 66 6c 61 72 65 2e 63 6f 6d 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 + 4f b0 25 11 6b f7 4d f8 ce f3 0f 59 ce d9 d6 df + 00 17 00 00 14 63 64 6e 6a 73 2e 63 6c 6f 75 64 + 66 6c 61 72 65 2e 63 6f 6d 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 "); let expected = hex!(" - 28 0a 3d 56 cd 30 9d 68 ac 98 1b 41 bb fb 85 26 - 48 ef 1a 83 c8 aa bd 12 15 80 44 10 50 2f c0 3d - 68 15 99 e0 47 6a 80 c2 e9 a0 df 86 16 7e a8 a4 - 37 8c 27 62 89 7e f8 60 4f 04 cf b5 ea 60 ed 99 - 51 59 70 a1 a5 ac b9 32 7d 35 86 e9 e2 01 d6 60 - 9d 8d de 81 03 69 13 dd 66 09 e9 18 76 f9 25 65 - 3d b7 ea 22 50 da 50 4d d8 74 31 5a 35 a2 29 7a - 09 31 0a 45 4e b2 29 fd 72 40 04 93 3a e3 a6 7d - 09 46 bb b5 8d e0 0f b5 12 e4 36 7d 38 32 3b b5 - ee 99 6f ad 2c ea af 39 9f a1 dc c9 70 dc 2f ad - 46 de 2a d6 8c 4e 3c e6 31 01 8a 97 f0 1f c9 3c - b8 c8 f1 45 02 c4 d7 3d ee b9 88 6f 53 cc 85 0b - 69 ce 61 dc 30 c8 85 2d e1 d0 d3 d6 10 c2 32 04 - 0d 96 2d d5 4a a4 1f e2 bc a3 77 15 72 61 20 75 - aa 9b 4a ee f7 25 cf 22 95 b9 77 88 48 f3 30 8e - a4 ab 3d b4 bd b4 e4 24 98 b7 ca 7e bf 26 ee 82 - b5 b4 fd f2 f0 65 04 ea 4c 7c 75 25 24 b0 be 92 - 9a a2 b7 e4 82 5a 37 cf 08 3f 0e 9b 6c 89 27 b4 - 33 15 75 24 + 28 0a 3d 56 cd 30 9d 68 ac 98 1b 41 bb fb 85 26 + 48 ef 1a 83 c8 aa bd 12 15 80 44 10 50 2f c0 3d + 68 15 99 e0 47 6a 80 c2 e9 a0 df 86 16 7e a8 a4 + 37 8c 27 62 89 7e f8 60 4f 04 cf b5 ea 60 ed 99 + 51 59 70 a1 a5 ac b9 32 7d 35 86 e9 e2 01 d6 60 + 9d 8d de 81 03 69 13 dd 66 09 e9 18 76 f9 25 65 + 3d b7 ea 22 50 da 50 4d d8 74 31 5a 35 a2 29 7a + 09 31 0a 45 4e b2 29 fd 72 40 04 93 3a e3 a6 7d + 09 46 bb b5 8d e0 0f b5 12 e4 36 7d 38 32 3b b5 + ee 99 6f ad 2c ea af 39 9f a1 dc c9 70 dc 2f ad + 46 de 2a d6 8c 4e 3c e6 31 01 8a 97 f0 1f c9 3c + b8 c8 f1 45 02 c4 d7 3d ee b9 88 6f 53 cc 85 0b + 69 ce 61 dc 30 c8 85 2d e1 d0 d3 d6 10 c2 32 04 + 0d 96 2d d5 4a a4 1f e2 bc a3 77 15 72 61 20 75 + aa 9b 4a ee f7 25 cf 22 95 b9 77 88 48 f3 30 8e + a4 ab 3d b4 bd b4 e4 24 98 b7 ca 7e bf 26 ee 82 + b5 b4 fd f2 f0 65 04 ea 4c 7c 75 25 24 b0 be 92 + 9a a2 b7 e4 82 5a 37 cf 08 3f 0e 9b 6c 89 27 b4 + 33 15 75 24 "); - let zx = super::zx(crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm, &z_bytes.to_vec()); - println!("{:?}", zx); + let hkdf_alg = crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm; + let zx = super::zx(hkdf_alg, &z_bytes.to_vec()); let aead_alg = crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(); - let key :ring::aead::UnboundKey = hkdf_expand(&zx, aead_alg, b"esni key", hash.as_ref()); + let key = hkdf_expand(&zx, aead_alg, b"esni key", hash.as_ref()); let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", hash.as_ref()); - println!("Iv {:02x?}", iv.value()); assert!(crate::msgs::handshake::slice_eq(&expected_iv, iv.value())); let aad = ring::aead::Aad::from(aad_bytes.to_vec()); let mut sni_bytes = Vec::from(plain_text.to_vec()); let encrypted = super::encrypt(key, iv, aad, &mut sni_bytes).unwrap(); - println!("{}, {:02x?}", encrypted.len(), encrypted); assert!(crate::msgs::handshake::slice_eq(&expected, &encrypted)); } @@ -449,82 +401,66 @@ mod tests { let iv_bytes = hex!("d0 c2 2c 42 3c 03 a7 1d 3d 36 36 51"); let aad_bytes = hex!(" - 00 69 00 1d 00 20 e7 41 - 94 4b 78 8d 6f cd 6b 5b 64 f6 69 35 83 d1 df c7 - e8 21 55 c6 f7 8d a5 c3 25 b9 7a 69 58 7d 00 17 - 00 41 04 d8 75 ac 7c 46 38 c6 eb 35 a9 90 60 6b - 1b be b1 70 dd 18 0c 80 82 8d 83 95 b1 aa a5 2e - 24 2e fb ed 9f 2a bd 7f 86 f0 8c 8b 6b ca db a6 - 28 69 88 1d fb 76 5f 34 d9 da 0b 07 02 64 80 d2 - d3 84 15 "); + 00 69 00 1d 00 20 e7 41 + 94 4b 78 8d 6f cd 6b 5b 64 f6 69 35 83 d1 df c7 + e8 21 55 c6 f7 8d a5 c3 25 b9 7a 69 58 7d 00 17 + 00 41 04 d8 75 ac 7c 46 38 c6 eb 35 a9 90 60 6b + 1b be b1 70 dd 18 0c 80 82 8d 83 95 b1 aa a5 2e + 24 2e fb ed 9f 2a bd 7f 86 f0 8c 8b 6b ca db a6 + 28 69 88 1d fb 76 5f 34 d9 da 0b 07 02 64 80 d2 + d3 84 15 + "); let mut plain_text = hex!(" - ad 1b f4 b3 d3 14 59 48 59 9e be c8 56 42 4f 66 - 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e - 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 "); + ad 1b f4 b3 d3 14 59 48 59 9e be c8 56 42 4f 66 + 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e + 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 + "); let expected = hex!(" - 6f f6 5d 1e bd 9c 35 2d 2c 1c ca 92 5d 3e 1a 65 - f6 30 fe 97 3b a0 24 9d 92 b8 cb 67 f0 1d 17 a4 - bc 11 9b ac 39 c4 48 f7 bb 86 04 b5 58 ad 76 15 - 10 c3 21 d0 3b 86 ac c9 d6 7e 9f 89 6e b0 73 cb - 69 97 f4 1b f5 17 e9 81 29 86 6f 3e df 49 99 3c - 59 00 24 6c 2d d6 3e 7b d2 b7 bd 3a c0 90 8f b6 - dc 2b 11 08 15 00 41 ca fb 79 ef 57 5b 17 18 00 - bf c2 0c 1b 2b cf 1e 9b f0 0f 9d 67 32 37 e1 06 - 22 f8 cb a8 a3 40 26 6e 50 85 32 29 d7 20 41 a5 - 0f 47 87 d0 af 01 ba 83 62 ad a0 b6 ac 8e d5 dd - 24 42 3d f8 a8 f9 9e 16 40 cf 85 b9 16 39 f8 94 - 4b bd cb a5 59 a8 a9 65 7a 83 95 b2 38 c7 3b d5 - d4 9b 6f f0 e3 18 d0 cb 65 65 c9 0c 8a 07 a1 ce - 5f 39 ed 6a 1b 6f e7 59 11 7d b3 81 e4 4b 51 d4 - db 28 f3 95 eb 16 62 de de 29 c7 dc 79 54 67 24 - d7 4d d1 3f 34 ca 64 6e 6c 12 9a e4 0c 1c ea 33 - c3 81 15 48 04 14 a4 ed ab 44 90 e9 0d c2 56 8a - df 4e 92 eb 3b 93 f5 5c 59 15 0e 7d 85 66 2d b4 - 62 ee 41 8a"); - - let key = ring::aead::UnboundKey::new(crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(), &key_bytes).unwrap(); + 6f f6 5d 1e bd 9c 35 2d 2c 1c ca 92 5d 3e 1a 65 + f6 30 fe 97 3b a0 24 9d 92 b8 cb 67 f0 1d 17 a4 + bc 11 9b ac 39 c4 48 f7 bb 86 04 b5 58 ad 76 15 + 10 c3 21 d0 3b 86 ac c9 d6 7e 9f 89 6e b0 73 cb + 69 97 f4 1b f5 17 e9 81 29 86 6f 3e df 49 99 3c + 59 00 24 6c 2d d6 3e 7b d2 b7 bd 3a c0 90 8f b6 + dc 2b 11 08 15 00 41 ca fb 79 ef 57 5b 17 18 00 + bf c2 0c 1b 2b cf 1e 9b f0 0f 9d 67 32 37 e1 06 + 22 f8 cb a8 a3 40 26 6e 50 85 32 29 d7 20 41 a5 + 0f 47 87 d0 af 01 ba 83 62 ad a0 b6 ac 8e d5 dd + 24 42 3d f8 a8 f9 9e 16 40 cf 85 b9 16 39 f8 94 + 4b bd cb a5 59 a8 a9 65 7a 83 95 b2 38 c7 3b d5 + d4 9b 6f f0 e3 18 d0 cb 65 65 c9 0c 8a 07 a1 ce + 5f 39 ed 6a 1b 6f e7 59 11 7d b3 81 e4 4b 51 d4 + db 28 f3 95 eb 16 62 de de 29 c7 dc 79 54 67 24 + d7 4d d1 3f 34 ca 64 6e 6c 12 9a e4 0c 1c ea 33 + c3 81 15 48 04 14 a4 ed ab 44 90 e9 0d c2 56 8a + df 4e 92 eb 3b 93 f5 5c 59 15 0e 7d 85 66 2d b4 + 62 ee 41 8a + "); + + let alg= crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(); + let key = ring::aead::UnboundKey::new(alg, &key_bytes).unwrap(); let iv = crate::cipher::Iv::new(iv_bytes); let aad = ring::aead::Aad::from(aad_bytes.to_vec()); let mut sni_bytes = Vec::from(plain_text.to_vec()); let encrypted = super::encrypt(key, iv, aad, &mut sni_bytes).unwrap(); - println!("{}, {:02x?}", encrypted.len(), encrypted); assert_eq!(expected.len(), encrypted.len()); assert!(crate::msgs::handshake::slice_eq(&expected, encrypted.as_slice())); } - - /// Generic newtype wrapper that lets us implement traits for externally-defined - /// types. - #[derive(Debug, PartialEq)] - struct My(T); - - impl ring::hkdf::KeyType for My { - fn len(&self) -> usize { - self.0 - } - } - - impl From>> for My> { - fn from(okm: ring::hkdf::Okm>) -> Self { - let mut r = vec![0u8; okm.len().0]; - okm.fill(&mut r).unwrap(); - My(r) - } - } } \ No newline at end of file From 72b96f887020dcd61ad8bc0f5dcc1d9b0b7686e0 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 13:17:52 -0800 Subject: [PATCH 14/20] Fix up --- rustls-mio/examples/esniclient.rs | 7 +------ rustls/src/client/hs.rs | 1 - rustls/src/esni.rs | 7 +++---- rustls/src/msgs/handshake.rs | 1 - rustls/src/suites.rs | 6 +++--- rustls/src/verify.rs | 6 ++---- 6 files changed, 9 insertions(+), 19 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index 7ba32c20835..bde115fb106 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -23,8 +23,6 @@ fn main() { let opts = ResolverOpts::default(); let addr = Address::new(domain); let esni_bytes = resolve_esni(dns_config, opts, &addr); - println!("esni_bytes: {:02x?}", esni_bytes); - let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); let mut config = rustls::esni::create_esni_config(); @@ -78,10 +76,7 @@ pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Addres } } - let decoded = decode(&bytes).unwrap(); - - println!("hmm? {:?}", decoded); - decoded + decode(&bytes).unwrap() } pub struct Address { diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 9bba325e8e9..bcd1448bf27 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -236,7 +236,6 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, encode_vec_u16(&mut ks_bytes, ks); let esni_ext = ClientExtension::make_esni(handshake.dns_name.as_ref(), esni, ks_bytes, &handshake.randoms); if let Some(ext) = esni_ext { - println!("Pushing ESNI..."); exts.push(ext); //exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 63828f506d1..4af104dcbdb 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -12,9 +12,9 @@ use ring::{digest, hkdf}; use webpki; use crate::SupportedCipherSuite; use crate::msgs::base::PayloadU16; -use ring::hkdf::{KeyType, Prk}; +use ring::hkdf::Prk; use crate::cipher::{Iv, IvLen}; -use ring::aead::{UnboundKey, Algorithm}; +use ring::aead::UnboundKey; use crate::session::SessionRandoms; use crate::key_schedule::hkdf_expand; @@ -96,7 +96,6 @@ fn record_digest(algorithm: &'static ring::digest::Algorithm, bytes: &[u8]) -> V } /// Compute the encrypted SNI -// TODO: this is big and messy, fix it up pub fn compute_esni(dns_name: webpki::DNSNameRef, hs_data: &ESNIHandshakeData, key_share_bytes: Vec, @@ -112,7 +111,7 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, None => return None, }; let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; - let mut contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); + let contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); let hash = esni_hash(&contents_bytes, hs_data.cipher_suite.get_hash()); let zx = zx(hs_data.cipher_suite.hkdf_algorithm, &exchange_result.premaster_secret); diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 500f7f01961..bc0c907988b 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -383,7 +383,6 @@ impl Codec for ESNIRecord { let digest = ctx.finish(); let checksum_valid = slice_eq(checksum.as_slice(), &digest.as_ref()[0..4]); - println!("rest: {:02x?}", rest); let tail_reader = &mut Reader::init(rest); Some(ESNIRecord { version, diff --git a/rustls/src/suites.rs b/rustls/src/suites.rs index 95303a8d214..1f5c24b0c3d 100644 --- a/rustls/src/suites.rs +++ b/rustls/src/suites.rs @@ -373,9 +373,9 @@ pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite hkdf_algorithm: ring::hkdf::HKDF_SHA256, }; -pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 1] = - [//&TLS13_CHACHA20_POLY1305_SHA256, - //&TLS13_AES_256_GCM_SHA384, +pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 3] = + [&TLS13_CHACHA20_POLY1305_SHA256, + &TLS13_AES_256_GCM_SHA384, &TLS13_AES_128_GCM_SHA256]; /// A list of all the cipher suites supported by rustls. diff --git a/rustls/src/verify.rs b/rustls/src/verify.rs index 49d65891b5b..89bc958bf16 100644 --- a/rustls/src/verify.rs +++ b/rustls/src/verify.rs @@ -111,11 +111,9 @@ impl ServerCertVerifier for WebPKIVerifier { debug!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec()); } - println!("here is the problem..."); - /*cert.verify_is_valid_for_dns_name(dns_name) + cert.verify_is_valid_for_dns_name(dns_name) .map_err(TLSError::WebPKIError) - .map(|_| ServerCertVerified::assertion())*/ - Ok(ServerCertVerified::assertion()) + .map(|_| ServerCertVerified::assertion()) } } From 92adf2b1d9c7925af9ac1fe2fc0856b41e7aa2af Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 13:06:53 -0800 Subject: [PATCH 15/20] Cleaned up esni.rs --- rustls/src/esni.rs | 420 +++++++++++++++++++-------------------------- 1 file changed, 178 insertions(+), 242 deletions(-) diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 639584d9232..63828f506d1 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -16,6 +16,7 @@ use ring::hkdf::{KeyType, Prk}; use crate::cipher::{Iv, IvLen}; use ring::aead::{UnboundKey, Algorithm}; use crate::session::SessionRandoms; +use crate::key_schedule::hkdf_expand; /// Data calculated for a client session from a DNS ESNI record. #[derive(Clone, Debug)] @@ -45,10 +46,7 @@ pub fn create_esni_config() -> ClientConfig { /// Creates a `ClientConfig` with defaults suitable for ESNI extension support. /// This creates a config that supports TLS 1.3 only. pub fn create_esni_handshake(record_bytes: &Vec) -> Option { - println!("ESNIKeys:{} {:02x?}", record_bytes.len(), record_bytes); let record = ESNIRecord::read(&mut Reader::init(&record_bytes))?; - - println!("record {:?}", record); // Check whether the record is still valid let now = now()?; @@ -56,10 +54,14 @@ pub fn create_esni_handshake(record_bytes: &Vec) -> Option) -> Option { let peer_share = match KeyExchange::supported_groups() .iter() .flat_map(|group| { - record.keys.iter().find(|key| { key.group == *group }) + record.keys.iter().find(|key| { key.group == *group }) }).nth(0) .cloned() { Some(entry) => entry, @@ -81,6 +83,14 @@ pub fn create_esni_handshake(record_bytes: &Vec) -> Option Option { + let start = SystemTime::now(); + match start.duration_since(UNIX_EPOCH) { + Err(_e) => None, + Ok(since_the_epoch) => Some(since_the_epoch.as_secs()) + } +} + fn record_digest(algorithm: &'static ring::digest::Algorithm, bytes: &[u8]) -> Vec { digest::digest(algorithm, bytes).as_ref().to_vec() } @@ -94,54 +104,24 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, let mut nonce = [0u8; 16]; rand::fill_random(&mut nonce); let mut sni_bytes = compute_client_esni_inner(dns_name, hs_data.padded_length, nonce); - println!("sni_bytes: {:02x?}, {}", sni_bytes, sni_bytes.len()); - - println!("Client key share: {:?}", hs_data.peer_share); let mut peer_bytes = Vec::new(); hs_data.peer_share.clone().encode(&mut peer_bytes); - println!("peer_bytes: {:02x?}, {}", peer_bytes, peer_bytes.len()); let key_exchange = match KeyExchange::start_ecdhe(hs_data.peer_share.group) { Some(ke) => ke, None => return None, }; - - println!("group: {:?}", key_exchange.group); - - - let keyex_Bytes = key_exchange.pubkey.as_ref(); - println!(" key_exchange: {:02x?}, {}", keyex_Bytes, keyex_Bytes.len()); - let payload = &hs_data.peer_share.payload; - println!("payload length: {:?}", payload); let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; - let mut result_bytes = exchange_result.pubkey.as_ref(); - println!(" Z result_bytes: {:02x?}, {}", result_bytes, result_bytes.len()); - - let premaster_bytes = exchange_result.premaster_secret.as_slice(); - println!("Z premaster_bytes: {:02x?}, {}", premaster_bytes, premaster_bytes.len()); - - let mut contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); let hash = esni_hash(&contents_bytes, hs_data.cipher_suite.get_hash()); - println!(" ESNIContents hash, {:02x?}, {}", hash, hash.len()); - let zx = zx(hs_data.cipher_suite.hkdf_algorithm, &exchange_result.premaster_secret); - let key = hkdf_expand(&zx, hs_data.cipher_suite.get_aead_alg(), b"esni key", &hash); - println!("Key {:?}", key); let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", &hash); - println!("Iv {:02x?}", iv.value()); - - - println!("key_share_bytes: {:02x?}, {}", key_share_bytes, key_share_bytes.len()); let aad = ring::aead::Aad::from(key_share_bytes.to_vec()); - let aad_bytes = aad.as_ref(); - println!("AAD: {:02x?}, {}", aad_bytes, aad_bytes.len()); match encrypt(key, iv, aad, &mut sni_bytes) { Some(bytes) => { - println!("cipher: {:02x?}, {}", bytes, bytes.len()); Some (ClientEncryptedSNI { suite: hs_data.cipher_suite.suite, key_share_entry: KeyShareEntry::new(hs_data.peer_share.group, exchange_result.pubkey.as_ref()), @@ -165,7 +145,6 @@ fn compute_esni_content(hs_data: &ESNIHandshakeData, pubkey: &Vec, random: [ let mut contents_bytes = Vec::new(); contents.encode(&mut contents_bytes); - println!("ESNIContents encoded, {:02x?}, {}", contents_bytes, contents_bytes.len()); contents_bytes } @@ -193,35 +172,7 @@ fn esni_hash(encoded_esni_contents: &Vec, algorithm: &'static ring::digest:: digest.as_ref().to_vec() } -fn hkdf_expand(secret: &Prk, key_type: L, label: &[u8], context: &[u8]) -> T - where - T: for <'a> From>, - L: KeyType, -{ - hkdf_expand_info(secret, key_type, label, context, |okm| okm.into()) -} - -fn hkdf_expand_info(secret: &Prk, key_type: L, label: &[u8], context: &[u8], f: F) - -> T - where - F: for<'b> FnOnce(hkdf::Okm<'b, L>) -> T, - L: KeyType -{ - const LABEL_PREFIX: &[u8] = b"tls13 "; - - let output_len = u16::to_be_bytes(key_type.len() as u16); - let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8); - let context_len = u8::to_be_bytes(context.len() as u8); - - let info = &[&output_len[..], &label_len[..], LABEL_PREFIX, label, &context_len[..], context]; - let okm = secret.expand(info, key_type).unwrap(); - - f(okm) -} - fn zx(algorithm: ring::hkdf::Algorithm, secret: &Vec) -> Prk { - let zeroes = [0u8; digest::MAX_OUTPUT_LEN]; - let zeroes = &zeroes[..algorithm.len()]; let salt = hkdf::Salt::new(algorithm, &[]); salt.extract(secret) } @@ -236,60 +187,61 @@ fn encrypt(unbound: UnboundKey, iv: Iv, aad: ring::aead::Aad>, sni_bytes } } -fn now() -> Option { - let start = SystemTime::now(); - match start.duration_since(UNIX_EPOCH) { - Err(_e) => None, - Ok(since_the_epoch) => Some(since_the_epoch.as_secs()) - } -} - -struct ESNILen { - bytes: Vec -} - -impl ESNILen { - fn new(suite: &SupportedCipherSuite) -> ESNILen { - ESNILen { - bytes: vec![0u8; suite.enc_key_len] - } - } -} - #[cfg(test)] mod tests { use crate::SupportedCipherSuite; - use super::hkdf_expand; + use crate::key_schedule::hkdf_expand; use crate::cipher::{Iv, IvLen}; - use crate::esni::ESNILen; use crate::msgs::handshake::ESNIRecord; use crate::msgs::codec::{Codec, Reader}; - use webpki; + use webpki::DNSNameRef; #[test] fn test_compute_esni_content() { - let esni_keys = hex!("ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de - ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d - aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 - 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da - 09 a0 00 00"); + let esni_keys = hex!(" + ff 01 a0 1f 3e 02 00 24 00 1d 00 20 f6 27 6f e9 + c5 63 14 c3 d7 0c 12 ce ab ea 55 97 28 9e f3 75 + ee 5a ae 04 41 af 5b ff d4 fa 78 5f 00 02 13 01 + 01 04 00 00 00 00 5d da 02 98 00 00 00 00 5d da + 17 b0 00 00 + "); let random = hex!(" - 1a 2b 12 8a 1e 08 7e 12 68 b7 53 90 97 05 21 36 - 6f 1c 27 ce 43 1b f2 1c fb 6e 95 7f af 1a 67 67 + 8a 96 02 a9 3c 80 de 46 01 22 c8 0a 65 48 fb c2 + b7 f6 be 87 07 8d 2d d8 e8 68 25 aa c3 44 1d 7f "); + let pubkey = hex!(" + b3 cf b7 0e eb e5 d4 40 c7 00 af 31 + ba 1e 32 a4 8a ce 7d 6d 24 ce ed 33 4b 82 b4 c9 + 4e 90 78 17 + "); - let hs_data = super::create_esni_handshake(&esni_keys).unwrap(); + let expected = hex!(" + 00 20 20 4f 08 28 be 7f 73 0e 92 bb 2b 1d 0e 3c + 35 05 86 1d 83 5a a8 7b ad fa 83 3b 17 9a f9 70 + 42 c9 00 1d 00 20 b3 cf b7 0e eb e5 d4 40 c7 00 + af 31 ba 1e 32 a4 8a ce 7d 6d 24 ce ed 33 4b 82 + b4 c9 4e 90 78 17 8a 96 02 a9 3c 80 de 46 01 22 + c8 0a 65 48 fb c2 b7 f6 be 87 07 8d 2d d8 e8 68 + 25 aa c3 44 1d 7f + "); + + let record = ESNIRecord::read(&mut Reader::init(&esni_keys)).unwrap(); + let hs_data = super::record_to_handshake_data(&record, &esni_keys.to_vec()).unwrap(); + let esni_contents = super::compute_esni_content(&hs_data, &pubkey.to_vec(), random); + assert!(crate::msgs::handshake::slice_eq(&expected, &esni_contents)); } #[test] fn test_record_digest() { - let esni_keys = hex!("ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de - ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d - aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 - 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da - 09 a0 00 00"); + let esni_keys = hex!(" + ff 01 f3 92 e6 e7 00 24 00 1d 00 20 10 9f e6 de + ac e8 f6 2f 94 61 9c 1d 61 c9 a2 b9 2f 45 92 3d + aa 93 87 e4 e5 51 39 e7 da 26 2b 65 00 02 13 01 + 01 04 00 00 00 00 5d d9 f4 88 00 00 00 00 5d da + 09 a0 00 00 + "); let expected = hex!(" 3b d7 25 90 a7 58 68 16 46 c5 22 93 2a 1e b0 8d @@ -304,27 +256,28 @@ mod tests { fn test_compute_client_esni_inner() { let nonce = hex!("c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28"); - let dns_name = webpki::DNSNameRef::try_from_ascii(b"canbe.esni.defo.ie").unwrap(); + let dns_name = DNSNameRef::try_from_ascii(b"canbe.esni.defo.ie").unwrap(); let expected = hex!(" - c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28 - 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e - 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00"); + c0 2b f3 39 f8 95 58 ac c4 7c d1 c6 b1 ff a7 28 + 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e + 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 + "); let result = super::compute_client_esni_inner(dns_name, 260u16, nonce); assert_eq!(expected.len(), result.len()); @@ -339,13 +292,14 @@ mod tests { #[test] fn test_hash() { let esni_bytes = hex!(" - 00 20 3e 06 06 98 4c 3b a9 70 3a fb a7 a1 2d 75 - 29 5b 05 81 7d 75 8f 40 9b 51 00 c8 37 8e 9d 08 - 7e f1 00 1d 00 20 72 d8 3a 31 da 1c cd c7 e5 89 - c1 c6 24 bd 7a 14 2d 90 de 7f 01 82 73 9d 25 14 - c2 66 e1 97 23 5b 64 c0 c4 7c 5b c8 14 a0 a4 2b - 0c 2f f4 23 51 00 10 f4 1d f4 c1 f4 3c 3e 89 c8 - fe 87 25 d1 9f 00 "); + 00 20 3e 06 06 98 4c 3b a9 70 3a fb a7 a1 2d 75 + 29 5b 05 81 7d 75 8f 40 9b 51 00 c8 37 8e 9d 08 + 7e f1 00 1d 00 20 72 d8 3a 31 da 1c cd c7 e5 89 + c1 c6 24 bd 7a 14 2d 90 de 7f 01 82 73 9d 25 14 + c2 66 e1 97 23 5b 64 c0 c4 7c 5b c8 14 a0 a4 2b + 0c 2f f4 23 51 00 10 f4 1d f4 c1 f4 3c 3e 89 c8 + fe 87 25 d1 9f 00 + "); let expected = hex!(" 21 5b ba fe a8 9e da 35 7b 7b 55 e4 6d 01 ac c8 @@ -372,72 +326,70 @@ mod tests { "); let aad_bytes = hex!(" - 00 69 00 1d 00 20 70 cb - 7e ce 36 ab c1 b6 e1 92 6a 9a f2 08 d9 91 70 f1 - 98 7a aa 0f e3 9b f0 b3 c5 4d 79 00 a8 07 00 17 - 00 41 04 03 1d 6c 6c e6 f3 28 1f 6f f2 78 d5 5c - 0f 5e f7 be 52 71 9f 7e c0 0e 6e 26 db 85 7b f9 - e0 73 91 e6 b5 3e 06 7b ef c8 f8 b5 f0 46 16 c2 - 9f 0d 52 c3 6a 9e 41 2f 68 ce 7e ee d0 27 99 e5 - 28 aa 9e + 00 69 00 1d 00 20 70 cb + 7e ce 36 ab c1 b6 e1 92 6a 9a f2 08 d9 91 70 f1 + 98 7a aa 0f e3 9b f0 b3 c5 4d 79 00 a8 07 00 17 + 00 41 04 03 1d 6c 6c e6 f3 28 1f 6f f2 78 d5 5c + 0f 5e f7 be 52 71 9f 7e c0 0e 6e 26 db 85 7b f9 + e0 73 91 e6 b5 3e 06 7b ef c8 f8 b5 f0 46 16 c2 + 9f 0d 52 c3 6a 9e 41 2f 68 ce 7e ee d0 27 99 e5 + 28 aa 9e "); let plain_text = hex!(" - 4f b0 25 11 6b f7 4d f8 ce f3 0f 59 ce d9 d6 df - 00 17 00 00 14 63 64 6e 6a 73 2e 63 6c 6f 75 64 - 66 6c 61 72 65 2e 63 6f 6d 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 + 4f b0 25 11 6b f7 4d f8 ce f3 0f 59 ce d9 d6 df + 00 17 00 00 14 63 64 6e 6a 73 2e 63 6c 6f 75 64 + 66 6c 61 72 65 2e 63 6f 6d 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 "); let expected = hex!(" - 28 0a 3d 56 cd 30 9d 68 ac 98 1b 41 bb fb 85 26 - 48 ef 1a 83 c8 aa bd 12 15 80 44 10 50 2f c0 3d - 68 15 99 e0 47 6a 80 c2 e9 a0 df 86 16 7e a8 a4 - 37 8c 27 62 89 7e f8 60 4f 04 cf b5 ea 60 ed 99 - 51 59 70 a1 a5 ac b9 32 7d 35 86 e9 e2 01 d6 60 - 9d 8d de 81 03 69 13 dd 66 09 e9 18 76 f9 25 65 - 3d b7 ea 22 50 da 50 4d d8 74 31 5a 35 a2 29 7a - 09 31 0a 45 4e b2 29 fd 72 40 04 93 3a e3 a6 7d - 09 46 bb b5 8d e0 0f b5 12 e4 36 7d 38 32 3b b5 - ee 99 6f ad 2c ea af 39 9f a1 dc c9 70 dc 2f ad - 46 de 2a d6 8c 4e 3c e6 31 01 8a 97 f0 1f c9 3c - b8 c8 f1 45 02 c4 d7 3d ee b9 88 6f 53 cc 85 0b - 69 ce 61 dc 30 c8 85 2d e1 d0 d3 d6 10 c2 32 04 - 0d 96 2d d5 4a a4 1f e2 bc a3 77 15 72 61 20 75 - aa 9b 4a ee f7 25 cf 22 95 b9 77 88 48 f3 30 8e - a4 ab 3d b4 bd b4 e4 24 98 b7 ca 7e bf 26 ee 82 - b5 b4 fd f2 f0 65 04 ea 4c 7c 75 25 24 b0 be 92 - 9a a2 b7 e4 82 5a 37 cf 08 3f 0e 9b 6c 89 27 b4 - 33 15 75 24 + 28 0a 3d 56 cd 30 9d 68 ac 98 1b 41 bb fb 85 26 + 48 ef 1a 83 c8 aa bd 12 15 80 44 10 50 2f c0 3d + 68 15 99 e0 47 6a 80 c2 e9 a0 df 86 16 7e a8 a4 + 37 8c 27 62 89 7e f8 60 4f 04 cf b5 ea 60 ed 99 + 51 59 70 a1 a5 ac b9 32 7d 35 86 e9 e2 01 d6 60 + 9d 8d de 81 03 69 13 dd 66 09 e9 18 76 f9 25 65 + 3d b7 ea 22 50 da 50 4d d8 74 31 5a 35 a2 29 7a + 09 31 0a 45 4e b2 29 fd 72 40 04 93 3a e3 a6 7d + 09 46 bb b5 8d e0 0f b5 12 e4 36 7d 38 32 3b b5 + ee 99 6f ad 2c ea af 39 9f a1 dc c9 70 dc 2f ad + 46 de 2a d6 8c 4e 3c e6 31 01 8a 97 f0 1f c9 3c + b8 c8 f1 45 02 c4 d7 3d ee b9 88 6f 53 cc 85 0b + 69 ce 61 dc 30 c8 85 2d e1 d0 d3 d6 10 c2 32 04 + 0d 96 2d d5 4a a4 1f e2 bc a3 77 15 72 61 20 75 + aa 9b 4a ee f7 25 cf 22 95 b9 77 88 48 f3 30 8e + a4 ab 3d b4 bd b4 e4 24 98 b7 ca 7e bf 26 ee 82 + b5 b4 fd f2 f0 65 04 ea 4c 7c 75 25 24 b0 be 92 + 9a a2 b7 e4 82 5a 37 cf 08 3f 0e 9b 6c 89 27 b4 + 33 15 75 24 "); - let zx = super::zx(crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm, &z_bytes.to_vec()); - println!("{:?}", zx); + let hkdf_alg = crate::suites::TLS13_AES_128_GCM_SHA256.hkdf_algorithm; + let zx = super::zx(hkdf_alg, &z_bytes.to_vec()); let aead_alg = crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(); - let key :ring::aead::UnboundKey = hkdf_expand(&zx, aead_alg, b"esni key", hash.as_ref()); + let key = hkdf_expand(&zx, aead_alg, b"esni key", hash.as_ref()); let iv: Iv = hkdf_expand(&zx, IvLen, b"esni iv", hash.as_ref()); - println!("Iv {:02x?}", iv.value()); assert!(crate::msgs::handshake::slice_eq(&expected_iv, iv.value())); let aad = ring::aead::Aad::from(aad_bytes.to_vec()); let mut sni_bytes = Vec::from(plain_text.to_vec()); let encrypted = super::encrypt(key, iv, aad, &mut sni_bytes).unwrap(); - println!("{}, {:02x?}", encrypted.len(), encrypted); assert!(crate::msgs::handshake::slice_eq(&expected, &encrypted)); } @@ -449,82 +401,66 @@ mod tests { let iv_bytes = hex!("d0 c2 2c 42 3c 03 a7 1d 3d 36 36 51"); let aad_bytes = hex!(" - 00 69 00 1d 00 20 e7 41 - 94 4b 78 8d 6f cd 6b 5b 64 f6 69 35 83 d1 df c7 - e8 21 55 c6 f7 8d a5 c3 25 b9 7a 69 58 7d 00 17 - 00 41 04 d8 75 ac 7c 46 38 c6 eb 35 a9 90 60 6b - 1b be b1 70 dd 18 0c 80 82 8d 83 95 b1 aa a5 2e - 24 2e fb ed 9f 2a bd 7f 86 f0 8c 8b 6b ca db a6 - 28 69 88 1d fb 76 5f 34 d9 da 0b 07 02 64 80 d2 - d3 84 15 "); + 00 69 00 1d 00 20 e7 41 + 94 4b 78 8d 6f cd 6b 5b 64 f6 69 35 83 d1 df c7 + e8 21 55 c6 f7 8d a5 c3 25 b9 7a 69 58 7d 00 17 + 00 41 04 d8 75 ac 7c 46 38 c6 eb 35 a9 90 60 6b + 1b be b1 70 dd 18 0c 80 82 8d 83 95 b1 aa a5 2e + 24 2e fb ed 9f 2a bd 7f 86 f0 8c 8b 6b ca db a6 + 28 69 88 1d fb 76 5f 34 d9 da 0b 07 02 64 80 d2 + d3 84 15 + "); let mut plain_text = hex!(" - ad 1b f4 b3 d3 14 59 48 59 9e be c8 56 42 4f 66 - 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e - 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 "); + ad 1b f4 b3 d3 14 59 48 59 9e be c8 56 42 4f 66 + 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e + 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 + "); let expected = hex!(" - 6f f6 5d 1e bd 9c 35 2d 2c 1c ca 92 5d 3e 1a 65 - f6 30 fe 97 3b a0 24 9d 92 b8 cb 67 f0 1d 17 a4 - bc 11 9b ac 39 c4 48 f7 bb 86 04 b5 58 ad 76 15 - 10 c3 21 d0 3b 86 ac c9 d6 7e 9f 89 6e b0 73 cb - 69 97 f4 1b f5 17 e9 81 29 86 6f 3e df 49 99 3c - 59 00 24 6c 2d d6 3e 7b d2 b7 bd 3a c0 90 8f b6 - dc 2b 11 08 15 00 41 ca fb 79 ef 57 5b 17 18 00 - bf c2 0c 1b 2b cf 1e 9b f0 0f 9d 67 32 37 e1 06 - 22 f8 cb a8 a3 40 26 6e 50 85 32 29 d7 20 41 a5 - 0f 47 87 d0 af 01 ba 83 62 ad a0 b6 ac 8e d5 dd - 24 42 3d f8 a8 f9 9e 16 40 cf 85 b9 16 39 f8 94 - 4b bd cb a5 59 a8 a9 65 7a 83 95 b2 38 c7 3b d5 - d4 9b 6f f0 e3 18 d0 cb 65 65 c9 0c 8a 07 a1 ce - 5f 39 ed 6a 1b 6f e7 59 11 7d b3 81 e4 4b 51 d4 - db 28 f3 95 eb 16 62 de de 29 c7 dc 79 54 67 24 - d7 4d d1 3f 34 ca 64 6e 6c 12 9a e4 0c 1c ea 33 - c3 81 15 48 04 14 a4 ed ab 44 90 e9 0d c2 56 8a - df 4e 92 eb 3b 93 f5 5c 59 15 0e 7d 85 66 2d b4 - 62 ee 41 8a"); - - let key = ring::aead::UnboundKey::new(crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(), &key_bytes).unwrap(); + 6f f6 5d 1e bd 9c 35 2d 2c 1c ca 92 5d 3e 1a 65 + f6 30 fe 97 3b a0 24 9d 92 b8 cb 67 f0 1d 17 a4 + bc 11 9b ac 39 c4 48 f7 bb 86 04 b5 58 ad 76 15 + 10 c3 21 d0 3b 86 ac c9 d6 7e 9f 89 6e b0 73 cb + 69 97 f4 1b f5 17 e9 81 29 86 6f 3e df 49 99 3c + 59 00 24 6c 2d d6 3e 7b d2 b7 bd 3a c0 90 8f b6 + dc 2b 11 08 15 00 41 ca fb 79 ef 57 5b 17 18 00 + bf c2 0c 1b 2b cf 1e 9b f0 0f 9d 67 32 37 e1 06 + 22 f8 cb a8 a3 40 26 6e 50 85 32 29 d7 20 41 a5 + 0f 47 87 d0 af 01 ba 83 62 ad a0 b6 ac 8e d5 dd + 24 42 3d f8 a8 f9 9e 16 40 cf 85 b9 16 39 f8 94 + 4b bd cb a5 59 a8 a9 65 7a 83 95 b2 38 c7 3b d5 + d4 9b 6f f0 e3 18 d0 cb 65 65 c9 0c 8a 07 a1 ce + 5f 39 ed 6a 1b 6f e7 59 11 7d b3 81 e4 4b 51 d4 + db 28 f3 95 eb 16 62 de de 29 c7 dc 79 54 67 24 + d7 4d d1 3f 34 ca 64 6e 6c 12 9a e4 0c 1c ea 33 + c3 81 15 48 04 14 a4 ed ab 44 90 e9 0d c2 56 8a + df 4e 92 eb 3b 93 f5 5c 59 15 0e 7d 85 66 2d b4 + 62 ee 41 8a + "); + + let alg= crate::suites::TLS13_AES_128_GCM_SHA256.get_aead_alg(); + let key = ring::aead::UnboundKey::new(alg, &key_bytes).unwrap(); let iv = crate::cipher::Iv::new(iv_bytes); let aad = ring::aead::Aad::from(aad_bytes.to_vec()); let mut sni_bytes = Vec::from(plain_text.to_vec()); let encrypted = super::encrypt(key, iv, aad, &mut sni_bytes).unwrap(); - println!("{}, {:02x?}", encrypted.len(), encrypted); assert_eq!(expected.len(), encrypted.len()); assert!(crate::msgs::handshake::slice_eq(&expected, encrypted.as_slice())); } - - /// Generic newtype wrapper that lets us implement traits for externally-defined - /// types. - #[derive(Debug, PartialEq)] - struct My(T); - - impl ring::hkdf::KeyType for My { - fn len(&self) -> usize { - self.0 - } - } - - impl From>> for My> { - fn from(okm: ring::hkdf::Okm>) -> Self { - let mut r = vec![0u8; okm.len().0]; - okm.fill(&mut r).unwrap(); - My(r) - } - } } \ No newline at end of file From 65a7910faa252610cb96861a2b13c84ae45813c5 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 13:17:52 -0800 Subject: [PATCH 16/20] Fix up --- rustls-mio/examples/esniclient.rs | 7 +------ rustls/src/client/hs.rs | 1 - rustls/src/esni.rs | 7 +++---- rustls/src/msgs/handshake.rs | 1 - rustls/src/suites.rs | 6 +++--- rustls/src/verify.rs | 6 ++---- 6 files changed, 9 insertions(+), 19 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index 7ba32c20835..bde115fb106 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -23,8 +23,6 @@ fn main() { let opts = ResolverOpts::default(); let addr = Address::new(domain); let esni_bytes = resolve_esni(dns_config, opts, &addr); - println!("esni_bytes: {:02x?}", esni_bytes); - let esni_hs = rustls::esni::create_esni_handshake(&esni_bytes).unwrap(); let mut config = rustls::esni::create_esni_config(); @@ -78,10 +76,7 @@ pub fn resolve_esni(config: ResolverConfig, opts: ResolverOpts, address: &Addres } } - let decoded = decode(&bytes).unwrap(); - - println!("hmm? {:?}", decoded); - decoded + decode(&bytes).unwrap() } pub struct Address { diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 9bba325e8e9..bcd1448bf27 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -236,7 +236,6 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, encode_vec_u16(&mut ks_bytes, ks); let esni_ext = ClientExtension::make_esni(handshake.dns_name.as_ref(), esni, ks_bytes, &handshake.randoms); if let Some(ext) = esni_ext { - println!("Pushing ESNI..."); exts.push(ext); //exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 63828f506d1..4af104dcbdb 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -12,9 +12,9 @@ use ring::{digest, hkdf}; use webpki; use crate::SupportedCipherSuite; use crate::msgs::base::PayloadU16; -use ring::hkdf::{KeyType, Prk}; +use ring::hkdf::Prk; use crate::cipher::{Iv, IvLen}; -use ring::aead::{UnboundKey, Algorithm}; +use ring::aead::UnboundKey; use crate::session::SessionRandoms; use crate::key_schedule::hkdf_expand; @@ -96,7 +96,6 @@ fn record_digest(algorithm: &'static ring::digest::Algorithm, bytes: &[u8]) -> V } /// Compute the encrypted SNI -// TODO: this is big and messy, fix it up pub fn compute_esni(dns_name: webpki::DNSNameRef, hs_data: &ESNIHandshakeData, key_share_bytes: Vec, @@ -112,7 +111,7 @@ pub fn compute_esni(dns_name: webpki::DNSNameRef, None => return None, }; let exchange_result = key_exchange.complete(&hs_data.peer_share.payload.0)?; - let mut contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); + let contents_bytes = compute_esni_content(&hs_data, &exchange_result.pubkey.clone().as_ref().to_vec(), randoms.client); let hash = esni_hash(&contents_bytes, hs_data.cipher_suite.get_hash()); let zx = zx(hs_data.cipher_suite.hkdf_algorithm, &exchange_result.premaster_secret); diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 500f7f01961..bc0c907988b 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -383,7 +383,6 @@ impl Codec for ESNIRecord { let digest = ctx.finish(); let checksum_valid = slice_eq(checksum.as_slice(), &digest.as_ref()[0..4]); - println!("rest: {:02x?}", rest); let tail_reader = &mut Reader::init(rest); Some(ESNIRecord { version, diff --git a/rustls/src/suites.rs b/rustls/src/suites.rs index 95303a8d214..1f5c24b0c3d 100644 --- a/rustls/src/suites.rs +++ b/rustls/src/suites.rs @@ -373,9 +373,9 @@ pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite hkdf_algorithm: ring::hkdf::HKDF_SHA256, }; -pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 1] = - [//&TLS13_CHACHA20_POLY1305_SHA256, - //&TLS13_AES_256_GCM_SHA384, +pub static TLS13_CIPHERSUITES: [&'static SupportedCipherSuite; 3] = + [&TLS13_CHACHA20_POLY1305_SHA256, + &TLS13_AES_256_GCM_SHA384, &TLS13_AES_128_GCM_SHA256]; /// A list of all the cipher suites supported by rustls. diff --git a/rustls/src/verify.rs b/rustls/src/verify.rs index 49d65891b5b..89bc958bf16 100644 --- a/rustls/src/verify.rs +++ b/rustls/src/verify.rs @@ -111,11 +111,9 @@ impl ServerCertVerifier for WebPKIVerifier { debug!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec()); } - println!("here is the problem..."); - /*cert.verify_is_valid_for_dns_name(dns_name) + cert.verify_is_valid_for_dns_name(dns_name) .map_err(TLSError::WebPKIError) - .map(|_| ServerCertVerified::assertion())*/ - Ok(ServerCertVerified::assertion()) + .map(|_| ServerCertVerified::assertion()) } } From 416e5907859c43e3ef67b2a82b6aca96c078e8d0 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 13:42:19 -0800 Subject: [PATCH 17/20] Patch quic.rs, fix warnings. --- rustls/src/esni.rs | 3 +-- rustls/src/lib.rs | 6 +++--- rustls/src/quic.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 4af104dcbdb..127ec2719ac 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -188,7 +188,6 @@ fn encrypt(unbound: UnboundKey, iv: Iv, aad: ring::aead::Aad>, sni_bytes #[cfg(test)] mod tests { - use crate::SupportedCipherSuite; use crate::key_schedule::hkdf_expand; use crate::cipher::{Iv, IvLen}; use crate::msgs::handshake::ESNIRecord; @@ -410,7 +409,7 @@ mod tests { d3 84 15 "); - let mut plain_text = hex!(" + let plain_text = hex!(" ad 1b f4 b3 d3 14 59 48 59 9e be c8 56 42 4f 66 00 15 00 00 12 63 61 6e 62 65 2e 65 73 6e 69 2e 64 65 66 6f 2e 69 65 00 00 00 00 00 00 00 00 00 diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index d16096e7ddb..3a094ba6a0b 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -211,9 +211,6 @@ mod log { macro_rules! error ( ($($tt:tt)*) => {{}} ); } -#[macro_use] -extern crate hex_literal; - #[allow(missing_docs)] #[macro_use] mod msgs; @@ -305,3 +302,6 @@ pub use crate::verify::{ServerCertVerifier, ServerCertVerified, #[cfg(feature = "dangerous_configuration")] pub use crate::client::danger::DangerousClientConfig; +#[cfg(test)] +#[macro_use] +extern crate hex_literal; \ No newline at end of file diff --git a/rustls/src/quic.rs b/rustls/src/quic.rs index c93d94b4e9a..badc4651936 100644 --- a/rustls/src/quic.rs +++ b/rustls/src/quic.rs @@ -152,7 +152,7 @@ pub trait ClientQuicExt { assert!(config.versions.iter().all(|x| x.get_u16() >= ProtocolVersion::TLSv1_3.get_u16()), "QUIC requires TLS version >= 1.3"); let mut imp = ClientSessionImpl::new(config); imp.common.protocol = Protocol::Quic; - imp.start_handshake(hostname.into(), vec![ + imp.start_handshake(hostname.into(), None,vec![ ClientExtension::TransportParameters(params), ]); ClientSession { imp } From ed1af79bbf894bbd782f53211a825373b0ba8d02 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 16:34:39 -0800 Subject: [PATCH 18/20] Formatting cleanup --- Cargo.toml | 3 +-- rustls-mio/examples/esniclient.rs | 25 +++++++++++++------------ rustls/src/client/hs.rs | 2 -- rustls/src/esni.rs | 5 ----- rustls/src/lib.rs | 2 +- rustls/src/msgs/handshake_test.rs | 2 +- 6 files changed, 16 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f31c3c5739d..26f7901de79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,7 @@ [workspace] - members = [ # the main library and tests "rustls", # tests and example code that depend on mio "rustls-mio", -] \ No newline at end of file +] diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index bde115fb106..1a5b6e18302 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -15,11 +15,10 @@ use trust_dns_resolver::config::*; use trust_dns_resolver::Resolver; fn main() { - let domain = "canbe.esni.defo.ie"; + let domain = "opaque.website"; println!("\nContacting {:?} over ESNI\n", domain); - //let dns_config = ResolverConfig::cloudflare_https(); - let dns_config= ResolverConfig::default(); + let dns_config = ResolverConfig::cloudflare_https(); let opts = ResolverOpts::default(); let addr = Address::new(domain); let esni_bytes = resolve_esni(dns_config, opts, &addr); @@ -29,16 +28,18 @@ fn main() { config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - let dns_name = webpki::DNSNameRef::try_from_ascii_str("canbe.esni.defo.ie").unwrap(); + let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap(); let mut sess = rustls::ClientSession::new_with_esni(&Arc::new(config), dns_name, esni_hs); - let mut sock = TcpStream::connect(domain.to_owned() + ":8443").unwrap(); + let mut sock = TcpStream::connect(domain.to_owned() + ":443").unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); - match tls.write(concat!("GET /stats HTTP/1.1\r\n", - "Host: canbe.esni.defo.ie\r\n", - "Connection: close\r\n", - "Accept-Encoding: identity\r\n", - "\r\n") - .as_bytes()) { + let host_header = format!("Host: {}\r\n", domain); + let mut headers = String::new(); + headers.push_str("GET / HTTP/1.1\r\n"); + headers.push_str(host_header.as_str()); + headers.push_str("Connection: close\r\n"); + headers.push_str("Accept-Encoding: identity\r\n"); + headers.push_str("\r\n"); + match tls.write(headers.as_bytes()) { Ok(size) => { println!("Received: {} bytes", size); } , @@ -97,4 +98,4 @@ impl Address { pub fn dns_address(&self) -> String { format!("{}.", self.domain) } -} \ No newline at end of file +} diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index bcd1448bf27..747ecf46c24 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -250,8 +250,6 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } - - exts.push(ClientExtension::ECPointFormats(ECPointFormatList::supported())); exts.push(ClientExtension::NamedGroups(suites::KeyExchange::supported_groups().to_vec())); exts.push(ClientExtension::SignatureAlgorithms(verify::supported_verify_schemes().to_vec())); diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 127ec2719ac..06f4cc9266e 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -279,11 +279,6 @@ mod tests { let result = super::compute_client_esni_inner(dns_name, 260u16, nonce); assert_eq!(expected.len(), result.len()); - - println!("expected: {:02x?}, {}", expected.to_vec(), expected.len()); - println!(" result: {:02x?}, {}", result, result.len()); - - assert!(crate::msgs::handshake::slice_eq(&expected, &result)); } diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index 3a094ba6a0b..457beddab51 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -304,4 +304,4 @@ pub use crate::client::danger::DangerousClientConfig; #[cfg(test)] #[macro_use] -extern crate hex_literal; \ No newline at end of file +extern crate hex_literal; diff --git a/rustls/src/msgs/handshake_test.rs b/rustls/src/msgs/handshake_test.rs index 3dbd256c5dd..163911f1dc0 100644 --- a/rustls/src/msgs/handshake_test.rs +++ b/rustls/src/msgs/handshake_test.rs @@ -938,4 +938,4 @@ fn test_esni() { let mut output = Vec::new(); record.encode(&mut output); assert_eq!(base64_esni, base64::encode(&output)); -} \ No newline at end of file +} From 65b07741f275a28dacaefb3da11aa5088f01eee2 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 16:36:32 -0800 Subject: [PATCH 19/20] Remove some useless changes --- rustls/Cargo.toml | 2 +- rustls/src/quic.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rustls/Cargo.toml b/rustls/Cargo.toml index 338cc90a316..2f18382d371 100644 --- a/rustls/Cargo.toml +++ b/rustls/Cargo.toml @@ -14,7 +14,7 @@ autobenches = false [dependencies] base64 = "0.10" log = { version = "0.4.4", optional = true } -ring = "0.16.9" +ring = "0.16.5" sct = "0.6.0" webpki = "0.21.0" hex-literal = "0.2.1" diff --git a/rustls/src/quic.rs b/rustls/src/quic.rs index badc4651936..901454fcfcd 100644 --- a/rustls/src/quic.rs +++ b/rustls/src/quic.rs @@ -152,7 +152,7 @@ pub trait ClientQuicExt { assert!(config.versions.iter().all(|x| x.get_u16() >= ProtocolVersion::TLSv1_3.get_u16()), "QUIC requires TLS version >= 1.3"); let mut imp = ClientSessionImpl::new(config); imp.common.protocol = Protocol::Quic; - imp.start_handshake(hostname.into(), None,vec![ + imp.start_handshake(hostname.into(), None, vec![ ClientExtension::TransportParameters(params), ]); ClientSession { imp } From 0920f9054ae9208adc1d2f23e923b64f53df47e3 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Sun, 24 Nov 2019 22:18:20 -0800 Subject: [PATCH 20/20] Formatting cleanup --- rustls-mio/examples/esniclient.rs | 7 +++++-- rustls/src/client/hs.rs | 5 ++--- rustls/src/client/mod.rs | 2 +- rustls/src/esni.rs | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rustls-mio/examples/esniclient.rs b/rustls-mio/examples/esniclient.rs index 1a5b6e18302..86b1168e60e 100644 --- a/rustls-mio/examples/esniclient.rs +++ b/rustls-mio/examples/esniclient.rs @@ -15,7 +15,7 @@ use trust_dns_resolver::config::*; use trust_dns_resolver::Resolver; fn main() { - let domain = "opaque.website"; + let domain = "canbe.esni.defo.ie"; println!("\nContacting {:?} over ESNI\n", domain); let dns_config = ResolverConfig::cloudflare_https(); @@ -30,7 +30,7 @@ fn main() { let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap(); let mut sess = rustls::ClientSession::new_with_esni(&Arc::new(config), dns_name, esni_hs); - let mut sock = TcpStream::connect(domain.to_owned() + ":443").unwrap(); + let mut sock = TcpStream::connect(domain.to_owned() + ":8443").unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); let host_header = format!("Host: {}\r\n", domain); let mut headers = String::new(); @@ -56,7 +56,10 @@ fn main() { println!("read bytes: {}", success); }, Err(e) => { + stdout().write_all(&plaintext).unwrap(); + println!("failure to read the bytes: {:?}", e); + return; } } diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 747ecf46c24..e035cc54037 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -237,16 +237,15 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, let esni_ext = ClientExtension::make_esni(handshake.dns_name.as_ref(), esni, ks_bytes, &handshake.randoms); if let Some(ext) = esni_ext { exts.push(ext); - //exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } - // TODO: what if ESNI fails? + + // TODO: what if ESNI fails? } } // TODO: what if ESNI is configured but there's no ESNI record? } else if sess.config.enable_sni { - println!("REGULAR SNI..."); exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); } diff --git a/rustls/src/client/mod.rs b/rustls/src/client/mod.rs index 9af03e66b4a..25c49f0cd59 100644 --- a/rustls/src/client/mod.rs +++ b/rustls/src/client/mod.rs @@ -614,7 +614,7 @@ impl ClientSession { /// encrypt the hostname in the ClientHello. pub fn new_with_esni(config: &Arc, hostname: webpki::DNSNameRef, esni: ESNIHandshakeData) -> ClientSession { let mut imp = ClientSessionImpl::new(config); - imp.start_handshake(hostname.into(), Some(esni),vec![]); + imp.start_handshake(hostname.into(), Some(esni), vec![]); ClientSession { imp } } diff --git a/rustls/src/esni.rs b/rustls/src/esni.rs index 06f4cc9266e..0152b52a0a6 100644 --- a/rustls/src/esni.rs +++ b/rustls/src/esni.rs @@ -456,4 +456,4 @@ mod tests { assert_eq!(expected.len(), encrypted.len()); assert!(crate::msgs::handshake::slice_eq(&expected, encrypted.as_slice())); } -} \ No newline at end of file +}