mirror of https://github.com/ctz/rustls
Test 0-RTT
This has some api tests, but mainly enables bogo tests. In passing, ensure TLS1.3 NewSessionTicket checks for duplicated extensions.
This commit is contained in:
parent
725870a480
commit
a80da9967b
|
@ -74,9 +74,6 @@
|
|||
"KeyUpdate-FromClient": "not implemented (no API yet)",
|
||||
"KeyUpdate-FromServer": "",
|
||||
"ExportTrafficSecrets-*": "",
|
||||
"SkipEarlyData*": "no 0rtt support",
|
||||
"TLS13-DuplicateTicketEarlyDataInfo": "",
|
||||
"TLS13-DuplicateTicketEarlyDataSupport": "",
|
||||
"*-InvalidSignature-*-SHA1-*": "no sha1",
|
||||
"NoCommonCurves": "nothing to fall back to",
|
||||
"ClientHelloPadding": "hello padding extension not implemented",
|
||||
|
@ -160,6 +157,8 @@
|
|||
"TrailingMessageData-TLS13-ServerFinished-TLS": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
|
||||
"TrailingMessageData-TLS13-ClientCertificate-TLS": ":BAD_HANDSHAKE_MSG:",
|
||||
"TrailingMessageData-TLS13-ClientCertificateVerify-TLS": ":BAD_HANDSHAKE_MSG:",
|
||||
"TrailingMessageData-TLS13-EndOfEarlyData-TLS": ":BAD_HANDSHAKE_MSG:",
|
||||
"Server-NonEmptyEndOfEarlyData-TLS13": ":BAD_HANDSHAKE_MSG:",
|
||||
"MissingKeyShare-Client-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"MissingKeyShare-Server-TLS13": ":INCOMPATIBLE:",
|
||||
"EmptyEncryptedExtensions-TLS13": ":BAD_HANDSHAKE_MSG:",
|
||||
|
@ -290,6 +289,7 @@
|
|||
"PartialClientFinishedWithClientHello-TLS12-Resume": ":PEER_MISBEHAVIOUR:",
|
||||
"PartialServerHelloWithHelloRetryRequest": ":PEER_MISBEHAVIOUR:",
|
||||
"PartialNewSessionTicketWithServerHelloDone": ":PEER_MISBEHAVIOUR:",
|
||||
"PartialEndOfEarlyDataWithClientHello": ":PEER_MISBEHAVIOUR:",
|
||||
"FragmentAcrossChangeCipherSpec-Client": ":PEER_MISBEHAVIOUR:",
|
||||
"FragmentAcrossChangeCipherSpec-Server-Packed": ":PEER_MISBEHAVIOUR:",
|
||||
"FragmentAcrossChangeCipherSpec-Client-Resume-Packed": ":PEER_MISBEHAVIOUR:",
|
||||
|
@ -340,6 +340,7 @@
|
|||
"SendServerHelloAsHelloRetryRequest": ":BAD_HANDSHAKE_MSG:",
|
||||
"TLS13-OnlyPadding": ":PEER_MISBEHAVIOUR:",
|
||||
"TLS13-EmptyRecords": ":PEER_MISBEHAVIOUR:",
|
||||
"TLS13-DuplicateTicketEarlyDataSupport": ":PEER_MISBEHAVIOUR:",
|
||||
"SupportedVersionSelection-TLS12": ":PEER_MISBEHAVIOUR:",
|
||||
"HelloRetryRequest-CipherChange-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"CurveTest-Client-Compressed-P-256-TLS12": ":PEER_MISBEHAVIOUR:",
|
||||
|
@ -364,7 +365,13 @@
|
|||
"TooManyChangeCipherSpec-Server-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"EarlyData-CipherMismatch-Client-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"EarlyDataWithoutResume-Client-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"EarlyDataVersionDowngrade-Client-TLS13": ":PEER_MISBEHAVIOUR:"
|
||||
"EarlyDataVersionDowngrade-Client-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"EarlyData-SkipEndOfEarlyData-TLS13": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
|
||||
"SkipEarlyData-TooMuchData-TLS13": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
|
||||
"SkipEarlyData-HRR-FatalAlert-TLS13": ":HANDSHAKE_FAILURE:",
|
||||
"SkipEarlyData-HRR-Interleaved-TLS13": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
|
||||
"SkipEarlyData-HRR-TooMuchData-TLS13": ":UNEXPECTED_MESSAGE:",
|
||||
"SkipEarlyData-SecondClientHelloEarlyData-TLS13": ":PEER_MISBEHAVIOUR:"
|
||||
},
|
||||
"TestLocalErrorMap": {
|
||||
"SendServerHelloAsHelloRetryRequest": "remote error: error decoding message",
|
||||
|
@ -375,6 +382,8 @@
|
|||
"Downgrade-TLS10-Client": "tls: no cipher suite supported by both client and server",
|
||||
"Downgrade-TLS10-Server": "remote error: protocol version not supported",
|
||||
"TrailingDataWithFinished-Client-TLS13": "local error: bad record MAC",
|
||||
"TrailingDataWithFinished-Resume-Client-TLS13": "local error: bad record MAC"
|
||||
}
|
||||
"TrailingDataWithFinished-Resume-Client-TLS13": "local error: bad record MAC",
|
||||
"SkipEarlyData-SecondClientHelloEarlyData-TLS13": "remote error: illegal parameter"
|
||||
},
|
||||
"HalfRTTTickets": 0
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustls;
|
|||
use rustls::internal::msgs::enums::ProtocolVersion;
|
||||
use rustls::quic::{self, ClientQuicExt, QuicExt, ServerQuicExt};
|
||||
use rustls::server::ClientHello;
|
||||
use rustls::{ClientConnection, Connection};
|
||||
use rustls::{ClientConnection, Connection, ServerConnection};
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
|
@ -391,6 +391,11 @@ fn make_server_cfg(opts: &Options) -> Arc<rustls::ServerConfig> {
|
|||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
if opts.enable_early_data {
|
||||
// see kMaxEarlyDataAccepted in boringssl, which bogo validates
|
||||
cfg.max_early_data_size = 14336;
|
||||
}
|
||||
|
||||
Arc::new(cfg)
|
||||
}
|
||||
|
||||
|
@ -512,6 +517,9 @@ fn handle_err(err: rustls::Error) -> ! {
|
|||
Error::CorruptMessage => quit(":GARBAGE:"),
|
||||
Error::DecryptError => quit(":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"),
|
||||
Error::PeerIncompatibleError(_) => quit(":INCOMPATIBLE:"),
|
||||
Error::PeerMisbehavedError(s) if s == "too much early_data received" => {
|
||||
quit(":TOO_MUCH_READ_EARLY_DATA:")
|
||||
}
|
||||
Error::PeerMisbehavedError(_) => quit(":PEER_MISBEHAVIOUR:"),
|
||||
Error::NoCertificatesPresented => quit(":NO_CERTS:"),
|
||||
Error::AlertReceived(AlertDescription::UnexpectedMessage) => quit(":BAD_ALERT:"),
|
||||
|
@ -546,6 +554,13 @@ fn client(conn: &mut Connection) -> &mut ClientConnection {
|
|||
conn.try_into().unwrap()
|
||||
}
|
||||
|
||||
fn server(conn: &mut Connection) -> &mut ServerConnection {
|
||||
match conn {
|
||||
Connection::Server(s) => s,
|
||||
_ => panic!("Connection is not a ServerConnection"),
|
||||
}
|
||||
}
|
||||
|
||||
fn exec(opts: &Options, mut sess: Connection, count: usize) {
|
||||
let mut sent_message = false;
|
||||
|
||||
|
@ -593,6 +608,23 @@ fn exec(opts: &Options, mut sess: Connection, count: usize) {
|
|||
}
|
||||
}
|
||||
|
||||
if opts.server && opts.enable_early_data {
|
||||
if let Some(ref mut ed) = server(&mut sess).early_data() {
|
||||
let mut data = Vec::new();
|
||||
let data_len = ed
|
||||
.read_to_end(&mut data)
|
||||
.expect("cannot read early_data");
|
||||
|
||||
for b in data.iter_mut() {
|
||||
*b ^= 0xff;
|
||||
}
|
||||
|
||||
sess.writer()
|
||||
.write_all(&data[..data_len])
|
||||
.expect("cannot echo early_data in 1rtt data");
|
||||
}
|
||||
}
|
||||
|
||||
if !sess.is_handshaking() && opts.export_keying_material > 0 && !sent_exporter {
|
||||
let mut export = Vec::new();
|
||||
export.resize(opts.export_keying_material, 0u8);
|
||||
|
@ -633,7 +665,7 @@ fn exec(opts: &Options, mut sess: Connection, count: usize) {
|
|||
quench_writes = true;
|
||||
}
|
||||
|
||||
if opts.enable_early_data && !sess.is_handshaking() && count > 0 {
|
||||
if opts.enable_early_data && !opts.server && !sess.is_handshaking() && count > 0 {
|
||||
if opts.expect_accept_early_data && !client(&mut sess).is_early_data_accepted() {
|
||||
quit_err("Early data was not accepted, but we expect the opposite");
|
||||
} else if opts.expect_reject_early_data && client(&mut sess).is_early_data_accepted() {
|
||||
|
@ -882,8 +914,8 @@ fn main() {
|
|||
"-enable-signed-cert-timestamps" => {
|
||||
opts.send_sct = true;
|
||||
}
|
||||
"-enable-early-data" |
|
||||
"-on-resume-enable-early-data" => {
|
||||
"-enable-early-data" => {
|
||||
opts.tickets = false;
|
||||
opts.enable_early_data = true;
|
||||
}
|
||||
"-on-resume-shim-writes-first" => {
|
||||
|
@ -985,6 +1017,7 @@ fn main() {
|
|||
"-on-initial-tls13-variant" |
|
||||
"-on-initial-expect-curve-id" |
|
||||
"-on-resume-export-early-keying-material" |
|
||||
"-on-resume-enable-early-data" |
|
||||
"-export-early-keying-material" |
|
||||
"-handshake-twice" |
|
||||
"-on-resume-verify-fail" |
|
||||
|
@ -1006,11 +1039,6 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
if opts.enable_early_data && opts.server {
|
||||
println!("For now we only test client-side early data");
|
||||
process::exit(BOGO_NACK);
|
||||
}
|
||||
|
||||
println!("opts {:?}", opts);
|
||||
|
||||
let mut server_cfg = if opts.server {
|
||||
|
|
|
@ -976,6 +976,14 @@ impl ExpectTraffic {
|
|||
cx: &mut ClientContext<'_>,
|
||||
nst: &NewSessionTicketPayloadTLS13,
|
||||
) -> Result<(), Error> {
|
||||
if nst.has_duplicate_extension() {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::IllegalParameter);
|
||||
return Err(Error::PeerMisbehavedError(
|
||||
"peer sent duplicate NewSessionTicket extensions".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let handshake_hash = self.transcript.get_current_hash();
|
||||
let secret = self
|
||||
.key_schedule
|
||||
|
|
|
@ -2025,6 +2025,21 @@ impl NewSessionTicketPayloadTLS13 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn has_duplicate_extension(&self) -> bool {
|
||||
let mut seen = collections::HashSet::new();
|
||||
|
||||
for ext in &self.exts {
|
||||
let typ = ext.get_type().get_u16();
|
||||
|
||||
if seen.contains(&typ) {
|
||||
return true;
|
||||
}
|
||||
seen.insert(typ);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn find_extension(&self, ext: ExtensionType) -> Option<&NewSessionTicketExtension> {
|
||||
self.exts
|
||||
.iter()
|
||||
|
|
|
@ -214,11 +214,6 @@ impl Tls13ClientSessionValue {
|
|||
bytes
|
||||
}
|
||||
|
||||
// Test-only
|
||||
pub fn set_max_early_data_size(&mut self, new: u32) {
|
||||
self.max_early_data_size = new;
|
||||
}
|
||||
|
||||
pub fn max_early_data_size(&self) -> u32 {
|
||||
self.max_early_data_size
|
||||
}
|
||||
|
|
|
@ -639,6 +639,27 @@ impl EarlyDataState {
|
|||
}
|
||||
}
|
||||
|
||||
// these branches not reachable externally, unless something else goes wrong.
|
||||
#[test]
|
||||
fn test_read_in_new_state() {
|
||||
assert_eq!(
|
||||
format!("{:?}", EarlyDataState::default().read(&mut [0u8; 5])),
|
||||
"Err(Kind(BrokenPipe))"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "read_buf")]
|
||||
#[test]
|
||||
fn test_read_buf_in_new_state() {
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
EarlyDataState::default().read_buf(&mut io::ReadBuf::new(&mut [0u8; 5]))
|
||||
),
|
||||
"Err(Kind(BrokenPipe))"
|
||||
);
|
||||
}
|
||||
|
||||
/// State associated with a server connection.
|
||||
#[derive(Default)]
|
||||
pub struct ServerConnectionData {
|
||||
|
|
|
@ -1126,8 +1126,7 @@ impl ExpectFinished {
|
|||
(id, stateful_lifetime)
|
||||
};
|
||||
|
||||
let age_add = rand::random_u32()?; // nb, we don't do 0-RTT data, so whatever
|
||||
#[allow(unused_mut)]
|
||||
let age_add = rand::random_u32()?;
|
||||
let mut payload = NewSessionTicketPayloadTLS13::new(lifetime, age_add, nonce, ticket);
|
||||
|
||||
if config.max_early_data_size > 0 {
|
||||
|
|
|
@ -12,8 +12,6 @@ use std::sync::Arc;
|
|||
use std::sync::Mutex;
|
||||
|
||||
use rustls::client::ResolvesClientCert;
|
||||
use rustls::internal::msgs::codec::{Codec, Reader};
|
||||
use rustls::internal::msgs::persist::Tls13ClientSessionValue;
|
||||
#[cfg(feature = "quic")]
|
||||
use rustls::quic::{self, ClientQuicExt, QuicExt, ServerQuicExt};
|
||||
use rustls::server::{ClientHello, ResolvesServerCert};
|
||||
|
@ -2628,11 +2626,25 @@ fn key_log_for_tls13() {
|
|||
assert_eq!("SERVER_TRAFFIC_SECRET_0", client_resume_log[3].label);
|
||||
assert_eq!("EXPORTER_SECRET", client_resume_log[4].label);
|
||||
|
||||
assert_eq!(client_resume_log[0], server_resume_log[0]);
|
||||
assert_eq!(client_resume_log[1], server_resume_log[1]);
|
||||
assert_eq!(client_resume_log[2], server_resume_log[2]);
|
||||
assert_eq!(client_resume_log[3], server_resume_log[3]);
|
||||
assert_eq!(client_resume_log[4], server_resume_log[4]);
|
||||
assert_eq!(6, server_resume_log.len());
|
||||
assert_eq!("CLIENT_EARLY_TRAFFIC_SECRET", server_resume_log[0].label);
|
||||
assert_eq!(
|
||||
"CLIENT_HANDSHAKE_TRAFFIC_SECRET",
|
||||
server_resume_log[1].label
|
||||
);
|
||||
assert_eq!(
|
||||
"SERVER_HANDSHAKE_TRAFFIC_SECRET",
|
||||
server_resume_log[2].label
|
||||
);
|
||||
assert_eq!("CLIENT_TRAFFIC_SECRET_0", server_resume_log[3].label);
|
||||
assert_eq!("SERVER_TRAFFIC_SECRET_0", server_resume_log[4].label);
|
||||
assert_eq!("EXPORTER_SECRET", server_resume_log[5].label);
|
||||
|
||||
assert_eq!(client_resume_log[0], server_resume_log[1]);
|
||||
assert_eq!(client_resume_log[1], server_resume_log[2]);
|
||||
assert_eq!(client_resume_log[2], server_resume_log[3]);
|
||||
assert_eq!(client_resume_log[3], server_resume_log[4]);
|
||||
assert_eq!(client_resume_log[4], server_resume_log[5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3066,53 +3078,24 @@ fn early_data_not_available() {
|
|||
assert!(client.early_data().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn early_data_is_available_on_resumption() {
|
||||
fn early_data_configs() -> (Arc<ClientConfig>, Arc<ServerConfig>) {
|
||||
let kt = KeyType::Rsa;
|
||||
let mut client_config = make_client_config(kt);
|
||||
client_config.enable_early_data = true;
|
||||
client_config.session_storage = Arc::new(ClientStorage::new());
|
||||
|
||||
let storage = Arc::new(ClientStorage::new());
|
||||
client_config.session_storage = storage.clone();
|
||||
let mut server_config = make_server_config(kt);
|
||||
server_config.max_early_data_size = 1234;
|
||||
(Arc::new(client_config), Arc::new(server_config))
|
||||
}
|
||||
|
||||
let client_config = Arc::new(client_config);
|
||||
#[test]
|
||||
fn early_data_is_available_on_resumption() {
|
||||
let (client_config, server_config) = early_data_configs();
|
||||
|
||||
let server_config = Arc::new(make_server_config(kt));
|
||||
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
|
||||
do_handshake(&mut client, &mut server);
|
||||
|
||||
/* discover the session data in the storage, and edit it to fool the
|
||||
* client on resumption that the server supports 0rtt. */
|
||||
let session_key = storage
|
||||
.last_put_key
|
||||
.lock()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.unwrap();
|
||||
|
||||
let session_value_bytes = storage
|
||||
.storage
|
||||
.get(&session_key)
|
||||
.unwrap();
|
||||
|
||||
let cs = CipherSuite::read_bytes(&session_value_bytes[..2]).unwrap();
|
||||
let suite = match ALL_CIPHER_SUITES
|
||||
.iter()
|
||||
.find(|supported| supported.suite() == cs)
|
||||
.unwrap()
|
||||
{
|
||||
#[cfg(feature = "tls12")]
|
||||
SupportedCipherSuite::Tls12(_) => unreachable!(),
|
||||
SupportedCipherSuite::Tls13(inner) => inner,
|
||||
};
|
||||
let mut reader = Reader::init(&session_value_bytes[2..]);
|
||||
let mut session_value = Tls13ClientSessionValue::read(suite, &mut reader).unwrap();
|
||||
session_value.set_max_early_data_size(128);
|
||||
|
||||
storage
|
||||
.storage
|
||||
.put(session_key, session_value.get_encoding());
|
||||
|
||||
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
|
||||
assert!(client.early_data().is_some());
|
||||
assert_eq!(
|
||||
|
@ -3120,7 +3103,7 @@ fn early_data_is_available_on_resumption() {
|
|||
.early_data()
|
||||
.unwrap()
|
||||
.bytes_left(),
|
||||
128
|
||||
1234
|
||||
);
|
||||
client
|
||||
.early_data()
|
||||
|
@ -3135,8 +3118,59 @@ fn early_data_is_available_on_resumption() {
|
|||
.unwrap(),
|
||||
5
|
||||
);
|
||||
let err = do_handshake_until_error(&mut client, &mut server);
|
||||
assert_eq!(err, Err(ErrorFromPeer::Server(Error::DecryptError)));
|
||||
do_handshake(&mut client, &mut server);
|
||||
|
||||
let mut received_early_data = [0u8; 5];
|
||||
assert_eq!(
|
||||
server
|
||||
.early_data()
|
||||
.expect("early_data didn't happen")
|
||||
.read(&mut received_early_data)
|
||||
.expect("early_data failed unexpectedly"),
|
||||
5
|
||||
);
|
||||
assert_eq!(&received_early_data[..], b"hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn early_data_not_available_on_server_before_client_hello() {
|
||||
let mut server = ServerConnection::new(Arc::new(make_server_config(KeyType::Rsa))).unwrap();
|
||||
assert!(server.early_data().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn early_data_can_be_rejected_by_server() {
|
||||
let (client_config, server_config) = early_data_configs();
|
||||
|
||||
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
|
||||
do_handshake(&mut client, &mut server);
|
||||
|
||||
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
|
||||
assert!(client.early_data().is_some());
|
||||
assert_eq!(
|
||||
client
|
||||
.early_data()
|
||||
.unwrap()
|
||||
.bytes_left(),
|
||||
1234
|
||||
);
|
||||
client
|
||||
.early_data()
|
||||
.unwrap()
|
||||
.flush()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client
|
||||
.early_data()
|
||||
.unwrap()
|
||||
.write(b"hello")
|
||||
.unwrap(),
|
||||
5
|
||||
);
|
||||
server.reject_early_data();
|
||||
do_handshake(&mut client, &mut server);
|
||||
|
||||
assert_eq!(client.is_early_data_accepted(), false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
|
|
Loading…
Reference in New Issue